jsharness 1.0.0

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.
Files changed (68) hide show
  1. package/.harness/README.md +199 -0
  2. package/.harness/agents/code-reviewer/contract.yaml +64 -0
  3. package/.harness/agents/developer/contract.yaml +72 -0
  4. package/.harness/agents/gate-controller/contract.yaml +64 -0
  5. package/.harness/agents/project-manager/contract.yaml +77 -0
  6. package/.harness/agents/prompt-templates.md +352 -0
  7. package/.harness/agents/requirements-analyst/contract.yaml +64 -0
  8. package/.harness/agents/solution-designer/contract.yaml +75 -0
  9. package/.harness/agents/tester/contract.yaml +92 -0
  10. package/.harness/config/models.yaml +67 -0
  11. package/.harness/dev-map/backend/api-definition.md +131 -0
  12. package/.harness/dev-map/backend/auth-security.md +131 -0
  13. package/.harness/dev-map/backend/conventions-java.md +471 -0
  14. package/.harness/dev-map/backend/conventions.md +192 -0
  15. package/.harness/dev-map/backend/database.md +106 -0
  16. package/.harness/dev-map/backend/structure.md +140 -0
  17. package/.harness/dev-map/decisions.md +275 -0
  18. package/.harness/dev-map/frontend/api-integration.md +139 -0
  19. package/.harness/dev-map/frontend/components.md +178 -0
  20. package/.harness/dev-map/frontend/conventions.md +416 -0
  21. package/.harness/dev-map/frontend/state-management.md +170 -0
  22. package/.harness/dev-map/frontend/structure.md +103 -0
  23. package/.harness/dev-map/overview.md +267 -0
  24. package/.harness/docs/integration-test-plan.md +248 -0
  25. package/.harness/docs/team-guidelines/README.md +161 -0
  26. package/.harness/docs/team-guidelines/arch-team.md +811 -0
  27. package/.harness/docs/team-guidelines/collaboration.md +556 -0
  28. package/.harness/docs/team-guidelines/pm-team.md +337 -0
  29. package/.harness/docs/team-guidelines/qa-team.md +562 -0
  30. package/.harness/docs/team-guidelines/rd-team.md +714 -0
  31. package/.harness/docs/training-materials.md +280 -0
  32. package/.harness/gate/baseline.js +220 -0
  33. package/.harness/gate/checks/build-gates-frontend.js +152 -0
  34. package/.harness/gate/checks/build-gates-java.js +155 -0
  35. package/.harness/gate/checks/build-gates.js +119 -0
  36. package/.harness/gate/checks/engineering-consistency.js +138 -0
  37. package/.harness/gate/checks/security-quality.js +129 -0
  38. package/.harness/gate/checks/static-compliance.js +313 -0
  39. package/.harness/gate/checks/test-compliance.js +114 -0
  40. package/.harness/gate/index.js +315 -0
  41. package/.harness/mcp/config.yaml +435 -0
  42. package/.harness/rules/global/coding-standard.md +232 -0
  43. package/.harness/rules/global/commit-convention.md +165 -0
  44. package/.harness/rules/global/process-discipline.md +192 -0
  45. package/.harness/rules/global/security-baseline.md +306 -0
  46. package/.harness/rules/project/frontend-vue3.md +293 -0
  47. package/.harness/rules/project/java-backend.md +460 -0
  48. package/.harness/rules/project/web-specific.md +231 -0
  49. package/.harness/skills/build.md +192 -0
  50. package/.harness/skills/code-review.md +251 -0
  51. package/.harness/skills/docker-build.md +227 -0
  52. package/.harness/skills/docs-update.md +164 -0
  53. package/.harness/skills/java-build.md +261 -0
  54. package/.harness/skills/lint-check.md +482 -0
  55. package/.harness/skills/task-board-maintenance.md +105 -0
  56. package/.harness/skills/test-api.md +461 -0
  57. package/.harness/skills/test-e2e.md +431 -0
  58. package/.harness/skills/test-unit.md +649 -0
  59. package/.harness/skills/vue-frontend-build.md +344 -0
  60. package/.harness/specs/quality-feedback/implementation-guide.md +350 -0
  61. package/.harness/task-board.md +121 -0
  62. package/.harness/workflow/definition.yaml +504 -0
  63. package/.harness/workflow/validate.js +320 -0
  64. package/.harness/workflow/variants.yaml +253 -0
  65. package/README.md +237 -0
  66. package/bin/jsharness.js +53 -0
  67. package/lib/index.mjs +778 -0
  68. package/package.json +1 -0
@@ -0,0 +1,106 @@
1
+ # 后端分区 — 数据库设计与迁移
2
+
3
+ ## 当前数据库
4
+
5
+ | 项目 | 值 |
6
+ |------|-----|
7
+ | 数据库类型 | PostgreSQL 15+ |
8
+ | ORM | Prisma / TypeORM |
9
+ | 连接池 | pg-pool (默认 10 连接) |
10
+ | 迁移工具 | Prisma Migrate / TypeORM Migration |
11
+ | 字符集 | UTF-8 |
12
+ | 时区 | UTC (应用层做时区转换) |
13
+
14
+ ## 数据模型总览
15
+
16
+ ```
17
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
18
+ │ users │ │ roles │ │ permissions │
19
+ ├──────────────┤ ├──────────────┤ ├──────────────┤
20
+ │ id (PK) │◄──┐ │ id (PK) │ │ id (PK) │
21
+ │ email (UQ) │ └─┤ name │ │ resource │
22
+ │ password │ │ description │ │ action │
23
+ │ name │ └──────┬───────┘ └──────┬───────┘
24
+ │ avatar_url │ │ │
25
+ │ status │ ┌──────┴───────┐ ┌──────┴───────┐
26
+ │ created_at │ │ user_roles │ │role_permissions│
27
+ │ updated_at │ ├──────────────┤ ├──────────────┤
28
+ └──────────────┘ │ user_id (FK) │ │ role_id (FK) │
29
+ │ role_id (FK) │ │ perm_id (FK) │
30
+ └──────────────┘ └──────────────┘
31
+
32
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
33
+ │ sessions │ │ audit_logs │ │ files │
34
+ ├──────────────┤ ├──────────────┤ ├──────────────┤
35
+ │ id (PK) │ │ id (PK) │ │ id (PK) │
36
+ │ user_id (FK) │ │ user_id (FK) │ │ uploader_id │
37
+ │ token_hash │ │ action │ │ url │
38
+ │ expires_at │ │ entity_type │ │ file_name │
39
+ │ created_at │ │ entity_id │ │ mime_type │
40
+ └──────────────┘ │ ip_address │ │ size_bytes │
41
+ │ created_at │ │ created_at │
42
+ └──────────────┘ └──────────────┘
43
+ ```
44
+
45
+ ## 迁移工作流
46
+
47
+ ### 1. 创建迁移
48
+
49
+ ```bash
50
+ # Prisma
51
+ npx prisma migrate dev --name add_user_avatar_field
52
+
53
+ # TypeORM
54
+ npx typeorm migration:generate src/database/migrations/AddUserAvatarField
55
+ ```
56
+
57
+ ### 2. 迁移文件规范
58
+
59
+ ```sql
60
+ -- YYYYMMDDHHMMSS_description.sql
61
+
62
+ -- =============================================
63
+ -- Migration: Add avatar_url to users table
64
+ -- Author: {developer-name}
65
+ -- Ticket: #{issue-number}
66
+ -- Description: Add avatar storage support for user profiles
67
+ -- =============================================
68
+
69
+ BEGIN;
70
+
71
+ -- Up migration
72
+ ALTER TABLE users ADD COLUMN avatar_url VARCHAR(512);
73
+ ALTER TABLE users ADD COLUMN avatar_updated_at TIMESTAMPTZ;
74
+ CREATE INDEX idx_users_avatar ON users(avatar_url) WHERE avatar_url IS NOT NULL;
75
+
76
+ -- Down migration (必须可逆)
77
+ -- ALTER TABLE users DROP COLUMN IF EXISTS avatar_url;
78
+ -- ALTER TABLE users DROP COLUMN IF EXISTS avatar_updated_at;
79
+ -- DROP INDEX IF EXISTS idx_users_avatar;
80
+
81
+ COMMIT;
82
+ ```
83
+
84
+ ### 3. 迁移注意事项
85
+
86
+ | 规则 | 说明 |
87
+ |------|------|
88
+ | **必须可逆** | 每个 up 都要有对应的 down |
89
+ | **不加锁大表** | >100 万行表的 DDL 要分段执行 |
90
+ | **默认值** | 新增非空列必须有 DEFAULT |
91
+ | **索引** | 新增索引考虑 CONCURRENTLY 模式 |
92
+ | **备份** | 生产环境变更前先备份 |
93
+
94
+ ## 数据库命名规范
95
+
96
+ | 对象 | 规范 | 示例 |
97
+ |------|------|------|
98
+ | 表名 | snake_case 复数 | `user_profiles`, `order_items` |
99
+ | 列名 | snake_case | `created_at`, `is_active` |
100
+ | 主键 | `id` (UUID v4) 或 `{table}_id` | `id`, `order_id` |
101
+ | 外键 | `{referenced_table}_id` | `user_id`, `role_id` |
102
+ | 索引 | `idx_{table}_{column}` | `idx_users_email`, `idx_orders_status_created` |
103
+ | 唯一约束 | `uq_{table}_{columns}` | `uq_users_email` |
104
+ | 时间戳 | `created_at`, `updated_at` | 统一命名 |
105
+ | 布尔列 | `is_` / `has_` / `should_` 前缀 | `is_active`, `is_deleted`, `should_notify` |
106
+ | 枚举 | 不用 ENUM 类型,用 CHECK 约束或外键表 | `CHECK (status IN ('active','inactive','banned'))` |
@@ -0,0 +1,140 @@
1
+ # 后端分区 — 模块划分与分层
2
+
3
+ ## 整体架构分层
4
+
5
+ ```
6
+ ┌─────────────────────────────────────────────────────┐
7
+ │ Controller Layer │
8
+ │ (路由 / 参数解析 / 响应格式化) │
9
+ ├─────────────────────────────────────────────────────┤
10
+ │ Service Layer │
11
+ │ (业务逻辑 / 事务协调 / 编排) │
12
+ ├─────────────────────────────────────────────────────┤
13
+ │ Repository Layer │
14
+ │ (数据访问 / SQL 构建 / 缓存) │
15
+ ├─────────────────────────────────────────────────────┤
16
+ │ Entity / Model │
17
+ │ (数据结构 / 校验 / 序列化) │
18
+ └─────────────────────────────────────────────────────┘
19
+ ```
20
+
21
+ ## 目录结构
22
+
23
+ ```
24
+ src/
25
+ ├── modules/ # 按业务域分模块
26
+ │ ├── user/ # 用户模块
27
+ │ │ ├── user.controller.ts # 路由控制器
28
+ │ │ ├── user.service.ts # 业务逻辑
29
+ │ │ ├── user.repository.ts # 数据访问
30
+ │ │ ├── user.entity.ts # 数据实体
31
+ │ │ ├── dto/ # 数据传输对象
32
+ │ │ │ ├── create-user.dto.ts
33
+ │ │ │ ├── update-user.dto.ts
34
+ │ │ │ └── query-user.dto.ts
35
+ │ │ └── user.module.ts # NestJS 模块定义
36
+ │ │
37
+ │ ├── auth/ # 认证模块
38
+ │ │ ├── auth.controller.ts
39
+ │ │ ├── auth.service.ts
40
+ │ │ ├── auth.strategy.ts
41
+ │ │ └── ...
42
+ │ │
43
+ │ └── [domain]/ # 其他业务域...
44
+
45
+ ├── common/ # 公共模块
46
+ │ ├── decorators/ # 自定义装饰器
47
+ │ ├── filters/ # 异常过滤器
48
+ │ ├── guards/ # 守卫(认证/授权)
49
+ │ ├── interceptors/ # 拦截器(日志/转换)
50
+ │ ├── pipes/ # 管道(验证/转换)
51
+ │ └── utils/ # 通用工具
52
+
53
+ ├── database/ # 数据库相关
54
+ │ ├── migrations/ # 迁移文件
55
+ │ └── seeds/ # 种子数据
56
+
57
+ └── config/ # 配置
58
+ ├── app.config.ts
59
+ ├── database.config.ts
60
+ └── redis.config.ts
61
+ ```
62
+
63
+ ## 模块职责边界
64
+
65
+ | 层 | 职责 | 可以做 | 不能做 |
66
+ |----|------|--------|--------|
67
+ | **Controller** | 接收请求、返回响应 | 参数解析、调用 Service、HTTP 状态码 | 业务逻辑、SQL 查询 |
68
+ | **Service** | 业务逻辑编排 | 调用多个 Repository、事务管理、外部服务调用 | HTTP 相关操作 |
69
+ | **Repository** | 数据 CRUD | SQL/ORM 操作、数据库查询优化 | 业务判断 |
70
+ | **Entity** | 数据结构定义 | 字段定义、关联关系、基础校验 | 业务流程逻辑 |
71
+ | **DTO** | 数据传输 | 请求/响应的格式定义 | 数据转换以外的逻辑 |
72
+
73
+ ## 模块间依赖方向
74
+
75
+ ```
76
+ Controller
77
+ ↓ 调用
78
+ Service
79
+ ↙ ↘ 调用 Repository
80
+ Repository 外部服务
81
+
82
+ Entity
83
+
84
+ ⚠️ 禁止:
85
+ - Controller 直接调用 Repository
86
+ - Service 之间循环依赖
87
+ - 下层引用上层(Entity 引用 Service)
88
+ ```
89
+
90
+ ## 示例:完整模块实现骨架
91
+
92
+ ```typescript
93
+ // modules/user/user.module.ts
94
+ @Module({
95
+ controllers: [UserController],
96
+ providers: [UserService, UserRepository],
97
+ exports: [UserService],
98
+ })
99
+ export class UserModel {}
100
+
101
+ // modules/user/user.service.ts
102
+ @Injectable()
103
+ export class UserService {
104
+ constructor(private readonly userRepo: UserRepository) {}
105
+
106
+ async findById(id: string): Promise<User> {
107
+ return this.userRepo.findById(id);
108
+ }
109
+
110
+ async create(dto: CreateUserDto): Promise<User> {
111
+ // 业务逻辑:检查唯一性等
112
+ const exists = await this.userRepo.findByEmail(dto.email);
113
+ if (exists) throw new ConflictException('Email already registered');
114
+
115
+ // 密码加密
116
+ const hashedPassword = await hash(dto.password, 12);
117
+
118
+ return this.userRepo.create({ ...dto, password: hashedPassword });
119
+ }
120
+ }
121
+
122
+ // modules/user/user.repository.ts
123
+ @Injectable()
124
+ export class UserRepository {
125
+ constructor(@InjectRepository(User) private repo: Repository<User>) {}
126
+
127
+ async findById(id: string): Promise<User | null> {
128
+ return this.repo.findOne({ where: { id }, select: ['id', 'name', 'email'] });
129
+ }
130
+
131
+ async findByEmail(email: string): Promise<User | null> {
132
+ return this.repo.findOne({ where: { email: email.toLowerCase() }});
133
+ }
134
+
135
+ async create(data: Partial<User>): Promise<User> {
136
+ const user = this.repo.create(data);
137
+ return this.repo.save(user);
138
+ }
139
+ }
140
+ ```
@@ -0,0 +1,275 @@
1
+ # 技术决策记录 (ADR — Architecture Decision Records)
2
+
3
+ > 每个重要技术决策都应该记录在这里。
4
+ > 格式参考 Michael Nygard 的 ADR 模板。
5
+
6
+ ---
7
+
8
+ ## ADR-001: 采用 Pinia 作为 Vue3 前端状态管理
9
+
10
+ - **状态**: 已采纳
11
+ - **日期**: 2026-05-20
12
+ - **决策者**: 技术团队
13
+
14
+ ### 背景
15
+ Vue3 项目需要选择合适的状态管理方案。候选包括 Vuex 4(Vue3 兼容版)、Pinia(Vue 官方推荐新一代)、以及轻量级组合式 API 方案。
16
+
17
+ ### 决策
18
+ 采用 **Pinia** 作为主要状态管理方案,配合 Composition API (`ref`/`reactive`/`computed`) 处理组件局部状态。
19
+
20
+ ### 备选方案对比
21
+
22
+ | 维度 | Vuex 4 | Pinia | Composition API (无库) |
23
+ |------|--------|-------|----------------------|
24
+ | 样板代码 | 多 (mutations/actions) | 极少 | 无 |
25
+ | TypeScript 支持 | 需要额外装饰器 | 原生推断优秀 | 原生支持 |
26
+ | DevTools | Vue DevTools 原生集成 | Vue DevTools 原生集成 | 有限 |
27
+ | 模块化 | namespaced modules | 每个 Store 独立文件 | N/A |
28
+ | 学习成本 | 中 | 低 | 低 (但大型状态难管理) |
29
+ | 包体积 | ~5KB gzip | ~1KB gzip | 0 |
30
+ | Vue3 组合式友好度 | ★★ | ★★★★★ | ★★★★★ |
31
+
32
+ ### 理由
33
+ - **Vue 官方推荐**:Pinia 是 Vuely 团队打造的 Vuex 继任者,已进入官方生态
34
+ - **Composition API 原生设计**:Store 定义用 `defineStore()` + setup 函数,与组件 `<script setup>` 风格一致
35
+ - **更好的 TS 推断**:无需手动声明接口,store state/action/getter 类型自动推导
36
+ - **更简洁 API**:去掉了 mutation,直接 dispatch action 修改 state
37
+ - **Code Splitting 友好**:Store 可按需懒加载
38
+
39
+ ### 后果
40
+ - **正面**:减少样板代码、更快的开发体验、优秀的 DX、与 Vue3 生态完美融合
41
+ - **负面**:相比 Vuex 4 社区迁移案例较少(但新项目无此问题)
42
+ - **风险**:低 — Pinia 已非常成熟,大量 Vue3 生产项目在使用
43
+
44
+ ### 使用约定
45
+ ```
46
+ stores/
47
+ ├── useUserStore.ts # 用户状态
48
+ ├── useAuthStore.ts # 认证状态
49
+ └── index.ts # Store 实例导出 (pinia 实例)
50
+ ```
51
+
52
+ ---
53
+
54
+ ## ADR-002: JWT 双 Token 认证方案
55
+
56
+ - **状态**: 已采纳
57
+ - **日期**: 2026-05-20
58
+ - **决策者**: 安全架构师
59
+
60
+ ### 背景
61
+ 需要选择合适的认证方案来保护 API 和用户会话。
62
+
63
+ ### 决策
64
+ 采用 **JWT Access Token (短期) + Opaque Refresh Token (长期)** 的双 Token 方案。
65
+
66
+ ### 备选方案对比
67
+
68
+ | 方案 | 安全性 | 性能 | 复杂度 | 可撤销性 |
69
+ |------|--------|------|--------|----------|
70
+ | Session Cookie | ★★★★★ | ★★★ | ★★ | ★★★★★ |
71
+ | 单 JWT | ★★★ | ★★★★★ | ★★★★★ | ★ |
72
+ | 双 JWT (Access+Refresh) | ★★★★ | ★★★★ | ★★★ | ★★★ |
73
+ | **双 Token (JWT+Opaque)** | **★★★★★** | **★★★★** | **★★★** | **★★★★★** ← 选中 |
74
+
75
+ ### 理由
76
+ - Access Token 短有效期(15min),即使泄露影响有限
77
+ - Refresh Token 存储在 DB + HttpOnly Cookie,可随时吊销
78
+ - 每次轮换 Refresh Token,发现异常可立即撤销
79
+ - 无需服务端 Session 存储(相比纯 Session 方案节省内存)
80
+
81
+ ### 后果
82
+ - 需要维护 sessions 表
83
+ - 需要处理 Token 并发刷新问题
84
+ - 需要在移动端和 Web 端分别适配 Cookie / LocalStorage 策略
85
+
86
+ ---
87
+
88
+ ## ADR-003: API 版本化采用 URL 路径策略
89
+
90
+ - **状态**: 已采纳
91
+ - **日期**: 2026-05-20
92
+
93
+ ### 决策
94
+ API 版本号放在 URL 路径中:`/api/v1/...`, `/api/v2/...`
95
+
96
+ ### 理由
97
+ - 直观明确,易于调试
98
+ - CDN 和代理可以按路径做不同缓存策略
99
+ - 前端可以同时对接多版本(渐进迁移)
100
+ - 业界主流做法(GitHub, Stripe, Slack 等)
101
+
102
+ ### 后果
103
+ - URL 变长
104
+ - 需要在反向代理层面做好路由转发
105
+ - OpenAPI 文档需要按版本分开维护
106
+
107
+ ---
108
+
109
+ ## ADR-004: 双技术栈架构 — 前端 Vue3 + 后端 Spring Boot(JDK21)
110
+
111
+ - **状态**: 已采纳
112
+ - **日期**: 2026-05-21
113
+ - **决策者**: 技术团队
114
+
115
+ ### 背景
116
+ 项目从纯前端扩展为全栈应用,后端技术选型需要确定。团队具备 Java 生态经验,企业基础设施也以 JVM 为主。
117
+
118
+ ### 决策
119
+ 采用 **Vue3 + Vite(前端)** + **Spring Boot 3.x + JDK21 + Maven(后端)** 的双技术栈并行架构。
120
+
121
+ ### 备选方案对比
122
+
123
+ | 方案 | 团队熟悉度 | 企业基建匹配 | 性能 | 生态成熟度 |
124
+ |------|-----------|-------------|------|-----------|
125
+ | Node.js (NestJS/Express) | ★★★★ | ★★★ | ★★★★ | ★★★★ |
126
+ | Go (Gin/Echo) | ★★★ | ★★★ | ★★★★★ | ★★★ |
127
+ | Python (FastAPI/Django) | ★★★ | ★★ | ★★★ | ★★★★ |
128
+ | **Spring Boot 3 + JDK21** | **★★★★★** | **★★★★★** | **★★★★** | **★★★★★** ← 选中 |
129
+
130
+ ### 理由
131
+ - JDK21 Virtual Threads 提升并发吞吐,适合 IO 密集型业务
132
+ - Spring Boot 3 原生 GraalVM 支持,未来可迁移 Native Image
133
+ - 企业 CI/CD 已有 Maven/SonarQube/Jenkins 完整工具链
134
+ - 与前端独立部署,解耦迭代节奏
135
+
136
+ ### 后果
137
+ - **正面**: 团队上手快、企业基建零成本接入、生态完善
138
+ - **负面**: 构建产物较大(JRE ~50MB+)、启动时间比 Go/Node 慢
139
+ - **风险**: 需要维护两套 CI 流水线
140
+ - **依赖**: JDK21+、Maven 3.8+、MySQL 8.0+、Redis 7+
141
+
142
+ ---
143
+
144
+ ## ADR-005: Gate 门禁分流检测 — 按项目类型自动路由
145
+
146
+ - **状态**: 已采纳
147
+ - **日期**: 2026-05-21
148
+ - **决策者**: 架构师
149
+
150
+ ### 背景
151
+ 原 `build-gates.js` 仅支持前端检测(tsc/npm/eslint)。Java 后端加入后需要编译检查(mvn)、单元测试(JUnit)、代码风格(Checkstyle/PMD)、静态分析(SpotBugs)等不同维度的门禁。如果混在同一个文件中会变得臃肿难维护。
152
+
153
+ ### 决策
154
+ 将 Gate 拆分为 **路由器 + 两个专用模块**:
155
+ ```
156
+ gate/checks/
157
+ ├── build-gates.js ← 路由器: detectProjectType() 自动检测
158
+ ├── build-gates-frontend.js ← 前端门禁: tsc / npm ls / build / eslint / audit
159
+ └── build-gates-java.js ← Java门禁: mvn compile / test / checkstyle / dependency / spotbugs / jacoco
160
+ ```
161
+
162
+ ### 路由检测策略
163
+
164
+ | 检测文件 | 判定类型 | 分发目标 |
165
+ |---------|---------|---------|
166
+ | `pom.xml` 存在 | `java` | `build-gates-java.js` |
167
+ | `package.json` 存在 | `frontend (node)` | `build-gates-frontend.js` |
168
+ | `build.gradle` / `build.gradle.kts` 存在 | `gradle` | (预留) |
169
+
170
+ ### 理由
171
+ - 单一职责:每个门禁文件只关注一种技术栈的检查逻辑
172
+ - 可扩展:未来加 Gradle/.NET 只需新增文件 + 路由规则
173
+ - 自动化:无需手动指定参数,脚本自动识别当前目录的项目类型
174
+
175
+ ### 后果
176
+ - **正面**: 代码清晰易维护、新技术栈接入成本低
177
+ - **负面**: 多了一层路由间接调用
178
+ - **风险**: 低 — detectProjectType() 逻辑简单可靠
179
+
180
+ ---
181
+
182
+ ## ADR-006: Workflow 直接按技术栈引用 Skill(去中间路由)
183
+
184
+ - **状态**: 已采纳
185
+ - **日期**: 2026-05-21
186
+ - **决策者**: 架构师
187
+
188
+ ### 背景
189
+ 原 workflow 的 Step4(Build) 引用通用的 `build.md` skill,而 `build.md` 内部再按技术栈分发到具体文档。这增加了一层间接跳转,且 `build.md` 本身内容单薄(仅一行 `mvn compile`),实际价值低。
190
+
191
+ ### 决策
192
+ Workflow definition.yaml 中 **直接条件引用技术栈专属 skill**:
193
+ ```yaml
194
+ # Before
195
+ skill: build
196
+
197
+ # After (条件引用)
198
+ skill: "java-build | vue-frontend-build" # 由 Agent 根据项目类型选择
199
+ ```
200
+
201
+ 同时将原 `skills/build.md` 标记为 **DEPRECATED**(废弃),重定向到 `java-build.md` 和 `vue-frontend-build.md`。
202
+
203
+ ### 理由
204
+ - 减少 indirection:Agent 不需要先读 `build.md` 再跳转
205
+ - 输出格式对齐:workflow output_formats 新增 `.java`,与实际产出一致
206
+ - 规则引用补齐:development/testing 阶段 rules_referenced 增加 `rules/project/java-backend.md`
207
+
208
+ ### 后果
209
+ - **原有 `build.md` 废弃但保留**,避免外部引用断裂
210
+ - workflow 更新后需同步更新 agents/developer/contract.yaml 的输出格式定义
211
+
212
+ ---
213
+
214
+ ## ADR-007: Java 后端质量保障工具链选型
215
+
216
+ - **状态**: 已采纳
217
+ - **日期**: 2026-05-21
218
+ - **决策者**: QA Tech Lead
219
+
220
+ ### 背景
221
+ Java 后端需要覆盖「编译 → 测试 → 风格 → 静态分析 → 覆盖率」完整质量链。每个环节有多个候选工具需要统一选型。
222
+
223
+ ### 决策
224
+
225
+ | 质量维度 | 选型工具 | 版本要求 | 理由 |
226
+ |---------|---------|---------|------|
227
+ | 单元测试 | **JUnit 5** + Mockito 5 | JUnit 5.10+, Mockito 5.x | 业界标准,Spring Boot 原生集成 |
228
+ | BDD 验证 | **BDDMockito** | — | then()/should() 语法更可读 |
229
+ | 集成测试 | **Spring Boot Test** (@Testcontainers) | 3.2+ | 真实 MySQL/Redis 容器隔离 |
230
+ | 代码覆盖率 | **JaCoCo** | 0.8.11+ | Maven 插件原生集成,0% 文件告警 |
231
+ | 代码风格 | **Checkstyle** | 10.x | Google/Sun 规则集可定制 |
232
+ | 缺陷扫描 | **PMD** + **SpotBugs** | PMD 7.x / SpotBugs 4.8+ | PMD 管复杂度,SpotBugs 管 NPE/空指针等 Bug 模式 |
233
+ | 持续质检 | **SonarQube** | 9.9+ LTS | Quality Gate 阻断 + 技术债追踪 |
234
+ | API 测试 | **REST Assured** | 5.x | DSL 风格,与 MockMvc 无缝对接 |
235
+ | 服务端 Mock | **WireMock** | 3.x | 下游依赖 Stub,契约测试基础 |
236
+ | DB 迁移 | **Flyway** | 10.x | 版本化 SQL 脚本,CI 可回滚 |
237
+
238
+ ### 后果
239
+ - **pom.xml** 会变长:需配置 maven-checkstyle/pmd/spotbugs/jacoco/sonar 等约 10 个插件
240
+ - **首次配置成本高**,但后续零维护——所有开发者 `mvn verify` 一条命令跑完所有检查
241
+ - MCP 层需注册 sonarqube/maven 工具定义供 AI Agent 调用查询
242
+
243
+ ---
244
+
245
+ ## ADR 模板(供后续使用)
246
+
247
+ ```markdown
248
+ ## ADR-NNN: {决策标题}
249
+
250
+ - **状态**: [提议中 / 已采纳 / 已废弃 / 已替代]
251
+ - **日期**: {YYYY-MM-DD}
252
+ - **决策者**: {角色/姓名}
253
+
254
+ ### 背景
255
+ {描述驱动这个决策的背景和上下文}
256
+
257
+ ### 决策
258
+ {简短描述做出的决定}
259
+
260
+ ### 备选方案
261
+ {列出考虑过的替代方案及其优劣}
262
+
263
+ ### 理由
264
+ {解释为什么选择这个方案而非其他}
265
+
266
+ ### 后果
267
+ - **正面**:
268
+ - **负面**:
269
+ - **风险**:
270
+ - **依赖**:
271
+
272
+ ### 相关链接
273
+ - Issue/PR: #{number}
274
+ - 讨论: [link]
275
+ ```
@@ -0,0 +1,139 @@
1
+ # 前端分区 — API 调用规范
2
+
3
+ ## 统一 API 客户端
4
+
5
+ 所有 API 调用必须经过封装后的客户端实例:
6
+
7
+ ```typescript
8
+ // lib/api-client.ts
9
+ import axios from 'axios';
10
+ import { getAuthToken } from './auth';
11
+
12
+ const apiClient = axios.create({
13
+ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || '/api/v1',
14
+ timeout: 15000,
15
+ headers: {
16
+ 'Content-Type': 'application/json',
17
+ },
18
+ });
19
+
20
+ // 请求拦截器 — 自动附加认证 token
21
+ apiClient.interceptors.request.use((config) => {
22
+ const token = getAuthToken();
23
+ if (token) {
24
+ config.headers.Authorization = `Bearer ${token}`;
25
+ }
26
+ return config;
27
+ });
28
+
29
+ // 响应拦截器 — 统一错误处理
30
+ apiClient.interceptors.response.use(
31
+ (response) => response.data, // 直接返回 data 部分
32
+ (error) => {
33
+ // Token 过期自动跳转登录
34
+ if (error.response?.status === 401) {
35
+ window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname);
36
+ }
37
+
38
+ // 统一错误格式
39
+ const message = error.response?.data?.message || error.message || '请求失败';
40
+ return Promise.reject(new Error(message));
41
+ }
42
+ );
43
+
44
+ export default apiClient;
45
+ ```
46
+
47
+ ## API 调用约定
48
+
49
+ ### GET 请求
50
+
51
+ ```typescript
52
+ import apiClient from '@/lib/api-client';
53
+
54
+ interface UserListParams {
55
+ page?: number;
56
+ pageSize?: number;
57
+ role?: string;
58
+ keyword?: string;
59
+ }
60
+
61
+ interface UserListResponse {
62
+ code: number;
63
+ data: {
64
+ items: User[];
65
+ total: number;
66
+ page: number;
67
+ pageSize: number;
68
+ };
69
+ }
70
+
71
+ async function getUserList(params?: UserListParams): Promise<UserListResponse['data']> {
72
+ return apiClient.get('/users', { params });
73
+ }
74
+ ```
75
+
76
+ ### POST/PUT/DELETE 请求
77
+
78
+ ```typescript
79
+ async function createUser(data: CreateUserDTO): Promise<User> {
80
+ return apiClient.post('/users', data);
81
+ }
82
+
83
+ async function updateUser(id: string, data: UpdateUserDTO): Promise<User> {
84
+ return apiClient.put(`/users/${id}`, data);
85
+ }
86
+
87
+ async function deleteUser(id: string): Promise<void> {
88
+ return apiClient.delete(`/users/${id}`);
89
+ }
90
+ ```
91
+
92
+ ### 文件上传
93
+
94
+ ```typescript
95
+ async function uploadAvatar(file: File): Promise<{ url: string }> {
96
+ const formData = new FormData();
97
+ formData.append('file', file);
98
+
99
+ return apiClient.post('/upload/avatar', formData, {
100
+ headers: { 'Content-Type': 'multipart/form-data' },
101
+ timeout: 60000, // 文件上传超时更长
102
+ });
103
+ }
104
+ ```
105
+
106
+ ## 错误处理策略
107
+
108
+ | HTTP 状态码 | 处理方式 | 用户提示 |
109
+ |-------------|----------|----------|
110
+ | 400 | 表单字段级错误展示 | 字段下方显示具体错误信息 |
111
+ | 401 | 跳转登录页 | "登录已过期,请重新登录" |
112
+ | 403 | 显示权限不足页 | "您没有权限执行此操作" |
113
+ | 404 | 显示 404 页面 | "请求的资源不存在" |
114
+ | 422 | 表单验证错误 | 字段级错误展示 |
115
+ | 429 | 显示限流提示 | "操作太频繁,请稍后再试" |
116
+ | 500 | 显示通用错误页 | "服务器出了点问题,请稍后重试" |
117
+ | 网络异常 | 显示离线提示 | "网络连接失败,请检查网络" |
118
+
119
+ ## 取消重复请求
120
+
121
+ ```typescript
122
+ // 使用 AbortController 取消未完成的请求
123
+ function useSearchUsers() {
124
+ const abortRef = useRef<AbortController | null>(null);
125
+
126
+ return useCallback(async (keyword: string) => {
127
+ // 取消上一次请求
128
+ abortRef.current?.abort();
129
+
130
+ const controller = new AbortController();
131
+ abortRef.current = controller;
132
+
133
+ return apiClient.get('/users/search', {
134
+ params: { q: keyword },
135
+ signal: controller.signal,
136
+ });
137
+ }, []);
138
+ }
139
+ ```