HaniBlindBox/.kiro/specs/goods-system-migration/design.md
2026-01-02 19:03:15 +08:00

366 lines
10 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的技术方案。商品系统是抽赏平台的核心模块需要实现商品列表、详情、箱号管理、收藏、奖品统计、中奖记录等功能同时保持与原PHP API的兼容性。
## Architecture
### 系统架构图
```mermaid
graph TB
subgraph "API Layer"
GC[GoodsController]
CC[CollectionController]
end
subgraph "Service Layer"
GS[GoodsService]
CS[CollectionService]
PS[PrizeService]
CacheS[GoodsCacheService]
end
subgraph "Data Layer"
DB[(MySQL Database)]
Redis[(Redis Cache)]
end
GC --> GS
GC --> PS
CC --> CS
GS --> CacheS
GS --> DB
CS --> DB
PS --> DB
CacheS --> Redis
```
### 数据流图
```mermaid
sequenceDiagram
participant Client
participant Controller
participant Service
participant Cache
participant Database
Client->>Controller: GET /goods
Controller->>Service: GetGoodsListAsync()
Service->>Cache: TryGetGoodsList()
alt Cache Hit
Cache-->>Service: Cached Data
else Cache Miss
Service->>Database: Query Goods
Database-->>Service: Goods Data
Service->>Cache: SetGoodsList()
end
Service-->>Controller: GoodsListDto
Controller-->>Client: JSON Response
```
## Components and Interfaces
### 1. GoodsService
```csharp
public interface IGoodsService
{
// 商品列表查询
Task<PagedResult<GoodsListDto>> GetGoodsListAsync(GoodsQueryRequest request, int userId);
// 商品详情查询
Task<GoodsDetailResponse> GetGoodsDetailAsync(int goodsId, int goodsNum, int userId);
// 商品子奖品查询
Task<List<GoodsChildrenDto>> GetGoodsChildrenAsync(int goodsId, int goodsNum, int goodsListId);
// 商品扩展配置查询
Task<GoodsExtendDto> GetGoodsExtendAsync(int goodsId, int goodsType);
// 箱号列表查询
Task<List<BoxGroupDto>> GetBoxListAsync(int goodsId);
// 箱号详情查询
Task<List<BoxDetailDto>> GetBoxDetailAsync(int goodsId, int pageNo, int sort);
}
```
### 2. CollectionService
```csharp
public interface ICollectionService
{
// 收藏/取消收藏
Task<bool> ToggleCollectionAsync(int userId, int goodsId, int goodsNum, string action);
// 收藏列表查询
Task<PagedResult<CollectionDto>> GetCollectionListAsync(int userId, int page, int limit);
// 检查收藏状态
Task<bool> IsCollectedAsync(int userId, int goodsId, int goodsNum);
}
```
### 3. PrizeService
```csharp
public interface IPrizeService
{
// 奖品数量统计
Task<PrizeCountResponse> GetPrizeCountAsync(int goodsId);
// 奖品内容查询
Task<PrizeContentResponse> GetPrizeContentAsync(int goodsId, int num);
// 中奖记录查询
Task<PrizeLogsResponse> GetPrizeLogsAsync(int goodsId, int goodsNum, int shangId, int page);
}
```
### 4. GoodsCacheService
```csharp
public interface IGoodsCacheService
{
// 获取商品参与次数
Task<int> GetJoinCountAsync(int goodsId);
// 更新商品参与次数
Task IncrementJoinCountAsync(int goodsId);
// 清除商品缓存
Task InvalidateGoodsCacheAsync(int goodsId);
}
```
## Data Models
### Request Models
```csharp
public class GoodsQueryRequest
{
public int Type { get; set; } = -1;
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 15;
}
public class GoodsDetailRequest
{
public int GoodsId { get; set; }
public int GoodsNum { get; set; } = 0;
}
public class BoxDetailRequest
{
public int GoodsId { get; set; }
public int PageNo { get; set; } = 0;
public int Sort { get; set; } = 0; // 0=箱号升序, 1=箱号降序, 2=余量降序
}
public class CollectionRequest
{
public int GoodsId { get; set; }
public int GoodsNum { get; set; }
public string Action { get; set; } // "add" or "remove"
}
public class PrizeLogsRequest
{
public int GoodsId { get; set; }
public int GoodsNum { get; set; }
public int ShangId { get; set; } = 0;
public int Page { get; set; } = 1;
}
```
### Response Models
```csharp
public class GoodsListDto
{
public int Id { get; set; }
public string Title { get; set; }
public string ImgUrl { get; set; }
public string Price { get; set; }
public int Type { get; set; }
public string TypeText { get; set; }
public int Stock { get; set; }
public int SaleStock { get; set; }
public int Status { get; set; }
public int LockIs { get; set; }
public int IsShouZhe { get; set; }
public int NewIs { get; set; }
public int JoinCount { get; set; }
public int NeedDrawNum { get; set; }
}
public class GoodsDetailResponse
{
public GoodsInfoDto Goods { get; set; }
public LockInfoDto LockInfo { get; set; }
public List<string> JoinUser { get; set; }
public int JoinCount { get; set; }
public List<GoodsListItemDto> GoodsList { get; set; }
public LimitInfoDto LimitInfo { get; set; }
}
public class GoodsListItemDto
{
public int Id { get; set; }
public int ShangId { get; set; }
public ShangInfoDto ShangInfo { get; set; }
public string Title { get; set; }
public int Stock { get; set; }
public int SurplusStock { get; set; }
public string ImgUrl { get; set; }
public int GoodsType { get; set; }
public string Price { get; set; }
public string ScMoney { get; set; }
public string SaleTime { get; set; }
public string Pro { get; set; }
public bool Children { get; set; }
}
public class BoxDetailDto
{
public int Num { get; set; }
public int SurplusAllStock { get; set; }
public List<BoxGoodsListDto> GoodsList { get; set; }
}
public class PrizeLogsResponse
{
public List<CategoryDto> Category { get; set; }
public List<PrizeLogDto> Data { get; set; }
public int LastPage { 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* goods query with type filter, the returned list SHALL only contain goods matching the specified type (or all goods if type=-1), with status=1, unlock_amount <= user consumption, sorted by sort DESC then id DESC.
**Validates: Requirements 1.1, 1.4, 1.5, 1.6**
### Property 2: 商品详情数据完整性
*For any* goods detail request, the response SHALL contain complete goods info, lock_info structure, join_user list, collection_is flag, and limitInfo structure with all required fields present.
**Validates: Requirements 2.1, 2.4, 2.5, 2.6, 2.7**
### Property 3: 奖品概率计算正确性
*For any* prize item in goods detail, the probability (pro) SHALL be calculated as (surplus_stock / total_surplus_stock * 100) and formatted correctly.
**Validates: Requirements 2.3, 3.2**
### Property 4: 子奖品数据完整性
*For any* child prize request, the response SHALL contain all child items with real_pro calculated and shang_info included for each item.
**Validates: Requirements 3.1, 3.2, 3.3**
### Property 5: 箱号管理数据正确性
*For any* box detail request, the response SHALL contain boxes with correct surplus_stock, support specified sort order, and include goodslist summary for each box.
**Validates: Requirements 5.1, 5.2, 5.3, 5.4**
### Property 6: 收藏操作往返一致性
*For any* collection add operation followed by collection list query, the added goods SHALL appear in the list; for any remove operation, the goods SHALL not appear in the list.
**Validates: Requirements 6.1, 6.2, 6.4**
### Property 7: 中奖记录数据完整性
*For any* prize logs request, the response SHALL contain paginated records with user_info, prize_info, shang_info, and addtime for each record, supporting shang_id filtering.
**Validates: Requirements 8.1, 8.2, 8.3, 8.4**
### Property 8: API响应格式一致性
*For any* API response, the structure SHALL contain status (int), msg (string), and data (object) fields consistent with PHP API format.
**Validates: Requirements 10.1, 10.3**
## Error Handling
### 错误码定义
| 错误码 | 描述 | 场景 |
|--------|------|------|
| 0 | 请求失败 | 通用错误 |
| 1 | 请求成功 | 成功响应 |
| -1 | 未登录 | Token无效或过期 |
| -2 | 商品不存在 | 商品ID无效 |
| -3 | 商品已下架 | 商品状态非1 |
| -4 | 已收藏 | 重复收藏 |
| -5 | 未收藏 | 取消未收藏的商品 |
### 异常处理策略
```csharp
public class GoodsNotFoundException : BusinessException
{
public GoodsNotFoundException(int goodsId)
: base(-2, $"商品不存在: {goodsId}") { }
}
public class GoodsOfflineException : BusinessException
{
public GoodsOfflineException(int goodsId)
: base(-3, $"商品已下架: {goodsId}") { }
}
public class AlreadyCollectedException : BusinessException
{
public AlreadyCollectedException()
: base(-4, "已收藏") { }
}
```
## Testing Strategy
### 单元测试
- 测试商品列表过滤逻辑
- 测试概率计算算法
- 测试箱号排序逻辑
- 测试收藏状态切换
### 属性测试
使用FsCheck或类似库进行属性测试
- **Property 1**: 商品列表过滤和排序 - 生成随机商品数据,验证过滤和排序正确性
- **Property 3**: 概率计算 - 生成随机库存数据,验证概率计算公式
- **Property 6**: 收藏往返 - 生成随机收藏操作,验证状态一致性
- **Property 8**: API格式 - 验证所有响应符合标准格式
### 集成测试
- 测试完整的商品查询流程
- 测试缓存命中和失效
- 测试与数据库的交互
### 测试配置
```csharp
// 属性测试配置
[Property(MaxTest = 100)]
public Property GoodsListFilteringProperty()
{
// 生成随机商品和查询条件
// 验证过滤结果正确性
}
```