## 后端功能概览 > 本文档说明当前后端(Backend)已完成的主要功能与技术实现,便于后续开发和前后端联调。 > 最后更新时间:2026-02-11 --- ## 一、整体技术栈与架构 ### 1.1 技术栈 - **.NET 10** - 最新的 .NET 运行时 - **EF Core + SQLite** - 数据持久化(已从 PostgreSQL 迁移到 SQLite) - **CAP + NetCorePal** - 分布式事务与集成事件框架 - **FastEndpoints** - 高性能 API 端点框架 - **Hangfire** - 后台任务调度 - **Redis** - 分布式缓存与 CAP 消息存储 - **MediatR** - CQRS 模式实现 - **Swashbuckle/Swagger** - API 文档 ### 1.2 架构风格 - **CleanDDD 分层结构**:Domain / Infrastructure / Web - **聚合根**:统一继承 `Entity, IAggregateRoot` - **强类型 ID**:所有实体 ID 使用 `xxxId : IGuidStronglyTypedId` - **领域事件 + 集成事件**:解耦跨聚合、跨事务操作 - **CQRS**:命令(Command)与查询(Query)分离,命令处理器操作仓储,查询直接访问 DbContext - **最终一致性**:通过领域事件 → 集成事件 → 事件处理器实现 --- ## 二、已完成的核心聚合与领域模型 ### 2.1 会员聚合 (MemberAggregate) - **实体**:`Member` - **核心字段**: - 手机号、密码(加密)、昵称 - 累计总积分 `TotalPoints` - 可用积分 `AvailablePoints` - 状态(正常/禁用)、注册时间 - **值对象**: - `MemberLevel`:等级编码、名称、所需积分、积分倍率 - **领域行为**: - `AddPoints(amount, source, reason, relatedId, expiryDate)` - 增加积分 - `ConsumePoints(amount, reason, orderId)` - 消费积分 - `RefundPoints(amount, reason, orderId)` - 退还积分 - **领域事件**: - `PointsAddedDomainEvent` - `PointsConsumedDomainEvent` - `PointsRefundedDomainEvent` ### 2.2 营销码聚合 (MarketingCodeAggregate) - **实体**:`MarketingCode` - **核心字段**: - 营销码 `Code`(唯一) - 批次号 `BatchNo` - 是否已使用 `IsUsed` - 使用者会员ID、使用时间 - 过期时间 - **值对象**: - `ProductInfo`:产品ID、产品名称、品类ID、品类名称 - **领域行为**: - `Use(memberId)` - 使用营销码 - **领域事件**: - `MarketingCodeUsedDomainEvent` ### 2.3 积分规则聚合 (PointsRuleAggregate) - **实体**:`PointsRule` - **核心字段**: - 规则名称、规则类型(产品/时间/会员等级) - 积分值、奖励倍数 - 产品ID、品类ID、会员等级编码 - 生效时间范围(开始/结束) - 是否激活 - **领域行为**: - `CalculatePoints(memberBonusRate)` - 计算应得积分 ### 2.4 积分交易聚合 (PointsTransactionAggregate) - **实体**:`PointsTransaction` - **核心字段**: - 会员ID、交易类型(获取/消费/退还/过期) - 积分数量、来源、原因描述 - 关联ID(营销码ID/订单ID等) - 过期时间(仅获取类型有效) - 创建时间 - **静态工厂方法**: - `CreateEarnTransaction(...)` - 创建获取积分记录 - `CreateConsumeTransaction(...)` - 创建消费积分记录 - `CreateRefundTransaction(...)` - 创建退还积分记录 - `CreateExpireTransaction(...)` - 创建过期积分记录 - **说明**:通过集成事件异步创建,与会员聚合解耦 ### 2.5 礼品聚合 (GiftAggregate) - **实体**:`Gift` - **核心字段**: - 礼品名称、类型(实物/虚拟/自有产品) - 描述、图片URL - 所需积分、总库存、可用库存 - 每人限兑数量、是否上架、排序 - 创建/更新时间 - **领域行为**: - `Update(...)` - 更新礼品信息 - `AddStock(quantity)` - 增加库存 - `ReserveStock(quantity)` - 预留库存(下单时) - `DeductStock(quantity)` - 扣减总库存(发货时) - `ReleaseStock(quantity)` - 释放库存(订单取消时) - `PutOnShelf()` / `PutOffShelf()` - 上架/下架 - **领域事件**: - `GiftCreatedDomainEvent` - `GiftUpdatedDomainEvent` - `GiftStockChangedDomainEvent` ### 2.6 兑换订单聚合 (RedemptionOrderAggregate) - **实体**:`RedemptionOrder` - **核心字段**: - 订单号、会员ID、礼品ID/名称/类型 - 数量、消耗积分 - 物流单号、取消原因 - 状态(待处理/已发货/已送达/已完成/已取消) - 创建/更新时间 - **值对象**: - `Address`:收货人姓名、电话、省市区、详细地址(实物礼品必填) - **领域行为**: - `MarkAsDispatched(trackingNo)` - 标记为已发货 - `MarkAsDelivered()` - 标记为已送达 - `Complete()` - 完成订单 - `Cancel(reason)` - 取消订单 - **领域事件**: - `RedemptionOrderCreatedDomainEvent` - `RedemptionOrderDispatchedDomainEvent` - `RedemptionOrderDeliveredDomainEvent` - `RedemptionOrderCompletedDomainEvent` - `RedemptionOrderCancelledDomainEvent` --- ## 三、核心业务流程 ### 3.1 会员注册与登录 #### 会员注册 - **端点**:`POST /api/members/register` - **请求**:`RegisterMemberRequest { Phone, Password, Nickname? }` - **命令**:`RegisterMemberCommand` - **流程**: 1. 校验手机号唯一性 2. 密码加密存储 3. 创建会员并设置初始等级 4. 返回会员ID #### 会员登录 - **端点**:`POST /api/members/login` - **请求**:`LoginMemberRequest { Phone, Password }` - **命令**:`LoginMemberCommand` - **流程**: 1. 验证手机号和密码 2. 返回会员信息和 Token(TODO: 完善 JWT 认证) ### 3.2 扫码获取积分流程 #### 使用营销码 - **端点**:`POST /api/marketing-codes/scan` - **请求**:`UseMarketingCodeRequest { Code, MemberId }` - **命令**:`UseMarketingCodeCommand` #### 完整流程 ``` 1. UseMarketingCodeCommand ├─ 校验营销码存在且未使用 ├─ 校验未过期 ├─ 关联会员并标记为已使用 └─ 发布 MarketingCodeUsedDomainEvent 2. MarketingCodeUsedDomainEvent └─ MarketingCodeUsedDomainEventHandlerForEarnPoints ├─ 加载会员聚合 ├─ 匹配积分规则(IPointsRuleRepository.GetEffectiveRulesAsync) ├─ 计算应得积分和过期时间 ├─ 调用 Member.AddPoints(...) ├─ 更新会员聚合 └─ 发布 PointsAddedDomainEvent 3. PointsAddedDomainEvent └─ PointsAddedToPointsEarnedConverter (集成事件转换器) └─ 发布 PointsEarnedIntegrationEvent 4. PointsEarnedIntegrationEvent └─ PointsEarnedIntegrationEventHandler ├─ 幂等性检查(基于 RelatedId) ├─ 创建 PointsTransaction.CreateEarnTransaction(...) └─ 保存到积分交易表 ``` ### 3.3 礼品兑换流程 #### 创建兑换订单 - **端点**:`POST /api/gifts/redeem` - **请求**:`CreateRedemptionOrderCommand { MemberId, GiftId, Quantity, ShippingAddress? }` #### 完整流程 ``` 1. CreateRedemptionOrderCommand ├─ 校验礼品存在且已上架 ├─ 校验库存充足 ├─ 校验限兑数量(IRedemptionOrderRepository.GetMemberRedemptionCountAsync) ├─ 校验会员积分充足 ├─ 调用 Member.ConsumePoints(...) 扣减积分 ├─ 更新会员聚合 → 发布 PointsConsumedDomainEvent ├─ 构造收货地址(实物礼品必填) ├─ 创建 RedemptionOrder 聚合 └─ 发布 RedemptionOrderCreatedDomainEvent 2. PointsConsumedDomainEvent └─ PointsConsumedToPointsConsumedIntegrationEventConverter └─ 发布 PointsConsumedIntegrationEvent └─ 创建消费积分交易记录 3. RedemptionOrderCreatedDomainEvent └─ RedemptionOrderCreatedDomainEventHandler ├─ 加载礼品聚合 ├─ 调用 Gift.ReserveStock(quantity) └─ 更新礼品聚合(预留库存) ``` #### 订单状态流转 ``` 订单发货: POST /api/admin/redemption-orders/{OrderId}/dispatch ├─ MarkOrderAsDispatchedCommand ├─ Order.MarkAsDispatched(trackingNo) ├─ 发布 RedemptionOrderDispatchedDomainEvent └─ RedemptionOrderDispatchedDomainEventHandler └─ Gift.DeductStock(quantity) 扣减总库存 订单取消: POST /api/admin/redemption-orders/{OrderId}/cancel ├─ CancelOrderCommand ├─ Order.Cancel(reason) ├─ 发布 RedemptionOrderCancelledDomainEvent ├─ RedemptionOrderCancelledDomainEventHandler │ └─ Gift.ReleaseStock(quantity) 释放库存 └─ 退还积分(TODO: 待实现) ``` --- ## 四、积分交易与集成事件架构 ### 4.1 领域事件与集成事件流转 #### 领域事件 (Domain Events) 在会员聚合中发布,用于触发同一事务内或跨事务的业务逻辑: - `PointsAddedDomainEvent` - 会员增加积分时 - `PointsConsumedDomainEvent` - 会员消费积分时 - `PointsRefundedDomainEvent` - 积分退还时 - `PointsExpiredDomainEvent` - 积分过期时 #### 集成事件 (Integration Events) 实现 `IIntegrationEvent` 接口,通过 CAP 框架发布和消费: - `PointsEarnedIntegrationEvent` - 对应 Earn 交易 - `PointsConsumedIntegrationEvent` - 对应 Consume 交易 - `PointsRefundedIntegrationEvent` - 对应 Refund 交易 - `PointsExpiredIntegrationEvent` - 对应 Expire 交易 #### 集成事件转换器 (IIntegrationEventConverter) 将领域事件转换为集成事件: - `PointsAddedToPointsEarnedConverter` - `PointsConsumedToPointsConsumedIntegrationEventConverter` - `PointsRefundedToPointsRefundedIntegrationEventConverter` - `PointsExpiredToPointsExpiredIntegrationEventConverter` ### 4.2 集成事件处理器 位于 Web 层,通过 `ApplicationDbContext` 操作积分交易表: ```csharp // 示例:处理获取积分集成事件 public class PointsEarnedIntegrationEventHandler : IIntegrationEventHandler { public async Task Handle(PointsEarnedIntegrationEvent @event, CancellationToken cancellationToken) { // 1. 幂等性检查:基于 RelatedId 查询是否已存在 var exists = await dbContext.PointsTransactions .AnyAsync(x => x.RelatedId == @event.RelatedId, cancellationToken); if (exists) return; // 已处理过,跳过 // 2. 创建积分交易记录 var transaction = PointsTransaction.CreateEarnTransaction( memberId: new MemberId(@event.MemberId), amount: @event.Amount, source: @event.Source, reason: @event.Reason, relatedId: @event.RelatedId, expiryDate: @event.ExpiryDate ); // 3. 保存到数据库 await dbContext.PointsTransactions.AddAsync(transaction, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); } } ``` ### 4.3 事务与一致性保证 #### 最终一致性 - **会员积分变更**(会员聚合)和 **积分交易记录**(PointsTransaction 聚合)处于**不同事务** - 通过 **领域事件 → 集成事件 → 事件处理器** 链路实现最终一致性 - CAP 框架保证集成事件至少一次送达 #### 幂等性保证 - 基于 `RelatedId`(营销码ID、订单ID等)进行幂等性检查 - 防止重复消费导致的数据不一致 --- ## 五、API 端点一览 ### 5.1 会员模块 (Members) | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/members/register` | POST | 会员注册 | 匿名 | | `/api/members/login` | POST | 会员登录 | 匿名 | | `/api/members/{MemberId}` | GET | 获取会员信息 | 需要 | ### 5.2 营销码模块 (MarketingCodes) | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/marketing-codes/scan` | POST | 扫码使用营销码 | 匿名 | | `/api/admin/marketing-codes/generate` | POST | 批量生成营销码(管理端) | 需要 | ### 5.3 礼品模块 (Gifts) #### 会员端 | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/gifts` | GET | 获取上架礼品列表 | 匿名 | | `/api/gifts/{GiftId}` | GET | 获取礼品详情 | 匿名 | | `/api/gifts/redeem` | POST | 兑换礼品 | 需要 | #### 管理端 | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/admin/gifts` | POST | 创建礼品 | 需要 | | `/api/admin/gifts/{GiftId}` | PUT | 更新礼品信息 | 需要 | | `/api/admin/gifts/{GiftId}/putonshelf` | POST | 上架礼品 | 需要 | | `/api/admin/gifts/{GiftId}/putoffshelf` | POST | 下架礼品 | 需要 | | `/api/admin/gifts/{GiftId}/addstock` | POST | 增加库存 | 需要 | | `/api/admin/gifts` | GET | 获取礼品列表(支持筛选) | 需要 | | `/api/admin/gifts/{GiftId}` | GET | 获取礼品详情 | 需要 | ### 5.4 兑换订单模块 (RedemptionOrders) #### 会员端 | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/redemption-orders/my` | GET | 获取我的兑换订单列表 | 需要 | | `/api/redemption-orders/{OrderId}` | GET | 获取订单详情 | 需要 | #### 管理端 | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/admin/redemption-orders` | GET | 获取兑换订单列表 | 需要 | | `/api/admin/redemption-orders/{OrderId}` | GET | 获取订单详情 | 需要 | | `/api/admin/redemption-orders/{OrderId}/dispatch` | POST | 标记为已发货 | 需要 | | `/api/admin/redemption-orders/{OrderId}/complete` | POST | 完成订单 | 需要 | | `/api/admin/redemption-orders/{OrderId}/cancel` | POST | 取消订单 | 需要 | ### 5.5 积分规则模块 (PointsRules) | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/admin/points-rules` | POST | 创建积分规则(管理端) | 需要 | ### 5.6 通用端点 | 端点 | 方法 | 说明 | 认证 | |------|------|------|------| | `/api/hello` | GET | 健康检查 | 匿名 | | `/swagger` | GET | API 文档 | 匿名 | **注意**: - 所有端点返回统一的 `ResponseData` 格式 - 强类型ID(如 `MemberId`, `GiftId`)在 Swagger 中映射为字符串类型 - 当前 JWT 认证尚未完全实现,部分端点暂时从请求参数获取 MemberId --- ## 六、数据库设计 ### 6.1 数据库信息 - **数据库类型**:SQLite - **数据库文件**:`app.db`(开发环境) - **迁移工具**:EF Core Migrations - **最新迁移**:`20260211044819_Init` ### 6.2 核心数据表 #### Members - 会员表 主要字段:Id, Phone(唯一), Password, Nickname, Status, TotalPoints, AvailablePoints, LevelCode, LevelName, BonusRate, RequiredPoints, RegisteredAt #### MarketingCodes - 营销码表 主要字段:Id, Code(唯一), BatchNo, IsUsed, UsedByMemberId, UsedAt, ExpiryDate, ProductId, ProductName, CategoryId, CategoryName #### PointsRules - 积分规则表 主要字段:Id, RuleName, RuleType, PointsValue, BonusMultiplier, ProductId, CategoryId, MemberLevelCode, StartDate, EndDate, IsActive #### PointsTransactions - 积分交易表 主要字段:Id, MemberId, Type, Amount, Source, Reason, RelatedId, ExpiryDate, CreatedAt **重要索引**: - `IX_PointsTransactions_MemberId` - 会员积分查询 - `IX_PointsTransactions_RelatedId` - 幂等性检查(非唯一) - `IX_PointsTransactions_Type` - 按类型筛选 - `IX_PointsTransactions_CreatedAt` - 时间排序 #### Gifts - 礼品表 主要字段:Id, Name, Type, Description, ImageUrl, RequiredPoints, TotalStock, AvailableStock, LimitPerMember, IsOnShelf, SortOrder, CreatedAt, UpdatedAt #### RedemptionOrders - 兑换订单表 主要字段:Id, OrderNo(唯一), MemberId, GiftId, GiftName, GiftType, Quantity, ConsumedPoints, Status, TrackingNo, CancelReason, ReceiverName, ReceiverPhone, Province, City, District, DetailAddress, CreatedAt, UpdatedAt ### 6.3 数据表关系说明 - 所有表使用 GUID(Version 7)作为主键 - 使用软删除(`Deleted` 字段) - 使用行版本(`RowVersion`)实现乐观并发控制 - 积分交易表与会员表通过 MemberId 关联,但不设置外键(最终一致性) - 兑换订单表包含礼品快照信息,减少关联查询 --- ## 七、当前状态与后续建议 ### 7.1 当前状态 ✅ **已完成功能**: - 后端项目成功编译和运行(`dotnet build` & `dotnet run`) - 数据库迁移已修复(SQLite 切换完成,EF Core 模型与迁移一致) - Swagger API 文档正常生成(Schema 冲突已解决) - 会员、营销码、积分规则、积分交易、礼品、兑换订单等核心领域模型完整 - 扫码获取积分、礼品兑换等主要业务流程已打通 - 集成事件架构实现(领域事件 → 集成事件 → 事件处理器) ⚠️ **已知问题**: - 存在若干 Sonar 质量规则警告(TODO 注释、异常处理建议等),不影响运行 - JWT 认证尚未完全实现,部分端点暂时从请求参数获取 MemberId - 订单取消后的积分退还逻辑待完善 ### 7.2 后续建议 #### 短期优化 1. **完善认证鉴权** - 实现完整的 JWT Token 认证流程 - 从 Token 中获取当前登录会员ID - 添加基于角色的权限控制(会员端/管理端) 2. **补充查询功能** - 会员积分流水查询(分页、筛选) - 礼品库存统计和销售报表 - 订单统计和数据分析 3. **完善业务逻辑** - 实现订单取消后的积分退还 - 添加积分过期处理定时任务 - 完善会员等级自动升级逻辑 #### 长期规划 - 根据前端实际需求调整 API 结构和 DTO 字段 - 增加单元测试和集成测试覆盖率 - 性能优化和缓存策略 - 监控和日志完善 --- ## 八、开发与部署 ### 8.1 本地开发 ```bash # 启动基础设施(Redis) cd Backend/scripts ./init-infrastructure.sh # Linux/Mac ./init-infrastructure.ps1 # Windows # 运行后端 cd Backend/src/Fengling.Backend.Web dotnet run # 访问 Swagger 文档 http://localhost:5511/swagger ``` ### 8.2 数据库迁移 ```bash # 添加新迁移 cd Backend/src/Fengling.Backend.Infrastructure dotnet ef migrations add MigrationName # 应用迁移(自动在启动时执行) dotnet ef database update ``` ### 8.3 项目结构 ``` Backend/ ├── src/ │ ├── Fengling.Backend.Domain/ # 领域层:聚合、实体、值对象、领域事件 │ ├── Fengling.Backend.Infrastructure/ # 基础设施层:数据库、仓储实现 │ └── Fengling.Backend.Web/ # 应用层:API端点、命令查询、事件处理器 ├── test/ # 测试项目 └── scripts/ # 基础设施脚本