29 KiB
Design Document: Admin Business Migration
Overview
本设计文档描述了 HoneyBox 后台管理系统业务模块迁移的技术架构和实现方案。核心目标是创建一个独立的 HoneyBox.Admin.Business 类库,将业务模块(系统配置、用户管理、商品管理、订单管理、财务管理、仪表盘)与基础后台管理框架解耦,实现业务模块的可插拔设计。
Architecture
整体架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ HoneyBox.Admin (Web API) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Program.cs │ │
│ │ - AddHoneyBoxAdmin() // 基础服务注册 │ │
│ │ - AddAdminBusiness() // 业务模块注册 (新增) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────┼─────────────────────────────────────┐ │
│ │ Controllers/ │ │ │
│ │ ├── AuthController.cs │ (基础控制器 - 已有) │ │
│ │ ├── MenuController.cs │ │ │
│ │ ├── RoleController.cs │ │ │
│ │ └── ... │ │ │
│ └─────────────────────────────────┼─────────────────────────────────────┘ │
└────────────────────────────────────┼────────────────────────────────────────┘
│ AddApplicationPart
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ HoneyBox.Admin.Business (Class Library) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Controllers/ │ │
│ │ ├── ConfigController.cs // 系统配置 │ │
│ │ ├── UserController.cs // 用户管理 │ │
│ │ ├── GoodsController.cs // 商品管理 │ │
│ │ ├── OrderController.cs // 订单管理 │ │
│ │ ├── FinanceController.cs // 财务管理 │ │
│ │ └── DashboardController.cs // 仪表盘 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Services/ │ │
│ │ ├── IConfigService.cs / ConfigService.cs │ │
│ │ ├── IUserBusinessService.cs / UserBusinessService.cs │ │
│ │ ├── IGoodsService.cs / GoodsService.cs │ │
│ │ ├── IOrderService.cs / OrderService.cs │ │
│ │ ├── IFinanceService.cs / FinanceService.cs │ │
│ │ └── IDashboardService.cs / DashboardService.cs │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Extensions/ │ │
│ │ └── ServiceCollectionExtensions.cs // AddAdminBusiness 扩展方法 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ 引用
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ HoneyBox.Model (Class Library) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Data/HoneyBoxDbContext.cs // 业务数据库上下文 │ │
│ │ Entities/ // 业务实体 (User, Goods, Order...) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
项目依赖关系
HoneyBox.Admin
├── HoneyBox.Admin.Business (业务模块)
└── (自包含基础功能)
HoneyBox.Admin.Business
└── HoneyBox.Model (业务实体和 DbContext)
Components and Interfaces
1. HoneyBox.Admin.Business 项目结构
HoneyBox.Admin.Business/
├── Controllers/
│ ├── ConfigController.cs
│ ├── UserController.cs
│ ├── GoodsController.cs
│ ├── OrderController.cs
│ ├── FinanceController.cs
│ └── DashboardController.cs
├── Services/
│ ├── Interfaces/
│ │ ├── IConfigService.cs
│ │ ├── IUserBusinessService.cs
│ │ ├── IGoodsService.cs
│ │ ├── IOrderService.cs
│ │ ├── IFinanceService.cs
│ │ └── IDashboardService.cs
│ ├── ConfigService.cs
│ ├── UserBusinessService.cs
│ ├── GoodsService.cs
│ ├── OrderService.cs
│ ├── FinanceService.cs
│ └── DashboardService.cs
├── Models/
│ ├── Config/
│ │ ├── ConfigDto.cs
│ │ └── ConfigUpdateRequest.cs
│ ├── User/
│ │ ├── UserListRequest.cs
│ │ ├── UserListResponse.cs
│ │ ├── UserDetailResponse.cs
│ │ └── UserMoneyChangeRequest.cs
│ ├── Goods/
│ │ ├── GoodsListRequest.cs
│ │ ├── GoodsListResponse.cs
│ │ ├── GoodsCreateRequest.cs
│ │ └── PrizeDto.cs
│ ├── Order/
│ │ ├── OrderListRequest.cs
│ │ ├── OrderListResponse.cs
│ │ └── ShipOrderRequest.cs
│ └── Finance/
│ ├── RankingListResponse.cs
│ └── FinanceDetailResponse.cs
├── Extensions/
│ └── ServiceCollectionExtensions.cs
└── HoneyBox.Admin.Business.csproj
2. 核心接口定义
IConfigService
public interface IConfigService
{
Task<T?> GetConfigAsync<T>(string key) where T : class;
Task<bool> UpdateConfigAsync<T>(string key, T config) where T : class;
Task<bool> ValidateConfigAsync(string key, object config);
}
IUserBusinessService
public interface IUserBusinessService
{
Task<PagedResult<UserListResponse>> GetUserListAsync(UserListRequest request);
Task<UserDetailResponse?> GetUserDetailAsync(long userId);
Task<bool> ChangeUserMoneyAsync(long userId, UserMoneyChangeRequest request);
Task<bool> SetUserStatusAsync(long userId, int status);
Task<bool> SetTestAccountAsync(long userId, int isTest);
Task<bool> ClearMobileAsync(long userId);
Task<bool> ClearWeChatAsync(long userId);
Task<bool> GiftCouponAsync(long userId, GiftCouponRequest request);
Task<bool> GiftCardAsync(long userId, GiftCardRequest request);
Task<PagedResult<VipLevelDto>> GetVipLevelsAsync();
Task<bool> UpdateVipLevelAsync(int id, VipLevelUpdateRequest request);
Task<UserProfitLossResponse> GetUserProfitLossAsync(long userId, DateRange? dateRange);
Task<PagedResult<UserListResponse>> GetSubordinateUsersAsync(long userId, int page, int pageSize);
}
IGoodsService
public interface IGoodsService
{
Task<PagedResult<GoodsListResponse>> GetGoodsListAsync(GoodsListRequest request);
Task<GoodsDetailResponse?> GetGoodsDetailAsync(long goodsId);
Task<long> CreateGoodsAsync(GoodsCreateRequest request);
Task<bool> UpdateGoodsAsync(long goodsId, GoodsUpdateRequest request);
Task<bool> DeleteGoodsAsync(long goodsId);
Task<bool> SetGoodsStatusAsync(long goodsId, int status);
Task<List<PrizeDto>> GetPrizesAsync(long goodsId);
Task<long> AddPrizeAsync(long goodsId, PrizeCreateRequest request);
Task<bool> UpdatePrizeAsync(long prizeId, PrizeUpdateRequest request);
Task<bool> DeletePrizeAsync(long prizeId);
Task<List<GoodsTypeDto>> GetGoodsTypesAsync();
}
IOrderService
public interface IOrderService
{
Task<PagedResult<OrderListResponse>> GetOrderListAsync(OrderListRequest request);
Task<OrderDetailResponse?> GetOrderDetailAsync(long orderId);
Task<PagedResult<OrderListResponse>> GetStuckOrdersAsync(OrderListRequest request);
Task<PagedResult<RecoveryOrderResponse>> GetRecoveryOrdersAsync(OrderListRequest request);
Task<PagedResult<ShippingOrderResponse>> GetShippingOrdersAsync(ShippingOrderListRequest request);
Task<bool> ShipOrderAsync(long orderId, ShipOrderRequest request);
Task<bool> CancelShippingOrderAsync(long orderId);
Task<byte[]> ExportOrdersAsync(OrderExportRequest request);
}
IFinanceService
public interface IFinanceService
{
Task<PagedResult<ConsumptionRankingResponse>> GetConsumptionRankingAsync(FinanceQueryRequest request);
Task<PagedResult<BalanceDetailResponse>> GetBalanceDetailsAsync(FinanceQueryRequest request);
Task<PagedResult<IntegralDetailResponse>> GetIntegralDetailsAsync(FinanceQueryRequest request);
Task<PagedResult<ScoreDetailResponse>> GetScoreDetailsAsync(FinanceQueryRequest request);
Task<PagedResult<RechargeRecordResponse>> GetRechargeRecordsAsync(FinanceQueryRequest request);
}
IDashboardService
public interface IDashboardService
{
Task<DashboardOverviewResponse> GetOverviewAsync();
Task<List<AdAccountDto>> GetAdAccountsAsync();
Task<long> CreateAdAccountAsync(AdAccountCreateRequest request);
}
Data Models
1. 请求/响应模型
通用分页请求
public class PagedRequest
{
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
public class PagedResult<T>
{
public List<T> List { get; set; } = new();
public int Total { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
用户管理模型
public class UserListRequest : PagedRequest
{
public long? UserId { get; set; }
public string? Mobile { get; set; }
public string? Nickname { get; set; }
public string? LastLoginIp { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public long? ParentId { get; set; }
}
public class UserListResponse
{
public long Id { get; set; }
public string? Uid { get; set; }
public string? Nickname { get; set; }
public string? Avatar { get; set; }
public string? Mobile { get; set; }
public decimal Balance { get; set; }
public decimal Integral { get; set; }
public decimal Diamond { get; set; }
public int VipLevel { get; set; }
public DateTime CreatedAt { get; set; }
public string? LastLoginIp { get; set; }
public int Status { get; set; }
public int IsTest { get; set; }
// 统计字段
public decimal TotalConsumption { get; set; }
public decimal BoxValue { get; set; }
public decimal WeChatPayment { get; set; }
public decimal BalancePayment { get; set; }
public decimal IntegralPayment { get; set; }
public decimal RecoveryAmount { get; set; }
public decimal ShippingValue { get; set; }
}
public class UserMoneyChangeRequest
{
public int Type { get; set; } // 1-余额 2-积分 3-钻石
public decimal Amount { get; set; }
public int Operation { get; set; } // 1-增加 2-扣除
public string? Remark { get; set; }
}
商品管理模型
public class GoodsListRequest : PagedRequest
{
public string? Title { get; set; }
public int? Status { get; set; }
public int? Type { get; set; }
}
public class GoodsListResponse
{
public long Id { get; set; }
public string Title { get; set; } = string.Empty;
public string ImgUrl { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Type { get; set; }
public string TypeName { get; set; } = string.Empty;
public int Status { get; set; }
public int Stock { get; set; }
public int SaleStock { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
public class GoodsCreateRequest
{
public string Title { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Type { get; set; }
public string ImgUrl { get; set; } = string.Empty;
public string ImgUrlDetail { get; set; } = string.Empty;
public int Stock { get; set; }
public int Sort { get; set; }
public int DailyLimit { get; set; }
public int LockIs { get; set; }
public int LockTime { get; set; }
public int IntegralIs { get; set; }
public int ShowIs { get; set; }
public int CouponIs { get; set; }
public int CouponPro { get; set; }
// 福利屋专用字段
public DateTime? FlwStartTime { get; set; }
public DateTime? FlwEndTime { get; set; }
public DateTime? OpenTime { get; set; }
public int? ChoujiangXianzhi { get; set; }
}
订单管理模型
public class OrderListRequest : PagedRequest
{
public long? UserId { get; set; }
public string? Mobile { get; set; }
public string? OrderNum { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? Status { get; set; }
}
public class OrderListResponse
{
public long Id { get; set; }
public string OrderNum { get; set; } = string.Empty;
public long UserId { get; set; }
public string? UserNickname { get; set; }
public string? UserMobile { get; set; }
public string? UserUid { get; set; }
public string GoodsTitle { get; set; } = string.Empty;
public int OrderType { get; set; }
public decimal OrderTotal { get; set; }
public decimal Discount { get; set; }
public decimal DiscountTotal { get; set; }
public decimal WeChatPayment { get; set; }
public decimal BalancePayment { get; set; }
public decimal IntegralPayment { get; set; }
public decimal ScorePayment { get; set; }
public int Status { get; set; }
public DateTime CreatedAt { get; set; }
}
public class ShipOrderRequest
{
public string CourierName { get; set; } = string.Empty;
public string CourierNumber { get; set; } = string.Empty;
}
2. 配置模型
public class WeixinPaySetting
{
public List<WeixinPayMerchant> Merchants { get; set; } = new();
}
public class WeixinPayMerchant
{
public string Name { get; set; } = string.Empty;
public string MchId { get; set; } = string.Empty;
public string OrderPrefix { get; set; } = string.Empty; // 必须3位
public string ApiKey { get; set; } = string.Empty;
public string CertPath { get; set; } = string.Empty;
}
public class MiniprogramSetting
{
public List<MiniprogramConfig> Miniprograms { get; set; } = new();
}
public class MiniprogramConfig
{
public string Name { get; set; } = string.Empty;
public string AppId { get; set; } = string.Empty;
public string AppSecret { get; set; } = string.Empty;
public string OrderPrefix { get; set; } = string.Empty; // 必须2位
public int IsDefault { get; set; }
public int MerchantIndex { get; set; }
}
public class H5Setting
{
public List<H5AppConfig> H5Apps { get; set; } = new();
}
public class H5AppConfig
{
public string Name { get; set; } = string.Empty;
public string OrderPrefix { get; set; } = string.Empty; // 必须2位
public int IsDefault { get; set; }
public int WxMerchantIndex { get; set; }
public int AliMerchantIndex { get; set; }
}
Correctness Properties
A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.
Property 1: Configuration Round-Trip Consistency
For any valid configuration object, saving it via UpdateConfigAsync and then retrieving it via GetConfigAsync should produce an equivalent object.
Validates: Requirements 3.1, 3.2
Property 2: Merchant Prefix Uniqueness Validation
For any weixinpay_setting configuration with duplicate merchant prefixes or prefixes not exactly 3 characters, the validation should fail and return an error.
Validates: Requirements 3.4
Property 3: Default App Validation
For any miniprogram_setting or h5_setting configuration without at least one default app, the validation should fail and return an error.
Validates: Requirements 3.5, 3.6
Property 4: User List Pagination Consistency
For any user list request with valid page and pageSize parameters, the returned list should contain at most pageSize items, and the total should reflect the actual count of matching users.
Validates: Requirements 4.1
Property 5: User List Filter Accuracy
For any user list request with filter parameters, all returned users should match all specified filter criteria.
Validates: Requirements 4.3
Property 6: User Balance Change Audit Trail
For any user balance/integral/diamond modification, a corresponding profit record should be created with the same amount and user ID.
Validates: Requirements 4.4
Property 7: User Status Toggle Consistency
For any user ban or unban operation, the user's status field should be updated to the expected value (disabled for ban, enabled for unban).
Validates: Requirements 4.5, 4.6
Property 8: Goods Stock Increase Prize Replication
For any goods stock increase operation, the number of prize configurations for new sets should equal the number of prize configurations in the first set.
Validates: Requirements 5.6
Property 9: Prize Code Uniqueness
For any prize added to a box, the generated prize_code should be unique within the entire goods_list table.
Validates: Requirements 5.8
Property 10: Order List Filter Accuracy
For any order list request with filter parameters, all returned orders should match all specified filter criteria.
Validates: Requirements 6.2
Property 11: Order Prize Grouping
For any order detail request, the returned prize list should be correctly grouped by prize_code with accurate counts.
Validates: Requirements 6.4
Property 12: Shipping Order Cancellation Inventory Restoration
For any shipping order cancellation, all prizes in the order should be restored to the user's inventory (status changed from shipped to available).
Validates: Requirements 6.6
Property 13: Consumption Ranking Sort Order
For any consumption ranking request, the returned users should be sorted by total consumption in descending order.
Validates: Requirements 7.1
Property 14: Financial Query Filter Accuracy
For any financial query (balance, integral, score, recharge) with filter parameters, all returned records should match all specified filter criteria.
Validates: Requirements 7.7
Property 15: Dashboard Statistics Accuracy
For any dashboard overview request, the returned statistics (today's registrations, consumption, new consumers) should match the actual counts from the database for the current day.
Validates: Requirements 8.1, 8.2, 8.3
Property 16: API Response Format Consistency
For any successful API call, the response should follow the format { code: 0, message: "success", data: {...} }. For any failed API call, the response should follow the format { code: error_code, message: "error description", data: null }.
Validates: Requirements 9.1, 9.2
Property 17: Paginated Response Format
For any paginated API request, the response data should contain list, total, page, and pageSize fields with correct values.
Validates: Requirements 9.3
Property 18: Authentication Enforcement
For any business API call without a valid JWT token, the system should return a 401 Unauthorized response.
Validates: Requirements 10.1
Property 19: Permission Enforcement
For any business API call where the user lacks the required permission, the system should return a 403 Forbidden response.
Validates: Requirements 10.2, 10.4
Error Handling
错误码定义
| 错误码 | 说明 | 场景 |
|---|---|---|
| 0 | 成功 | 所有成功响应 |
| 40001 | 认证失败 | Token 无效或过期 |
| 40101 | 权限不足 | 用户无权访问该资源 |
| 40201 | 参数验证失败 | 请求参数不符合要求 |
| 40401 | 资源不存在 | 请求的数据不存在 |
| 50001 | 服务器内部错误 | 未预期的异常 |
异常处理策略
- 业务异常 - 使用自定义 BusinessException,包含错误码和消息
- 验证异常 - 使用 FluentValidation 或 DataAnnotations,返回字段级错误
- 系统异常 - 通过全局异常过滤器捕获,记录日志并返回通用错误
public class BusinessException : Exception
{
public int Code { get; }
public BusinessException(int code, string message) : base(message)
{
Code = code;
}
}
// 全局异常过滤器
public class BusinessExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is BusinessException bex)
{
context.Result = new JsonResult(new ApiResponse
{
Code = bex.Code,
Message = bex.Message,
Data = null
});
context.ExceptionHandled = true;
}
}
}
Testing Strategy
测试框架选择
- 单元测试: xUnit + Moq
- 属性测试: FsCheck (C# 属性测试库)
- 集成测试: WebApplicationFactory
测试分层
-
单元测试 - 测试 Service 层的业务逻辑
- 配置验证逻辑
- 数据转换逻辑
- 业务规则验证
-
属性测试 - 验证通用属性
- 分页一致性
- 过滤准确性
- 数据完整性
-
集成测试 - 测试 API 端到端
- 认证流程
- 权限验证
- 数据库操作
属性测试配置
// 使用 FsCheck 进行属性测试
// 每个属性测试至少运行 100 次迭代
[Property(MaxTest = 100)]
public Property ConfigRoundTrip_ShouldPreserveData()
{
return Prop.ForAll<ValidConfigData>(config =>
{
// Feature: admin-business-migration, Property 1: Configuration Round-Trip Consistency
var saved = _configService.UpdateConfigAsync("test", config).Result;
var retrieved = _configService.GetConfigAsync<ValidConfigData>("test").Result;
return saved && config.Equals(retrieved);
});
}
测试覆盖要求
- 所有 Service 方法必须有单元测试
- 所有正确性属性必须有对应的属性测试
- 关键 API 端点必须有集成测试
- 边界条件和错误场景必须有测试覆盖
API Endpoints
系统配置 API
GET /api/admin/business/config/{key} # 获取配置
PUT /api/admin/business/config/{key} # 更新配置
用户管理 API
GET /api/admin/business/users # 用户列表
GET /api/admin/business/users/{id} # 用户详情
PUT /api/admin/business/users/{id}/status # 封号/解封
PUT /api/admin/business/users/{id}/money # 资金变动
PUT /api/admin/business/users/{id}/test # 设置测试账号
DELETE /api/admin/business/users/{id}/mobile # 清空手机号
DELETE /api/admin/business/users/{id}/wechat # 清空微信绑定
POST /api/admin/business/users/{id}/coupon # 赠送优惠券
POST /api/admin/business/users/{id}/card # 赠送卡牌
GET /api/admin/business/users/{id}/team # 下级用户
GET /api/admin/business/users/{id}/profit # 盈亏统计
GET /api/admin/business/vip # VIP等级列表
PUT /api/admin/business/vip/{id} # 编辑VIP等级
商品管理 API
GET /api/admin/business/goods # 盒子列表
GET /api/admin/business/goods/{id} # 盒子详情
POST /api/admin/business/goods # 新增盒子
PUT /api/admin/business/goods/{id} # 编辑盒子
DELETE /api/admin/business/goods/{id} # 删除盒子
PUT /api/admin/business/goods/{id}/status # 上架/下架
GET /api/admin/business/goods/{id}/prizes # 奖品列表
POST /api/admin/business/goods/{id}/prizes # 添加奖品
PUT /api/admin/business/prizes/{id} # 编辑奖品
DELETE /api/admin/business/prizes/{id} # 删除奖品
GET /api/admin/business/goods-types # 盒子类型列表
订单管理 API
GET /api/admin/business/orders # 订单列表
GET /api/admin/business/orders/{id} # 订单详情
GET /api/admin/business/orders/stuck # 卡单列表
GET /api/admin/business/orders/recovery # 兑换订单
GET /api/admin/business/orders/shipping # 发货订单
PUT /api/admin/business/orders/{id}/ship # 发货处理
DELETE /api/admin/business/orders/{id}/ship # 取消发货
GET /api/admin/business/orders/export # 导出订单
财务管理 API
GET /api/admin/business/finance/ranking # 消费排行
GET /api/admin/business/finance/balance # 余额明细
GET /api/admin/business/finance/integral # 积分明细
GET /api/admin/business/finance/score # 钻石明细
GET /api/admin/business/finance/recharge # 充值记录
仪表盘 API
GET /api/admin/business/dashboard # 仪表盘数据
GET /api/admin/business/dashboard/ads # 广告账户列表
POST /api/admin/business/dashboard/ads # 新增广告账户