M1: Add complete documentation structure and contract specifications

- Add comprehensive docs/ directory with standardized structure
- Include architecture specification and contract documents
- Add prompt templates for development workflows
- Establish M1 deliverables as frozen specifications

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Bruno Gao
2026-02-11 23:41:04 +08:00
co-authored by Qwen-Coder
commit 43d1229bd7
36 changed files with 2169 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
---
title: java21-template 文档总入口
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 📘 java21-template 文档导航
## 我是谁?
本服务负责:
- 提供基于 Java 21 的现代化服务模板
- 集成 Clean Architecture 最佳实践
- 提供开箱即用的开发环境和工具链
不负责:
- 业务逻辑实现(由具体项目承担)
---
## 🚀 快速开始
- [本地运行](01-start-here/local-dev.md)
- [开发流程](01-start-here/dev-workflow.md)
- [架构总览](02-architecture/overview.md)
---
## 🏗 架构与设计
- [架构总览](02-architecture/overview.md)
- [架构约束](02-architecture/constraints.md)
- [依赖关系](02-architecture/dependencies.md)
- [ADR 列表](02-architecture/adrs/)
---
## 🔌 API
- 契约(Single Source of Truth):`04-apis/openapi.yaml`
- [鉴权说明](04-apis/auth.md)
- [接口示例](04-apis/examples.md)
---
## 🔍 开发与规范
- [日志规范](05-guides/logging.md)
- [异常处理](05-guides/error-handling.md)
- [测试策略](05-guides/testing.md)
---
## 🛠 运维
- [发版流程](06-operations/release.md)
- [配置管理](06-operations/config-management.md)
- [Runbook](06-operations/runbooks/)
+32
View File
@@ -0,0 +1,32 @@
---
title: 新人入门
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 新人入门
## 1. 环境要求
- JDK 21
- Docker
- Maven
- PostgreSQL(本地或容器)
## 2. 本地启动
```bash
mvn clean spring-boot:run
```
访问:
- http://localhost:8080/actuator/health
- http://localhost:8080/swagger-ui.html
## 3. 常见问题
| 问题 | 原因 | 解决方式 |
|------|------|----------|
| DB 连接失败 | 本地未启动 | docker-compose up |
+36
View File
@@ -0,0 +1,36 @@
---
title: 开发工作流
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 开发工作流
## 分支策略
- `main`: 生产就绪代码
- `develop`: 开发集成分支
- `feature/*`: 功能开发分支
- `hotfix/*`: 紧急修复分支
## 提交规范
使用 Conventional Commits:
```
feat: 新功能
fix: 修复缺陷
docs: 文档更新
style: 代码格式调整
refactor: 重构
test: 测试相关
chore: 构建过程或辅助工具变动
```
## 代码审查
- 至少一名同事审查
- 所有 CI 检查通过
- 测试覆盖率不低于 70%
+30
View File
@@ -0,0 +1,30 @@
---
title: 本地开发指南
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 本地开发
## 数据库
默认 profile: dev
配置位置:
`application-dev.yml`
## 初始化数据库
Flyway 自动执行:
`db/migration/V1__init.sql`
## 常用命令
```bash
mvn test
mvn spotless:apply
mvn verify
```
@@ -0,0 +1,37 @@
---
title: ADR-0001-模板初始化
owner: team-java21-template
status: accepted
last_reviewed: 2026-02-11
review_cycle: 180d
---
# ADR-0001: 采用 Clean Architecture 模板初始化项目
## 状态
已采纳
## 上下文
需要为新项目提供一个标准化的 Java 21 模板,以确保架构一致性、最佳实践和快速启动能力。
## 决策
采用 Clean Architecture 模式,分为四个主要层次:
- Domain 层:包含核心业务逻辑和实体
- Application 层:包含用例和业务流程
- Infrastructure 层:包含技术实现细节
- API 层:处理外部接口和协议
## 影响
- 代码结构更加清晰
- 便于单元测试
- 降低技术栈变更成本
- 提高可维护性
## 参考
- Robert C. Martin 的 Clean Architecture 原则
- Spring Boot 最佳实践
+27
View File
@@ -0,0 +1,27 @@
---
title: 架构约束
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 180d
---
# 架构约束
## 1. 依赖方向
```
api → application → domain
infrastructure → domain/application
```
## 2. 禁止模式
- @Autowired 字段注入
- domain 使用 @Entity
- 事务声明在 infrastructure 层
## 3. ArchUnit 自动检查规则
详见测试目录:
`src/test/java/.../ArchitectureTest.java`
+30
View File
@@ -0,0 +1,30 @@
---
title: 依赖关系
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 180d
---
# 依赖关系
## 内部模块依赖
```
api → application → domain
↑
infrastructure
```
## 外部服务依赖
| 服务 | 类型 | 重要性 | 备注 |
|------|------|--------|------|
| Auth Service | HTTP API | 高 | 认证授权 |
| User Service | HTTP API | 高 | 用户信息 |
| Notification Service | HTTP API | 中 | 通知服务 |
## 数据库依赖
- PostgreSQL 作为主数据库
- Redis 用于缓存
+37
View File
@@ -0,0 +1,37 @@
---
title: 架构总览
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 180d
---
# 架构总览
## 1. 架构风格
Clean Architecture(四层)
```
api → application → domain
↑
infrastructure
```
## 2. 模块说明
| 模块 | 职责 |
|------|------|
| domain | 领域模型 |
| application | 用例 |
| infrastructure | 技术实现 |
| api | REST 接口 |
## 3. 关键约束
- 事务只允许在 application 层
- Controller 不允许直接依赖 repository
- 所有查询必须带 customerId
详见:
- 全局规范:ljwx-docs/03-standards/
+24
View File
@@ -0,0 +1,24 @@
---
title: RFC 模板
owner: team-java21-template
status: draft
last_reviewed: 2026-02-11
review_cycle: 30d
---
# RFC-XXXX-标题
## 背景
## 目标
## 方案对比
| 方案 | 优点 | 风险 |
|------|------|------|
## 最终选择
## 风险与回滚
## 是否需要新增 ADR
+22
View File
@@ -0,0 +1,22 @@
---
title: 鉴权说明
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 180d
---
# 鉴权机制
## Header
| Header | 必填 | 说明 |
|--------|------|------|
| X-Customer-Id | 是 | 租户 |
| X-Org-Id | 是 | 组织 |
| X-User-Id | 是 | 用户 |
## 规则
- 所有数据查询必须携带 customerId
- DTO 禁止传入 customerId
+36
View File
@@ -0,0 +1,36 @@
---
title: API 示例
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 180d
---
# API 示例
## 请求示例
```bash
curl -X GET http://localhost:8080/api/v1/example \
-H "X-Customer-Id: customer-123" \
-H "X-Org-Id: org-456" \
-H "X-User-Id: user-789"
```
## 响应示例
```json
{
"message": "Hello from java21-template"
}
```
## 错误响应
```json
{
"error": "Bad Request",
"message": "Missing required header: X-Customer-Id",
"timestamp": "2026-02-11T10:00:00Z"
}
```
+56
View File
@@ -0,0 +1,56 @@
openapi: 3.0.1
info:
title: java21-template API
description: Java 21 Template Service API
version: 1.0.0
servers:
- url: http://localhost:8080
description: Development server
paths:
/health:
get:
summary: Health check endpoint
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: UP
/api/v1/example:
get:
summary: Example endpoint
parameters:
- name: customerId
in: header
required: true
schema:
type: string
description: Customer identifier
- name: orgId
in: header
required: true
schema:
type: string
description: Organization identifier
- name: userId
in: header
required: true
schema:
type: string
description: User identifier
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Hello from java21-template
+57
View File
@@ -0,0 +1,57 @@
---
title: 异常处理
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 异常处理
## 异常分类
### 业务异常
- 继承自 `BusinessException`
- 表示业务规则违反
- 返回 4xx HTTP 状态码
### 系统异常
- 继承自 `SystemException`
- 表示系统内部错误
- 返回 5xx HTTP 状态码
## 统一异常处理器
使用 `@ControllerAdvice` 实现全局异常处理:
```java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
// 返回 4xx 状态码
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
// 返回 5xx 状态码
}
}
```
## 错误响应格式
```json
{
"error": "VALIDATION_ERROR",
"message": "Invalid input parameters",
"details": [
{
"field": "email",
"message": "Email format is invalid"
}
],
"timestamp": "2026-02-11T10:00:00Z"
}
```
+39
View File
@@ -0,0 +1,39 @@
---
title: 日志规范
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 日志规范
## 日志级别
- ERROR: 系统错误,需要立即关注
- WARN: 潜在问题,需要监控
- INFO: 重要业务事件
- DEBUG: 调试信息,仅开发环境
## 结构化日志
使用 JSON 格式输出,包含以下字段:
```json
{
"timestamp": "2026-02-11T10:00:00Z",
"level": "INFO",
"logger": "com.example.service.UserService",
"traceId": "abc123",
"spanId": "def456",
"customerId": "customer-123",
"message": "User retrieved successfully",
"thread": "http-nio-8080-exec-1"
}
```
## 日志内容
- 包含业务上下文(如 customerId)
- 记录关键业务操作
- 避免记录敏感信息
+41
View File
@@ -0,0 +1,41 @@
---
title: 测试策略
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 测试策略
## 单元测试
- 覆盖率 ≥ 70%
- UseCase 必须有测试
- Domain 层必须有充分的单元测试
## 架构测试
ArchUnit 强制:
- domain 不依赖 Spring
- Controller 不直接调用 Repository
- 依赖方向符合 Clean Architecture
## 集成测试
使用 Testcontainers:
- PostgreSQL
- Redis
## 测试命名约定
```java
// 单元测试
@Test
void shouldCalculateTotalAmountWhenValidInputs() { ... }
// 集成测试
@Test
void shouldReturnUserDetailsWhenValidCustomerIdProvided() { ... }
```
+36
View File
@@ -0,0 +1,36 @@
---
title: 配置管理
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 配置管理
## 配置层级
1. 默认配置 (`application.yml`)
2. 环境配置 (`application-{env}.yml`)
3. 外部配置 (环境变量、配置中心)
## 配置项分类
### 应用配置
- 服务器端口
- 日志级别
- 应用名称
### 数据库配置
- 连接池大小
- 超时设置
- 连接字符串
### 服务发现配置
- 注册中心地址
- 服务名称
- 健康检查路径
## 配置验证
所有配置项在应用启动时进行验证,无效配置会导致应用启动失败。
+32
View File
@@ -0,0 +1,32 @@
---
title: 发版流程
owner: team-java21-template
status: published
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 发版流程
## 1. Merge 到 main
触发 CI:
1. 编译
2. 测试
3. 镜像构建
4. 推送 Harbor
5. 更新 deploy 仓库 tag
## 2. ArgoCD 自动部署
查看:
- ArgoCD 控制台
- /actuator/health
## 版本号管理
使用语义化版本控制 (SemVer):
- 主版本号:重大变更
- 次版本号:向后兼容的功能添加
- 修订号:向后兼容的问题修正
@@ -0,0 +1,21 @@
---
title: 故障处理模板
owner: team-java21-template
status: draft
last_reviewed: 2026-02-11
review_cycle: 90d
---
# 故障名称
## 现象
## 影响范围
## 排查步骤
## 恢复方式
## 根因分析
## 后续改进
+225
View File
@@ -0,0 +1,225 @@
# 架构规范 v1.0
## 1. 分层架构
### 1.1 模块划分
| 模块 | 职责 | 允许的依赖 |
|------|------|-----------|
| `${name}-domain` | 领域模型、业务规则、领域事件、领域异常 | 仅 JDK 标准库 + Jakarta Validation API |
| `${name}-application` | 用例编排、应用服务、端口定义、Command/Query | domain |
| `${name}-infrastructure` | 技术实现:JPA、Redis、MQ、HTTP Client | domain + application + Spring 全家桶 |
| `${name}-api` | REST Controller、DTO、安全配置、启动入口 | application + infrastructure + Spring Web |
模板仓库中模块名使用 tmpl- 前缀(tmpl-domain / tmpl-application /
tmpl-infrastructure / tmpl-api),初始化脚本会替换为实际服务名。
### 1.2 依赖方向(不可违反)
api → application → domain
↑
infrastructure ──┘
- domain 不依赖任何其他模块,不依赖任何框架
- application 只依赖 domain
- infrastructure 实现 application 中定义的 Port 接口
- api 负责组装所有模块(Spring Boot 启动入口在此层)
### 1.3 各层允许与禁止的 Spring 注解/类型(精确清单)
#### domain 层
允许:无(零框架依赖)
禁止:所有 Spring 注解、所有 JPA 注解、所有第三方框架类型
#### application 层
允许:
- @Service / @Component(类级别标注,用于 Spring Bean 注册)
- @Transactional(此层是事务声明的【唯一】位置)
禁止:
- Spring MVC 类型:RestController, RequestMapping, HttpServletRequest...
- JPA 类型:JpaRepository, EntityManager, @Entity, @Table, @Column...
- Cache 类型: @Cacheable, @CacheEvict, RedisTemplate...
- Messaging 类型:KafkaTemplate, RabbitTemplate, @RabbitListener...
- Security 类型: @PreAuthorize, SecurityFilterChain...
- HTTP Client 类型:RestTemplate, WebClient, FeignClient...
#### infrastructure 层
允许:Spring 全家桶、JPA、Redis、MQ 等所有技术框架
禁止:包含业务规则判断(业务逻辑只能在 domain 层)
#### api 层
允许:Spring MVC、Spring Security 配置、SpringDoc
禁止:直接调用 Repository/Adapter(必须经过 application 层的 UseCase)
### 1.4 包命名规范
com.{company}.{service}/
├── domain/
│ ├── entity/ # 聚合根、实体
│ ├── valueobject/ # 值对象(推荐用 Java 21 record)
│ ├── event/ # 领域事件
│ ├── exception/ # DomainException + ErrorCode
│ ├── repository/ # 仓储接口(只定义,不实现)
│ └── service/ # 领域服务接口(可选)
├── application/
│ ├── usecase/ # 用例类(一个类做一件事,入口方法 execute())
│ ├── port/
│ │ ├── in/ # 入站:Command, Query
│ │ └── out/ # 出站:PersistencePort, EventPublisher, ExternalServicePort
│ ├── service/ # 应用服务(编排多个 UseCase,可选)
│ └── assembler/ # Domain ↔ Command 转换(或 MapStruct)
├── infrastructure/
│ ├── persistence/
│ │ ├── entity/ # JPA @Entity(PO,持久化对象)
│ │ ├── repository/ # Spring Data JPA Repository + PersistenceAdapter
│ │ ├── mapper/ # MapStruct: PO ↔ Domain Entity
│ │ └── converter/ # JPA AttributeConverter
│ ├── cache/ # Redis 适配器(实现 out port)
│ ├── messaging/ # MQ 适配器(按需,实现 out port)
│ ├── external/ # 外部 HTTP 调用适配器(按需,实现 out port)
│ └── config/ # Spring @Configuration 类
└── api/
├── controller/ # REST Controller
├── dto/ # Request/Response DTO(面向前端/外部调用方)
├── mapper/ # MapStruct: DTO ↔ Command/Response
├── filter/ # Servlet Filter(TraceId、SecurityContext 注入)
├── exception/ # GlobalExceptionHandler
└── config/ # Web / Security / OpenAPI 配置
### 1.5 命名规范
#### 类命名
| 层级 | 类型 | 规则 | 示例 |
|------|------|------|------|
| domain | 聚合根/实体 | 业务名词 | Order, OrderItem |
| domain | 值对象 | 业务名词 | Money, OrderStatus |
| domain | 异常 | {名词}+Exception | OrderNotFoundException |
| domain | 仓储接口 | {聚合根}+Repository | OrderRepository |
| application | 用例 | {动词}{名词}+UseCase | CreateOrderUseCase |
| application | 命令 | {动词}{名词}+Command | CreateOrderCommand |
| application | 查询 | {名词}+Query | OrderDetailQuery |
| infrastructure | 持久化对象 | {名词}+PO | OrderPO |
| infrastructure | JPA 仓储 | {名词}+JpaRepository | OrderJpaRepository |
| infrastructure | 适配器 | {名词}{技术}+Adapter | OrderPersistenceAdapter |
| api | 控制器 | {名词}+Controller | OrderController |
| api | 请求 DTO | {动词}{名词}+Request | CreateOrderRequest |
| api | 响应 DTO | {名词}+Response | OrderResponse |
#### 方法命名
- UseCase 入口方法统一命名 execute(...)
- Repository 方法:findById / save / delete / findAllBy{条件}
- Controller 方法:create / update / delete / get / list
### 1.6 API 规范
#### URL 规范
- 格式:/api/v{n}/{resource}(resource 用复数)
- 示例:POST /api/v1/orders,GET /api/v1/orders/{id}
- 嵌套资源最多一层:GET /api/v1/orders/{id}/items
#### HTTP Method 语义
| 操作 | Method | 幂等 | 成功状态码 |
|------|--------|------|-----------|
| 创建 | POST | 否(除非带幂等键) | 201 Created |
| 查询单个 | GET | 是 | 200 OK |
| 查询列表 | GET | 是 | 200 OK |
| 全量更新 | PUT | 是 | 200 OK |
| 部分更新 | PATCH | 是 | 200 OK |
| 删除 | DELETE | 是 | 204 No Content |
#### 分页规范
- 请求参数:?page=0&size=20&sort=createTime,desc
- 响应:使用 Spring Data 的 Page 结构(content, totalElements, totalPages, number, size)
### 1.7 数据库规范
#### 表命名
- 小写蛇形,t_ 前缀:t_order, t_order_item
- 初始化脚本提供 --no-table-prefix 开关(非默认)
#### 审计列标准(每张业务表必须包含)
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| id | BIGINT (主键) 或 UUID | 自增/生成 | 主键 |
| create_user_id | BIGINT | - | 创建人 ID |
| create_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 创建时间(UTC) |
| update_user_id | BIGINT | - | 最后修改人 ID |
| update_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 最后修改时间(UTC) |
| is_deleted | BOOLEAN | FALSE | 软删除标记 |
| version | INTEGER | 0 | 乐观锁版本号 |
说明:统一使用 TIMESTAMPTZ(PostgreSQL 带时区时间戳),存储 UTC 时间。
#### 索引命名
- 普通索引:idx_{表名}_{字段名}
- 唯一索引:uk_{表名}_{字段名}
- 示例:idx_t_order_user_id, uk_t_order_order_no
#### Flyway 脚本命名
- V{版本}__{描述}.sql:结构变更(DDL)
- R__{描述}.sql:可重复执行脚本
- 版本号递增不跳号:V1__, V2__, V3__
### 1.8 Non-goals(本阶段不做,但预留扩展点)
- app-bootstrap 独立启动模块(当出现 REST + gRPC + CLI 多入口时再拆)
- 多数据源 / 读写分离
- gRPC 支持
- 消息队列集成(预留 infrastructure/messaging 包)
---
## ✅ 可自动检查项(ArchUnit + Maven Enforcer)
| 规则 | 检查方式 |
|------|---------|
| domain 不依赖 Spring/JPA | ArchUnit: noClasses().that().resideInAPackage("..domain..").should().dependOnClassesThat().resideInAnyPackage("org.springframework..", "jakarta.persistence..", "org.hibernate..") |
| application 不依赖 JPA/MVC/Cache/MQ | ArchUnit: 同上,精确到禁止包列表 |
| Controller 不直接调用 Repository | ArchUnit: noClasses().that().areAnnotatedWith(RestController.class).should().dependOnClassesThat().resideInAPackage("..infrastructure..") |
| UseCase 入口方法必须名为 execute | ArchUnit: methods().that().areDeclaredInClassesThat().haveSimpleNameEndingWith("UseCase").should().haveName("execute") |
| 禁止 @Autowired 字段注入 | ArchUnit: noFields().should().beAnnotatedWith(Autowired.class) |
| domain 模块 pom.xml 无 Spring 依赖 | Maven Enforcer Plugin: bannedDependencies |
## ❌ 典型违规示例
### 违规 1:domain 层引入 JPA 注解
// 错误!domain 层禁止 JPA 注解
@Entity @Table(name = "t_order")
public class Order { ... }
// 正确:domain 层的 Order 是纯 Java 类
// JPA @Entity 放在 infrastructure 层的 OrderPO 上
### 违规 2:Controller 直接调用 Repository
// 错误!Controller 绕过 UseCase 直接调用 Infrastructure
@RestController
public class OrderController {
private final OrderJpaRepository repo; // 直接依赖 infra
@GetMapping("/orders/{id}")
public Order get(@PathVariable Long id) {
return repo.findById(id); // 违规
}
}
// 正确:Controller 调用 UseCase
@RestController
public class OrderController {
private final QueryOrderUseCase queryOrderUseCase; // 依赖 application
@GetMapping("/orders/{id}")
public ApiResponse<OrderResponse> get(@PathVariable Long id) {
return ApiResponse.success(queryOrderUseCase.execute(id));
}
}
+122
View File
@@ -0,0 +1,122 @@
# 异常处理契约 v1.0
## 1. 异常体系
DomainException(基类)
├── 携带 ErrorCode(枚举)
├── 携带 message(面向用户的描述)
└── 可选携带 cause(原始异常)
所有业务异常必须继承 DomainException 并携带 ErrorCode。
不允许直接抛出 RuntimeException / IllegalStateException 等通用异常表达业务规则。
## 2. ErrorCode 组织方式
按模块前缀分组(锁定):
COMMON_VALIDATION_ERROR
COMMON_INTERNAL_ERROR
COMMON_OPTIMISTIC_LOCK_CONFLICT
AUTH_INVALID_CREDENTIALS
AUTH_TOKEN_EXPIRED
AUTH_ACCESS_DENIED
ORDER_NOT_FOUND
ORDER_ALREADY_CANCELLED
ORDER_STATUS_INVALID
...
命名规则:{MODULE}_{SCENE},全大写蛇形。
新增模块时在 ErrorCode 枚举中添加对应前缀组。
## 3. ErrorCode → HTTP Status 映射表
| ErrorCode 前缀/具体值 | HTTP Status | 说明 |
|----------------------|-------------|------|
| COMMON_VALIDATION_ERROR | 400 | 参数校验失败 |
| AUTH_INVALID_CREDENTIALS | 401 | 认证失败 |
| AUTH_TOKEN_EXPIRED | 401 | 令牌过期 |
| AUTH_ACCESS_DENIED | 403 | 无权限 |
| *_NOT_FOUND | 404 | 资源不存在 |
| *_ALREADY_* / *_STATUS_INVALID | 409 | 状态冲突 |
| COMMON_OPTIMISTIC_LOCK_CONFLICT | 409 | 乐观锁冲突 |
| COMMON_INTERNAL_ERROR | 500 | 系统内部错误 |
## 4. 统一错误响应格式
{
"code": 404, // HTTP Status(固定,便于前端/网关)
"errorCode": "ORDER_NOT_FOUND", // 稳定枚举值(便于埋点/告警/统计)
"message": "订单不存在", // 面向用户,可本地化
"traceId": "abc-123-def", // 链路追踪 ID
"timestamp": "2026-02-11T10:30:00Z", // 服务器时间
"details": null // 仅 VALIDATION_ERROR 时填充字段级明细
}
各字段职责:
- code:前端快速判断错误大类(4xx / 5xx)
- errorCode:后端精确定位错误场景,用于日志检索 / 告警规则 / 监控大盘
- message:展示给终端用户
- details:VALIDATION_ERROR 时返回 [{"field": "xxx", "message": "xxx"}]
- 生产环境【禁止】在响应中返回堆栈信息
## 5. 异常转换规则
### 规则 1:异常只在 API 层的 GlobalExceptionHandler 转换为 HTTP 响应
- domain/application 层只抛 DomainException
- infrastructure 层将技术异常包装为 DomainException:
- DataAccessException → COMMON_INTERNAL_ERROR
- OptimisticLockingFailureException → COMMON_OPTIMISTIC_LOCK_CONFLICT
- ConnectException / TimeoutException → COMMON_INTERNAL_ERROR
### 规则 2:禁止吞没异常
- 任何 catch 块必须:要么重新抛出,要么记录 WARN/ERROR 日志
- 空 catch 块会被 SpotBugs 检测并阻断 CI
### 规则 3:参数校验 vs 业务校验
- 参数格式校验:Jakarta Validation(@NotNull, @Size, @Pattern 等),
Controller 层 @Valid/@Validated 拦截,GlobalExceptionHandler 捕获
MethodArgumentNotValidException → 400 + COMMON_VALIDATION_ERROR + details
- 业务规则校验:Domain 层方法内判断并抛出具体 DomainException
---
## ✅ 可自动检查项
| 规则 | 检查方式 |
|------|---------|
| 所有自定义异常继承 DomainException | ArchUnit: classes in ..exception.. that extend RuntimeException should also extend DomainException |
| 禁止空 catch 块 | SpotBugs: DE_MIGHT_IGNORE + REC_CATCH_EXCEPTION |
| GlobalExceptionHandler 存在且标注 @RestControllerAdvice | ArchUnit: 至少有一个类标注 @RestControllerAdvice 且在 ..api.exception.. 包下 |
| Controller 中 @Valid/@Validated 注解存在 | 人工评审 + Prompt 约束 |
## ❌ 典型违规示例
### 违规 1:直接抛 RuntimeException 表达业务规则
// 错误!
if (order == null) {
throw new RuntimeException("订单不存在"); // 无 ErrorCode,GlobalExceptionHandler 只能兜底 500
}
// 正确
if (order == null) {
throw new DomainException(ErrorCode.ORDER_NOT_FOUND);
}
### 违规 2:Infrastructure 层不包装技术异常
// 错误!让 DataAccessException 直接穿透到 API 层
public class OrderPersistenceAdapter implements OrderPersistencePort {
public Order findById(OrderId id, String customerId) {
return repo.findByIdAndCustomerId(id.value(), customerId)
.map(mapper::toDomain)
.orElse(null); // 返回 null,让上层 NPE → 500
}
}
// 正确:包装为领域异常
public Order findById(OrderId id, String customerId) {
return repo.findByIdAndCustomerId(id.value(), customerId)
.map(mapper::toDomain)
.orElseThrow(() -> new DomainException(ErrorCode.ORDER_NOT_FOUND));
}
+154
View File
@@ -0,0 +1,154 @@
# 可观测性契约 v1.0
## 1. 结构化日志字段(必须写入 MDC)
| 字段 | 来源 | 必填 | 说明 |
|------|------|------|------|
| traceId | TraceIdFilter | 是 | 全链路追踪 ID |
| spanId | 预留(OTel 接入后自动填充) | 否 | 当前跨度 ID |
| requestId | Header: X-Request-Id | 否 | 网关生成的请求唯一 ID |
| customerId | SecurityContextFilter | 是 | 租户 |
| orgId | SecurityContextFilter | 是 | 组织 |
| userId | SecurityContextFilter | 是 | 操作人 |
| requestMethod | Filter | 是 | HTTP Method |
| requestUri | Filter | 是 | 请求路径 |
## 2. 日志格式
### dev profile:明文(人读友好)
2026-02-11 10:30:45.123 INFO [traceId=abc-123] [userId=u001]
c.e.o.a.u.CreateOrderUseCase : 订单创建成功 orderId=12345 costMs=45
### prod profile:JSON(Loki 采集解析)
{
"timestamp": "2026-02-11T10:30:45.123Z",
"level": "INFO",
"logger": "c.e.o.a.u.CreateOrderUseCase",
"message": "订单创建成功",
"traceId": "abc-123",
"spanId": "",
"requestId": "req-456",
"customerId": "cust-001",
"orgId": "org-001",
"userId": "u001",
"orderId": "12345",
"costMs": 45
}
## 3. 日志级别规范
| 场景 | 级别 | 模板 |
|------|------|------|
| HTTP 请求进入/完成 | INFO | {method} {uri} {status} costMs={ms} |
| 业务异常 | WARN | {errorCode}: {message} + 关键业务字段 |
| 系统异常 | ERROR | {errorCode}: {message} + 完整堆栈 |
| 外部 HTTP 调用成功 | DEBUG | HTTP {method} {service} {status} costMs={ms} |
| 外部 HTTP 调用失败 | WARN | HTTP {method} {service} {status} costMs={ms} |
| SQL 慢查询 | WARN | slowQuery costMs={ms} rows={n} |
| UseCase 入口 | DEBUG | execute() 开始 + 关键入参摘要 |
| UseCase 完成 | INFO | execute() 完成 + 关键出参摘要 + costMs |
## 4. 慢查询阈值(可配置)
app:
observability:
slow-query-threshold-ms: 200 # 默认 200ms
slow-http-call-threshold-ms: 3000 # 默认 3s
超过阈值时输出 WARN 日志,包含:costMs, sql 摘要(或 HTTP 目标地址)
## 5. Actuator 端点配置
| 端点 | 用途 | 暴露策略 |
|------|------|---------|
| /actuator/health | K8s 探针 | 公开(无需认证) |
| /actuator/health/liveness | K8s liveness 探针 | 公开 |
| /actuator/health/readiness | K8s readiness 探针 | 公开 |
| /actuator/prometheus | Prometheus 指标采集 | 仅内网(通过 K8s Service 暴露给 Prometheus) |
| /actuator/info | 版本/构建/Git 信息 | 仅内网 |
| 其他端点 | 默认关闭 | - |
## 6. Metrics 命名规范
格式:{service}_{module}_{type}_{name}
类型后缀:_total(Counter), _seconds(Timer/Histogram), _active(Gauge)
示例:
- order_api_http_requests_total
- order_usecase_create_order_seconds
- order_infra_db_pool_active
- order_infra_redis_commands_seconds
自定义业务指标使用 Micrometer API(Counter/Timer/Gauge),
禁止直接使用 Prometheus Client 库。
## 7. 健康检查与优雅停机
### K8s 探针配置
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 30 # 最多等 150 秒启动
### 优雅停机配置
Spring 侧:
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
K8s 侧:
terminationGracePeriodSeconds: 45(> Spring 的 30s)
preStop hook: sleep 5(等待 Service 摘除 endpoint 后再开始停机)
时序:
K8s 发 SIGTERM → preStop sleep 5s → Spring 停止接收新请求 →
等待已有请求完成(最长 30s) → 进程退出 → K8s 确认 Pod 终止
---
## ✅ 可自动检查项
| 规则 | 检查方式 |
|------|---------|
| TraceIdFilter 存在 | ArchUnit: 至少一个类在 ..api.filter.. 包下实现 Filter 并操作 MDC |
| logback-spring.xml 存在且包含 JSON 格式 | CI 脚本检查文件存在性 |
| actuator 端点配置正确 | 集成测试:启动后 GET /actuator/health 返回 200 |
| 优雅停机配置 | application.yml 检查 server.shutdown=graceful 存在 |
## ❌ 典型违规示例
### 违规 1:日志不带 traceId
// 错误!直接 System.out 或未经 MDC 的 logger
System.out.println("订单创建成功: " + orderId);
// 正确:使用 SLF4J,traceId 通过 MDC 自动注入
log.info("订单创建成功 orderId={} costMs={}", orderId, costMs);
### 违规 2:直接使用 Prometheus Client
// 错误!
import io.prometheus.client.Counter;
Counter orderCounter = Counter.build().name("orders").help("xxx").register();
// 正确:使用 Micrometer 抽象
import io.micrometer.core.instrument.MeterRegistry;
meterRegistry.counter("order_usecase_create_order_total").increment();
+147
View File
@@ -0,0 +1,147 @@
# 安全上下文契约 v1.0
## 1. 上下文字段定义
### 必填字段
| 字段 | 类型 | 来源 | 说明 |
|------|------|------|------|
| customerId | String | 网关 Header: X-Customer-Id | 租户标识 |
| orgId | String | 网关 Header: X-Org-Id | 组织标识 |
| userId | String | 网关 Header: X-User-Id | 操作人标识 |
| roles | List<String> | 网关 Header: X-User-Roles | 角色列表(逗号分隔) |
### 可选字段
| 字段 | 类型 | 来源 | 说明 |
|------|------|------|------|
| traceId | String | Header: X-Trace-Id | 链路 ID(无则服务自动生成 UUID) |
| requestId | String | Header: X-Request-Id | 网关生成的请求唯一 ID(排障用) |
| clientId | String | Header: X-Client-Id | 来源系统标识(管理端/大屏/移动端) |
## 2. 上下文注入流程
网关(JWT 验证 → 解析 claims → 注入 Header)
→ TraceIdFilter(生成或接收 traceId/requestId,写入 MDC)
→ SecurityContextFilter(解析 Header → 构造 TenantContext 对象 → 写入 ThreadLocal)
→ Controller → UseCase → Port → Adapter
## 3. 宪法级规则
### 规则 1:可信来源——禁"来源",不禁"显式参数"
核心原则:customerId/orgId/userId 的【值】只能来源于网关注入的 Header,
不能来源于请求 DTO 中客户端传入的字段。
具体条款:
(a) Controller/DTO 层:
- 请求 DTO 中【禁止】定义 customerId/orgId/userId 字段用于业务查询
- 如果 DTO 中存在这些字段(如管理后台的"代操作"场景),
【必须】在 Controller 中校验"DTO 传入值 == TenantContext 中的值",否则拒绝
(b) Application 层(UseCase):
- 【默认】从 TenantContext.current() 获取 customerId
- 【允许】将 TenantScope(只读对象,包含 customerId/orgId)作为
显式参数传入 UseCase.execute(command, tenantScope),便于单元测试
- 【禁止】从 Command/Query 对象中读取 customerId 作为查询条件
(c) Port / Adapter 层:
- Out Port 接口【允许】将 customerId 作为显式方法参数
例如:OrderPersistencePort.findByIdAndCustomerId(OrderId id, String customerId)
- 这样做的目的是让 Port 接口具有明确的契约,提高可测试性
### 规则 2:多租户数据隔离
- 所有数据查询【必须】携带 customerId 作为过滤条件
- 实现方式(二选一,在项目初始化时决定):
(a) 显式 WHERE:每个查询方法都带 customerId 参数
(b) Hibernate @Filter:全局自动注入(需要在配置中启用)
- 写入操作:create_user_id/update_user_id 从安全上下文自动填充
### 规则 3:上下文在线程间的传播
(a) 同步请求链(Controller → UseCase → Repo):
ThreadLocal + MDC 正常工作,无需额外处理
(b) 异步场景(@Async / CompletableFuture / 线程池 / 调度任务):
【必须】使用 TaskDecorator 或 ContextWrapper 显式传播 TenantContext
【禁止】在异步代码中直接调用 TenantContext.current()(除非已确认上下文已传播)
(c) Virtual Threads 注意事项:
Java 21 Virtual Threads 默认继承 ThreadLocal,但在大量并发下应
评估是否迁移到 ScopedValue(JEP 481,Preview)。
模板默认使用 ThreadLocal,预留迁移接口。
### 规则 4:SecurityContext 类定义
位置:${name}-application/src/.../port/in/TenantContext.java
public final class TenantContext {
private static final ThreadLocal<TenantContext> HOLDER = new ThreadLocal<>();
private final String customerId;
private final String orgId;
private final String userId;
private final List<String> roles;
// static current(), set(), clear()
// 构造器、getter(不可变)
}
位置:${name}-application/src/.../port/in/TenantScope.java(只读快照,用于传参)
public record TenantScope(String customerId, String orgId) {
public static TenantScope fromContext() {
var ctx = TenantContext.current();
return new TenantScope(ctx.getCustomerId(), ctx.getOrgId());
}
}
---
## ✅ 可自动检查项
| 规则 | 检查方式 |
|------|---------|
| Request DTO 中禁止出现 customerId 字段 | ArchUnit: classes in ..api.dto.. 的 fields 不含名为 customerId/orgId/userId 的字段(管理后台例外需 @SuppressWarning 注解标注) |
| Command/Query 中禁止出现 customerId 字段 | ArchUnit: classes in ..port.in.. 的 fields 不含名为 customerId/orgId 的字段 |
| Out Port 接口允许 customerId 参数 | 不做限制(这是允许的) |
| 异步代码不直接调用 TenantContext.current() | 人工评审 + 编码规范约束(ArchUnit 难以静态检测) |
## ❌ 典型违规示例
### 违规 1:从 DTO 透传 customerId
// 错误!DTO 的 customerId 不可信
public record CreateOrderRequest(
String customerId, // 客户端传入 → 不可信
List<OrderItemRequest> items
) {}
@PostMapping("/orders")
public ApiResponse<?> create(@RequestBody CreateOrderRequest req) {
createOrderUseCase.execute(new CreateOrderCommand(
req.customerId(), // 直接把 DTO 的值当查询条件 → 违规!
req.items()
));
}
// 正确:从安全上下文获取
@PostMapping("/orders")
public ApiResponse<?> create(@RequestBody CreateOrderRequest req) {
var scope = TenantScope.fromContext();
createOrderUseCase.execute(new CreateOrderCommand(req.items()), scope);
}
### 违规 2:异步任务中直接使用 TenantContext
// 错误!异步线程中 ThreadLocal 可能为空
@Async
public void sendNotification(OrderId orderId) {
var customerId = TenantContext.current().getCustomerId(); // 可能 NPE!
// ...
}
// 正确:在发起方捕获上下文,显式传入
var scope = TenantScope.fromContext();
asyncNotificationService.sendNotification(orderId, scope);
+118
View File
@@ -0,0 +1,118 @@
# 事务边界契约 v1.0
## 1. 事务声明位置
| 层级 | 允许声明事务 | 说明 |
|------|-------------|------|
| domain | 禁止 | 领域层不感知事务 |
| application | 唯一允许位置 | UseCase 或 AppService 上声明 @Transactional |
| infrastructure | 禁止 | Adapter/Repository 不声明事务 |
| api | 禁止 | Controller 不声明事务 |
这是【唯一】允许声明事务的位置,没有例外。
## 2. 默认事务策略
- 读操作:@Transactional(readOnly = true)
- 写操作:@Transactional(默认 REQUIRED + RuntimeException 回滚)
- 传播级别:除非有明确理由并经过架构评审,否则不使用 REQUIRES_NEW
## 3. 长事务规避
- 事务内【禁止】调用外部 HTTP 接口
- 事务内【禁止】发送 MQ 消息
- 正确做法:先完成 DB 操作 → 提交事务 → 再发 MQ / 调 HTTP
- 需要"提交后触发":使用 @TransactionalEventListener(phase = AFTER_COMMIT)
## 4. 乐观锁
- 所有聚合根对应的数据库表【必须】包含 version 字段(INTEGER DEFAULT 0)
- JPA @Entity 使用 @Version 注解
- 冲突时抛出 OptimisticLockingFailureException
→ GlobalExceptionHandler 映射为 409 Conflict (OPTIMISTIC_LOCK_CONFLICT)
## 5. 批处理场景策略
事务声明位置不变(仍在 application 层),但允许 infrastructure 层提供批处理原语:
(a) 批处理用例由 application 发起:
@Transactional
public class BatchImportOrdersUseCase {
private final OrderBatchWriter batchWriter; // out port
public void execute(List<CreateOrderCommand> commands, TenantScope scope) {
List<Order> orders = commands.stream()
.map(cmd -> Order.create(...))
.toList();
batchWriter.batchInsert(orders, scope.customerId());
// 事务边界由此 UseCase 控制
}
}
(b) Infrastructure 提供无事务注解的批处理原语:
// 无 @Transactional —— 由调用方控制事务
public class OrderBatchWriterAdapter implements OrderBatchWriter {
private final JdbcTemplate jdbc;
@Override
public void batchInsert(List<Order> orders, String customerId) {
jdbc.batchUpdate("INSERT INTO ...", ...);
}
}
(c) 事务粒度由 UseCase 决定:
- 整批一个事务(默认)
- 分批次(每 N 条一个事务)—— UseCase 自行控制循环和事务边界
---
## ✅ 可自动检查项
| 规则 | 检查方式 |
|------|---------|
| @Transactional 只出现在 application 层 | ArchUnit: noClasses().that().resideInAnyPackage("..domain..", "..infrastructure..", "..api..").should().beAnnotatedWith(Transactional.class) 以及方法级检查 |
| domain 层无事务相关 import | ArchUnit: noClasses in ..domain.. should depend on org.springframework.transaction.. |
| @Version 注解出现在 infrastructure PO 上 | ArchUnit: classes in ..infrastructure.persistence.entity.. that are annotated with @Entity should have field annotated with @Version |
## ❌ 典型违规示例
### 违规 1:Infrastructure 层声明事务
// 错误!Adapter 不允许声明事务
@Repository
public class OrderPersistenceAdapter implements OrderPersistencePort {
@Transactional // 违规!
public void save(Order order) { ... }
}
// 正确:事务在 UseCase 层
@Service @Transactional
public class CreateOrderUseCase {
public OrderId execute(CreateOrderCommand cmd, TenantScope scope) {
// ...
persistencePort.save(order); // Adapter 无事务注解
}
}
### 违规 2:事务内调用外部 HTTP
// 错误!事务内调用外部服务会导致长事务 + 锁表
@Transactional
public class CreateOrderUseCase {
public OrderId execute(...) {
persistencePort.save(order);
paymentClient.charge(order); // 外部 HTTP 调用在事务内 → 违规!
}
}
// 正确:事务提交后再调外部服务
@Transactional
public class CreateOrderUseCase {
public OrderId execute(...) {
persistencePort.save(order);
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
// 由 @TransactionalEventListener(AFTER_COMMIT) 触发外部调用
}
}
+78
View File
@@ -0,0 +1,78 @@
# P0_system_rules.md - 宪法级规则
## 核心架构原则
### 1. 依赖方向(不可违反)
```
api → application → domain
↑
infrastructure ──┘
```
- domain 不依赖任何其他模块,不依赖任何框架
- application 只依赖 domain
- infrastructure 实现 application 中定义的 Port 接口
- api 负责组装所有模块
### 2. 安全上下文规则
#### 可信来源原则
- customerId/orgId/userId 的值只能来源于网关注入的 Header
- 禁止从请求 DTO 中读取这些字段作为业务查询条件
- Controller/DTO 中禁止定义 customerId/orgId/userId 字段
#### 多租户隔离
- 所有数据查询必须携带 customerId 作为过滤条件
- 写入操作:create_user_id/update_user_id 从安全上下文自动填充
### 3. 事务边界规则
- @Transactional 唯一允许位置:application 层(UseCase 或 AppService)
- domain/infrastructure/api 层禁止声明事务
- 事务内禁止调用外部 HTTP 接口或发送 MQ 消息
### 4. 异常处理规则
- 所有业务异常必须继承 DomainException 并携带 ErrorCode
- 禁止直接抛出 RuntimeException 表达业务规则
- ErrorCode → HTTP Status 有固定映射关系
### 5. 禁止模式清单
#### domain 层禁止
- 所有 Spring 注解
- 所有 JPA 注解
- 所有第三方框架类型
- @Autowired 字段注入
#### application 层禁止
- Spring MVC 类型(RestController, RequestMapping 等)
- JPA 类型(JpaRepository, @Entity 等)
- Cache 类型(@Cacheable, RedisTemplate 等)
- Messaging 类型(KafkaTemplate, @RabbitListener 等)
- Security 类型(@PreAuthorize, SecurityFilterChain 等)
#### infrastructure 层禁止
- 包含业务规则判断(业务逻辑只能在 domain 层)
#### api 层禁止
- 直接调用 Repository/Adapter(必须经过 application 层 UseCase)
### 6. 命名规范摘要
#### UseCase 入口方法
- 统一命名:execute(...)
#### 包结构
- domain/: entity, valueobject, event, exception, repository, service
- application/: usecase, port(in/out), service, assembler
- infrastructure/: persistence, cache, messaging, external, config
- api/: controller, dto, mapper, filter, exception, config
#### 类命名示例
- Domain Entity: Order, OrderItem
- Value Object: Money, OrderStatus
- UseCase: CreateOrderUseCase
- Command: CreateOrderCommand
- Controller: OrderController
- DTO: CreateOrderRequest
@@ -0,0 +1,50 @@
# P1_task_templates/_route_selection.md - 路线选择指南
## 路线选择原则
根据需求类型选择开发路线,不得机械固定顺序。
### Route A: API-first(外部接口驱动)
适用于:
- 新功能开发
- 对外提供新接口
- 前端驱动的需求
流程:
```
OpenAPI → DTO → Controller → UseCase → Domain → Infra → Tests
```
### Route B: Domain-first(内部领域驱动)
适用于:
- 领域规则变更
- 批处理功能
- 内部服务重构
- 业务逻辑复杂变更
流程:
```
Domain Model → UseCase → Persistence → (可选) API/Events → Tests
```
### Route C: Integration-first(集成驱动)
适用于:
- 第三方服务集成
- 消息队列处理
- 缓存优化
流程:
```
External Adapter → Port ← UseCase ← Domain → Persistence → Tests
```
### 选择判断矩阵
| 需求特征 | 推荐路线 | 理由 |
|----------|----------|------|
| 新业务功能 | Route A | 从接口定义开始,便于前后端协作 |
| 业务规则变更 | Route B | 从领域模型入手,保证业务逻辑正确性 |
| 第三方集成 | Route C | 从技术实现入手,隔离外部依赖变化 |
| 性能优化 | Route B | 从领域层开始,避免影响接口契约 |
| 批处理任务 | Route B | 无外部接口,专注内部逻辑 |
| 数据迁移 | Route C | 专注基础设施层操作 |
@@ -0,0 +1,64 @@
# P1_task_templates/generate-controller.md - 生成 Controller + DTO + 校验
## 任务目标
生成 REST Controller 及其相关的 DTO 和校验逻辑,确保符合 API 规范和安全上下文要求。
## 输入信息
- API 接口定义
- HTTP 方法和路径
- 请求参数
- 响应结构
- 涉及的 UseCase
## 输出产物
- Controller 类(api.controller 包)
- Request DTO(api.dto 包)
- Response DTO(api.dto 包)
- 参数校验注解
- API 文档注解
## 生成规则
### Controller 类
- 位于 api.controller 包
- 使用 @RestController 注解
- 类名格式:{名词}Controller
- 依赖 application 层的 UseCase
- 不直接依赖 infrastructure 层
- 方法名:create / get / list / update / delete
### Request DTO
- 位于 api.dto 包
- 使用 record 类型
- 包含参数校验注解(@NotNull, @Size, @Pattern 等)
- 严禁包含 customerId, orgId, userId 等安全上下文字段
- 字段命名使用驼峰格式
### Response DTO
- 位于 api.dto 包
- 使用 record 类型
- 包含业务结果数据
- 不暴露内部实现细节
### 参数校验
- 使用 Jakarta Validation 注解
- Controller 方法参数使用 @Valid 或 @Validated
- 校验失败由 GlobalExceptionHandler 统一处理
### API 文档
- 使用 SpringDoc OpenAPI 注解
- 包含接口描述、参数说明、响应示例
- 标注安全要求
### 安全上下文
- 从 TenantContext 获取 customerId, orgId, userId
- 不从 DTO 中获取安全上下文信息
- 将安全上下文信息传递给 UseCase
## 架构约束检查
- [ ] 不直接依赖 infrastructure 层
- [ ] Request DTO 中不含安全上下文字段
- [ ] 正确依赖 application 层 UseCase
- [ ] 包含适当的参数校验
- [ ] 使用正确的 HTTP 方法和状态码
- [ ] 正确处理安全上下文
@@ -0,0 +1,48 @@
# P1_task_templates/generate-domain-entity.md - 生成聚合根/实体/值对象
## 任务目标
根据业务需求生成符合 DDD 的领域模型,包括聚合根、实体、值对象和领域服务。
## 输入信息
- 业务实体名称
- 业务属性列表
- 业务规则约束
- 领域事件(如有)
## 输出产物
- 聚合根/实体类(domain.entity 包)
- 值对象类(domain.valueobject 包,优先使用 record)
- 领域异常类(domain.exception 包)
- 仓储接口(domain.repository 包)
- 领域事件类(domain.event 包,如有)
## 生成规则
### 聚合根/实体
- 使用 record 或普通 class(如需可变状态)
- 包含业务校验逻辑
- 实现业务方法而非贫血模型
- 包含工厂方法(如构造复杂)
### 值对象
- 优先使用 Java 21 record
- 不可变性
- 包含业务校验逻辑
- 实现 equals/hashCode
### 仓储接口
- 定义在 domain.repository 包
- 方法签名体现业务意图
- 返回领域对象而非 POJO
### 领域事件
- 包含触发事件所需的所有上下文信息
- 不包含业务逻辑
- 使用不可变对象
## 架构约束检查
- [ ] 不包含任何 Spring 注解
- [ ] 不包含任何 JPA 注解
- [ ] 不依赖任何外部框架
- [ ] 业务校验逻辑正确
- [ ] 领域规则得到体现
@@ -0,0 +1,67 @@
# P1_task_templates/generate-flyway-migration.md - 生成 DDL 脚本(含审计列)
## 任务目标
生成 Flyway 数据库迁移脚本,包括表结构定义和审计列。
## 输入信息
- 实体对象定义
- 数据库表名
- 字段类型和约束
- 索引需求
## 输出产物
- Flyway 迁移脚本(resources/db/migration/ 目录)
- 表结构定义
- 索引定义
- 初始数据(如有)
## 生成规则
### 表命名
- 使用小写蛇形命名
- 业务表使用 t_ 前缀(如 t_order, t_order_item)
- 关联表使用 t_主表_从表 形式(如 t_order_user)
### 审计列标准
每张业务表必须包含以下审计列:
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| id | BIGINT (主键) 或 UUID | 自增/生成 | 主键 |
| create_user_id | BIGINT | - | 创建人 ID |
| create_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 创建时间(UTC) |
| update_user_id | BIGINT | - | 最后修改人 ID |
| update_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 最后修改时间(UTC) |
| is_deleted | BOOLEAN | FALSE | 软删除标记 |
| version | INTEGER | 0 | 乐观锁版本号 |
### 字段类型建议
- 主键:BIGINT AUTO_INCREMENT 或 UUID
- 字符串:VARCHAR(长度) - 根据实际需求设定长度
- 时间:TIMESTAMPTZ - 存储 UTC 时间
- 数值:DECIMAL(精度) - 用于金额等精确计算
- 布尔:BOOLEAN
### 索引命名
- 普通索引:idx_{表名}_{字段名}
- 唯一索引:uk_{表名}_{字段名}
- 组合索引:idx_{表名}_{字段1}_{字段2}
### Flyway 脚本命名
- V{版本}__{描述}.sql:结构变更(DDL)
- 版本号递增不跳号:V1__, V2__, V3__
- 描述使用大写蛇形:V1__INITIAL_SCHEMA
### SQL 规范
- 使用标准 SQL 语法
- 添加适当注释
- 考虑数据库兼容性
## 架构约束检查
- [ ] 包含所有审计列
- [ ] 使用正确的命名规范
- [ ] 主键定义正确
- [ ] 乐观锁版本号字段存在
- [ ] 软删除字段存在
- [ ] 索引命名规范
- [ ] Flyway 脚本命名规范
@@ -0,0 +1,57 @@
# P1_task_templates/generate-persistence-adapter.md - 生成 JPA PO + Adapter + MapStruct
## 任务目标
生成基础设施层的数据持久化组件,包括 JPA 实体、仓储适配器和对象映射。
## 输入信息
- 领域实体定义
- 数据库表结构
- 查询需求
- 需要的仓储方法
## 输出产物
- JPA Entity(infrastructure.persistence.entity 包)
- Spring Data JPA Repository(infrastructure.persistence.repository 包)
- Persistence Adapter(infrastructure.persistence.repository 包)
- MapStruct Mapper(infrastructure.persistence.mapper 包)
- 对应的 domain.port.out 接口
## 生成规则
### JPA Entity (PO)
- 位于 infrastructure.persistence.entity 包
- 使用 @Entity, @Table 注解
- 包含审计字段(create_user_id, create_time, update_user_id, update_time, is_deleted, version)
- 使用 @Version 注解支持乐观锁
- 不包含业务逻辑
### Spring Data Repository
- 位于 infrastructure.persistence.repository 包
- 继承 JpaRepository 或相关接口
- 定义查询方法(遵循 Spring Data 命名规范)
### Persistence Adapter
- 实现 domain.port.out 中定义的仓储接口
- 位于 infrastructure.persistence.repository 包
- 依赖 JPA Repository 和 MapStruct Mapper
- 将技术异常包装为 DomainException
- 不声明 @Transactional(由 application 层控制)
### MapStruct Mapper
- 位于 infrastructure.persistence.mapper 包
- 使用 @Mapper 注解
- 实现 PO ↔ Domain Entity 的双向转换
- 处理集合类型的转换
### Out Port 接口
- 位于 domain.port.out 包
- 定义业务意图明确的方法
- 方法参数包含必要的安全上下文(如 customerId)
## 架构约束检查
- [ ] 不在 infrastructure 层声明 @Transactional
- [ ] 正确实现 domain.port.out 接口
- [ ] 包含审计字段
- [ ] 使用 @Version 支持乐观锁
- [ ] 技术异常正确包装为 DomainException
- [ ] 不包含业务逻辑判断
@@ -0,0 +1,61 @@
# P1_task_templates/generate-unit-test.md - 生成单元测试(Mock Port)
## 任务目标
生成高质量的单元测试,重点测试业务逻辑和架构合规性。
## 输入信息
- 被测试类(通常是 UseCase)
- 依赖的 Ports
- 业务场景描述
- 预期行为
## 输出产物
- 单元测试类(test source set)
- Mock 的 Ports
- 测试数据准备
- 断言验证
## 生成规则
### 测试类结构
- 使用 JUnit 5
- 使用 Mockito 进行依赖 Mock
- 测试方法命名:should{预期行为}When{条件}
- 遵循 Given-When-Then 模式
### UseCase 测试
- Mock 所有 Out Ports
- 验证业务逻辑正确性
- 验证安全上下文正确传递
- 验证事务边界(如果适用)
- 验证异常处理
### 测试数据
- 使用有意义的测试数据
- 覆盖正常路径和异常路径
- 包含边界条件测试
- 使用测试常量组织数据
### Mock 策略
- Mock domain.port.out 接口
- 验证 Mock 的交互次数和参数
- 验证业务规则执行
### 断言重点
- 业务结果正确性
- 依赖方法调用验证
- 安全上下文传递验证
- 异常抛出验证
### 测试覆盖率
- 目标:业务逻辑部分 100% 覆盖
- 重点关注 UseCase 中的业务判断逻辑
- 验证领域对象的行为
## 架构约束检查
- [ ] 正确 Mock 依赖的 Ports
- [ ] 验证业务逻辑而非实现细节
- [ ] 测试方法命名符合规范
- [ ] 覆盖正常和异常场景
- [ ] 验证安全上下文处理
- [ ] 遵循 Given-When-Then 模式
@@ -0,0 +1,56 @@
# P1_task_templates/generate-usecase.md - 生成用例(含事务、安全上下文)
## 任务目标
根据业务需求生成 UseCase,包含事务管理、安全上下文处理和业务编排逻辑。
## 输入信息
- 业务用例描述
- 输入参数(Command 对象)
- 输出结果(Response 对象)
- 涉及的领域对象
- 依赖的端口(Ports)
## 输出产物
- UseCase 类(application.usecase 包)
- Command/Query 对象(application.port.in 包)
- Response 对象(application.port.in 包)
- 事务配置
- 安全上下文处理
## 生成规则
### UseCase 类
- 类名格式:{动词}{名词}UseCase
- 入口方法必须命名为 execute(...)
- 使用 @Transactional 注解(application 层唯一允许位置)
- 从 TenantContext 或通过参数获取安全上下文
- 依赖 domain 层对象和 out ports
- 不直接依赖 infrastructure 层
### Command/Query 对象
- 位于 application.port.in 包
- 使用 record 类型
- 包含输入参数校验
- 不包含 customerId/orgId 等安全上下文字段
### Response 对象
- 位于 application.port.in 包
- 使用 record 类型
- 包含业务结果数据
### 事务处理
- 读操作:@Transactional(readOnly = true)
- 写操作:@Transactional(默认 REQUIRED)
- 避免在事务内调用外部服务
### 安全上下文
- 从 TenantContext.current() 或参数获取
- 将 customerId 等作为显式参数传递给 Ports
- 不从 Command 对象中提取安全上下文信息
## 架构约束检查
- [ ] @Transactional 仅在 UseCase 类级别使用
- [ ] 不直接依赖 infrastructure 层
- [ ] 不从 Command/Query 中读取安全上下文字段
- [ ] 事务内无外部 HTTP 调用
- [ ] 正确处理安全上下文
@@ -0,0 +1,54 @@
# P2_review_templates/review-architecture.md - 架构合规专项评审
## 评审目标
专门评审架构分层、依赖方向和架构约束的合规性。
## 评审维度
### 1. 分层架构
- [ ] domain 层是否零框架依赖
- [ ] application 层是否仅依赖 domain
- [ ] infrastructure 层是否实现 application 的 Port 接口
- [ ] api 层是否正确组装所有模块
- [ ] 各层职责是否清晰
### 2. 依赖方向
- [ ] 是否存在反向依赖
- [ ] domain 是否依赖其他层
- [ ] application 是否依赖 infrastructure 或 api
- [ ] infrastructure 是否依赖 api
- [ ] 依赖图是否符合规定方向
### 3. 注解使用合规性
- [ ] domain 层是否无 Spring/JPA 注解
- [ ] application 层是否无 MVC/Cache/MQ 注解
- [ ] infrastructure 层是否仅使用技术框架注解
- [ ] api 层是否仅使用 MVC/Security 注解
- [ ] 是否避免字段注入(@Autowired)
### 4. 事务边界
- [ ] @Transactional 是否仅在 application 层
- [ ] 事务内是否无外部 HTTP 调用
- [ ] 事务策略是否正确(readOnly=true for queries)
- [ ] 乐观锁是否正确实现
- [ ] 事务传播是否合理
### 5. 异常处理
- [ ] 业务异常是否继承 DomainException
- [ ] 是否有适当的 ErrorCode
- [ ] 技术异常是否包装为业务异常
- [ ] GlobalExceptionHandler 是否存在
- [ ] 错误响应格式是否统一
### 6. 命名规范
- [ ] 类命名是否符合规范
- [ ] 包结构是否正确
- [ ] UseCase 方法是否命名为 execute
- [ ] API 路径是否规范
- [ ] 数据库命名是否规范
## 评审输出
- [ ] 架构违规清单
- [ ] 修复优先级排序
- [ ] 架构改进建议
- [ ] 自动检查规则建议
@@ -0,0 +1,45 @@
# P2_review_templates/review-general.md - 通用评审
## 评审目标
全面检查代码的架构合规性、代码规范性和业务逻辑正确性。
## 评审维度
### 1. 架构合规性
- [ ] 依赖方向是否正确(api → application → domain ← infrastructure)
- [ ] 各层是否遵守允许/禁止的注解规则
- [ ] UseCase 入口方法是否命名为 execute
- [ ] 事务是否仅在 application 层声明
- [ ] Controller 是否直接依赖 infrastructure 层
### 2. 代码规范性
- [ ] 命名是否符合规范(类名、方法名、包名)
- [ ] 代码结构是否清晰
- [ ] 是否有适当的注释
- [ ] 是否遵循 Java 最佳实践
- [ ] 异常处理是否恰当
### 3. 业务逻辑正确性
- [ ] 业务规则是否正确实现
- [ ] 数据校验是否充分
- [ ] 边界条件是否考虑周全
- [ ] 业务流程是否合理
- [ ] 安全控制是否到位
### 4. 安全上下文
- [ ] Request DTO 是否包含安全上下文字段
- [ ] 是否从 TenantContext 获取安全信息
- [ ] 是否正确传递安全上下文到下游
- [ ] 多租户隔离是否实现
### 5. 测试覆盖
- [ ] 是否有充足的单元测试
- [ ] 测试是否覆盖主要业务场景
- [ ] 异常情况是否有测试
- [ ] Mock 策略是否合适
## 评审输出
- [ ] 问题清单(严重程度分级)
- [ ] 修复建议
- [ ] 架构改进建议
- [ ] 代码优化建议
@@ -0,0 +1,47 @@
# P2_review_templates/review-security.md - 安全专项评审
## 评审目标
专门评审安全上下文实现、多租户隔离和权限控制。
## 评审维度
### 1. 安全上下文注入
- [ ] TraceIdFilter 是否正确生成/传递 traceId
- [ ] SecurityContextFilter 是否正确解析 Header
- [ ] TenantContext 是否正确存储在 ThreadLocal
- [ ] MDC 是否正确设置安全上下文字段
- [ ] 上下文字段是否完整(customerId, orgId, userId, roles)
### 2. 可信来源验证
- [ ] Request DTO 是否包含 customerId/orgId/userId 字段
- [ ] Controller 是否从 TenantContext 而非 DTO 获取安全信息
- [ ] Command/Query 是否包含安全上下文字段
- [ ] UseCase 是否从 TenantContext 获取安全信息
- [ ] 是否有机制防止 DTO 字段覆盖安全上下文
### 3. 多租户数据隔离
- [ ] 所有查询是否包含 customerId 过滤条件
- [ ] 写入操作是否正确设置 create_user_id/update_user_id
- [ ] 是否有机制防止跨租户数据访问
- [ ] 审计字段是否正确填充
- [ ] 软删除是否正确实现
### 4. 线程间传播
- [ ] 异步场景是否正确传播安全上下文
- [ ] @Async 方法是否使用 TaskDecorator
- [ ] CompletableFuture 是否正确处理上下文
- [ ] 线程池任务是否传播上下文
- [ ] 调度任务是否正确设置上下文
### 5. 安全漏洞
- [ ] 敏感信息是否泄露到日志
- [ ] 是否有权限校验机制
- [ ] API 是否有适当的认证/授权
- [ ] 是否有防重放攻击机制
- [ ] 密码等敏感数据是否加密存储
## 评审输出
- [ ] 安全风险清单
- [ ] 修复优先级排序
- [ ] 安全加固建议
- [ ] 合规性检查结果
@@ -0,0 +1,59 @@
# P3_fix_templates/fix-ci-failure.md - CI 失败修复模板
## 修复目标
根据 CI 失败信息进行最小化修复,保持原有逻辑完整性。
## 分析步骤
### 1. 失败类型识别
- [ ] 编译错误(Compilation Error)
- [ ] 单元测试失败(Unit Test Failure)
- [ ] 集成测试失败(Integration Test Failure)
- [ ] 架构检查失败(ArchUnit Violation)
- [ ] 代码规范失败(SpotBugs/Checkstyle)
- [ ] 依赖检查失败(Dependency Violation)
### 2. 失败原因分析
- [ ] 详细阅读错误日志
- [ ] 定位具体失败文件和行号
- [ ] 理解失败的根本原因
- [ ] 评估修复的影响范围
### 3. 修复策略
- [ ] 仅修复导致 CI 失败的具体问题
- [ ] 保持原有业务逻辑不变
- [ ] 遵循现有代码风格
- [ ] 最小化代码变更范围
## 修复类别及处理方式
### 编译错误
- 检查语法错误
- 确保导入正确
- 修复类型不匹配
### 测试失败
- 检查断言逻辑
- 验证测试数据
- 确保业务逻辑正确
### 架构违规
- 调整依赖关系
- 移动类到正确层级
- 修复注解使用
### 代码规范
- 调整代码格式
- 修复命名问题
- 处理潜在缺陷
## 修复验证
- [ ] 本地运行 mvn verify 通过
- [ ] 相关单元测试通过
- [ ] 架构检查通过
- [ ] 业务逻辑未受影响
## 提交信息
- [ ] 使用 fix: 前缀
- [ ] 简要描述修复内容
- [ ] 引用相关 issue(如有)
+64
View File
@@ -0,0 +1,64 @@
# 仓库布局规范 v1.0
## 1. 双仓库策略
每个服务对应两个 Gitea 仓库:
| 仓库 | 内容 | CI/CD 角色 |
|------|------|-----------|
| {service-name} | 应用代码 + Dockerfile + Gitea CI workflow | CI:编译/测试/构建镜像/推送 Harbor |
| {service-name}-deploy | Kustomize 部署清单 | CD:ArgoCD 监听此仓库 |
## 2. 命名映射关系
以 order-service 为例:
| 资源 | 命名 |
|------|------|
| 应用仓库 | order-service |
| 部署仓库 | order-service-deploy |
| Maven groupId | com.{company}.order |
| Maven artifactId | order-service(父)/ order-domain / order-application / ... |
| Java 包根 | com.{company}.order |
| Docker 镜像 | harbor.{domain}/{org}/order-service:{git-sha-short} |
| K8s Namespace | {env}(dev / staging / prod) |
| K8s Deployment | order-service |
| K8s Service | order-service |
| ArgoCD Application | order-service-dev / order-service-staging / order-service-prod |
## 3. 模板仓库初始化脚本
模板仓库名:java-service-template
初始化命令:
./init.sh --name order-service --group com.example.order --org myteam
脚本执行的替换清单:
- 目录名:tmpl-* → order-service-*
- pom.xml:groupId, artifactId, name
- Java 包名:com.example.tmpl → com.example.order
- Dockerfile:镜像名
- Kustomize:image, app label
- Gitea CI workflow:镜像推送路径
- ArgoCD Application 名
## 4. 部署仓库结构
{service-name}-deploy/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ └── hpa.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml # image tag 在此修改
│ └── patch-resources.yaml # 资源配额覆盖
├── staging/
│ ├── kustomization.yaml
│ └── patch-resources.yaml
└── prod/
├── kustomization.yaml
├── patch-resources.yaml
└── patch-replicas.yaml # 生产副本数