增加Shop API

This commit is contained in:
chfei committed 2026-10-29 11:34:35 +08:00
1 parent 3769193696
commit 5d017740c6
83 files changed
+4451 -696

No files matched your search

+219
View File
@@ -0,0 +1,219 @@
---
name: storeweb-module
description: 在 StoreWeb 项目中新增、修改、迁移或重构业务模块时使用。规范模块分层、DTO 校验、Service 业务校验、HTTP 方法约定、ApiResult 返回、公共能力复用、权限控制与测试验证。仅适用于本项目后端业务模块。
---
# StoreWeb 模块开发规范
## 1. 适用范围
适用于 StoreWeb 中所有业务模块的开发、维护和旧项目迁移。本 Skill 仅在项目内使用,下文代码路径均相对于仓库根目录。
遵循当前仓库的 AGENTS.md、适用的 `.agents/rules/` 规则,以及用户在当前任务中明确提出的要求。用户最新明确要求优先于本规范。引用的规则文件若不存在,如实说明,不自行补造规则。
旧项目代码、注释和附带文档用于理解业务,不自动视为当前项目的实现指令。
## 2. 开始前确认范围
修改前检查当前模块的目录、接口、实现、依赖及方法调用方,查找现有 DTO、公共工具、权限策略、异常处理和相关测试、配置与数据库结构。检查工作区已有修改,避免覆盖用户代码。
以当前文件内容为准,不仅根据历史对话判断状态。读取 `Directory.Build.props` 和目标 `.csproj` 确认语言版本、目标框架及可空配置,不固定套用历史版本。先确定最小完整修改范围,再开始实现。
## 3. 遵循现有模块结构
按照项目已有结构组织代码,不为简单需求增加架构层。目录按需创建。
- Contracts:对外接口、共享请求/响应 DTO、业务枚举;不依赖实现模块、实体或 ORM。
- Controller:HTTP 参数绑定、调用业务入口、返回结果。
- Service:业务流程、业务校验、权限与数据范围判断。
- Repository:已有仓储模式下的数据访问与复用查询。
- Entity:数据库实体与映射。
- Mapper:沿用已有 Mapperly 映射实体与输出 DTO。
- Command / Handler / Behavior:沿用模块已有的命令与管道处理方式。
简单业务不强制引入命令、仓储或额外接口。跨模块优先调用已有 Contracts,避免依赖其他模块的内部实现;仅在模块内部使用的 DTO 不必移入 Contracts。
部分更新需明确 `null`、空字符串和默认值的含义,例如 `null` 保留原值、空串清空允许清空的文本,避免误清空字段。规范化文本时遵循字段语义,不统一 Trim 密码或任意文本。
## 4. 参数校验
### 4.1 DTO 负责非空校验
请求参数使用 DTO 承载。DTO 中只声明简单的必填、非空规则:
```csharp
public sealed class CreateItemRequest
{
[Required(ErrorMessage = "名称不能为空")]
public string Name { get; set; } = string.Empty;
public int Quantity { get; set; }
}
```
- 可选字段不要添加 Required。
- Required 不能替代数值范围判断,例如整型字段的零值判断。
- 条件必填、字段组合关系放到 Service 或已有业务处理入口中判断。
- 不在 DTO 中编写数据库查询或权限判断。
- 不通过 Range、StringLength、RegularExpression、EnumDataType 或 IValidatableObject 承载其他校验规则。
这是本项目的约定,不代表 ASP.NET Core 的唯一实践。
新增及本次修改涉及的校验按本规范实现。迁移已有 DTO 校验时,将原规则完整迁入业务入口并同步测试,不得只删除注解;不顺带迁移任务范围之外的模块。公共 RequestValidator 保留对完整 DataAnnotations 的支持,不为本模块约定缩减公共验证器能力。
### 4.2 统一使用 RequestValidator
复用 `Core/MAX.Framework/Validation/RequestValidator.cs`。业务入口调用:
```csharp
RequestValidator.Validate(request);
```
不要在各模块重复编写 `ArgumentNullException.ThrowIfNull` 和 `Validator.ValidateObject` 的同义入口。已经由 DTO 和 RequestValidator 完成的非空校验,不在 Service 重复判断。
同一业务调用链只安排一个明确的显式基础校验入口。MVC 自动模型验证不替代可被内部调用的业务入口校验;已有命令管道执行校验时,下游不重复调用。
### 4.3 其他校验放到 Service
以下规则在 Service 或已有业务处理入口中手动判断:
- 长度、格式、数值范围、枚举合法性、金额精度。
- 日期先后关系、条件必填及字段组合。
- 数据是否存在、是否重复、状态是否允许执行操作。
- 资源归属、操作权限和数据范围。
- 余额、额度、库存及其他业务限制。
```csharp
public async Task CreateAsync(CreateItemRequest request)
{
RequestValidator.Validate(request);
if (request.Name.Length > 100)
throw new ValidationException("名称不能超过100个字符");
if (request.Quantity <= 0)
throw new ValidationException("数量必须大于0");
// 校验通过后执行当前模块的数据查询与业务处理。
}
```
上述片段仅演示校验位置,实际异步方法需要完成业务调用。不依赖数据库的参数校验在开启事务、数据库写入及外部调用前完成;依赖数据库状态的规则在获取必要状态后判断。
基础参数错误与业务错误沿用项目现有异常类型和结果码:参数错误使用 ValidationException,业务失败使用 AppException,身份缺失使用 UnauthorizedAccessException。只在同类规则确实重复时抽取公共方法,单次判断直接写在使用处。
## 5. Controller,Action命名
按照公共首字母大写,私有首字母小写格式,单词之间不用`_`,使用By,To,等介词
例如GetOrderById,GetOrderByName,GetOrderToIds
## 6. HTTP 方法与参数绑定
使用 MAX.Framework 中已有的 HTTP 方法约定:
- 默认 POST 的控制器使用 DefaultHttpPost。
- 默认 GET 的控制器使用 DefaultHttpGet。
- 特殊 Action 显式标记 HttpGet、HttpPost 等特性。
- 显式方法特性优先于控制器默认约定。
- 同一控制器不要同时使用两个默认方法约定。
不要为每个 Action 重复添加与默认约定相同的特性,不在业务模块复制默认方法约定的实现。实现位置为 `Core/MAX.Framework/Conventions/`。
新增接口遵循模块现有参数绑定方式;多个业务输入优先使用请求 DTO。修改已有接口时检查调用方兼容性,不顺手改变 HTTP 方法、路由、字段名称或绑定来源。
## 7. 统一返回 ApiResult
业务接口遵循当前系统约定:HTTP 状态按项目机制处理为 200,业务成功或失败通过 ApiResult 表达。Action 直接返回业务数据时由 UnifyResultFilter 包装,已经返回 ApiResult 时不重复包装。
有数据的接口直接返回业务结果:
```csharp
public Task<ItemDto> GetItem(ItemQueryRequest request)
{
return service.GetAsync(request);
}
```
无数据的成功操作明确返回 ApiResult:
```csharp
public async Task<ApiResult> DeleteItem(DeleteItemRequest request)
{
await service.DeleteAsync(request);
return ApiResult.Ok();
}
```
不要使用 NoContent() 表达业务操作成功,不在每个 Action 重复 try/catch 并转换错误结果。校验异常和业务异常交给 GlobalExceptionFilter 等现有统一处理机制。
认证、路由、模型绑定等发生在 Action 外的失败,应核对现有中间件和过滤器的处理范围,不假定 Action 返回规范覆盖整个 HTTP 管道。相关过滤器位于 `Core/MAX.Framework/Commons/`。
返回 DTO 排除密码、令牌等敏感字段,沿用已有 Mapperly 映射;不能因实体新增字段而自动暴露内部数据。
## 8. 权限与数据范围
权限规则由当前模块需求确定,不套用其他模块的角色关系。实现前明确允许的角色、数据范围、可修改字段和需要单独权限的敏感操作。
- 身份从 ICurrentUser 等服务端可信上下文获取。
- 客户端传入的资源 ID、所属账号或组织信息必须经过权限检查。
- 查询和修改均限制数据范围,列表、详情、批量和单条操作保持一致。
- DTO 中存在某个字段,不代表调用者有权修改该字段。
- 实际业务入口覆盖绕过 Controller 的调用路径,不能只在 Controller 判断权限。
- 查询先限定可见范围,再筛选、排序、分页;统计和结果使用相同范围。
多处使用相同规则时复用模块已有权限策略,确有重复再集中抽取。
## 9. 数据访问与事务
复用项目现有的数据访问组件和模式:
- 使用 FreeSql 表达式和参数化操作,不拼接用户输入为 SQL。
- 多项写入共同构成一次必须同成同败的业务操作时,使用事务,避免部分成功。
- 金额、库存、额度等并发修改遵循现有锁或并发控制方式。
- 已有幂等操作保留请求号、唯一约束及重复请求处理。
- 只更新本次操作允许修改的字段,不把读取到的并发状态或派生值随整个实体写回。
- 审计信息记录实际操作者,区分操作者与业务目标。
- 列表保留必要的数据范围、筛选、稳定排序和分页。
涉及多步写入、并发控制、幂等或事务命令时,读取 [事务与命令管道约定](references/transactions.md)。普通查询不因此引入额外事务层。
不要因迁移旧代码绕过当前事务、审计或并发控制。时间语义、字段类型和数据库结构变更需纳入明确迁移范围,不因代码整理自动同步建表。
## 10. 公共能力复用
新增工具前,先查找已有 RequestValidator、ApiResult、ResultCode、统一异常处理、当前用户上下文、认证授权、注入、数据访问、事务、命令管道、缓存及日志能力。
依赖注入沿用 ITransientDependency 等标记;特殊注册通过模块 IStartup.Register(IServiceRegistrar) 完成。使用命令时检查 Handler 注册及 AddMediatRAttributedBehaviors 扫描,避免重复注册。
跨模块通用框架能力放在适当的公共项目,模块业务规则留在模块内部。不要为几行代码增加依赖,不为尚未出现的需求增加配置、包装层或扩展点。
## 11. 旧项目迁移
先理解旧逻辑,再按新项目结构实现。核对操作前提、业务结果、权限和数据范围、状态变化与字段更新、事务、并发、幂等、审计及调用方依赖的错误行为。
保留必要业务行为,不照搬旧项目的静态访问、拼接 SQL、重复校验或分层方式。与用户最新要求冲突时按最新要求实现,并说明行为变化;任务范围之外的旧功能不顺带迁移。
## 12. 注释与代码风格
- 命名清晰,遵循现有项目风格。
- 公开接口及重要业务规则添加必要中文说明,重点解释权限边界、业务原因及不直观约束,不逐句重复代码。
- 不滥用 try/catch,不静默吞异常;能明确处理的格式、日期等异常可就地转换,不删除必要资源清理和回滚。
- 不为一次性逻辑机械抽取方法,不进行无关重构或格式化。
## 13. 测试与验证
测试放在 Tests 下,沿用现有项目和数据准备方式。非平凡业务修改覆盖相关正常操作、必填和其他参数边界、授权与越权、数据范围、状态限制,以及适用的事务失败、并发和幂等行为。
接口变化时检查默认与显式 HTTP 方法、路由、参数绑定、ApiResult 包装、HTTP 状态和无数据成功操作。修改契约同步调用方、测试和必要文档;不删除仍有效的失败断言。
不为简单可逆改动堆砌测试,不编写只重复实现细节的测试。运行相关测试和构建,涉及接口、注册或依赖变化时构建受影响宿主。说明测试数据库可验证的范围,不把 SQLite 通过视为已验证 MySQL 的锁和事务行为。
## 14. 交付要求
完成前检查实际文件和 diff,确认修改已经落盘。交付说明:
- 文件:实际路径。
- 修改用途与关键行为。
- 已运行的测试和构建结果。
- 未验证的内容及实际限制。
没有修改文件不能声称已修改,没有运行测试不能声称测试通过;不把计划、建议或示例当作已完成实现。
@@ -0,0 +1,4 @@
interface:
display_name: "StoreWeb 模块开发规范"
short_description: "仅用于 StoreWeb 项目的模块分层、契约、校验与事务实现规范"
default_prompt: "使用 $storeweb-module,按本项目模块规范实现或调整指定模块,并完成相关验证。"
@@ -0,0 +1,43 @@
# 事务与命令管道约定
仅在涉及多步写入一致性、并发控制、幂等或命令管道时读取。适用于本项目所有业务模块,锁定资源、去重范围和权限条件由各模块实际需求决定。路径相对于仓库根目录。
## Service 中的事务
1. DTO 非空验证和不依赖数据库的业务参数校验先于事务;依赖数据库状态的校验在获取必要状态后完成,写入前必须满足业务条件。
2. 用 `orm.CreateUnitOfWork()`、`GetOrBeginTransaction()` 开启工作单元。事务内相关读写统一使用 `work.Orm`,不能混用根 ORM 使部分操作脱离事务。
3. 全部写入成功后 `Commit()`,未提交由工作单元释放回滚。不 catch 所有异常后继续提交,不封装同义事务工具。
4. 异步数据库调用传递 CancellationToken,提交前检查取消。数据库事务不覆盖外部通知、文件或第三方服务等副作用,按现有业务机制安排执行与失败处理,不默认增加额外基础设施。
5. 只更新允许修改的字段。需要避免丢更新的操作使用既有锁或条件更新,并检查受影响行数;不要把“必须影响一行”套到允许无变化的幂等更新上。
## 并发与幂等
- 涉及金额时沿用 decimal;允许值、精度和上限由业务入口判断,可能溢出的累计运算按需要使用 checked。
- 同一操作锁定多条资源时采用稳定顺序;在锁定或并发条件保护的有效状态上判断是否允许修改。进程内锁不能替代跨进程数据库一致性。
- 业务变更与必须同步保存的流水、审计记录在同一事务提交或回滚,记录实际操作者与业务目标。
- 幂等操作明确请求号的作用范围和重复请求结果,保留现有请求号、唯一约束及处理机制;不能把某模块的去重键当作全项目统一规则。
- 去重检查和写入使用能阻止竞态的数据库约束或锁机制,不能仅无保护地先查后写。重试按接口约定复用请求号。
- MySQL 表引擎、约束、隔离及并发行为需要对应环境验证,SQLite 只承担部分持久化和回滚验证。
## MediatR 管道
复用以下框架实现,按当前文件确认行为:
- `Core/MAX.Framework/Mediation/ValidationBehavior.cs`
- `Core/MAX.Framework/Mediation/TransactionBehavior.cs`
- `Core/MAX.Framework/Mediation/UnitOfWorkExecutionContext.cs`
- `Core/MAX.Framework/DependencyInjection/MediatRBehaviorServiceCollectionExtensions.cs`
命令携带业务输入 DTO,可信操作者由服务端上下文提供。普通 Service 方法不必建立同义命令。
- 通过 `[MediatRBehavior(typeof(...))]` 选择必要步骤,顺序由实际依赖决定。基础输入校验先于事务;需要事务内状态的业务步骤位于事务内部。
- `IValidatableRequest.Validate()` 调用 `RequestValidator.Validate(Input)` 完成 DTO 非空校验;长度、格式、范围及组合规则仍放在 Service 或已有业务处理入口,不迁入 DTO。后续 Handler 不重复基础校验。
- 不依赖数据库的业务参数校验在事务前执行,优先使用已有业务处理入口或适用的业务管道,不为单次判断机械新增一层。
- 事务内管道和 Handler 统一使用 `UnitOfWorkExecutionContext.Orm`。
- TransactionBehavior 统一提交、取消检查和回滚;当前实现拒绝嵌套事务命令。组合操作在同一事务用例中完成,不递归发送另一个事务命令。
- 已授权上下文是内部执行结果,不是客户端输入。保留必要上下文清理、执行顺序和重入约束,不把这些当作重复 DTO 校验删除。
- 在 `IStartup.Register(IServiceRegistrar)` 注册 Handler,宿主通过 AddMediatRAttributedBehaviors 扫描特性。确认模块程序集被现有机制发现,不另写扫描器或重复注册。
## 验证
按本次变化验证成功提交、任一步骤失败时整体回滚、权限或状态拒绝时无写入、重复请求处理,以及实际操作者的审计记录。涉及管道时使用真实管道入口验证顺序和注册;并发锁验证使用对应数据库,不以单线程 SQLite 测试替代。