adspecs 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,724 +1,613 @@
1
- ---
2
-
3
- description: "ECP 平台功能实现的 TDD 任务列表模板"
4
- ---
5
-
6
- ### 修订记录
7
-
8
- | 版本号 | 修订日期 | 修订人 | 修订内容 |
9
- | ------ | -------- | ------ | -------- |
10
- | v2.0 | 2026-06-13 | AI | ECP 平台专项改造:通用 Python 模板 → ECP Java Maven 三模块 TDD 模板。新增 ECP 任务类型速查表(17 种)、ECP 编码规范检查清单(10 条)、TDD 执行指南(RED→GREEN→REFACTOR)。Phase 1-10 全部替换为 M06-01 销售预测单真实示例(Entity/DTO/DAO/Service/Controller/Feign/枚举/i18n)。格式扩展:新增 `[TDD]` 标记。路径约定替换为 ECP 三模块结构。与 adspecs-tasks skill §ECP 平台编码规范完全对齐 |
11
- | v1.0 | 2026-06-12 | AI | 初始版本:通用 spec-kit 任务模板(Python 示例),含 Phase 1~5+N、依赖关系、并行策略 |
12
-
13
- # 任务清单:[功能名称]
14
-
15
- **设计文档来源**:
16
- - 系统设计文档:`docs/30-system-design/{crm|mom}/[功能名]_system-design.md`(必读,由 `adspecs-plan` 产出,包含 OOA+OOD 13 章节统一设计)
17
- - PRD 文档:`docs/20-prd/{crm|mom}/[模块]/[需求规格说明书].md`(可选,用户故事验收场景参考)
18
- - 建表 SQL:`docs/30-system-design/{crm|mom}/[功能名]_schema.sql`(可选,DDL 直接可用)
19
-
20
- **前置条件**:
21
- - 调用 `/adspecs-tasks` 前,必须已完成 `/adspecs-plan` 产出的 `_system-design.md`
22
- - 系统设计文档必须包含:§1 需求映射、§2 实体+DTO+Ro、§3 数据模型(含 MyBatis XML)、§4 Service 设计、§5 Controller 端点、§6 枚举+校验、§7 异常+消息码、§13 代码结构
23
-
24
- **TDD 约定**:本模板遵循 `test-driven-development` skill 的"测试先行"原则——所有实现任务之前必须先有失败的测试任务。模板中的样本任务包含 `[TDD]` 标记的测试任务。
25
-
26
- **组织方式**:任务按用户故事分组(设计文档 §1.1.1),以支持每个故事的独立 TDD 实现和独立测试。
27
-
28
- ---
29
-
30
- ## 任务格式:`- [ ] [TaskID] [P?] [TDD?] [Story?] 描述 + 精确文件路径`
31
-
32
- **格式组成**:
33
-
34
- | 元素 | 说明 |
35
- |------|------|
36
- | `- [ ]` | Markdown 复选框(始终存在) |
37
- | `[TaskID]` | 全局递增序号:T001, T002, T003... |
38
- | `[P]` | 可并行执行标记(操作不同文件,无未完成依赖) |
39
- | `[TDD]` | 测试任务标记(Service 层单元测试 / Controller 层集成测试),**必须在同故事实现任务之前完成且验证失败** |
40
- | `[Story]` | 用户故事标签:[US1], [US2]...(对应设计文档 §1.1.1)。Phase 1/2/Final 无此标签 |
41
- | 描述 + 文件路径 | 明确操作描述 + 从三模块根开始的完整文件路径。多文件任务可分行列出路径 |
42
-
43
- **格式示例(ECP)**:
44
-
45
- ```text
46
- 正确:
47
- - [ ] T012 [TDD] [US1] 编写提交预测单 Service 单元测试(含正常+提前期拦截+MOQ警告),测试文件:
48
- 3103-sales-forecast-application/src/test/java/com/sie/gushen/salesforecast/model/service/impl/ForecastOrderServiceTest.java
49
- - [ ] T013 [US1] 创建 ForecastOrderEntity(extends BaseEntity<ForecastOrderEntity>, @TableName(`forecast_order`), IdType.ASSIGN_ID),文件:
50
- 3103-sales-forecast-model/src/main/java/com/sie/gushen/salesforecast/model/entity/ForecastOrderEntity.java
51
- - [ ] T014 [P] [US1] 创建 ForecastOrderDto(extends ReqeustCommonDataBean, 含 saveForecastOrder/updateForecastOrder 校验分组),文件:
52
- 3103-sales-forecast-api/src/main/java/com/sie/gushen/salesforecast/api/dto/ForecastOrderDto.java
53
-
54
- 错误:
55
- - [ ] 创建 ForecastOrder 实体(缺少 TaskID + 文件路径 + Story 标签)
56
- - T001 [US1] 创建 Entity(缺少复选框 + 精确文件路径)
57
- - [ ] T001 [US1] 创建 Entity(缺少文件路径)
58
- - [ ] T010 [US1] 实现提交预测单 Service 方法(故事阶段内未先写 [TDD] 测试,违反 TDD 规则)
59
- - [ ] T020 [TDD] [US1] 测试提交(描述模糊,文件路径缺失)
60
- ```
61
-
62
- ---
63
-
64
- ## ECP 三模块路径约定
65
-
66
- > 来源:CLAUDE.md §"Repository Overview" + 设计文档 §1.2(模块定位)
67
-
68
- ECP 后端采用 Maven 三模块结构,所有文件路径从服务根目录开始:
69
-
70
- ```
71
- {port}-{service}/ # 服务根目录,如 3103-sales-forecast/
72
- ├── {port}-{service}-api/ # 契约层:Feign Client 接口 + DTO
73
- │ └── src/main/java/com/sie/gushen/{service}/api/
74
- │ ├── client/ # @FeignClient 接口
75
- │ └── dto/ # DTO(extends ReqeustCommonDataBean,注意平台拼写)
76
- ├── {port}-{service}-model/ # 数据层:Entity + DAO + XML + Ro
77
- │ ├── src/main/java/com/sie/gushen/{service}/model/
78
- │ │ ├── entity/ # Entity(extends BaseEntity<T>)
79
- │ │ │ └── readonly/ # Roimplements Serializable)
80
- │ │ └── dao/ # DAO 接口 + MyBatis XML 同包
81
- │ └── src/main/resources/liquibase/mysql/{service}/ # Liquibase
82
- └── {port}-{service}-application/ # 实现层:Service + Controller + Config + Consumer + i18n
83
- ├── src/main/java/com/sie/gushen/{service}/
84
- │ ├── {Service}Application.java # 启动类(extends CommonApplication)
85
- │ ├── api/controller/ # Controller(extends CommonAbstractService)
86
- │ ├── model/service/ # Service 接口(II{Entity})+ 实现(extends BaseCommonServer)
87
- │ ├── app/consume/ # Kafka Consumer
88
- │ └── infra/
89
- │ ├── config/ # DataSource / MybatisPlus / SpringDoc
90
- │ ├── constant/ # ResponseMsgCode 常量
91
- │ ├── enumeration/ # 枚举类
92
- │ ├── interceptor/ # @CheckToken 拦截器
93
- │ └── utils/ # 工具类
94
- └── src/main/resources/config/ # i18n 7 文件
95
- ```
96
-
97
- **变量替换**:
98
- - `{port}`:微服务端口号,如 `3103`
99
- - `{service}`:服务名(kebab-case),如 `sales-forecast`
100
- - 包路径中转化为 camelCase:`com.sie.gushen.[camelService]`,如 `com.sie.gushen.salesforecast`
101
-
102
- ---
103
-
104
- ## ECP 任务类型速查表
105
-
106
- > 每个任务描述中必须包含正确的 ECP 父类/接口/注解。此表同时作为 `adspecs-tasks` skill 生成任务时的文件路径映射。
107
-
108
- | 任务类型 | ECP 父类/接口 | 关键注解/约定 | 所在模块 | 文件路径模式 |
109
- |---------|--------------|-------------|---------|-------------|
110
- | **Entity** | `extends BaseEntity<Entity>` | `@TableName(``table``)`, `@TableId(type=ASSIGN_ID)`, 所有字段 `@TableField` + `@Schema` | `-model` | `model/entity/XxxEntity.java` |
111
- | **DTO** | `extends ReqeustCommonDataBean` | 校验分组内部接口(`saveXxx`/`updateXxx`),日期 `@JSONField(format="yyyy-MM-dd HH:mm:ss")`,消息码 `{port}_{table}_{三位序号}` | `-api` | `api/dto/XxxDto.java` |
112
- | **Ro** | `implements Serializable` | `serialVersionUID`, 手动声明所有字段, `@Schema`, 日期 `@DateTimeFormat @JsonFormat` | `-model` | `model/entity/readonly/XxxRo.java` |
113
- | **DAO 接口** | `extends BaseCommonDAO_HI<Entity>` | `@Component("xxxDAO")` | `-model` | `model/dao/XxxDAO.java` |
114
- | **DAO XML** | — | `resultMap` 含所有字段+审计字段, `Base_Column_List`, `WHERE delete_flag=0`, 禁止 QueryWrapper | `-model` | `model/dao/XxxDAO.xml`(同包路径) |
115
- | **Service 接口** | `extends IBaseCommon<Entity>` | 命名 `II{Entity}`(双I前缀) | `-application` | `model/service/IIXxx.java` |
116
- | **Service 实现** | `extends BaseCommonServer<Entity>` | `@Component("xxxService")`, `@Autowired`, Save→FastJSON, Update→SpringBeanUtils, 异常在Service抛出 | `-application` | `model/service/impl/XxxService.java` |
117
- | **Controller** | `extends CommonAbstractService` | `@Tag`, `@RestController`, `@RequestMapping("/xxx")`, 重写 `getBaseCommonServer()`, 所有端点 `@CheckToken` | `-application` | `api/controller/XxxController.java` |
118
- | **Feign Client** | `@FeignClient` 接口 | 放入 `-api/client/` | `-api` | `api/client/XxxClient.java` |
119
- | **枚举** | 普通 `enum` | 存储值 `String`, `infra/enumeration/` | `-application` | `infra/enumeration/XxxEnum.java` |
120
- | **消息码** | `enum ResponseMsgCode` | 格式 `{port}_{table}_{三位序号}`, 字段校验 001~005, 业务异常 006+ | `-application` | `infra/constant/ResponseMsgCode.java` |
121
- | **Kafka Consumer** | `@Configuration` + `Consumer<String>` Bean | Bean name 必须匹配 YAML `spring.cloud.stream.function.definition` | `-application` | `app/consume/XxxConsumer.java` |
122
- | **i18n** | 7 个 `.properties` | default/zh_CN/zh_TW/en_US/vi_VN/th_TH/hu_HU,每个文件含该语言实际内容 | `-application` | `resources/config/{port}-{service}-application[_locale].properties` |
123
- | **Liquibase** | 3 文件 | master.xml version.xml DDL.sql | `-model` | `resources/liquibase/mysql/{service}/` |
124
- | **工具类** | 普通 `class` | Hutool 优先, SLF4J 日志 | `-application` | `infra/utils/XxxUtils.java` |
125
- | **配置类** | `@Configuration` | DataSourceConfig / MybatisPlusConfig(分页+乐观锁)/ SpringDocConfig | `-application` | `infra/config/XxxConfig.java` |
126
- | **启动类** | `extends CommonApplication` | `@SpringBootApplication`, `@EnableDiscoveryClient`, `@EnableFeignClients`, `@MapperScan`, `@ComponentScan` | `-application` | `{Service}Application.java` |
127
-
128
- ---
129
-
130
- <!--
131
- ============================================================================
132
- 重要提示:以下任务是**模板示例**,展示了 ECP 平台使用 M06-01 销售预测单的真实类名和路径。
133
-
134
- /adspecs-tasks 命令将根据以下内容替换为实际任务:
135
- - 系统设计文档 §1.1.1 中的用户故事(及其优先级 P1、P2...)
136
- - 系统设计文档 §2 中的实体/DTO/Ro 清单
137
- - 系统设计文档 §3 中的数据模型与 MyBatis 映射
138
- - 系统设计文档 §4 中的 Service 方法与业务行为
139
- - 系统设计文档 §5 中的 Controller 端点
140
- - 系统设计文档 §6 中的枚举定义
141
- - 系统设计文档 §7 中的异常与消息码
142
- - 系统设计文档 §13 中的完整目录树(~45 文件)
143
-
144
- 每个生成的任务必须:
145
- - 包含精确的 ECP 三模块文件路径
146
- - 遵循 TDD 测试先行顺序([TDD] 标记的任务在实现任务之前)
147
- - 包含正确的 ECP 父类/接口(见 §ECP 任务类型速查表)
148
- - 通过 §ECP 编码规范检查清单 的 10 条校验
149
- ============================================================================
150
- -->
151
-
152
- ---
153
-
154
- ## Phase 1:环境搭建(共享基础设施)
155
-
156
- **目的**:Maven 三模块项目初始化与基础结构搭建
157
-
158
- - [ ] T001 创建 Maven 三模块 parent POM(`com.sie.irradiator:sie-irradiator:3.0.2`),定义 `{port}-{service}-api` / `{port}-{service}-model` / `{port}-{service}-application` 三个 module,文件:
159
- `{port}-{service}/pom.xml`
160
- - [ ] T002 [P] 创建 `{port}-{service}-api` 模块 POM + 目录结构(`com/sie/gushen/{service}/api/client/`, `api/dto/`),文件:
161
- `{port}-{service}-api/pom.xml`
162
- - [ ] T003 [P] 创建 `{port}-{service}-model` 模块 POM + 目录结构(`model/entity/`, `model/entity/readonly/`, `model/dao/`),文件:
163
- `{port}-{service}-model/pom.xml`
164
- - [ ] T004 [P] 创建 `{port}-{service}-application` 模块 POM + 目录结构(`api/controller/`, `model/service/impl/`, `infra/config/`, `infra/constant/`, `infra/enumeration/`, `infra/utils/`, `app/consume/`),文件:
165
- `{port}-{service}-application/pom.xml`
166
- - [ ] T005 创建启动类 `{Service}Application`(`extends CommonApplication`,注解 `@SpringBootApplication`(排除 DataSource/HibernateJpa)、`@EnableDiscoveryClient`、`@EnableFeignClients`、`@MapperScan`、`@ComponentScan`),文件:
167
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/{Service}Application.java`
168
- - [ ] T006 [P] 创建 `application.yml` 基础配置(server.port、spring.datasource、mybatis-plus.mapper-locations、spring.cloud.stream 等),文件:
169
- `{port}-{service}-application/src/main/resources/application.yml`
170
-
171
- **检查点**:Maven 三模块结构就绪,`mvn clean compile` 可通过
172
-
173
- ---
174
-
175
- ## Phase 2:基础设施(阻塞性前置依赖)
176
-
177
- **目的**:枚举、消息码、数据库 DDL、基础配置等被所有用户故事依赖的底层构件
178
-
179
- **⚠️ 关键**:在此阶段完成之前,不得开始任何用户故事的工作
180
-
181
- ### 2.1 枚举类(所有枚举可并行创建)
182
-
183
- - [ ] T007 [P] 创建 `ForecastOrderStatusEnum`(9 状态:DRAFT/SUBMITTED/REJECTED/ACCEPTED/SCHEDULED/PARTIAL_CONVERTED/FULLY_CONVERTED/RELEASED/CANCELLED),文件:
184
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/ForecastOrderStatusEnum.java`
185
- - [ ] T008 [P] 创建 `ChangeOrderStatusEnum`(5 状态:DRAFT/SUBMITTED/REJECTED/CHANGED/VOIDED),文件:
186
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/ChangeOrderStatusEnum.java`
187
- - [ ] T009 [P] 创建 `DomesticForeignEnum`(DOMESTIC 内销 / FOREIGN 外销),文件:
188
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/DomesticForeignEnum.java`
189
- - [ ] T010 [P] 创建 `ProductTypeEnum`(STANDARD 标品 / NON_STANDARD 非标),文件:
190
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/ProductTypeEnum.java`
191
- - [ ] T011 [P] 创建 `TransferStatusEnum`(PENDING 待转单 / TRANSFERRED 已转单 / RELEASED 已释放),文件:
192
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/TransferStatusEnum.java`
193
- - [ ] T012 [P] 创建 `QuantityUnitEnum`(KG 公斤 / TON 吨),文件:
194
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/QuantityUnitEnum.java`
195
- - [ ] T013 [P] 创建 `DeliveryLocationEnum`(SHANGHAI 上海 / CHONGQING 重庆),文件:
196
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/enumeration/DeliveryLocationEnum.java`
197
-
198
- ### 2.2 消息码与配置
199
-
200
- - [ ] T014 创建 `ResponseMsgCode` 枚举常量类(字段校验 001~005 + 业务异常 006~017,消息码格式 `{port}_{table}_{三位序号}`),文件:
201
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/constant/ResponseMsgCode.java`
202
- - [ ] T015 [P] 创建 `MybatisPlusConfig`(分页插件 `PaginationInnerInterceptor` + 乐观锁插件 `OptimisticLockerInnerInterceptor`),文件:
203
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/config/MybatisPlusConfig.java`
204
- - [ ] T016 [P] 创建 `DataSourceConfig`(数据源配置),文件:
205
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/config/DataSourceConfig.java`
206
- - [ ] T017 [P] 创建 `SpringDocConfig`(API 文档配置),文件:
207
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/config/SpringDocConfig.java`
208
- - [ ] T018 创建工具类 `ForecastUtils`(强制转FO时间计算:`requiredWarehouseDate - N天`;提报提前期校验:`requiredWarehouseDate >= now + leadTimeDays`),文件:
209
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/infra/utils/ForecastUtils.java`
210
-
211
- ### 2.3 Liquibase 数据库迁移
212
-
213
- - [ ] T019 创建 Liquibase `master.xml`(入口文件,引用版本 XML),文件:
214
- `{port}-{service}-model/src/main/resources/liquibase/mysql/{service}/master.xml`
215
- - [ ] T020 创建 Liquibase `v1.0.xml`(版本变更日志,包含 changeSet),文件:
216
- `{port}-{service}-model/src/main/resources/liquibase/mysql/{service}/v1.0.xml`
217
- - [ ] T021 创建 DDL SQL(`forecast_order` 表 35+ 字段 + 6 索引 + `forecast_change_order` 表 20+ 字段 + 3 索引,含全部审计字段),文件:
218
- `{port}-{service}-model/src/main/resources/liquibase/mysql/{service}/ddl/DDL_1.0_YYYYMMDD.sql`
219
-
220
- **检查点**:基础设施就绪——所有枚举可编译,Liquibase DDL 可执行建表,现在可以并行开始用户故事的 TDD 实现
221
-
222
- ---
223
-
224
- ## Phase 3:US-01 — 创建并提报预测单(优先级:P1)🎯 MVP 起点
225
-
226
- **目标**:销售人员能针对某客户某物料创建预测单,填写 CTCT 编码(自动回填物料+客户信息)、销售需求量、要求成品入库时间,保存草稿或提交至计划部门评审
227
-
228
- **独立测试**:登录销售人员账号 → 输入 CTCT 编码 → 填写需求量 5000kg + 入库时间 +35 天 → 点击提交 → 验证预测单进入"已提交"状态且生成单号
229
-
230
- **来源**:设计文档 §1.1.1 US-01, §4.1 行为 1-2, §5.1 端点 1-7, §12.1 TC1, §12.2 TC1
231
-
232
- ### 用户故事 1 的 TDD 测试(先写,确保失败后再实现)
233
-
234
- > ⚠️ **TDD 铁律**:在编写任何实现代码之前,先编写以下测试并验证它们**因功能缺失而失败**。
235
-
236
- - [ ] T022 [TDD] [US1] 编写 `ForecastOrderService` 提交预测单测试(正常场景:CTCT 有效,需求量 5000kg,入库时间+35 天 → 状态=已提交,生成单号),测试文件:
237
- `{port}-{service}-application/src/test/java/com/sie/gushen/{service}/model/service/impl/ForecastOrderServiceTest.java`
238
- - [ ] T023 [TDD] [P] [US1] 编写 `ForecastOrderService` 提交预测单异常测试(提报提前期不满足 → 抛出 `GushenRuntimeException("3103_forecast_order_007")`),测试文件:同上
239
- - [ ] T024 [TDD] [P] [US1] 编写 `ForecastOrderService` 提交预测单边界测试(需求量=MOQ 正常通过;需求量<MOQ 警告但通过;保税手册余量不足 → 抛出异常),测试文件:同上
240
- - [ ] T025 [TDD] [P] [US1] 编写 `ForecastOrderService` 保存草稿测试(部分字段可空,状态=草稿,单号=null),测试文件:同上
241
- - [ ] T026 [TDD] [US1] 编写 `ForecastOrderController` 保存/提交端点集成测试(POST `/forecast-order/save` → 200 + 草稿返回;POST `/forecast-order/submit` → 200 + 已提交),测试文件:
242
- `{port}-{service}-application/src/test/java/com/sie/gushen/{service}/api/controller/ForecastOrderControllerTest.java`
243
-
244
- ### 用户故事 1 的实现(测试验证失败后开始)
245
-
246
- - [ ] T027 [US1] 创建 `ForecastOrderEntity`(`extends BaseEntity<ForecastOrderEntity>`, `@TableName(``forecast_order``)`, `@TableId(type=ASSIGN_ID)`, 35+ 字段全带 `@TableField` + `@Schema`),文件:
247
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/entity/ForecastOrderEntity.java`
248
- - [ ] T028 [P] [US1] 创建 `ForecastOrderDto`(`extends ReqeustCommonDataBean`,含 `saveForecastOrder`/`updateForecastOrder` 校验分组内部接口,日期 `@JSONField(format="yyyy-MM-dd HH:mm:ss")`),文件:
249
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/dto/ForecastOrderDto.java`
250
- - [ ] T029 [P] [US1] 创建 `ForecastOrderEntityRo`(`implements Serializable` + `serialVersionUID`,手动声明所有 Entity 字段 + 审计字段 + `List<ForecastChangeOrderEntity> forecastChangeOrderList`),文件:
251
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/entity/readonly/ForecastOrderEntityRo.java`
252
- - [ ] T030 [US1] 创建 `ForecastOrderDAO` 接口 + `ForecastOrderDAO.xml`(`extends BaseCommonDAO_HI<ForecastOrderEntity>`, `@Component("forecastOrderDAO")`, `BaseResultMap` 含全字段+审计, `Base_Column_List`, `BASE_QUERY_CONDITION` 支持多条件筛选, `findPagination` 含 `WHERE delete_flag=0`),文件:
253
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/dao/ForecastOrderDAO.java`
254
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/dao/ForecastOrderDAO.xml`
255
- - [ ] T031 [US1] 创建 `IIForecastOrder` Service 接口(`extends IBaseCommon<ForecastOrderEntity>`,定义 `saveForecastOrder`/`updateForecastOrder`/`submitForecastOrder`/`findById`/`findPagination` 方法签名),文件:
256
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/model/service/IIForecastOrder.java`
257
- - [ ] T032 [US1] 实现 `ForecastOrderService`(`extends BaseCommonServer<ForecastOrderEntity>`, `@Component("forecastOrderService")`, `@Autowired ForecastOrderDAO`):
258
- - `saveForecastOrder(dto)`:FastJSON `JSONObject.parseObject(JSONObject.toJSONString(dto), Entity.class)`,设置初始状态 DRAFT,transferredQty=0
259
- - `updateForecastOrder(dto)`:校验仅草稿/已驳回可编辑 Spring `BeanUtils.copyProperties(dto, entity, "createdBy", "creationDate", "versionNum")`
260
- - `submitForecastOrder(id)`:BR-01 提报提前期校验(拦截)→ BR-02 MOQ 校验(警告)→ `AutoCodeRuleService.generate` 生成单号 → 状态→已提交 → 计算 forceFoDate
261
- - `findById(id)`:含变更单子表列表组装 ForecastOrderEntityRo
262
- - `findPagination(dto, pageIndex, pageRows, orderByBean)`:`BeanUtil.beanToMap` → `Condition.getPage` → DAO 分页查询
263
- 文件:`{port}-{service}-application/src/main/java/com/sie/gushen/{service}/model/service/impl/ForecastOrderService.java`
264
- - [ ] T033 [US1] 创建 `ForecastOrderController`(`extends CommonAbstractService`, `@Tag`, `@RestController`, `@RequestMapping("/forecast-order")`, 重写 `getBaseCommonServer()`, `@Autowired IIForecastOrder`):
265
- - `GET /get-id` `forecastOrderService.findById(id)`
266
- - `POST /find-pagination` → `forecastOrderService.findPagination(dto, pageIndex, pageRows, orderByBean)`
267
- - `POST /save` 参数空校验 → `forecastOrderService.saveForecastOrder(dto)`
268
- - `POST /update` → 参数空校验 → `forecastOrderService.updateForecastOrder(dto)`(仅草稿/已驳回)
269
- - `POST /submit` → `forecastOrderService.submitForecastOrder(id)`
270
- 文件:`{port}-{service}-application/src/main/java/com/sie/gushen/{service}/api/controller/ForecastOrderController.java`
271
- - [ ] T034 [US1] 创建 `CtctClient` Feign 接口(`@FeignClient`, `GET /p01/ctct/query?ctctCode={code}` → CTCT物料+客户聚合信息),文件:
272
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/client/CtctClient.java`
273
-
274
- **检查点**:[TDD] 所有测试由红变绿,用户故事 1 可独立运行:创建草稿 → 填写完整 → 提交 → 生成单号 → 状态=已提交
275
-
276
- ---
277
-
278
- ## Phase 4:US-02 — 计划部门评审预测单(优先级:P1)
279
-
280
- **目标**:计划部门人员查看已提交的预测单,执行接收(填写计划建议量、答复入库时间、答复意见)或驳回(填写驳回原因)
281
-
282
- **独立测试**:登录计划部门人员账号 进入待评审列表 对一条预测单执行"接收"并填写答复 验证状态变更为"计划接收";对另一条执行"驳回" → 验证状态变更为"已驳回"
283
-
284
- **来源**:设计文档 §1.1.1 US-02, §4.1 行为 4-5, §5.1 端点 9-10, §12.1 TC1
285
-
286
- ### 用户故事 2 的 TDD 测试
287
-
288
- - [ ] T035 [TDD] [US2] 编写 `ForecastOrderService` 接收预测单测试(状态=已提交 → 填写答复 → 状态=计划接收,记录 replyTime),测试文件:同 ForecastOrderServiceTest.java
289
- - [ ] T036 [TDD] [P] [US2] 编写 `ForecastOrderService` 驳回预测单测试(状态=已提交 → 填写驳回原因 → 状态=已驳回),测试文件:同上
290
- - [ ] T037 [TDD] [P] [US2] 编写 `ForecastOrderService` 评审状态校验测试(非已提交状态 → 抛出 `GushenRuntimeException("3103_forecast_order_013")`),测试文件:同上
291
- - [ ] T038 [TDD] [US2] 编写 `ForecastOrderController` 接收/驳回端点集成测试(POST `/forecast-order/accept` → 200;POST `/forecast-order/reject` → 200),测试文件:同 ForecastOrderControllerTest.java
292
-
293
- ### 用户故事 2 的实现
294
-
295
- - [ ] T039 [US2] 扩展 `IIForecastOrder` 接口:新增 `acceptForecastOrder(id, dto)` / `rejectForecastOrder(id, dto)` 方法签名
296
- - [ ] T040 [US2] 扩展 `ForecastOrderService`:
297
- - `acceptForecastOrder(id, dto)`:校验状态=已提交 设置 planSuggestQty/replyWarehouseDate/replyComment/replyTime → 状态→计划接收
298
- - `rejectForecastOrder(id, dto)`:校验状态=已提交 → 设置 replyComment → 状态→已驳回
299
- - [ ] T041 [US2] 扩展 `ForecastOrderController`:
300
- - `POST /accept` → `forecastOrderService.acceptForecastOrder(id, dto)`
301
- - `POST /reject` → `forecastOrderService.rejectForecastOrder(id, dto)`
302
-
303
- **检查点**:计划部门可接收/驳回预测单,TDD 测试红→绿
304
-
305
- ---
306
-
307
- ## Phase 5:US-09 + US-08 — 撤回/取消/删除预测单(优先级:P1+P2)
308
-
309
- **目标**:销售人员撤回已提交/计划接收(未排产)的预测单;取消有单号草稿/已驳回预测单;物理删除无单号草稿
310
-
311
- **独立测试**:已提交预测单 → 撤回 → 状态=草稿;有单号草稿 → 取消 → 状态=取消;无单号草稿 → 删除 → 物理删除
312
-
313
- **来源**:设计文档 §1.1.1 US-08+US-09, §4.1 行为 3+9+10, §5.1 端点 5+7+8, §12.2 TC2
314
-
315
- ### 用户故事 5 的 TDD 测试
316
-
317
- - [ ] T042 [TDD] [US5] 编写撤回预测单测试(已提交→草稿、计划接收未排产→草稿),测试文件:同 ForecastOrderServiceTest.java
318
- - [ ] T043 [TDD] [P] [US5] 编写撤回阻止测试(已排产→抛出 `GushenRuntimeException("3103_forecast_order_010")`),测试文件:同上
319
- - [ ] T044 [TDD] [P] [US5] 编写取消/删除测试(有单号草稿→取消终态;无单号草稿→物理删除;有单号不可物理删除→异常),测试文件:同上
320
-
321
- ### 用户故事 5 的实现
322
-
323
- - [ ] T045 [US5] 扩展 `ForecastOrderService`:
324
- - `withdrawForecastOrder(id)`:状态=已提交→草稿;状态=计划接收且 scheduleConfirmTime=null→草稿;否则抛异常
325
- - `cancelForecastOrder(id)`:状态∈{草稿(有单号), 已驳回}→取消终态
326
- - `deleteForecastOrder(id)`:状态=草稿且单号=null→物理删除;否则抛出 `GushenRuntimeException("3103_forecast_order_016")`
327
- - [ ] T046 [US5] 扩展 `ForecastOrderController`:
328
- - `POST /withdraw` → `forecastOrderService.withdrawForecastOrder(id)`
329
- - `POST /cancel` → `forecastOrderService.cancelForecastOrder(id)`
330
- - `GET /delete` → `forecastOrderService.deleteForecastOrder(id)`
331
-
332
- **检查点**:撤回/取消/删除功能正常,TDD 测试红→绿
333
-
334
- ---
335
-
336
- ## Phase 6:US-04 预测单转履约单(FO)(优先级:P1)
337
-
338
- **目标**:销售人员将已排产(全部或部分)预测单数量转为 FO,全部转完自动关闭
339
-
340
- **独立测试**:已排产待转单 选择 SO 行项目 输入转单量 5000kg 全部转单 → 状态=已全部转单完成
341
-
342
- **来源**:设计文档 §1.1.1 US-04, §4.1 行为 7, §5.1 端点 12, §12.1 TC1
343
-
344
- ### 用户故事 4 的 TDD 测试
345
-
346
- - [ ] T047 [TDD] [US4] 编写转 FO 测试(全部转:状态→已全部转单完成+剩余归零;部分转:状态→已部分转单+剩余扣减),测试文件:同 ForecastOrderServiceTest.java
347
- - [ ] T048 [TDD] [P] [US4] 编写转 FO 异常测试(转单量>剩余→异常、状态非法→异常),测试文件:同上
348
- - [ ] T049 [TDD] [US4] 编写转 FO 端点集成测试(POST `/forecast-order/transfer-to-fo` → 200),测试文件:同 ForecastOrderControllerTest.java
349
-
350
- ### 用户故事 4 的实现
351
-
352
- - [ ] T050 [P] [US4] 创建 `TransferToFoDto`(`extends ReqeustCommonDataBean`,字段:forecastOrderId + soLineItemId + transferQty),文件:
353
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/dto/TransferToFoDto.java`
354
- - [ ] T051 [US4] 扩展 `ForecastOrderService.transferToFO(dto)`:校验状态∈{已排产待转单,已部分转单} → 校验转单量≤剩余 → Feign 调用 FO 模块创建 → 更新 transferredQty/remainingQty/状态
355
- - [ ] T052 [US4] 创建 `FoClient` Feign 接口(`POST /fo/create` → 返回 foOrderNo),文件:
356
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/client/FoClient.java`
357
- - [ ] T053 [P] [US4] 创建 `SoClient` Feign 接口(SO 行项目查询),文件:
358
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/client/SoClient.java`
359
- - [ ] T054 [US4] 扩展 `ForecastOrderController`:`POST /transfer-to-fo``forecastOrderService.transferToFO(dto)`
360
-
361
- **检查点**:转 FO 功能正常,Feign 联调通过,TDD 测试红→绿
362
-
363
- ---
364
-
365
- ## Phase 7:US-03 — 查看 APS 排产反馈信息(优先级:P1)
366
-
367
- **目标**:APS 系统排产完成后通知本模块(状态→已排产待转单);销售人员在详情页查看排产进度
368
-
369
- **独立测试**:模拟 APS 发送排产确认通知 → 状态变更为"已排产待转单"→ 详情页展示排产进度
370
-
371
- **来源**:设计文档 §1.1.1 US-03, §4.1 行为 6, §5.1 端点 11, §9.2 消费事件, §12.1 TC1
372
-
373
- ### 用户故事 3 的 TDD 测试
374
-
375
- - [ ] T055 [TDD] [US3] 编写 APS 排产确认测试(状态=计划接收 → 收到通知 → 状态=已排产待转单,乐观锁校验),测试文件:同 ForecastOrderServiceTest.java
376
- - [ ] T056 [TDD] [P] [US3] 编写排产确认并发冲突测试(撤回与排产确认竞争 → version_num 冲突 → 后到者拒绝),测试文件:同上
377
-
378
- ### 用户故事 3 的实现
379
-
380
- - [ ] T057 [P] [US3] 创建 `ScheduleConfirmDto`(字段:forecastOrderNo + confirmTime),文件:
381
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/dto/ScheduleConfirmDto.java`
382
- - [ ] T058 [US3] 扩展 `ForecastOrderService.scheduleConfirm(forecastOrderNo, confirmTime)`:查询 → 校验状态=计划接收 → 乐观锁 version_num → 状态→已排产待转单 → 记录 scheduleConfirmTime
383
- - [ ] T059 [US3] 扩展 `ForecastOrderController`:`POST /schedule-confirm` → `forecastOrderService.scheduleConfirm(dto.getForecastOrderNo(), dto.getConfirmTime())`
384
- - [ ] T060 [P] [US3] 创建 `ApsScheduleConfirmConsumer`(如果采用 Kafka 入站,`@Configuration`, `Consumer<String>` Bean,Bean name 匹配 `spring.cloud.stream.function.definition`),文件:
385
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/app/consume/ApsScheduleConfirmConsumer.java`
386
- - [ ] T061 [P] [US3] 创建 `ApsClient` Feign 接口(`GET /aps/schedule/progress?forecastOrderNo={no}` → 排产进度),文件:
387
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/client/ApsClient.java`
388
-
389
- **检查点**:APS 排产确认通知 → 状态变更;详情页展示排产进度
390
-
391
- ---
392
-
393
- ## Phase 8:US-05 — 通过变更单修改预测单(优先级:P1)
394
-
395
- **目标**:对已排产预测单发起变更申请(客户/入库时间/保税手册/释放资源),提交流程审批,审批通过后联动更新预测单字段
396
-
397
- **独立测试**:已排产预测单 → 新增变更单填写变更后入库时间 → 提交 → 审批通过 → 验证预测单入库时间联动更新
398
-
399
- **来源**:设计文档 §1.1.1 US-05, §4.1 行为 13-18, §5.1 端点 15-23, §12.2 TC4-5
400
-
401
- ### 用户故事 8 的 TDD 测试
402
-
403
- - [ ] T062 [TDD] [US8] 编写变更单新增+审批通过联动测试(库时间变更→预测单入库时间更新;客户变更→客户信息联动;释放资源→需求量扣减+剩余重算),测试文件:
404
- `{port}-{service}-application/src/test/java/com/sie/gushen/{service}/model/service/impl/ForecastChangeOrderServiceTest.java`
405
- - [ ] T063 [TDD] [P] [US8] 编写变更单异常测试(并发控制→异常、CTCT校验失败→异常、非标释放→异常、释放量超限→异常、手册余量不足→异常),测试文件:同上
406
- - [ ] T064 [TDD] [P] [US8] 编写变更单审批驳回测试(已提交→已驳回;驳回后可重新编辑提交),测试文件:同上
407
- - [ ] T065 [TDD] [US8] 编写变更单删除/作废测试(草稿→物理删除;已提交→仅可作废不可物理删除),测试文件:同上
408
-
409
- ### 用户故事 8 的实现
410
-
411
- - [ ] T066 [US8] 创建 `ForecastChangeOrderEntity`(`extends BaseEntity<ForecastChangeOrderEntity>`, `@TableName(``forecast_change_order``)`, 20+ 字段含快照字段+变更字段+审批字段),文件:
412
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/entity/ForecastChangeOrderEntity.java`
413
- - [ ] T067 [P] [US8] 创建 `ForecastChangeOrderDto`(`extends ReqeustCommonDataBean`,含 `saveForecastChangeOrder`/`updateForecastChangeOrder` 分组,按需填写字段),文件:
414
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/dto/ForecastChangeOrderDto.java`
415
- - [ ] T068 [P] [US8] 创建 `ApproveDto`(`extends ReqeustCommonDataBean`,字段:changeOrderId + approvalComment),文件:
416
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/dto/ApproveDto.java`
417
- - [ ] T069 [US8] 创建 `ForecastChangeOrderDAO` 接口 + XML(`extends BaseCommonDAO_HI`, `@Component`, `findListByForecastOrderId` + `findIncompleteByForecastOrderId`,含 `WHERE delete_flag=0`),文件:
418
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/dao/ForecastChangeOrderDAO.java`
419
- `{port}-{service}-model/src/main/java/com/sie/gushen/{service}/model/dao/ForecastChangeOrderDAO.xml`
420
- - [ ] T070 [US8] 创建 `IIForecastChangeOrder` Service 接口(`extends IBaseCommon<ForecastChangeOrderEntity>`),文件:
421
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/model/service/IIForecastChangeOrder.java`
422
- - [ ] T071 [US8] 实现 `ForecastChangeOrderService`(`extends BaseCommonServer<ForecastChangeOrderEntity>`, `@Component`):
423
- - `saveForecastChangeOrder(dto)`:BR-10 并发控制 自动快照原始预测单信息 状态=DRAFT
424
- - `submitForecastChangeOrder(id)`:BR-05 CTCT校验 / BR-12 手册余量 / BR-13 释放量≤剩余 / BR-03 非标不可释放 生成单号 → 状态=已提交
425
- - `approveForecastChangeOrder(id, comment)`:状态=已提交 **原子操作**:变更单状态+预测单联动更新(入库时间/客户/手册/释放量)
426
- - `rejectForecastChangeOrder(id, comment)`:状态=已提交 状态=已驳回
427
- - `withdrawForecastChangeOrder(id)`:状态=已提交 → 状态=草稿
428
- - `voidForecastChangeOrder(id)`:状态∈{草稿,已提交,已驳回} → 状态=已作废
429
- - `deleteForecastChangeOrder(id)`:状态∈{草稿,已驳回} → 物理删除
430
- 文件:`{port}-{service}-application/src/main/java/com/sie/gushen/{service}/model/service/impl/ForecastChangeOrderService.java`
431
- - [ ] T072 [US8] 创建 `ForecastChangeOrderController`(`extends CommonAbstractService`, `@RequestMapping("/forecast-change-order")`, 9 个端点),文件:
432
- `{port}-{service}-application/src/main/java/com/sie/gushen/{service}/api/controller/ForecastChangeOrderController.java`
433
- - [ ] T073 [P] [US8] 创建 `BondedManualClient` Feign 接口(P03 保税手册查询),文件:
434
- `{port}-{service}-api/src/main/java/com/sie/gushen/{service}/api/client/BondedManualClient.java`
435
-
436
- **检查点**:变更单全流程通过——新增→提交→审批通过→预测单联动更新,原子操作验证通过
437
-
438
- ---
439
-
440
- ## Phase 9:US-06 + US-07 — 多维度概览 + 强制释放(优先级:P2)
441
-
442
- **目标**:提供按时间/客户/品种维度的预测单概览视图;定时任务对标品超期未转单自动强制释放
443
-
444
- **来源**:设计文档 §1.1.1 US-06+US-07, §4.1 行为 11-12, §5.1 端点 13-14
445
-
446
- ### 用户故事 9 的 TDD 测试
447
-
448
- - [ ] T074 [TDD] [US9] 编写概览聚合查询测试(按时间/客户/品种维度查询 → 返回聚合数据),测试文件:同 ForecastOrderServiceTest.java
449
- - [ ] T075 [TDD] [P] [US9] 编写强制释放测试(标品+超期+已排产待转单+已转单=0→状态=已释放;已转单>0→状态=已全部转单完成;非标不触发),测试文件:同上
450
-
451
- ### 用户故事 9 的实现
452
-
453
- - [ ] T076 [US9] 扩展 `ForecastOrderDAO.xml`:新增 `findOverview` 聚合查询 SQLGROUP BY + 聚合函数)
454
- - [ ] T077 [US9] 扩展 `ForecastOrderService`:
455
- - `findOverview(params)`:调用 DAO 聚合查询
456
- - `forceRelease()`:查询标品+超期+状态∈{已排产待转单,已部分转单} → 已转单>0→已全部转单完成;已转单=0→已释放
457
- - [ ] T078 [US9] 扩展 `ForecastOrderController`:
458
- - `POST /overview` → `forecastOrderService.findOverview(params)`
459
- - `POST /force-release` `forecastOrderService.forceRelease()`(系统级 Token)
460
-
461
- **检查点**:概览视图可用,强制释放定时任务可触发
462
-
463
- ---
464
-
465
- ## Phase 10:US-10 — 系统配置管理(优先级:P2)
466
-
467
- **目标**:系统管理员配置提报提前期天数(1~365)和强制转FO天数 N(1~90),Redis 缓存加速
468
-
469
- **来源**:设计文档 §1.1.1 US-10, §8.1 缓存策略
470
-
471
- - [ ] T079 [TDD] [US10] 编写配置读写测试(读取缓存命中 → 返回;写入 → 双写 Redis+DB),测试文件:同 ForecastOrderServiceTest.java
472
- - [ ] T080 [US10] 扩展 `ForecastOrderService`:`getLeadTimeConfig()` + `updateLeadTimeConfig(days)` / `getForceFoDaysConfig()` + `updateForceFoDaysConfig(days)`(通过 `ServerConfigConmonUtil` + `DawnRedis` 实现)
473
- - [ ] T081 [US10] 扩展 `ForecastOrderController`:配置管理端点
474
-
475
- ---
476
-
477
- ## Final Phase:集成与收尾
478
-
479
- **目的**:跨故事的质量保证、国际化、文档与合规验证
480
-
481
- ### 集成测试
482
-
483
- - [ ] T082 [TDD] 编写端到端集成测试(创建→提交→接收→排产确认→转FO 全流程),文件:
484
- `{port}-{service}-application/src/test/java/com/sie/gushen/{service}/integration/ForecastOrderE2ETest.java`
485
- - [ ] T083 [TDD] [P] 编写变更单审批联动端到端测试(变更入库时间→审批→预测单联动),文件:同上
486
-
487
- ### i18n 国际化(7 文件)
488
-
489
- - [ ] T084 [P] 创建 `{port}-{service}-application.properties`(默认中文,Unicode 转义),含所有消息码(字段校验 001~005 + 业务异常 006~017),文件:
490
- `{port}-{service}-application/src/main/resources/config/{port}-{service}-application.properties`
491
- - [ ] T085 [P] 创建 `{port}-{service}-application_zh_CN.properties`(简体中文),文件:同上目录
492
- - [ ] T086 [P] 创建 `{port}-{service}-application_zh_TW.properties`(繁体中文),文件:同上目录
493
- - [ ] T087 [P] 创建 `{port}-{service}-application_en_US.properties`(英文),文件:同上目录
494
- - [ ] T088 [P] 创建 `{port}-{service}-application_vi_VN.properties`(越南语),文件:同上目录
495
- - [ ] T089 [P] 创建 `{port}-{service}-application_th_TH.properties`(泰语),文件:同上目录
496
- - [ ] T090 [P] 创建 `{port}-{service}-application_hu_HU.properties`(匈牙利语),文件:同上目录
497
-
498
- ### 代码评审与安全
499
-
500
- - [ ] T091 执行 ECP 编码规范 10 条合规检查(对照 §ECP 编码规范检查清单,逐条确认)
501
- - [ ] T092 安全清单验证(所有端点 `@CheckToken`、权限矩阵 §5.4 逐项检查、敏感操作二次确认、SQL 注入防范)
502
- - [ ] T093 代码评审(按 `.adspecs/templates/06-代码评审报告模板.md` 执行)
503
-
504
- ### 文档与构建
505
-
506
- - [ ] T094 `mvn clean -U package` 构建验证(三模块编译通过 + 单元测试全绿)
507
- - [ ] T095 API 文档完善(SpringDoc 注解 → Swagger UI 可访问,按 `.adspecs/templates/03-系统设计模板.md` 中的 SpringDocConfig 配置)
508
-
509
- ---
510
-
511
- ## 依赖关系与执行顺序
512
-
513
- ### 阶段依赖
514
-
515
- | 阶段 | 依赖 | 说明 |
516
- |------|------|------|
517
- | Phase 1(环境搭建) | | 可立即开始 |
518
- | Phase 2(基础设施) | Phase 1 完成 | **阻塞所有用户故事** |
519
- | Phase 3(US-01 创建提报) | Phase 2 完成 | P1 MVP 起点,US-04/05/08/09 依赖其实体 |
520
- | Phase 4(US-02 评审) | Phase 2 + Phase 3 实体 | 可独立并行(不同 Service 方法,同一实体) |
521
- | Phase 5(US-09/08 撤回取消) | Phase 2 + Phase 3 实体 | 可独立并行 |
522
- | Phase 6(US-04 转FO) | Phase 2 + Phase 3 实体 | 依赖 ForecastOrderEntity |
523
- | Phase 7(US-03 排产) | Phase 2 + Phase 3 实体 | 可独立并行 |
524
- | Phase 8(US-05 变更) | Phase 2 + Phase 3 + 子表实体 | 依赖 ForecastOrderEntity + ForecastChangeOrderEntity |
525
- | Phase 9(US-06/07 概览释放) | Phase 2 + Phase 3 实体 | P2 优先级,可在 P1 完成后开始 |
526
- | Phase 10(US-10 配置) | Phase 2 | P2 优先级,与 P1 故事独立 |
527
- | Final Phase | 所有目标用户故事完成 | 集成测试需全功能就绪 |
528
-
529
- ### 用户故事间依赖
530
-
531
- - **US-01(P1)**:Phase 3,创建 ForecastOrderEntity + Service + Controller,是所有 P1 故事的基础。**阻塞:无**
532
- - **US-02(P1)**:Phase 4,仅新增 Service 方法,可与 US-01 并行(同 Service 类,不同方法)。**阻塞:ForecastOrderEntity 创建完成**
533
- - **US-09+US-08(P1+P2)**:Phase 5,仅新增 Service 方法,可独立并行。**阻塞:ForecastOrderEntity + Service 基本方法完成**
534
- - **US-04(P1)**:Phase 6,新增 DTO + Feign Client + Service 方法。**阻塞:ForecastOrderEntity + Service**
535
- - **US-03(P1)**:Phase 7,新增 DTO + Consumer + Feign Client。**阻塞:ForecastOrderEntity + Service**
536
- - **US-05(P1)**:Phase 8,新增 Entity + DTO + DAO + Service + Controller,变更量最大。**阻塞:ForecastOrderEntity(主表)**
537
- - **US-06+US-07(P2)**:Phase 9,P2 可最后实现。**阻塞:P1 故事完成**
538
- - **US-10(P2)**:Phase 10,P2 独立于业务故事。**阻塞:Phase 2**
539
-
540
- ### 每个用户故事内部的 TDD 执行顺序
541
-
542
- ```
543
- [STOP] 确认 Phase 2 基础设施已就绪
544
-
545
- [TDD] 编写 Service 层单元测试(正常 + 异常 + 边界)→ mvn test → 确认失败 ❌
546
-
547
- [TDD] 编写 Controller 层集成测试 → mvn test → 确认失败 ❌
548
-
549
- 创建 Entity(extends BaseEntity, @TableName, @TableField+@Schema, IdType.ASSIGN_ID)
550
-
551
- 创建 DTO(extends ReqeustCommonDataBean, 校验分组, @JSONField)
552
-
553
- 创建 Ro(implements Serializable, 手动字段, @Schema)
554
-
555
- 创建 DAO 接口 + XML(extends BaseCommonDAO_HI, resultMap, Base_Column_List, WHERE delete_flag=0)
556
-
557
- 创建 Service 接口 + 实现(Save→FastJSON, Update→SpringBeanUtils, 异常在Service抛出)
558
-
559
- 创建 Controller(extends CommonAbstractService, @CheckToken, ResponseData 包装)
560
-
561
- 创建 Feign Client(@FeignClient 接口,放入 -api/client/)
562
-
563
- [TDD] 重新运行测试 → mvn test → 全部通过 ✅
564
-
565
- [REFACTOR] 提取公共逻辑、优化命名、清理重复代码 → 保持测试绿
566
- ```
567
-
568
- **并行加速**:标记 `[P]` Entity/DTO/Ro/DAO 可同时创建(互不依赖),Service 实现必须在 DAO 完成后。
569
-
570
- ### 并行机会
571
-
572
- - **Phase 2**:所有 7 个枚举类可并行创建(T007~T013)
573
- - **Phase 2**:Liquibase 3 文件可串行(依赖顺序),但与枚举/配置可并行
574
- - **Phase 2**:消息码与配置类可并行(T014~T018)
575
- - **Phase 3**:Entity + DTO + Ro 可并行(T027~T029),Service 接口+DAO 可并行(T030~T031)
576
- - **跨故事并行**:Phase 3~8 中,US-02/US-03/US-04/US-05/US-09 可在 US-01 Entity 完成后并行推进(各自独立的 Controller 方法或独立子表)
577
- - **Final Phase**:7 个 i18n 文件可完全并行(T084~T090)
578
-
579
- ---
580
-
581
- ## 并行示例:US-01 创建并提报预测单
582
-
583
- ```bash
584
- # Step 1: 先编写所有测试(并行)→ 确保失败
585
- Task T022: "[TDD] 编写提交预测单测试(正常场景)"
586
- Task T023: "[TDD] 编写提交预测单异常测试(提前期拦截)"
587
- Task T024: "[TDD] 编写提交预测单边界测试(MOQ/手册余量)"
588
- Task T025: "[TDD] 编写保存草稿测试"
589
- Task T026: "[TDD] 编写 Controller 集成测试"
590
-
591
- # 所有测试验证失败后 ↓
592
-
593
- # Step 2: 并行创建 Entity + DTO + Ro(互不依赖)
594
- Task T027: "创建 ForecastOrderEntity.java"
595
- Task T028: "创建 ForecastOrderDto.java"
596
- Task T029: "创建 ForecastOrderEntityRo.java"
597
-
598
- # Step 3: 并行创建 DAO + Service 接口(都依赖 Entity)
599
- Task T030: "创建 ForecastOrderDAO.java + ForecastOrderDAO.xml"
600
- Task T031: "创建 IIForecastOrder.java"
601
-
602
- # Step 4: Service 实现(依赖 DAO + Service 接口)
603
- Task T032: "实现 ForecastOrderService.java"
604
-
605
- # Step 5: Controller + Feign Client 可并行
606
- Task T033: "创建 ForecastOrderController.java"
607
- Task T034: "创建 CtctClient.java"
608
-
609
- # 运行测试 → 全部通过 ✅
610
- ```
611
-
612
- ---
613
-
614
- ## 实现策略
615
-
616
- ### MVP 优先(P1 用户故事:US-01 + US-02 + US-09 + US-04 + US-03 + US-05)
617
-
618
- 1. Phase 1:Maven 三模块环境搭建
619
- 2. Phase 2:基础设施(枚举 + Liquibase + 消息码 + 配置)**← 关键阻塞点**
620
- 3. Phase 3:US-01 创建并提报预测单 **← MVP 核心**
621
- 4. Phase 4:US-02 计划部门评审(并行追加)
622
- 5. Phase 5:US-09+US-08 撤回/取消/删除(并行追加)
623
- 6. Phase 6:US-04 转履约单(FO)(并行追加)
624
- 7. Phase 7:US-03 APS 排产反馈(并行追加)
625
- 8. Phase 8:US-05 变更单修改预测单(并行追加)
626
- 9. **STOP & VALIDATE**:全部 P1 故事端到端测试通过
627
- 10. P1 MVP 就绪 → 部署/演示
628
-
629
- ### 增量交付
630
-
631
- 1. Phase 1+2 → 基础就绪
632
- 2. Phase 3 → US-01 可独立测试 → 部署(MVP 最小可交付!)
633
- 3. Phase 4+5+6+7+8 → 逐个追加 P1 故事 → 每个故事独立测试 → 增量部署
634
- 4. Phase 9+10 → P2 故事追加
635
- 5. Final Phase → 集成收尾
636
-
637
- ### 团队并行策略
638
-
639
- 1. 团队共同完成 Phase 1+2(环境搭建 + 基础设施)
640
- 2. Phase 2 完成后:
641
- - **开发者 A**:Phase 3(US-01 创建提报)**← 最高优先级,产出核心 Entity**
642
- - **开发者 B**:Phase 2 的 Liquibase + 配置类
643
- 3. US-01 Entity 完成后:
644
- - **开发者 A**:Phase 8(US-05 变更单,子表工作量大)
645
- - **开发者 B**:Phase 4+5+6+7(US-02/09/04/03,同一主表 Service 的不同方法)
646
- 4. P1 全部完成 → 开发者 A+B 共同完成 Phase 9+10(P2 故事)+ Final Phase
647
-
648
- ---
649
-
650
- ## ECP 编码规范检查清单
651
-
652
- > 生成 tasks.md 时必须逐条确认。此清单与 `adspecs-tasks` skill §ECP 平台编码规范完全对齐。
653
-
654
- | # | 规范 | 检查项 | 通过 |
655
- |---|------|--------|------|
656
- | 1 | **Entity** | `extends BaseEntity<Entity>`, `@TableName(``table``)`, 所有字段 `@TableField` + `@Schema`, `IdType.ASSIGN_ID`,审计字段继承不重复 | ☐ |
657
- | 2 | **DTO** | `extends ReqeustCommonDataBean`(注意拼写),校验分组内部接口(`save/update`),`@JSONField(format)`,消息码格式 `{port}_{table}_{三位序号}` | ☐ |
658
- | 3 | **DAO** | `extends BaseCommonDAO_HI<Entity>`, `@Component("xxxDAO")`,XML 同包,`resultMap` 含全字段+审计,所有 SELECT 含 `WHERE delete_flag = 0` | ☐ |
659
- | 4 | **禁止 QueryWrapper** | 所有 DB 操作通过 MyBatis XML,任务描述中无 QueryWrapper/LambdaQueryWrapper 字样 | ☐ |
660
- | 5 | **Service** | 接口 `II{Entity}` extends `IBaseCommon<Entity>`,实现 `@Component("xxxService")` extends `BaseCommonServer<Entity>`, Save→FastJSON, Update→SpringBeanUtils | ☐ |
661
- | 6 | **Controller** | `extends CommonAbstractService`,重写 `getBaseCommonServer()`,`@CheckToken`, `ResponseData<T>` 包装 | ☐ |
662
- | 7 | **Ro** | `implements Serializable` + `serialVersionUID`,手动字段,`@DateTimeFormat @JsonFormat`,置于 `model/entity/readonly/` | ☐ |
663
- | 8 | **异常** | 异常在 Service 层抛出,Controller 仅参数空校验,消息码枚举禁止硬编码 | ☐ |
664
- | 9 | **i18n** | 7 个 `.properties`(default/zh_CN/zh_TW/en_US/vi_VN/th_TH/hu_HU),每个含该语言实际内容 | ☐ |
665
- | 10 | **注入与工具** | `@Autowired` 注入,FastJSON 序列化,Hutool 优先,SLF4J 日志 | ☐ |
666
-
667
- ---
668
-
669
- ## TDD 执行指南
670
-
671
- > 本模板任务顺序与 `test-driven-development` skill 的 RED→GREEN→REFACTOR 循环完全对齐。
672
-
673
- ### RED 阶段:编写测试并验证失败
674
-
675
- ```bash
676
- # 1. 按 [TDD] 标记的任务编写测试代码
677
- # 2. 运行并确认失败
678
- mvn test -Dtest=ForecastOrderServiceTest
679
-
680
- # 预期输出:Tests run: 5, Failures: 5(功能未实现,预期失败)
681
- # 如果测试通过 → 测试缺陷,修测试
682
- # 如果测试报错(非失败) → 编译错误/依赖缺失,修环境
683
- ```
684
-
685
- ### GREEN 阶段:最小代码使测试通过
686
-
687
- ```
688
- 按 TDD 任务之后的实现任务顺序:
689
- Entity → DTO → Ro → DAO+XML → Service → Controller → Feign Client
690
-
691
- 每完成一个实现任务:
692
- mvn test -Dtest=ForecastOrderServiceTest # 逐步变绿
693
-
694
- Service 实现完成后 → 全部测试应变绿 ✅
695
- ```
696
-
697
- ### REFACTOR 阶段:清理与优化
698
-
699
- ```bash
700
- # 所有测试通过后:
701
- - 提取重复的校验逻辑到工具类
702
- - 优化命名(方法名/变量名与业务一致)
703
- - 添加日志注解(关键操作 @Slf4j)
704
- - 运行全量测试确认不破坏
705
- mvn test # 必须全绿
706
- ```
707
-
708
- ---
709
-
710
- ## 注意事项
711
-
712
- - `[P]` 任务 = 操作不同文件,无未完成依赖,可并行执行
713
- - `[TDD]` 任务 = 测试任务,**必须在同故事的实现任务之前编写并通过失败验证**
714
- - `[Story]` 标签将任务映射到设计文档 §1.1.1 中的具体用户故事,支持全链路追溯
715
- - **所有文件路径必须精确到三模块**(`{port}-{service}-api/model/application/`)
716
- - **ECP 编码规范 10 条是硬性约束**,违反任一条 = 任务不合格
717
- - **Save 用 FastJSON**:`JSONObject.parseObject(JSONObject.toJSONString(dto), Entity.class)`
718
- - **Update 用 Spring BeanUtils**:`BeanUtils.copyProperties(dto, entity, "createdBy", "creationDate", "versionNum")`
719
- - **WHERE delete_flag = 0**:所有 MyBatis XML SELECT 语句必须包含
720
- - **消息码禁止硬编码**:所有异常和校验信息引用 `ResponseMsgCode` 枚举
721
- - **异常在 Service 层抛出**:Controller 仅做参数空校验返回 `ResponseData.fail()`
722
- - 每个任务或逻辑分组后建议 git commit
723
- - 可在任何检查点停下验证故事的独立可用性
724
- - 避免:模糊任务、同文件冲突、跨故事强耦合、违反 TDD 顺序(实现先于测试)
1
+ ---
2
+
3
+ description: "后端功能实现的 TDD 任务列表模板。支持 ECP Java / Yudao Java / Python 技术栈自动检测"
4
+ ---
5
+
6
+ ### 修订记录
7
+
8
+ | 版本号 | 修订日期 | 修订人 | 修订内容 |
9
+ | ------ | -------- | ------ | -------- |
10
+ | v3.0 | 2026-07-28 | AI | 技术栈解耦改造:去掉 M06-01 销售预测单具体类名,抽象为纯结构模板;新增三栈检测(ECP Java / Yudao Java / Python)+ 三栈映射表;编码规范检查清单拆分为栈无关项 + 栈专属项;Phase 任务示例全部使用 `{Entity}`/`{Service}`/`{Controller}` 等占位符 |
11
+ | v2.0 | 2026-06-13 | AI | ECP 平台专项改造:通用 Python 模板 ECP Java Maven 三模块 TDD 模板 |
12
+ | v1.0 | 2026-06-12 | AI | 初始版本:通用 spec-kit 任务模板(Python 示例) |
13
+
14
+ # 任务清单:[功能名称]
15
+
16
+ **设计文档来源**:
17
+ - 系统设计文档:`docs/30-system-design/{module}/[功能名]_system-design.md`(必读,由 `adspecs-plan` 产出,包含 OOA+OOD 13 章节统一设计)
18
+ - PRD 文档:`docs/20-prd/{module}/[模块]/[需求规格说明书].md`(可选,用户故事验收场景参考)
19
+ - 建表 SQL:`docs/30-system-design/{module}/[功能名]_schema.sql`(可选,DDL 直接可用)
20
+
21
+ **前置条件**:
22
+ - 调用 `/adspecs-tasks` 前,必须已完成 `/adspecs-plan` 产出的 `_system-design.md`
23
+ - 系统设计文档必须包含:§1 需求映射、§2 实体+DTO/Ro、§3 数据模型、§4 Service 设计、§5 Controller 端点、§6 枚举+校验、§7 异常+消息码、§13 代码结构
24
+
25
+ **TDD 约定**:本模板遵循 `test-driven-development` skill 的"测试先行"原则——所有实现任务之前必须先有失败的测试任务。模板中的样本任务包含 `[TDD]` 标记的测试任务。
26
+
27
+ **组织方式**:任务按用户故事分组(设计文档 §1.1.1),以支持每个故事的独立 TDD 实现和独立测试。
28
+
29
+ ---
30
+
31
+ ## 技术栈检测(任务生成前置)
32
+
33
+ > **目的**:确定本项目的后端技术栈,后续所有术语、路径、示例均按检测到的栈展开。
34
+ > **输出变量**:`BACKEND_STACK`(`ecp-java` | `yudao-java` | `python`)和 `BACKEND_SERVICE_DIR`(后端服务根目录名,默认 `service/`)。
35
+
36
+ 按以下优先级依次尝试,命中任意一级即停止:
37
+
38
+ | 优先级 | 检测源 | 判定规则 |
39
+ | ------ | ------ | -------- |
40
+ | 1 | `.adspecs/project.json` | 读取 `backend_stack` 字段(`ecp-java` / `yudao-java` / `python`)。`/project-init` 在初始化时写入 |
41
+ | 2 | 项目根 `CLAUDE.md` §Backend | 关键字命中:含 `ECP`/`com.sie.gushen`/`BaseCommonServer` `ecp-java`;含 `Yudao`/`yudao-cloud`/`BaseMapperX` → `yudao-java`;含 `FastAPI`/`SQLAlchemy`/`Celery` → `python` |
42
+ | 3 | 系统设计文档 `_system-design.md` §技术栈 | 文档中声明的框架 |
43
+ | 4 | 项目目录结构启发式 | `pom.xml` + `com/sie/gushen` → `ecp-java`;`pom.xml` + `com/yudaocloud` 或 `com/sie/yudao` → `yudao-java`;`pyproject.toml` 或 `requirements.txt` → `python` |
44
+ | 5 | 回退:询问用户 | 使用 AskUserQuestion 让用户选择 `ECP Java` / `Yudao Java` / `Python` |
45
+
46
+ **`BACKEND_SERVICE_DIR` 检测**:
47
+ - 优先从 `.adspecs/project.json` `backend_dir` 字段读取
48
+ - 其次从 `CLAUDE.md` §Backend 中提取后端服务根目录名
49
+ - 默认值:`service/`
50
+
51
+ 检测完成后,在内部记录两个变量,后续章节按此展开:
52
+ ```
53
+ BACKEND_STACK = ecp-java | yudao-java | python
54
+ BACKEND_SERVICE_DIR = {后端服务根目录名}
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 任务格式:`- [ ] [TaskID] [P?] [TDD?] [Story?] 描述 + 精确文件路径`
60
+
61
+ **格式组成**:
62
+
63
+ | 元素 | 说明 |
64
+ |------|------|
65
+ | `- [ ]` | Markdown 复选框(始终存在) |
66
+ | `[TaskID]` | 全局递增序号:T001, T002, T003... |
67
+ | `[P]` | 可并行执行标记(操作不同文件,无未完成依赖) |
68
+ | `[TDD]` | 测试任务标记(Service 层单元测试 / Controller 层集成测试),**必须在同故事实现任务之前完成且验证失败** |
69
+ | `[Story]` | 用户故事标签:[US1], [US2]...(对应设计文档 §1.1.1)。Phase 1/2/Final 无此标签 |
70
+ | 描述 + 文件路径 | 明确操作描述 + 从服务根目录开始的完整文件路径。多文件任务可分行列出路径 |
71
+
72
+ **格式示例(按 `BACKEND_STACK` 展开)**:
73
+
74
+ *ECP Java 栈示例*:
75
+
76
+ ```text
77
+ - [ ] T012 [TDD] [US1] 编写 {Service} 提交{Entity} 单元测试(正常+拦截+边界),测试文件:
78
+ {BACKEND_SERVICE_DIR}/{port}-{service}-application/src/test/java/{PACKAGE_PATH}/model/service/impl/{Entity}ServiceTest.java
79
+ - [ ] T013 [US1] 创建 {Entity}extends BaseEntity<{Entity}>, @TableName(`{table}`), @TableId(type=ASSIGN_ID), N+ 字段全带 @TableField+@Schema),文件:
80
+ {BACKEND_SERVICE_DIR}/{port}-{service}-model/src/main/java/{PACKAGE_PATH}/model/entity/{Entity}Entity.java
81
+ ```
82
+
83
+ *Yudao Java 栈示例*:
84
+
85
+ ```text
86
+ - [ ] T012 [TDD] [US1] 编写 {Service} 单元测试(正常+异常+边界),测试文件:
87
+ {BACKEND_SERVICE_DIR}/{module}-service/src/test/java/{PACKAGE_PATH}/service/{Entity}ServiceTest.java
88
+ - [ ] T013 [US1] 创建 {Entity}(extends BaseDO, @TableName(`{table}`), N+ 字段全带 @TableField+@Schema),文件:
89
+ {BACKEND_SERVICE_DIR}/{module}-service/src/main/java/{PACKAGE_PATH}/dal/dataobject/{Entity}DO.java
90
+ ```
91
+
92
+ *Python 栈示例*:
93
+
94
+ ```text
95
+ - [ ] T012 [TDD] [US1] 编写 {service} 服务层单元测试(正常+异常+边界),测试文件:
96
+ {BACKEND_SERVICE_DIR}/tests/test_{service}.py
97
+ - [ ] T013 [US1] 创建 {Entity}Model(SQLAlchemy ORM,Column 定义 N+ 字段),文件:
98
+ {BACKEND_SERVICE_DIR}/app/models/{service}.py
99
+ ```
100
+
101
+ **错误示例**:
102
+
103
+ ```text
104
+ - 错误:`- [ ] 创建 {Entity} 实体`(缺少 TaskID + 文件路径 + Story 标签)
105
+ - ❌ 错误:`T001 [US1] 创建 Entity`(缺少复选框 + 精确文件路径)
106
+ - 错误:`- [ ] T001 [US1] 创建 Entity`(缺少文件路径)
107
+ - ❌ 错误:`- [ ] T010 [US1] 实现提交方法`(故事阶段内未先写 [TDD] 测试,违反 TDD 规则)
108
+ - 错误:`- [ ] T020 [TDD] [US1] 测试提交`(描述模糊,文件路径缺失)
109
+ ```
110
+
111
+ ---
112
+
113
+ ## 后端技术栈映射表
114
+
115
+ > 根据 §技术栈检测 得到的 `BACKEND_STACK` 选择对应列。所有 Phase 任务中的占位符均按此表展开。
116
+
117
+ | 概念 | ECP Java (`ecp-java`) | Yudao Java (`yudao-java`) | Python (`python`) |
118
+ | ---- | --------------------- | ------------------------- | ----------------- |
119
+ | 框架 | Spring Boot + ECP 平台扩展 | Spring Boot + Yudao Cloud | FastAPI + SQLAlchemy |
120
+ | 模块结构 | Maven 三模块(`-api`/`-model`/`-application`) | 单模块 / 多模块(`-service` 主模块) | 单包(`app/` + `tests/`) |
121
+ | 实体 | `extends BaseEntity<T>` + `@TableName` + `@TableField` + `@Schema`,`IdType.ASSIGN_ID` | `extends BaseDO` + `@TableName` + `@TableField` + `@Schema` | `class {Entity}Model(Base)` + SQLAlchemy Column |
122
+ | DTO | `extends ReqeustCommonDataBean`,校验分组内部接口,`@JSONField(format)` | `implements Serializable` + `@Schema` + JSR-303 注解 | Pydantic `BaseModel` + Field 校验 |
123
+ | Ro/VO | `implements Serializable` + `serialVersionUID`,手动字段,置于 `model/entity/readonly/` | `extends BaseConvert` VO 对象 | Pydantic Response Schema |
124
+ | DAO/Repository | `extends BaseCommonDAO_HI<T>` + MyBatis XML 同包,`@Component("xxxDAO")` | `extends BaseMapperX<T>` + MyBatis-Plus `@Mapper` | SQLAlchemy `session.query` / async session |
125
+ | Service 接口 | `II{Entity}` extends `IBaseCommon<T>`(双 I 前缀) | 直接 Java 接口定义 | `Protocol` `ABC` 抽象类 |
126
+ | Service 实现 | `extends BaseCommonServer<T>` + `@Component("xxxService")`,Save→FastJSON,Update→SpringBeanUtils | `@Service` + `BaseServiceImpl` POJO | 函数 + `Depends` 或 class |
127
+ | Controller | `extends CommonAbstractService` + `@CheckToken` + `ResponseData<T>` 包装 | `@RestController` + `@Tag` + `@PreAuthorize` + `CommonResult<T>` | `@router` + `Depends` 权限 |
128
+ | Feign/RPC Client | `@FeignClient` 接口,放入 `-api/client/` | `@FeignClient` 接口 | `httpx.AsyncClient` 封装 |
129
+ | 枚举 | 普通 `enum`,存储值 `String` | `@Getter` + `@AllArgsConstructor` enum | Python `Enum` / `StrEnum` |
130
+ | 消息码/异常 | `enum ResponseMsgCode`,格式 `{port}_{table}_{三位序号}` | `ErrorCodeEnum` + `ServiceException` | 自定义 Exception + error_code |
131
+ | 消息消费 | Kafka `Consumer<String>` Bean,Bean name 匹配 YAML | RocketMQ `@RocketMQMessageListener` | Celery `@app.task` |
132
+ | i18n | 7 个 `.properties`(default/zh_CN/zh_TW/en_US/vi_VN/th_TH/hu_HU) | `messages_{locale}.properties` | `.po` / `.json` 文件 |
133
+ | DB 迁移 | Liquibase(master.xml → version.xml → DDL.sql) | Flyway(`V{version}__{name}.sql`) | Alembic(`env.py` + migration scripts) |
134
+ | 工具类 | Hutool + SLF4J | Hutool / Spring 工具类 | Python 标准库 + loguru |
135
+ | 配置类 | `@Configuration`(DataSource / MybatisPlus / SpringDoc) | `@Configuration` + `@ConfigurationProperties` | Pydantic Settings |
136
+ | 启动类 | `extends CommonApplication` + `@SpringBootApplication` + `@EnableDiscoveryClient` + `@EnableFeignClients` + `@MapperScan` | `@SpringBootApplication` + `@MapperScan` | `uvicorn` 入口 |
137
+ | 测试框架 | JUnit 5 + Mockito + `@SpringBootTest` | JUnit 5 + Mockito + `@SpringBootTest` | pytest + pytest-asyncio |
138
+ | 包路径 | `com.sie.gushen.{service}` | `com.yudaocloud.{module}` 或项目自定义 | Python 模块(`app.{module}`) |
139
+ | 参考规范 | `references/ecp-end-standard/` | `references/yudaocloud-end-standard/` | `references/python-end-standard/` |
140
+
141
+ ---
142
+
143
+ ## 后端项目路径约定
144
+
145
+ > 来源:CLAUDE.md §Backend + 系统设计文档 §13 代码结构。下方目录树按 `BACKEND_STACK` 展开。
146
+
147
+ ### ECP Java 三模块路径
148
+
149
+ ```
150
+ {BACKEND_SERVICE_DIR}/{port}-{service}/ # 服务根目录
151
+ ├── {port}-{service}-api/ # 契约层
152
+ │ └── src/main/java/{PACKAGE_PATH}/api/
153
+ │ ├── client/ # @FeignClient 接口
154
+ │ └── dto/ # DTO
155
+ ├── {port}-{service}-model/ # 数据层
156
+ │ ├── src/main/java/{PACKAGE_PATH}/model/
157
+ │ │ ├── entity/ # Entity
158
+ │ │ │ └── readonly/ # Ro
159
+ │ │ └── dao/ # DAO 接口 + MyBatis XML 同包
160
+ │ └── src/main/resources/liquibase/mysql/{service}/ # Liquibase
161
+ └── {port}-{service}-application/ # 实现层
162
+ ├── src/main/java/{PACKAGE_PATH}/
163
+ │ ├── {Service}Application.java # 启动类
164
+ │ ├── api/controller/ # Controller
165
+ │ ├── model/service/ # Service 接口 + 实现
166
+ │ ├── app/consume/ # Kafka Consumer
167
+ │ └── infra/ # 配置/常量/枚举/工具
168
+ └── src/main/resources/config/ # i18n .properties
169
+ ```
170
+
171
+ ### Yudao Java 路径
172
+
173
+ ```
174
+ {BACKEND_SERVICE_DIR}/{module}-{service}/ # 服务根目录
175
+ ├── {module}-service/ # 主模块
176
+ │ └── src/main/java/{PACKAGE_PATH}/
177
+ │ ├── controller/admin/ # Controller
178
+ │ ├── controller/app/ # App 端 Controller
179
+ │ ├── dal/ # 数据层
180
+ │ │ ├── dataobject/ # DO(extends BaseDO)
181
+ │ │ ├── mysql/ # Mapper(extends BaseMapperX)
182
+ │ │ └── redis/ # Redis
183
+ │ ├── service/ # Service
184
+ │ ├── convert/ # VO ↔ DO 转换
185
+ │ ├── controller/admin/vo/ # VO 对象
186
+ │ ├── framework/ # 模块配置
187
+ │ └── enums/ # 枚举 + ErrorCode
188
+ └── {module}-api/ # API 模块(可选)
189
+ └── src/main/java/{PACKAGE_PATH}/api/
190
+ ├── enums/ # API 枚举
191
+ └── dto/ # API DTO
192
+ ```
193
+
194
+ ### Python 路径
195
+
196
+ ```
197
+ {BACKEND_SERVICE_DIR}/ # 服务根目录
198
+ ├── app/ # 应用主包
199
+ │ ├── {module}/ # 业务模块
200
+ │ │ ├── router.py # FastAPI 路由
201
+ │ │ ├── service.py # 服务层
202
+ │ │ ├── schemas.py # Pydantic DTO/Schema
203
+ │ │ ├── dependencies.py # Depends 注入
204
+ │ │ └── crud.py # CRUD 封装
205
+ │ ├── models/ # SQLAlchemy Model
206
+ │ │ └── {module}.py
207
+ │ ├── core/ # 核心配置
208
+ │ │ ├── config.py # Pydantic Settings
209
+ │ │ ├── security.py # 权限/Token
210
+ │ │ └── exceptions.py # 自定义异常
211
+ │ ├── db/ # 数据库
212
+ │ │ ├── session.py # Session 工厂
213
+ │ │ └── migrations/ # Alembic 迁移
214
+ │ ├── tasks/ # Celery 异步任务
215
+ │ │ └── {module}.py
216
+ │ └── utils/ # 工具函数
217
+ ├── tests/ # 测试
218
+ │ └── test_{module}.py
219
+ └── pyproject.toml # 依赖配置
220
+ ```
221
+
222
+ **变量替换**:
223
+ - `{port}`:微服务端口号(仅 Java 栈),如 `3103`
224
+ - `{service}`/`{module}`:服务/模块名(kebab-case),如 `sales-forecast`
225
+ - `{PACKAGE_PATH}`:Java 包路径(`.` 分隔),Python 栈无此项
226
+
227
+ ---
228
+
229
+ <!--
230
+ ============================================================================
231
+ 重要提示:以下任务是**模板结构示例**,使用占位符 `{Entity}`/`{Service}`/`{Controller}` 等。
232
+
233
+ /adspecs-tasks 命令将根据系统设计文档替换为实际任务:
234
+ - 系统设计文档 §1.1.1 中的用户故事(及其优先级 P1、P2...)
235
+ - 系统设计文档 §2 中的实体/DTO/Ro 清单
236
+ - 系统设计文档 §3 中的数据模型
237
+ - 系统设计文档 §4 中的 Service 方法与业务行为
238
+ - 系统设计文档 §5 中的 Controller 端点
239
+ - 系统设计文档 §6 中的枚举定义
240
+ - 系统设计文档 §7 中的异常与消息码
241
+ - 系统设计文档 §13 中的完整目录树
242
+
243
+ 每个生成的任务必须:
244
+ - 包含精确的后端文件路径(按 BACKEND_STACK 展开)
245
+ - 遵循 TDD 测试先行顺序([TDD] 标记的任务在实现任务之前)
246
+ - 包含正确的栈特定父类/接口(见 §后端技术栈映射表)
247
+ - 通过 §编码规范检查清单 的校验
248
+ ============================================================================
249
+ -->
250
+
251
+ ---
252
+
253
+ ## Phase 1:环境搭建(共享基础设施)
254
+
255
+ **目的**:项目初始化与基础结构搭建
256
+
257
+ - [ ] T001 创建项目主配置文件(Maven `pom.xml` / Python `pyproject.toml`),定义模块结构,文件:
258
+ `{BACKEND_SERVICE_DIR}/{port|module}-{service}/{CONFIG_FILE}`
259
+ - [ ] T002 [P] 创建契约/API 模块目录结构(DTO、Client 接口目录),文件:
260
+ `{STACK_SPECIFIC_API_MODULE_PATH}`
261
+ - [ ] T003 [P] 创建数据层模块目录结构(Entity/DO、DAO/Mapper、迁移脚本),文件:
262
+ `{STACK_SPECIFIC_DATA_MODULE_PATH}`
263
+ - [ ] T004 [P] 创建实现层模块目录结构(Controller、Service、Config、Consumer、i18n),文件:
264
+ `{STACK_SPECIFIC_APP_MODULE_PATH}`
265
+ - [ ] T005 创建启动类/入口文件(按 §后端技术栈映射表 的"启动类"行),文件:
266
+ `{STACK_SPECIFIC_APPLICATION_PATH}`
267
+ - [ ] T006 [P] 创建应用配置文件(`application.yml` / `.env` / `settings.py`:server.port、数据源、ORM 配置、消息队列等),文件:
268
+ `{STACK_SPECIFIC_CONFIG_PATH}`
269
+
270
+ **检查点**:项目结构就绪,`mvn clean compile` 或 `python -c "import app"` 可通过
271
+
272
+ ---
273
+
274
+ ## Phase 2:基础设施(阻塞性前置依赖)
275
+
276
+ **目的**:枚举、消息码、数据库迁移、基础配置等被所有用户故事依赖的底层构件
277
+
278
+ **⚠️ 关键**:在此阶段完成之前,不得开始任何用户故事的工作
279
+
280
+ ### 2.1 枚举类(所有枚举可并行创建)
281
+
282
+ - [ ] T007 [P] 创建 `{Enum1}`(N 状态枚举),文件:
283
+ `{STACK_SPECIFIC_ENUM_PATH}/{Enum1}.java|py`
284
+ - [ ] T008 [P] 创建 `{Enum2}`(N 状态枚举),文件:同上目录
285
+ - [ ] ...(按设计文档 §6 枚举清单扩展)
286
+
287
+ ### 2.2 消息码与配置
288
+
289
+ - [ ] T0xx 创建消息码/错误码常量类/枚举(字段校验 001~005 + 业务异常 006+),文件:
290
+ `{STACK_SPECIFIC_MSGCODE_PATH}`
291
+ - [ ] T0xx [P] 创建 ORM 配置类(分页插件 + 乐观锁插件),文件:
292
+ `{STACK_SPECIFIC_ORM_CONFIG_PATH}`
293
+ - [ ] T0xx [P] 创建数据源配置类,文件:
294
+ `{STACK_SPECIFIC_DS_CONFIG_PATH}`
295
+ - [ ] T0xx [P] 创建 API 文档配置类,文件:
296
+ `{STACK_SPECIFIC_APIDOC_CONFIG_PATH}`
297
+ - [ ] T0xx 创建工具类 `{Utils}`(业务相关通用计算),文件:
298
+ `{STACK_SPECIFIC_UTILS_PATH}`
299
+
300
+ ### 2.3 数据库迁移
301
+
302
+ - [ ] T0xx 创建数据库迁移入口文件(Liquibase `master.xml` / Flyway 配置 / Alembic `env.py`)
303
+ - [ ] T0xx 创建版本变更日志文件
304
+ - [ ] T0xx 创建 DDL SQL(`{table1}` N+ 字段 + 索引 + `{table2}` N+ 字段 + 索引,含审计字段)
305
+
306
+ **检查点**:基础设施就绪——所有枚举可编译,DDL 可执行建表,现在可以并行开始用户故事的 TDD 实现
307
+
308
+ ---
309
+
310
+ ## Phase 3:US-01 — 创建并提报{业务对象}(优先级:P1)🎯 MVP 起点
311
+
312
+ **目标**:{用户角色}能针对{业务场景}创建{业务对象},填写{核心字段},保存草稿或提交至{下一环节}评审
313
+
314
+ **独立测试**:登录{用户角色}账号 → 输入{核心字段} → 点击提交 → 验证{业务对象}进入"{目标状态}"且生成单号
315
+
316
+ **来源**:设计文档 §1.1.1 US-01, §4.1 行为 1-2, §5.1 端点 1-N, §12.1 TC1
317
+
318
+ ### 用户故事 1 TDD 测试(先写,确保失败后再实现)
319
+
320
+ > ⚠️ **TDD 铁律**:在编写任何实现代码之前,先编写以下测试并验证它们**因功能缺失而失败**。
321
+
322
+ - [ ] T0xx [TDD] [US1] 编写 `{Service}` 提交{业务对象}测试(正常场景),测试文件:
323
+ `{STACK_SPECIFIC_SERVICE_TEST_PATH}`
324
+ - [ ] T0xx [TDD] [P] [US1] 编写 `{Service}` 提交{业务对象}异常测试(业务规则拦截),测试文件:同上
325
+ - [ ] T0xx [TDD] [P] [US1] 编写 `{Service}` 提交{业务对象}边界测试(边界条件),测试文件:同上
326
+ - [ ] T0xx [TDD] [P] [US1] 编写 `{Service}` 保存草稿测试(部分字段可空),测试文件:同上
327
+ - [ ] T0xx [TDD] [US1] 编写 `{Controller}` 保存/提交端点集成测试,测试文件:
328
+ `{STACK_SPECIFIC_CONTROLLER_TEST_PATH}`
329
+
330
+ ### 用户故事 1 的实现(测试验证失败后开始)
331
+
332
+ - [ ] T0xx [US1] 创建 `{Entity}`({STACK_SPECIFIC_ENTITY_DEF},N+ 字段全带 {STACK_SPECIFIC_FIELD_ANNOTATIONS}),文件:
333
+ `{STACK_SPECIFIC_ENTITY_PATH}`
334
+ - [ ] T0xx [P] [US1] 创建 `{DTO}`({STACK_SPECIFIC_DTO_DEF},含 `save`/`update` 校验分组),文件:
335
+ `{STACK_SPECIFIC_DTO_PATH}`
336
+ - [ ] T0xx [P] [US1] 创建 `{Ro}`({STACK_SPECIFIC_RO_DEF},含所有 Entity 字段 + 审计字段 + 子表列表),文件:
337
+ `{STACK_SPECIFIC_RO_PATH}`
338
+ - [ ] T0xx [US1] 创建 `{DAO}` 接口 + 映射文件({STACK_SPECIFIC_DAO_DEF},含全字段+审计,分页查询含 `WHERE delete_flag=0` 或等效逻辑),文件:
339
+ `{STACK_SPECIFIC_DAO_PATH}`
340
+ - [ ] T0xx [US1] 创建 `{Service}` 接口({STACK_SPECIFIC_SERVICE_IFACE_DEF},定义 N 个方法签名),文件:
341
+ `{STACK_SPECIFIC_SERVICE_IFACE_PATH}`
342
+ - [ ] T0xx [US1] 实现 `{Service}`({STACK_SPECIFIC_SERVICE_IMPL_DEF}):
343
+ - `save(dto)`:DTO→Entity 转换,设置初始状态
344
+ - `update(dto)`:状态校验 属性拷贝(排除审计字段)
345
+ - `submit(id)`:业务规则校验 → 生成单号 → 状态→目标状态
346
+ - `findById(id)`:含子表组装 Ro
347
+ - `findPagination(dto, pageIndex, pageRows, orderByBean)`:分页查询
348
+ 文件:`{STACK_SPECIFIC_SERVICE_IMPL_PATH}`
349
+ - [ ] T0xx [US1] 创建 `{Controller}`({STACK_SPECIFIC_CONTROLLER_DEF},N 个端点):
350
+ - `GET /get-id` → `service.findById(id)`
351
+ - `POST /find-pagination` → `service.findPagination(...)`
352
+ - `POST /save` `service.save(dto)`
353
+ - `POST /update` → `service.update(dto)`
354
+ - `POST /submit` `service.submit(id)`
355
+ 文件:`{STACK_SPECIFIC_CONTROLLER_PATH}`
356
+ - [ ] T0xx [US1] 创建 `{Client}` Feign/RPC 接口({STACK_SPECIFIC_CLIENT_DEF},调用外部服务),文件:
357
+ `{STACK_SPECIFIC_CLIENT_PATH}`
358
+
359
+ **检查点**:[TDD] 所有测试由红变绿,用户故事 1 可独立运行:创建草稿 填写完整提交 → 生成单号 → 状态=目标状态
360
+
361
+ ---
362
+
363
+ ## Phase 4~N:后续用户故事(按设计文档 §1.1.1 扩展)
364
+
365
+ **通用结构**(每个用户故事阶段):
366
+
367
+ ```
368
+ ## Phase N:US-{XX} — {故事标题}(优先级:P{X})
369
+
370
+ **目标**:{用户角色}能{业务动作}
371
+ **独立测试**:{端到端验收场景}
372
+ **来源**:设计文档 §1.1.1 US-{XX}, §4.1 行为 X-Y, §5.1 端点 X-N, §12 TC-N
373
+
374
+ ### 用户故事 X 的 TDD 测试
375
+ - [ ] T0xx [TDD] [USX] 编写 {Service} {业务动作}测试(正常+异常+边界),测试文件:同上
376
+ - [ ] T0xx [TDD] [USX] 编写 {Controller} 端点集成测试,测试文件:同上
377
+
378
+ ### 用户故事 X 的实现
379
+ - [ ] T0xx [USX] 扩展/创建 {Service}:{方法签名 + 业务规则}
380
+ - [ ] T0xx [USX] 扩展/创建 {Controller}:{端点定义}
381
+ - [ ] T0xx [P] [USX] 创建 {DTO}/{Client}/{Consumer}(按需)
382
+
383
+ **检查点**:用户故事 X 功能正常,TDD 测试红→绿
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Final Phase:集成与收尾
389
+
390
+ **目的**:跨故事的质量保证、国际化、文档与合规验证
391
+
392
+ ### 集成测试
393
+
394
+ - [ ] T0xx [TDD] 编写端到端集成测试({核心业务流程}全流程),文件:
395
+ `{STACK_SPECIFIC_E2E_TEST_PATH}`
396
+ - [ ] T0xx [TDD] [P] 编写{子对象}审批联动端到端测试,文件:同上
397
+
398
+ ### i18n 国际化
399
+
400
+ - [ ] T0xx [P] 创建 i18n 文件(按 §后端技术栈映射表 的"i18n"行配置所需语言文件),含所有消息码(字段校验 001~005 + 业务异常 006+),文件:
401
+ `{STACK_SPECIFIC_I18N_PATH}`
402
+
403
+ ### 代码评审与安全
404
+
405
+ - [ ] T0xx 执行编码规范合规检查(对照 §编码规范检查清单,按 `BACKEND_STACK` 逐条确认)
406
+ - [ ] T0xx 安全清单验证(所有端点权限校验、权限矩阵 §5.4 逐项检查、敏感操作二次确认、SQL 注入防范)
407
+ - [ ] T0xx 代码评审(按 `.adspecs/templates/06-代码评审报告模板.md` 执行)
408
+
409
+ ### 文档与构建
410
+
411
+ - [ ] T0xx 构建验证(`mvn clean -U package` `pytest`,全模块编译通过 + 单元测试全绿)
412
+ - [ ] T0xx API 文档完善(Swagger/OpenAPI 注解 → UI 可访问)
413
+
414
+ ---
415
+
416
+ ## 依赖关系与执行顺序
417
+
418
+ ### 阶段依赖
419
+
420
+ | 阶段 | 依赖 | 说明 |
421
+ |------|------|------|
422
+ | Phase 1(环境搭建) | | 可立即开始 |
423
+ | Phase 2(基础设施) | Phase 1 完成 | **阻塞所有用户故事** |
424
+ | Phase 3(US-01 创建提报) | Phase 2 完成 | P1 MVP 起点,是其他故事的基础 |
425
+ | Phase 4+(后续故事) | Phase 2 + Phase 3 实体 | 可独立并行 |
426
+ | Final Phase | 所有目标用户故事完成 | 集成测试需全功能就绪 |
427
+
428
+ ### 用户故事间依赖
429
+
430
+ - **US-01(P1)**:Phase 3,创建核心 Entity/Service/Controller,是其他故事的基础
431
+ - **后续 US(P1)**:仅新增 Service 方法,可与 US-01 并行(同 Service 类,不同方法)
432
+ - **P2 故事**:可在 P1 完成后开始
433
+
434
+ ### 每个用户故事内部的 TDD 执行顺序
435
+
436
+ ```
437
+ [STOP] 确认 Phase 2 基础设施已就绪
438
+
439
+ [TDD] 编写 Service 层单元测试(正常 + 异常 + 边界)→ 运行测试 → 确认失败 ❌
440
+
441
+ [TDD] 编写 Controller 层集成测试 → 运行测试 → 确认失败 ❌
442
+
443
+ 创建 Entity/DO/Model({STACK_SPECIFIC_ENTITY_DEF})
444
+
445
+ 创建 DTO/Schema({STACK_SPECIFIC_DTO_DEF})
446
+
447
+ 创建 Ro/VO/ResponseSchema({STACK_SPECIFIC_RO_DEF})
448
+
449
+ 创建 DAO/Mapper/Repository({STACK_SPECIFIC_DAO_DEF})
450
+
451
+ 创建 Service 接口 + 实现({STACK_SPECIFIC_SERVICE_IMPL_DEF})
452
+
453
+ 创建 Controller/Router{STACK_SPECIFIC_CONTROLLER_DEF})
454
+
455
+ 创建 Feign/RPC/HTTP Client({STACK_SPECIFIC_CLIENT_DEF})
456
+
457
+ [TDD] 重新运行测试 全部通过
458
+
459
+ [REFACTOR] 提取公共逻辑、优化命名、清理重复代码保持测试绿
460
+ ```
461
+
462
+ **并行加速**:标记 `[P]` 的 Entity/DTO/Ro/DAO 可同时创建(互不依赖),Service 实现必须在 DAO 完成后。
463
+
464
+ ### 并行机会
465
+
466
+ - **Phase 2**:所有枚举类可并行创建;DB 迁移与配置类可并行
467
+ - **Phase 3**:Entity + DTO + Ro 可并行,Service 接口+DAO 可并行
468
+ - **跨故事并行**:US-01 Entity 完成后,其他 US 可并行推进(各自独立的 Controller 方法或独立子表)
469
+ - **Final Phase**:i18n 文件可完全并行
470
+
471
+ ---
472
+
473
+ ## 实现策略
474
+
475
+ ### MVP 优先(P1 用户故事)
476
+
477
+ 1. Phase 1:项目环境搭建
478
+ 2. Phase 2:基础设施(枚举 + DB 迁移 + 消息码 + 配置)**← 关键阻塞点**
479
+ 3. Phase 3:US-01 创建并提报 **← MVP 核心**
480
+ 4. Phase 4+:逐个追加 P1 故事
481
+ 5. **STOP & VALIDATE**:全部 P1 故事端到端测试通过
482
+ 6. P1 MVP 就绪 → 部署/演示
483
+
484
+ ### 增量交付
485
+
486
+ 1. Phase 1+2 → 基础就绪
487
+ 2. Phase 3 → US-01 可独立测试 → 部署(MVP 最小可交付!)
488
+ 3. Phase 4+ → 逐个追加 P1 故事 → 每个故事独立测试 → 增量部署
489
+ 4. P2 故事追加
490
+ 5. Final Phase → 集成收尾
491
+
492
+ ### 团队并行策略
493
+
494
+ 1. 团队共同完成 Phase 1+2(环境搭建 + 基础设施)
495
+ 2. Phase 2 完成后:
496
+ - **开发者 A**:Phase 3(US-01)**← 最高优先级,产出核心 Entity**
497
+ - **开发者 B**:Phase 2 的 DB 迁移 + 配置类
498
+ 3. US-01 Entity 完成后:
499
+ - **开发者 A**:工作量最大的故事
500
+ - **开发者 B**:其他 P1 故事
501
+ 4. P1 全部完成 共同完成 P2 + Final Phase
502
+
503
+ ---
504
+
505
+ ## 编码规范检查清单
506
+
507
+ > 生成 tasks.md 时必须逐条确认。按 `BACKEND_STACK` 执行对应列的检查项。
508
+
509
+ ### 栈无关项(三栈共检)
510
+
511
+ - [ ] 实体/DO/Model 类型字段完整定义,含审计字段(created_by/creation_date/updated_by/update_date/version_num/delete_flag)
512
+ - [ ] DTO/Schema 含校验分组或校验规则
513
+ - [ ] 异常在 Service 层抛出,Controller 仅做参数空校验
514
+ - [ ] 消息码/错误码禁止硬编码,统一引用常量/枚举
515
+ - [ ] i18n 覆盖所需语言,每个文件含实际内容(非占位)
516
+ - [ ] 注入与工具使用项目约定(非自行实现)
517
+ - [ ] 日志规范(SLF4J / loguru / 标准 logging)
518
+
519
+ ### ECP Java 专属项(`BACKEND_STACK=ecp-java` 时检查)
520
+
521
+ - [ ] Entity:`extends BaseEntity<Entity>` + `@TableName` + `@TableField` + `@Schema`,`IdType.ASSIGN_ID`
522
+ - [ ] DTO:`extends ReqeustCommonDataBean`(注意拼写),校验分组内部接口
523
+ - [ ] DAO:`extends BaseCommonDAO_HI<Entity>` + `@Component("xxxDAO")`,MyBatis XML 同包
524
+ - [ ] 禁止 QueryWrapper:所有 DB 操作通过 MyBatis XML,任务描述中无 QueryWrapper/LambdaQueryWrapper 字样
525
+ - [ ] Service:接口 `II{Entity}` extends `IBaseCommon<T>`,实现 extends `BaseCommonServer<T>`,Save→FastJSON,Update→SpringBeanUtils
526
+ - [ ] Controller:`extends CommonAbstractService`,重写 `getBaseCommonServer()`,`@CheckToken`,`ResponseData<T>` 包装
527
+ - [ ] Ro:`implements Serializable` + `serialVersionUID`,手动字段,置于 `model/entity/readonly/`
528
+ - [ ] 所有 MyBatis XML SELECT 含 `WHERE delete_flag = 0`
529
+ - [ ] i18n:7 个 `.properties` 文件
530
+
531
+ ### Yudao Java 专属项(`BACKEND_STACK=yudao-java` 时检查)
532
+
533
+ - [ ] Entity:`extends BaseDO` + `@TableName` + `@TableField` + `@Schema`
534
+ - [ ] Mapper:`extends BaseMapperX<T>` + `@Mapper`,使用 MyBatis-Plus `LambdaQueryWrapperX`
535
+ - [ ] Service:`@Service` 注解,可直接实现或使用 `BaseServiceImpl`
536
+ - [ ] Controller:`@RestController` + `@Tag` + `@PreAuthorize`,返回 `CommonResult<T>`
537
+ - [ ] VO:使用 `BeanUtils.toBean` 或 `BaseConvert` 转换
538
+ - [ ] 错误码:`ErrorCodeEnum` + `ServiceException` 抛出
539
+ - [ ] DB 迁移:Flyway `V{version}__{name}.sql`
540
+
541
+ ### Python 专属项(`BACKEND_STACK=python` 时检查)
542
+
543
+ - [ ] Model:SQLAlchemy `Base` 派生,`Column` 定义完整
544
+ - [ ] Schema:Pydantic `BaseModel`,Field 校验完整
545
+ - [ ] Router:FastAPI `@router` + `Depends` 权限注入
546
+ - [ ] Service:函数式或 class 式,`Depends` 注入 Session
547
+ - [ ] 异步任务:Celery `@app.task`,配置 broker/result_backend
548
+ - [ ] DB 迁移:Alembic `env.py` + 迁移脚本
549
+ - [ ] 错误处理:自定义 Exception + error_code,全局异常处理器
550
+ - [ ] 测试:pytest + pytest-asyncio,fixture 复用
551
+
552
+ ---
553
+
554
+ ## TDD 执行指南
555
+
556
+ > 本模板任务顺序与 `test-driven-development` skill 的 RED→GREEN→REFACTOR 循环完全对齐。
557
+
558
+ ### RED 阶段:编写测试并验证失败
559
+
560
+ ```bash
561
+ # ECP Java / Yudao Java
562
+ mvn test -Dtest={Entity}ServiceTest
563
+
564
+ # Python
565
+ pytest tests/test_{module}.py
566
+
567
+ # 预期输出:测试失败(功能未实现)
568
+ # 如果测试通过 测试缺陷,修测试
569
+ # 如果测试报错(非失败) → 编译错误/依赖缺失,修环境
570
+ ```
571
+
572
+ ### GREEN 阶段:最小代码使测试通过
573
+
574
+ ```
575
+ TDD 任务之后的实现任务顺序:
576
+ Entity DTO → Ro/VO → DAO/Mapper Service Controller → Client
577
+
578
+ 每完成一个实现任务:
579
+ 运行测试 → 逐步变绿
580
+
581
+ Service 实现完成后 → 全部测试应变绿 ✅
582
+ ```
583
+
584
+ ### REFACTOR 阶段:清理与优化
585
+
586
+ ```bash
587
+ # 所有测试通过后:
588
+ - 提取重复的校验逻辑到工具类
589
+ - 优化命名(方法名/变量名与业务一致)
590
+ - 添加日志(关键操作)
591
+ - 运行全量测试确认不破坏
592
+
593
+ # Java
594
+ mvn test # 必须全绿
595
+
596
+ # Python
597
+ pytest # 必须全绿
598
+ ```
599
+
600
+ ---
601
+
602
+ ## 注意事项
603
+
604
+ - `[P]` 任务 = 操作不同文件,无未完成依赖,可并行执行
605
+ - `[TDD]` 任务 = 测试任务,**必须在同故事的实现任务之前编写并通过失败验证**
606
+ - `[Story]` 标签将任务映射到设计文档 §1.1.1 中的具体用户故事,支持全链路追溯
607
+ - **所有文件路径必须精确到模块**(按 §后端项目路径约定 的 `BACKEND_STACK` 展开)
608
+ - **编码规范检查清单是硬性约束**,违反任一条 = 任务不合格
609
+ - **消息码/错误码禁止硬编码**:所有异常和校验信息引用常量/枚举
610
+ - **异常在 Service 层抛出**:Controller 仅做参数空校验
611
+ - 每个任务或逻辑分组后建议 git commit
612
+ - 可在任何检查点停下验证故事的独立可用性
613
+ - 避免:模糊任务、同文件冲突、跨故事强耦合、违反 TDD 顺序(实现先于测试)