HaniBlindBox/.kiro/specs/order-system-migration/design.md
2026-01-02 20:38:29 +08:00

487 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Design Document: 订单系统迁移
## Overview
本设计文档描述将PHP订单系统迁移到.NET 8的技术方案。订单系统是抽奖盲盒平台的核心业务模块涉及订单金额计算、订单创建支付、订单查询管理、仓库/盒柜管理等功能。
## Architecture
### 系统架构
```
┌─────────────────────────────────────────────────────────────┐
│ API Layer │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ OrderController │ │WarehouseController│ │
│ └────────┬────────┘ └────────┬────────┘ │
└───────────┼─────────────────────┼───────────────────────────┘
│ │
┌───────────┼─────────────────────┼───────────────────────────┐
│ ▼ ▼ Service Layer │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ OrderService │ │WarehouseService │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────┐ ┌────────┴────────┐ │
│ │OrderCalculation │ │ PrizeService │ │
│ │ Service │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
┌───────────┼─────────────────────┼───────────────────────────┐
│ ▼ ▼ Data Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HoneyBoxDbContext │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ │ │
│ │ │ Orders │ │OrderList│ │Delivery │ │ Recovery │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └───────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Components and Interfaces
### 1. OrderController
```csharp
[ApiController]
[Route("api")]
public class OrderController : ControllerBase
{
// 一番赏订单金额计算
[HttpPost("ordermoney")]
Task<ApiResponse<OrderCalculationDto>> CalculateOrderMoney(OrderMoneyRequest request);
// 一番赏订单创建
[HttpPost("orderbuy")]
Task<ApiResponse<OrderBuyResponseDto>> CreateOrder(OrderBuyRequest request);
// 无限赏订单金额计算
[HttpPost("infinite_ordermoney")]
Task<ApiResponse<OrderCalculationDto>> CalculateInfiniteOrderMoney(InfiniteOrderMoneyRequest request);
// 无限赏订单创建
[HttpPost("infinite_orderbuy")]
Task<ApiResponse<OrderBuyResponseDto>> CreateInfiniteOrder(InfiniteOrderBuyRequest request);
// 商城订单金额计算
[HttpPost("mall_ordermoney")]
Task<ApiResponse<OrderCalculationDto>> CalculateMallOrderMoney(MallOrderMoneyRequest request);
// 订单列表
[HttpPost("order_list")]
Task<ApiResponse<PageResponse<OrderListDto>>> GetOrderList(OrderListRequest request);
// 订单详情
[HttpPost("order_detail")]
Task<ApiResponse<OrderDetailDto>> GetOrderDetail(OrderDetailRequest request);
// 一番赏抽奖结果
[HttpPost("prizeorderlog")]
Task<ApiResponse<List<PrizeOrderLogDto>>> GetPrizeOrderLog(PrizeOrderLogRequest request);
// 无限赏抽奖结果
[HttpPost("infinite_prizeorderlog")]
Task<ApiResponse<List<PrizeOrderLogDto>>> GetInfinitePrizeOrderLog(PrizeOrderLogRequest request);
}
```
### 2. WarehouseController
```csharp
[ApiController]
[Route("api")]
public class WarehouseController : ControllerBase
{
// 仓库首页
[HttpPost("warehouse_index")]
Task<ApiResponse<PageResponse<WarehouseItemDto>>> GetWarehouseIndex(WarehouseIndexRequest request);
// 奖品回收
[HttpPost("warehouse_recovery")]
Task<ApiResponse<RecoveryResultDto>> RecoveryPrizes(RecoveryRequest request);
// 奖品发货
[HttpPost("warehouse_send")]
Task<ApiResponse<SendResultDto>> SendPrizes(SendRequest request);
// 确认发货
[HttpPost("warehouse_send_confirm")]
Task<ApiResponse> ConfirmSend(ConfirmSendRequest request);
// 发货记录
[HttpPost("warehouse_send_record")]
Task<ApiResponse<PageResponse<SendRecordDto>>> GetSendRecords(SendRecordRequest request);
// 发货记录详情
[HttpPost("warehouse_send_record_detail")]
Task<ApiResponse<SendRecordDetailDto>> GetSendRecordDetail(SendRecordDetailRequest request);
// 回收记录
[HttpPost("warehouse_recovery_record")]
Task<ApiResponse<PageResponse<RecoveryRecordDto>>> GetRecoveryRecords(RecoveryRecordRequest request);
// 物流信息
[HttpPost("warehouse_order_logistics")]
Task<ApiResponse<LogisticsDto>> GetLogistics(LogisticsRequest request);
}
```
### 3. IOrderService
```csharp
public interface IOrderService
{
// 订单金额计算
Task<OrderCalculationDto> CalculateOrderMoneyAsync(int userId, OrderMoneyRequest request);
Task<OrderCalculationDto> CalculateInfiniteOrderMoneyAsync(int userId, InfiniteOrderMoneyRequest request);
Task<OrderCalculationDto> CalculateMallOrderMoneyAsync(int userId, MallOrderMoneyRequest request);
// 订单创建
Task<OrderBuyResponseDto> CreateOrderAsync(int userId, OrderBuyRequest request);
Task<OrderBuyResponseDto> CreateInfiniteOrderAsync(int userId, InfiniteOrderBuyRequest request);
// 订单查询
Task<PageResponse<OrderListDto>> GetOrderListAsync(int userId, OrderListRequest request);
Task<OrderDetailDto> GetOrderDetailAsync(int userId, string orderNum);
// 抽奖结果
Task<List<PrizeOrderLogDto>> GetPrizeOrderLogAsync(int userId, string orderNum);
Task<List<PrizeOrderLogDto>> GetInfinitePrizeOrderLogAsync(int userId, string orderNum);
}
```
### 4. IWarehouseService
```csharp
public interface IWarehouseService
{
// 仓库查询
Task<PageResponse<WarehouseItemDto>> GetWarehouseIndexAsync(int userId, int page, int status);
// 奖品回收
Task<RecoveryResultDto> RecoveryPrizesAsync(int userId, string orderListIds);
// 奖品发货
Task<SendResultDto> SendPrizesAsync(int userId, SendRequest request);
Task<bool> ConfirmSendAsync(int userId, int id);
// 记录查询
Task<PageResponse<SendRecordDto>> GetSendRecordsAsync(int userId, int page);
Task<SendRecordDetailDto> GetSendRecordDetailAsync(int userId, int id);
Task<PageResponse<RecoveryRecordDto>> GetRecoveryRecordsAsync(int userId, int page);
// 物流查询
Task<LogisticsDto> GetLogisticsAsync(int userId, int id);
}
```
## Data Models
### Request Models
```csharp
// 一番赏订单金额计算请求
public class OrderMoneyRequest
{
[JsonPropertyName("goods_id")]
public int GoodsId { get; set; }
[JsonPropertyName("num")]
public int Num { get; set; }
[JsonPropertyName("prize_num")]
public int PrizeNum { get; set; }
[JsonPropertyName("coupon_id")]
public string? CouponId { get; set; }
[JsonPropertyName("use_money_is")]
public int UseMoneyIs { get; set; } = 2;
[JsonPropertyName("use_integral_is")]
public int UseIntegralIs { get; set; } = 2;
[JsonPropertyName("use_money2_is")]
public int UseMoney2Is { get; set; } = 2;
}
// 无限赏订单金额计算请求
public class InfiniteOrderMoneyRequest
{
[JsonPropertyName("goods_id")]
public int GoodsId { get; set; }
[JsonPropertyName("prize_num")]
public int PrizeNum { get; set; }
[JsonPropertyName("use_money_is")]
public int UseMoneyIs { get; set; } = 2;
[JsonPropertyName("use_integral_is")]
public int UseIntegralIs { get; set; } = 2;
[JsonPropertyName("use_money2_is")]
public int UseMoney2Is { get; set; } = 2;
[JsonPropertyName("coupon_id")]
public string? CouponId { get; set; }
}
// 商城订单金额计算请求
public class MallOrderMoneyRequest
{
[JsonPropertyName("goods_id")]
public int GoodsId { get; set; }
[JsonPropertyName("prize_num")]
public int PrizeNum { get; set; }
[JsonPropertyName("goods_num")]
public int GoodsNum { get; set; }
[JsonPropertyName("use_money_is")]
public int UseMoneyIs { get; set; } = 2;
[JsonPropertyName("use_integral_is")]
public int UseIntegralIs { get; set; } = 2;
[JsonPropertyName("use_money2_is")]
public int UseMoney2Is { get; set; } = 2;
}
// 仓库首页请求
public class WarehouseIndexRequest
{
[JsonPropertyName("page")]
public int Page { get; set; } = 1;
[JsonPropertyName("status")]
public int Status { get; set; } = 0;
}
// 发货请求
public class SendRequest
{
[JsonPropertyName("order_list_ids")]
public string OrderListIds { get; set; } = string.Empty;
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
[JsonPropertyName("mobile")]
public string Mobile { get; set; } = string.Empty;
[JsonPropertyName("address")]
public string Address { get; set; } = string.Empty;
[JsonPropertyName("message")]
public string? Message { get; set; }
}
```
### Response Models
```csharp
// 订单金额计算响应
public class OrderCalculationDto
{
[JsonPropertyName("order_total")]
public string OrderTotal { get; set; } = "0.00";
[JsonPropertyName("price")]
public string Price { get; set; } = "0.00";
[JsonPropertyName("goods_info")]
public GoodsInfoDto? GoodsInfo { get; set; }
[JsonPropertyName("goodsExtend")]
public GoodsExtendDto? GoodsExtend { get; set; }
[JsonPropertyName("user_money")]
public string UserMoney { get; set; } = "0.00";
[JsonPropertyName("user_integral")]
public string UserIntegral { get; set; } = "0.00";
[JsonPropertyName("user_money2")]
public string UserMoney2 { get; set; } = "0.00";
}
// 订单创建响应
public class OrderBuyResponseDto
{
[JsonPropertyName("status")]
public int Status { get; set; }
[JsonPropertyName("order_num")]
public string OrderNum { get; set; } = string.Empty;
[JsonPropertyName("res")]
public WechatPayParamsDto? Res { get; set; }
}
// 微信支付参数
public class WechatPayParamsDto
{
[JsonPropertyName("appId")]
public string AppId { get; set; } = string.Empty;
[JsonPropertyName("timeStamp")]
public string TimeStamp { get; set; } = string.Empty;
[JsonPropertyName("nonceStr")]
public string NonceStr { get; set; } = string.Empty;
[JsonPropertyName("package")]
public string Package { get; set; } = string.Empty;
[JsonPropertyName("signType")]
public string SignType { get; set; } = "RSA";
[JsonPropertyName("paySign")]
public string PaySign { get; set; } = string.Empty;
}
// 订单列表项
public class OrderListDto
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("order_num")]
public string OrderNum { get; set; } = string.Empty;
[JsonPropertyName("goods_title")]
public string GoodsTitle { get; set; } = string.Empty;
[JsonPropertyName("goods_imgurl")]
public string GoodsImgUrl { get; set; } = string.Empty;
[JsonPropertyName("order_total")]
public string OrderTotal { get; set; } = "0.00";
[JsonPropertyName("price")]
public string Price { get; set; } = "0.00";
[JsonPropertyName("prize_num")]
public int PrizeNum { get; set; }
[JsonPropertyName("status")]
public int Status { get; set; }
[JsonPropertyName("addtime")]
public long AddTime { get; set; }
[JsonPropertyName("pay_time")]
public long? PayTime { get; set; }
}
// 仓库物品
public class WarehouseItemDto
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("goodslist_title")]
public string GoodsListTitle { get; set; } = string.Empty;
[JsonPropertyName("goodslist_imgurl")]
public string GoodsListImgUrl { get; set; } = string.Empty;
[JsonPropertyName("goodslist_price")]
public string GoodsListPrice { get; set; } = "0.00";
[JsonPropertyName("goodslist_money")]
public string GoodsListMoney { get; set; } = "0.00";
[JsonPropertyName("status")]
public int Status { get; set; }
[JsonPropertyName("addtime")]
public long AddTime { 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: 订单金额计算正确性
*For any* order calculation request with valid goods_id and prize_num, the calculated order_total should equal price * prize_num minus any applicable discounts (coupon, balance, integral, money2).
**Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5**
### Property 2: 订单创建数据完整性
*For any* successfully created order, the order record should contain all required fields (order_num, goods_id, user_id, order_total, status) and the order_num should be unique.
**Validates: Requirements 2.1, 2.2**
### Property 3: 仓库物品状态一致性
*For any* warehouse item, after recovery operation, the item status should be updated to recovered (status=1) and the user's balance should increase by the recovery amount.
**Validates: Requirements 11.2, 11.3**
### Property 4: 发货记录完整性
*For any* delivery request with valid order_list_ids and address info, a delivery record should be created with all items linked and status set to pending.
**Validates: Requirements 12.1, 12.2, 12.3, 12.4**
### Property 5: 订单列表分页正确性
*For any* order list request, the returned data should be correctly paginated and the last_page value should accurately reflect the total number of pages.
**Validates: Requirements 6.1, 6.3, 6.4**
## Error Handling
### 订单相关错误
| 错误场景 | 错误码 | 错误消息 |
|---------|--------|---------|
| 商品不存在 | 0 | 商品不存在 |
| 库存不足 | 0 | 库存不足 |
| 超出购买限制 | 0 | 超出购买限制 |
| 订单不存在 | 0 | 订单不存在 |
| 无权访问订单 | 0 | 无权访问该订单 |
| 优惠券无效 | 0 | 优惠券无效或已过期 |
### 仓库相关错误
| 错误场景 | 错误码 | 错误消息 |
|---------|--------|---------|
| 奖品不存在 | 0 | 奖品不存在 |
| 奖品不可回收 | 0 | 该奖品不可回收 |
| 奖品不可发货 | 0 | 该奖品不可发货 |
| 地址信息不完整 | 0 | 请填写完整的收货信息 |
| 发货记录不存在 | 0 | 发货记录不存在 |
## Testing Strategy
### 单元测试
- 测试订单金额计算逻辑的各种场景
- 测试优惠券、余额、积分抵扣逻辑
- 测试仓库物品状态转换逻辑
- 测试分页逻辑
### 属性测试
使用xUnit和FsCheck进行属性测试
- Property 1: 订单金额计算正确性
- Property 2: 订单创建数据完整性
- Property 3: 仓库物品状态一致性
- Property 4: 发货记录完整性
- Property 5: 订单列表分页正确性
### 集成测试
- 测试完整的订单创建流程
- 测试仓库回收和发货流程
- 测试API响应格式兼容性