Commit ace5841149caae2272f6aa3c8530139b1f17cd58
0 parents
chore: plan phase done
Showing
28 changed files
with
1863 additions
and
0 deletions
.githooks/pre-push
0 → 100755
.gitignore
0 → 100644
| 1 | +++ a/.gitignore | ||
| 1 | +# ==== ERP 插件推荐忽略项(skeleton-gen 追加) ==== | ||
| 2 | +# 本地运行时配置(含真实凭据,严禁入库) | ||
| 3 | +.env.local | ||
| 4 | +.env.*.local | ||
| 5 | + | ||
| 6 | +# Java / Maven | ||
| 7 | +target/ | ||
| 8 | +*.class | ||
| 9 | + | ||
| 10 | +# Node / 前端构建产物 | ||
| 11 | +node_modules/ | ||
| 12 | +dist/ | ||
| 13 | +build/ | ||
| 14 | +coverage/ | ||
| 15 | + | ||
| 16 | +# IDE | ||
| 17 | +.idea/ | ||
| 18 | +.vscode/ | ||
| 19 | +*.iml | ||
| 20 | + | ||
| 21 | +# OS | ||
| 22 | +.DS_Store | ||
| 23 | +Thumbs.db | ||
| 24 | + | ||
| 25 | +# 日志 | ||
| 26 | +*.log | ||
| 27 | +logs/ | ||
| 28 | + | ||
| 29 | +# 插件运行时临时文件 | ||
| 30 | +.tmp/ | ||
| 31 | +*.raw | ||
| 32 | +# ==== 结束 ==== |
CLAUDE.md
0 → 100644
| 1 | +++ a/CLAUDE.md | ||
| 1 | +# CLAUDE.md — ERP项目 Claude Code 主指令文件 | ||
| 2 | + | ||
| 3 | +> 本文件是 Claude Code 的"操作手册"。Claude Code 启动时会自动读取此文件。 | ||
| 4 | + | ||
| 5 | +--- | ||
| 6 | + | ||
| 7 | +## 🎯 项目概述 | ||
| 8 | + | ||
| 9 | +- **项目名称**: 小羚羊 | ||
| 10 | +- **项目简述**: 测试ERP | ||
| 11 | +- **目标用户**: 企业内部管理人员 | ||
| 12 | +- **部署方式**: 私有化部署 | ||
| 13 | + | ||
| 14 | +--- | ||
| 15 | + | ||
| 16 | +## 🔄 B 阶段开发流程(模块循环 + 功能循环) | ||
| 17 | + | ||
| 18 | +两层嵌套循环**全部固化到 skills**。入口:`/erp-workflow:coding-start`。 | ||
| 19 | + | ||
| 20 | +- **模块循环(外)**:`module-start` → `test-gate` → `module-report` → `mr-create` → 人工 Approve MR → 下一模块 | ||
| 21 | +- **功能循环(内,每 REQ-XXX-NNN 一遍)**:`feature-brainstorm` → `feature-plan` → `feature-tdd` → `feature-verify` → `feature-review` | ||
| 22 | + | ||
| 23 | +MR 前测试闸门: | ||
| 24 | + - `test-gate`(子会话跑 `scripts/test.sh` 全量——本模块所有 REQ + 已完成模块回归); | ||
| 25 | + - `.githooks/pre-push` 兜底 | ||
| 26 | + - `git push --no-verify` 被 `deny-no-verify.sh` 硬拦。 | ||
| 27 | + | ||
| 28 | +--- | ||
| 29 | + | ||
| 30 | +## ✅ 模块完成判定规则 | ||
| 31 | + | ||
| 32 | +`docs/08-模块任务管理.md § 二` 是**模块元数据表**——每个模块记录依赖 / 路径 / MR iid / 功能(REQ)子项清单。**模块完成由 `MR:` 字段 + `GitLab API state=merged` 判定**;功能子项勾选只作可视化进度,不参与模块完成判定。 | ||
| 33 | + | ||
| 34 | +### 规则定义 | ||
| 35 | + | ||
| 36 | +每个模块在 docs/08 § 二 中长这样: | ||
| 37 | + | ||
| 38 | +```markdown | ||
| 39 | +- module_0 系统管理 | ||
| 40 | + - 依赖: — | ||
| 41 | + - 路径: backend/module/sys/, frontend/pages/sys/ | ||
| 42 | + - MR: — | ||
| 43 | + - 功能: | ||
| 44 | + - [ ] REQ-SYS-001 用户登录 | ||
| 45 | + - [ ] REQ-SYS-002 用户注册 | ||
| 46 | +``` | ||
| 47 | + | ||
| 48 | +- `MR:` 字段由 `mr-create` 在创建 MR 时从 `—` 改为 `!<iid>`。 | ||
| 49 | +- 每个 `REQ-*` 子项由 `feature-review` 在 `verdict=approve` 时自动勾选为 `[x]` | ||
| 50 | +- 子项全部勾选不等于模块完成,模块完成判定仍以 `MR:` + GitLab API state 为准。 | ||
| 51 | + | ||
| 52 | +### 模块状态语义 | ||
| 53 | + | ||
| 54 | +| `MR:` 字段 | `GitLab API state` | 含义 | 你(Claude Code)的行为 | | ||
| 55 | +|---|---|---|---| | ||
| 56 | +| `—` | — | 模块未开始(未创建 MR) | ✅ 开始本模块开发 | | ||
| 57 | +| `!<iid>` | `opened` / `closed` | 模块开发中 / 打回 | ✅ 继续推进该模块 | | ||
| 58 | +| `!<iid>` | `merged` | 模块**已完成** | 🟢 进入下一未完成模块 | | ||
| 59 | + | ||
| 60 | +### 模块完成报告 | ||
| 61 | + | ||
| 62 | +由 `module-report` skill 产出,模板位于 由 module-report skill 持有(12 节标准化,含跨模块改动等 CLAUDE.md 软规则映射节)。CC 不手写模块报告,仅填模板。 | ||
| 63 | + | ||
| 64 | +--- | ||
| 65 | + | ||
| 66 | +## 🏷️ 占位符统一约定 | ||
| 67 | + | ||
| 68 | +项目文档里有 **2 种填写占位** + **1 种提示占位**: | ||
| 69 | + | ||
| 70 | +| 格式 | 谁填 | 使用阶段 | 说明 | | ||
| 71 | +|------|-----|---------|------| | ||
| 72 | +| `【人工填写:<简短说明>】` | 人 | 仅 A 阶段文档 | 密钥 / 账密 / 包名 / 命名约定 / 小版本号等人工才能决定的值;B 阶段 plan/spec 禁止出现,查不到真值时用 `AskUserQuestion` 问用户 | | ||
| 73 | +| `TBD(<责任人>)` | CC 自动 | A 或 B | 后缀附带责任方(如 `TBD(A3 自动补)` / `TBD(A5 自动补)`);由对应 skill 就地补填,`module-report` § ⑦ 检查 `TBD(CC 补)` 残留 | | ||
| 74 | + | ||
| 75 | +**HTML 注释 `<!-- ... -->`**:提示占位,是**插件内部大纲模板**里给 LLM 的**填空提示 / 章节引导**,指引 LLM 按结构填实际内容。skill 生成时会**剥除**这些注释,最终产物里注释不会保留。 | ||
| 76 | + | ||
| 77 | +--- | ||
| 78 | + | ||
| 79 | +## 📐 编码行为约束 | ||
| 80 | + | ||
| 81 | +### 你必须做的 ✅ | ||
| 82 | + | ||
| 83 | +1. **严格遵循** `docs/04-技术规范.md`——命名 / 编码 / 统一响应 / 异常处理 / 数据访问 / 配置与安全 等项目专属技术规约全部在此 | ||
| 84 | +2. **严格遵循** `docs/09-项目目录结构.md`——文件放对位置 | ||
| 85 | +3. **每个后端接口** 必须先在 `docs/05-API接口契约.md` 定义,再编码实现 | ||
| 86 | +4. **每个功能可追溯到 `REQ-XXX-NNN`**——commit tag + 代码注释(如 `// REQ-SYS-001: 用户登录`)+ plan/spec 文件名均用此 tag | ||
| 87 | +5. **遇到跨模块改动**(动到非当前模块的代码)——按 § 🟡 软规则 **S2** 执行(允许改,但必须留痕) | ||
| 88 | +6. **遇到技术栈外组件引入**(`docs/04 § 零` 技术栈表外的框架 / 中间件 / 关键库),按 § 🟡 软规则 **S1** 执行(允许引入,但必须先 AskUserQuestion) | ||
| 89 | + | ||
| 90 | +### 你禁止做的 🚫 | ||
| 91 | + | ||
| 92 | +1. **主会话直接 `mysql -e` 跑业务 DDL**(只读查询 / 临时本地调试除外)——业务 schema 必须走 `sql/migrations/V_n__*.sql`,详见下方 Schema 演化规约 | ||
| 93 | +2. **手动 Edit `docs/08 § 二` 的 `MR:` 字段**,必须要由 `mr-create` 自动回写 | ||
| 94 | + | ||
| 95 | +### Schema 演化规约(Flyway migration) | ||
| 96 | + | ||
| 97 | +1. **文件命名**:`sql/migrations/V<n>__<snake_case_desc>.sql`,例:`V5__add_user_email_unique_index.sql` | ||
| 98 | +2. **版本号分配**:建文件前 `ls sql/migrations/V*.sql` 查当前最大 n,新文件 `n_max + 1` | ||
| 99 | +3. **Apply 方式**:Spring Boot 启动 / 测试启动时 Flyway 自动 apply(项目必须在 `pom.xml` 声明 `flyway-core` + `flyway-mysql` 依赖)。`scripts/setup-test-db.sh` 只负责清空库,不做 apply | ||
| 100 | +4. **已合并的 migration 永不修改**:发现错了写一个补救 migration(如 `V7__fix_V5_index_name.sql`),旧 `V_n.sql` | ||
| 101 | +5. **临时调试 DDL**:临时在本地试字段/索引可手动 `mysql -e`,但不写 migration;下次 `setup-test-db.sh` 会 drop+create 清掉 | ||
| 102 | +6. **A4 生成的 V1**:`V1__initial_schema.sql` 是 A 阶段由 `db-init` 从 `docs/03-数据库设计文档.md`(A3 正向设计的 schema SSoT)翻译生成的初始版本;后续 V2/V3/... 由 B 阶段每个 REQ 按需写入,**同时**反向同步更新 docs/03 对应表小节以保持 SSoT 一致 | ||
| 103 | + | ||
| 104 | +--- | ||
| 105 | + | ||
| 106 | +## 🗂️ Git 提交规范 | ||
| 107 | + | ||
| 108 | +每次提交必须遵循以下格式: | ||
| 109 | + | ||
| 110 | +``` | ||
| 111 | +<type>(<scope>): <subject> | ||
| 112 | +``` | ||
| 113 | + | ||
| 114 | +- `scope`: 模块名,如 `user` / `inventory` / `order` | ||
| 115 | +- `subject`: 简短描述;业务类(feat / fix / test)必须带 `REQ-XXX-NNN` 后缀 | ||
| 116 | + | ||
| 117 | +`type` 含义: | ||
| 118 | + | ||
| 119 | +| type | 看到它意味着 | | ||
| 120 | +|-----|-------------| | ||
| 121 | +| `feat` | **新能力上线**——用户多了一个功能、接口、页面或业务规则 | | ||
| 122 | +| `fix` | **修 bug**——原来行为错了,这次改对 | | ||
| 123 | +| `refactor` | **重构**——外部行为不变,只改代码结构 / 命名 / 抽象 | | ||
| 124 | +| `docs` | **文档改动**——只动 Markdown / 代码注释,不动实现 | | ||
| 125 | +| `style` | **格式调整**——空白 / 缩进 / import 顺序,逻辑 0 变化 | | ||
| 126 | +| `test` | **只动测试代码**——补用例 / 修 fixture,不碰实现 | | ||
| 127 | +| `chore` | **流程维护**——构建 / 依赖 / 工具 / 证据档案 / MR 元数据等非业务动作 | | ||
| 128 | + | ||
| 129 | +--- | ||
| 130 | + | ||
| 131 | +## 🚩 中断机制 | ||
| 132 | + | ||
| 133 | +功能循环(每个功能 REQ-XXX 的 Brainstorm → Plan → TDD → Verify → AI 自审)默认 **静默编程**,但触发以下任何一条必须**立刻停下、记录原因、等人决策**,不得自行绕过: | ||
| 134 | + | ||
| 135 | +| # | 中断 | 例子 | | ||
| 136 | +| - | --- | --- | | ||
| 137 | +| 1 | **测试反复失败** | 同一测试同一功能内连续 **10 次**修复失败 | | ||
| 138 | +| 2 | **要改密钥 / 账密 / 包名** | `docs/07-环境配置.md` 里由人工标注必须填的字段 | | ||
| 139 | +| 3 | **外部接口不可达** | 第三方 API 无法连接、证书失效等环境问题,并无法自行解决 | | ||
| 140 | + | ||
| 141 | +> 其余需要人类判断的场景一律走普通 `AskUserQuestion` Q&A,不中断、不写 Blocker 文件。 | ||
| 142 | + | ||
| 143 | +**触发中断时的固定动作:** | ||
| 144 | + | ||
| 145 | +1. 在当前功能的 plan 文件里追加一节 `## 🚩 Blocker`(报告格式由 `interrupt-check` 的 `interrupt-block-template.md` 持有) | ||
| 146 | +2. 停止后续所有功能的静默执行 | ||
| 147 | +3. 在主会话输出一句话摘要 + 指向 blocker 文件的路径,等人回复 | ||
| 148 | + | ||
| 149 | +--- | ||
| 150 | + | ||
| 151 | +## 🟡 软规则(允许继续,但有强制后续动作) | ||
| 152 | + | ||
| 153 | +以下情况 **不触发中断**,CC 可自行继续推进,但必须在约定位置留痕,模块完成时统一审计。 | ||
| 154 | + | ||
| 155 | +| # | 软规则 | 允许动作 | 强制后续 | | ||
| 156 | +| - | ----- | ------- | ------- | | ||
| 157 | +| S1 | **技术栈外组件引入** | 用 `AskUserQuestion` 给用户三选一:接受引入 / 换方案 / 拒绝 | ① **接受** → 同会话直接在 `docs/04 § 零` 追加一行 → 继续流程 ② **换方案 / 拒绝** → 视为常规歧义澄清,继续 Q&A 收敛 ③ 不写 Blocker、不中断流程 | | ||
| 158 | +| S2 | **跨模块改动** | **默认不改**,仅为当前模块实现所必需时允许修改 | ① hook `log-cross-module.sh` 自动落存根 ② `module-report` 一次性调用 `cross-module-log` skill 批量补齐「原因 / 影响评估」+ 「跨模块改动」节完整贴入《模块完成报告》 | | ||
| 159 | + | ||
| 160 | +--- | ||
| 161 | + | ||
| 162 | +## 🧭 通用工作准则(General Principles) | ||
| 163 | + | ||
| 164 | +### 1. Think Before Coding | ||
| 165 | + | ||
| 166 | +**Don't assume. Don't hide confusion. Surface tradeoffs.** | ||
| 167 | + | ||
| 168 | +Before implementing: | ||
| 169 | +- State your assumptions explicitly. If uncertain, ask. | ||
| 170 | +- If multiple interpretations exist, present them - don't pick silently. | ||
| 171 | +- If a simpler approach exists, say so. Push back when warranted. | ||
| 172 | +- If something is unclear, stop. Name what's confusing. Ask. | ||
| 173 | + | ||
| 174 | +### 2. Simplicity First | ||
| 175 | + | ||
| 176 | +**Minimum code that solves the problem. Nothing speculative.** | ||
| 177 | + | ||
| 178 | +- No features beyond what was asked. | ||
| 179 | +- No abstractions for single-use code. | ||
| 180 | +- No "flexibility" or "configurability" that wasn't requested. | ||
| 181 | +- No error handling for impossible scenarios. | ||
| 182 | +- If you write 200 lines and it could be 50, rewrite it. | ||
| 183 | + | ||
| 184 | +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. | ||
| 185 | + | ||
| 186 | +### 3. Surgical Changes | ||
| 187 | + | ||
| 188 | +**Touch only what you must. Clean up only your own mess.** | ||
| 189 | + | ||
| 190 | +When editing existing code: | ||
| 191 | +- Don't "improve" adjacent code, comments, or formatting. | ||
| 192 | +- Don't refactor things that aren't broken. | ||
| 193 | +- Match existing style, even if you'd do it differently. | ||
| 194 | +- If you notice unrelated dead code, mention it - don't delete it. | ||
| 195 | + | ||
| 196 | +When your changes create orphans: | ||
| 197 | +- Remove imports/variables/functions that YOUR changes made unused. | ||
| 198 | +- Don't remove pre-existing dead code unless asked. | ||
| 199 | + | ||
| 200 | +The test: Every changed line should trace directly to the user's request. | ||
| 201 | + | ||
| 202 | +### 4. Goal-Driven Execution | ||
| 203 | + | ||
| 204 | +**Define success criteria. Loop until verified.** | ||
| 205 | + | ||
| 206 | +Transform tasks into verifiable goals: | ||
| 207 | +- "Add validation" → "Write tests for invalid inputs, then make them pass" | ||
| 208 | +- "Fix the bug" → "Write a test that reproduces it, then make it pass" | ||
| 209 | +- "Refactor X" → "Ensure tests pass before and after" | ||
| 210 | + | ||
| 211 | +For multi-step tasks, state a brief plan: | ||
| 212 | +``` | ||
| 213 | +1. [Step] → verify: [check] | ||
| 214 | +2. [Step] → verify: [check] | ||
| 215 | +3. [Step] → verify: [check] | ||
| 216 | +``` | ||
| 217 | + | ||
| 218 | +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. |
docs/01-需求清单/MOD-模块管理/REQ-MOD-001.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/MOD-模块管理/REQ-MOD-001.md | ||
| 1 | +### REQ-MOD-001 模块新增 | ||
| 2 | + | ||
| 3 | +**目标**: 新增一个 ERP 业务模块定义,作为系统功能与权限分组的基础单位 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | -------- | ---- | --- | ---- | ----------- | ---- | -------- | | ||
| 11 | + | 显示类型 | 文本 | 是 | 下拉单选 | 手机端/前端业务/系统配置/接口 | 手机端 | — | | ||
| 12 | + | 存储过程(审核)名称 | 文本 | 是 | 手工输入 | — | — | — | | ||
| 13 | + | 模块类型 | 文本 | 是 | 手工输入 | — | — | — | | ||
| 14 | + | 管理部门英文 | 文本 | 是 | 手工输入 | — | — | — | | ||
| 15 | + | 权限是否显示 | 布尔 | 否 | 复选框 | — | 否 | — | | ||
| 16 | + | 界面名称中文 | 文本 | 是 | 手工输入 | — | — | — | | ||
| 17 | + | ||
| 18 | +- **输出**: | ||
| 19 | + | ||
| 20 | + - **表1**: | ||
| 21 | + | ||
| 22 | + | 字段 | 类型 | 显示来源 | | ||
| 23 | + | ----- | ---- | ----- | | ||
| 24 | + | 模块 id | 文本 | `模块表` | | ||
| 25 | + | ||
| 26 | +- **跨字段规则**: - | ||
| 27 | +- **边界**: 必填项不能为空 | ||
| 28 | +- **验收**: 提交后模块记录持久化并返回新模块 id;字段格式错误或必填缺失时返回明确错误码并定位字段 | ||
| 29 | +- **依赖表**: `tModule`(写) | ||
| 30 | +- **依赖接口**: — | ||
| 31 | + |
docs/01-需求清单/MOD-模块管理/REQ-MOD-002.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/MOD-模块管理/REQ-MOD-002.md | ||
| 1 | +### REQ-MOD-002 模块修改 | ||
| 2 | + | ||
| 3 | +**目标**: 在不破坏唯一性的前提下,更新已有模块的可编辑信息 | ||
| 4 | + | ||
| 5 | +- **输入**: 选中目标 | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | -------- | ---- | --- | ---- | ----------- | ---- | -------- | | ||
| 11 | + | 显示类型 | 文本 | 是 | 下拉单选 | 手机端/前端业务/系统配置/接口 | 原值 | — | | ||
| 12 | + | 存储过程(审核)名称 | 文本 | 是 | 手工输入 | — | 原值 | — | | ||
| 13 | + | 模块类型 | 文本 | 是 | 手工输入 | — | 原值 | — | | ||
| 14 | + | 管理部门英文 | 文本 | 是 | 手工输入 | — | 原值 | — | | ||
| 15 | + | 权限是否显示 | 布尔 | 否 | 复选框 | — | 原值 | — | | ||
| 16 | + | 界面名称中文 | 文本 | 是 | 手工输入 | — | 原值 | — | | ||
| 17 | + | ||
| 18 | +- **输出**: | ||
| 19 | + | ||
| 20 | + - **表1**: | ||
| 21 | + | ||
| 22 | + | 字段 | 类型 | 显示来源 | | ||
| 23 | + | ----- | ---- | ----- | | ||
| 24 | + | 模块 id | 文本 | `模块表` | | ||
| 25 | + | ||
| 26 | +- **跨字段规则**: - | ||
| 27 | +- **边界**: 必须传入有效模块 id;字段格式与新增一致 | ||
| 28 | +- **验收**: 提交后字段更新成功并返回模块 id;非法 id 或必填缺失时返回明确错误码并定位字段 | ||
| 29 | +- **依赖表**: `tModule`(写) | ||
| 30 | +- **依赖接口**: — |
docs/01-需求清单/MOD-模块管理/REQ-MOD-003.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/MOD-模块管理/REQ-MOD-003.md | ||
| 1 | +### REQ-MOD-003 模块删除 | ||
| 2 | + | ||
| 3 | +**目标**: 删除一个已有模块,且不破坏数据引用完整性 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | ----- | ---- | --- | ----- | ----- | --- | -------- | | ||
| 11 | + | 模块 id | 文本 | — | 点击按钮 | — | — | — | | ||
| 12 | + | ||
| 13 | +- **输出**: 成功/失败 | ||
| 14 | + | ||
| 15 | +- **跨字段规则**: 已被业务引用(菜单 / 权限 / 用户角色等)的模块不允许直接删除;建议软删除并记录删除时间与操作人 | ||
| 16 | +- **边界**: 必须传入有效模块 id;删除接口需具备相应权限并支持二次确认 | ||
| 17 | +- **验收**: 合法删除返回成功状态;存在引用时返回拒绝原因与冲突来源;非法 id 给出明确错误码 | ||
| 18 | +- **依赖表**: `tModule`(软删除写:`bDeleted` / `tDeletedDate` / `sDeletedBy`;删除前在 service 层检查子模块或外部引用) | ||
| 19 | +- **依赖接口**: — | ||
| 20 | + | ||
| 21 | + |
docs/01-需求清单/MOD-模块管理/REQ-MOD-004.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/MOD-模块管理/REQ-MOD-004.md | ||
| 1 | +### REQ-MOD-004 模块查询 | ||
| 2 | + | ||
| 3 | +**目标**: 按关键字检索模块,并以树形结构展示匹配结果 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | --- | ---- | --- | ---- | ---- | --- | -------------- | | ||
| 11 | + | 关键字 | 文本 | 否 | 手工输入 | — | — | 对界面名称模糊匹配;空为匹配所有 | | ||
| 12 | + | ||
| 13 | +- **输出**: | ||
| 14 | + | ||
| 15 | + - **表1**: | ||
| 16 | + | ||
| 17 | + | 字段 | 类型 | 显示来源 | | ||
| 18 | + | ------ | ---- | ----- | | ||
| 19 | + | 模块 id | 文本 | `模块表` | | ||
| 20 | + | 界面名称中文 | 文本 | `模块表` | | ||
| 21 | + | 显示类型 | 文本 | `模块表` | | ||
| 22 | + | 管理部门英文 | 文本 | `模块表` | | ||
| 23 | + | 父级 id | 文本 | `模块表` | | ||
| 24 | + | 序号 | 数字 | `模块表` | | ||
| 25 | + | ||
| 26 | +- **跨字段规则**: - | ||
| 27 | +- **边界**: 查询为只读,不产生写副作用;空关键字返回完整模块树 | ||
| 28 | +- **验收**: 查询结果与库内数据一致;按界面名称模糊匹配生效;空结果返回空列表而非错误 | ||
| 29 | +- **依赖表**: `tModule`(读,按 `sModuleNameZh` 模糊匹配 + 按 `iParentId` 拼装树形) | ||
| 30 | +- **依赖接口**: — | ||
| 31 | + | ||
| 32 | + |
docs/01-需求清单/MOD-模块管理/_module.md
0 → 100644
docs/01-需求清单/USR-用户管理/REQ-USR-001.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/USR-用户管理/REQ-USR-001.md | ||
| 1 | +### REQ-USR-001 用户新增 | ||
| 2 | + | ||
| 3 | +**目标**: 录入新用户的基本信息并完成账户初始化,便于后续登录与权限分配 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | -------- | ---- | --- | ---- | ----------------- | --------- | ------------------- | | ||
| 11 | + | 创建时间 | 日期时间 | — | 系统生成 | — | 当前日期 | 保存后自动生成;只读 | | ||
| 12 | + | 制单人 | 文本 | — | 系统生成 | — | 当前登录用户 | 保存后自动生成;只读 | | ||
| 13 | + | 员工名 | 文本 | 否 | 下拉单选 | `职员表` | — | 关联职员(可选) | | ||
| 14 | + | 用户号 | 文本 | 是 | 手工输入 |— | — | 关联职员选择后自动输入员工姓名 | | ||
| 15 | + | 用户名 | 文本 | 是 | 手工输入 |— | — | 关联职员选择后自动输入员工姓名 | | ||
| 16 | + | 类型 | 文本 | 是 | 下拉单选 | 普通用户/超级管理员 | 普通用户 | — | | ||
| 17 | + | 语言 | 文本 | 是 | 下拉单选 | 中文/英文/繁体 | — | — | | ||
| 18 | + | 单据修改权限 | 布尔 | 否 | 复选框 | — | 否 | — | | ||
| 19 | + | 密码 | 文本 | - | 系统生成 | 不显示 | 666666 | 保存后自动设为初始化 | | ||
| 20 | + | ||
| 21 | + - **表2** - 权限组: | ||
| 22 | + | ||
| 23 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 24 | + | -------- | ---- | --- | ---- | ----------------- | --------- | ------------------- | | ||
| 25 | + | 复选框 | 布尔 | 否 | 复选框 | — | 否 | 是否选择当前行权限 | | ||
| 26 | + | 权限分类 | 文本 | — | — | — | — | — | | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +- **输出**: | ||
| 30 | + | ||
| 31 | + - **表1**: | ||
| 32 | + | ||
| 33 | + | 字段 | 类型 | 显示来源 | | ||
| 34 | + | --- | --- | --- | | ||
| 35 | + | 用户号 | 文本 | - | | ||
| 36 | + | ||
| 37 | +- **跨字段规则**: 用户名在系统内全局唯一;角色取值受系统配置约束 | ||
| 38 | +- **边界**: 密码以哈希形式存储 | ||
| 39 | +- **验收**: 提交合法表单后系统持久化用户记录并返回新用户 id;唯一性冲突或字段格式错误时返回明确错误码并定位到具体字段 | ||
| 40 | +- **依赖表**: `tUser`(写)、`tStaff`(员工名下拉读)、`tPermissionCategory`(权限分类下拉读)、`tUserPermission`(写权限组关联) | ||
| 41 | +- **依赖接口**: — |
docs/01-需求清单/USR-用户管理/REQ-USR-002.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/USR-用户管理/REQ-USR-002.md | ||
| 1 | +### REQ-USR-002 用户修改 | ||
| 2 | + | ||
| 3 | +**目标**: 在不破坏唯一性的前提下,更新已有用户的可编辑信息 | ||
| 4 | + | ||
| 5 | +- **输入**: 选中目标 | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | -------- | ---- | --- | ---- | ----------------- | --------- | ------------------- | | ||
| 11 | + | 创建时间 | 日期时间 | — | 系统生成 | — | 原值 | 保存后自动生成;只读 | | ||
| 12 | + | 制单人 | 文本 | — | 系统生成 | — | 原值 | 保存后自动生成;只读 | | ||
| 13 | + | 员工名 | 文本 | 否 | 下拉单选 | `职员表` | 原值 | 关联职员(可选) | | ||
| 14 | + | 用户号 | 文本 | 是 | 手工输入 |— | 原值 | 关联职员选择后自动输入员工姓名 | | ||
| 15 | + | 用户名 | 文本 | 是 | 手工输入 |— | 原值 | 关联职员选择后自动输入员工姓名 | | ||
| 16 | + | 类型 | 文本 | 是 | 下拉单选 | 普通用户/超级管理员 | 原值 | — | | ||
| 17 | + | 语言 | 文本 | 是 | 下拉单选 | 中文/英文/繁体 | 原值 | — | | ||
| 18 | + | 单据修改权限 | 布尔 | 否 | 复选框 | — | 原值 | — | | ||
| 19 | + | 密码 | 文本 | - | 系统生成 | 不显示 | 原值 | 保存后自动设为初始化 | | ||
| 20 | + | ||
| 21 | + - **表2** - 权限组: | ||
| 22 | + | ||
| 23 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 24 | + | -------- | ---- | --- | ---- | ----------------- | --------- | ------------------- | | ||
| 25 | + | 复选框 | 布尔 | 否 | 复选框 | — | 原值 | 是否选择当前行的权限 | | ||
| 26 | + | 权限分类 | 文本 | — | — | — | — | — | | ||
| 27 | + | ||
| 28 | +- **输出**: | ||
| 29 | + | ||
| 30 | + - **表1**: | ||
| 31 | + | ||
| 32 | + | 字段 | 类型 | 显示来源 | | ||
| 33 | + | --- | --- | --- | | ||
| 34 | + | 用户 id | 文本 | `职员表` | | ||
| 35 | + | ||
| 36 | +- **跨字段规则**: 密码不在该接口修改;角色变更需具备相应权限 | ||
| 37 | +- **边界**: 必须传入有效用户 id;字段格式与新增一致 | ||
| 38 | +- **验收**: 提交后字段更新成功并返回最新用户数据;非法 id、唯一冲突或格式错误返回明确错误码并定位字段 | ||
| 39 | +- **依赖表**: `tUser`(写)、`tStaff`(员工名下拉读)、`tPermissionCategory`(权限分类下拉读)、`tUserPermission`(写权限组关联) | ||
| 40 | +- **依赖接口**: — | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + | 创建时间 | 日期时间 | — | — | — | 原值 | 不变;只读 | | ||
| 46 | + | 制单人 | 文本 | — | — | — | 原值 | 不变;只读 | | ||
| 0 | \ No newline at end of file | 47 | \ No newline at end of file |
docs/01-需求清单/USR-用户管理/REQ-USR-003.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/USR-用户管理/REQ-USR-003.md | ||
| 1 | +### REQ-USR-003 用户查询 | ||
| 2 | + | ||
| 3 | +**目标**: 按条件检索用户列表,并支持查看单个用户的详情 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | ---- | ---- | --- | ---- | ----------------------------------------------- | ------- | --------------- | | ||
| 11 | + | 查询字段 | 文本 | 否 | 下拉单选 | 用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人 | 用户名 | — | | ||
| 12 | + | 匹配方式 | 文本 | 否 | 下拉单选 | 包含/不包含/等于 | 包含 | — | | ||
| 13 | + | 查询值 | 文本 | 否 | 手工输入 | — | — | 与「查询字段」配合使用,空为选择全部 | | ||
| 14 | + | ||
| 15 | +- **输出**: | ||
| 16 | + | ||
| 17 | + - **表1**: | ||
| 18 | + | ||
| 19 | + | 字段 | 类型 | 显示来源 | | ||
| 20 | + | ---- | ---- | ----- | | ||
| 21 | + | 序号 | 数字 | 系统生成 | | ||
| 22 | + | 用户名 | 文本 | `用户表` | | ||
| 23 | + | 员工名 | 文本 | `职员表` | | ||
| 24 | + | 用户号 | 文本 | `用户表` | | ||
| 25 | + | 部门 | 文本 | `职员表` | | ||
| 26 | + | 用户类型 | 文本 | `用户表` | | ||
| 27 | + | 语言 | 文本 | `用户表` | | ||
| 28 | + | 作废 | 布尔 | `用户表` | | ||
| 29 | + | 登录日期 | 日期时间 | `用户表` | | ||
| 30 | + | 制单人 | 文本 | `用户表` | | ||
| 31 | + | 制单日期 | 日期时间 | `用户表` | | ||
| 32 | + | ||
| 33 | +- **跨字段规则**: - | ||
| 34 | +- **边界**: 单页最大条数受限(默认 100);密码与敏感字段不返回;查询为只读,不产生写副作用 | ||
| 35 | +- **验收**: 查询结果与库内数据一致;分页 / 排序 / 过滤参数生效;空结果返回空列表而非错误 | ||
| 36 | +- **依赖表**: `tUser`(读)、`tStaff`(读,员工名 / 部门显示来源) | ||
| 37 | +- **依赖接口**: — |
docs/01-需求清单/USR-用户管理/REQ-USR-004.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/USR-用户管理/REQ-USR-004.md | ||
| 1 | +### REQ-USR-004 用户登录 | ||
| 2 | + | ||
| 3 | +**目标**: 用户使用账号密码完成身份认证并获取访问凭证 | ||
| 4 | + | ||
| 5 | +- **输入**: | ||
| 6 | + | ||
| 7 | + - **表1**: | ||
| 8 | + | ||
| 9 | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | ||
| 10 | + | --- | ---- | --- | ---- | ------- | --- | ------------------- | | ||
| 11 | + | 用户名 | 文本 | 是 | 手工输入 | — | — | - | | ||
| 12 | + | 密码 | 文本 | 是 | 手工输入 | — | — | 输入显示星号 | | ||
| 13 | + | 版本 | 文本 | 是 | 下拉单选 | 标准版/- | 标准版 | | | ||
| 14 | + | ||
| 15 | +- **输出**: 成功/失败 | ||
| 16 | + | ||
| 17 | +- **跨字段规则**: 校验用户名 + 密码哈希;连续失败达到阈值临时锁定账号;登录成功签发限时 token 并返回基本用户信息 | ||
| 18 | +- **边界**: token 设置合理过期时间;接口需具备防暴力破解保护 | ||
| 19 | +- **验收**: 正确凭据返回 token + 用户基本信息;错误凭据返回明确错误码;账号锁定状态返回剩余冷却时间 | ||
| 20 | +- **依赖表**: `tUser`(读 + 写 `tLastLoginDate`);登录失败计数走 Redis(不入 DB) | ||
| 21 | +- **依赖接口**: — |
docs/01-需求清单/USR-用户管理/_module.md
0 → 100644
docs/01-需求清单/index.md
0 → 100644
| 1 | +++ a/docs/01-需求清单/index.md | ||
| 1 | +# 需求清单 | ||
| 2 | + | ||
| 3 | +> 本目录按模块组织所有功能需求。每个模块一个子目录,含 `_module.md`(模块头)和 `REQ-XXX-NNN.md`(每张 REQ 卡片一个文件)。下方核心功能点供 CC 拆分出 REQ 编号 + 标题 + 草拟规则;卡片内输入 / 输出的简述句和 N 张字段表由人工编辑。 | ||
| 4 | + | ||
| 5 | +## 模块索引 | ||
| 6 | + | ||
| 7 | +| 模块代码 | 模块名称 | 核心功能点(简要) | | ||
| 8 | +| ---- | ---- | ------------------- | | ||
| 9 | +| USR | 用户管理 | 增加用户,修改用户,查询用户,登录用户 | | ||
| 10 | +| MOD | 模块管理 | 增加模块,修改模块,删除模块,查询模块 | | ||
| 11 | + | ||
| 12 | +## 填写说明 | ||
| 13 | + | ||
| 14 | +1. 每个模块占一行,`模块代码` 用大写英文缩写(如 SYS / PUR / INV / SAL / FIN / HR) | ||
| 15 | +2. `核心功能点` 只需列关键词,CC 会基于此拆分出 N 张 REQ 卡片骨架(卡片内输入 / 输出的简述句和字段表仍由人工编辑) | ||
| 16 | +3. 填完后运行 `/erp-workflow:plan-start`,CC 会自动检测并进入需求生成阶段 |
docs/02-开发计划.md
0 → 100644
| 1 | +++ a/docs/02-开发计划.md | ||
| 1 | +# 02-开发计划 | ||
| 2 | + | ||
| 3 | +## 一、模块依赖表 | ||
| 4 | + | ||
| 5 | +| 模块 ID | 模块名 | 依赖模块 | 依赖表 | | ||
| 6 | +|---|---|---|---| | ||
| 7 | +| module_mod | 模块管理 | — | `tModule` | | ||
| 8 | +| module_usr | 用户管理 | — | `tUser`、`tStaff`、`tPermissionCategory`、`tUserPermission` | | ||
| 9 | + | ||
| 10 | +> 说明:MOD 与 USR 之间无 schema 级外键依赖;按 `module_id` 字母序铺开。USR 业务上的"权限分类"未来可能与 MOD 维护的模块树打通(多对多授权),届时如需新增 FK 再以 V_n migration 引入。 | ||
| 11 | + | ||
| 12 | +## 二、开发顺序清单(CC 分发权威) | ||
| 13 | + | ||
| 14 | +> 本清单由 A5 `downstream-gen` 一次性生成。**每行是一个 REQ**,不是模块。CC 按表格行序从上到下扫描,对每个 REQ 所属模块查 `docs/08 § 二` 的 `MR:` 字段 + GitLab API `state`:`merged` 跳过,其他(`—` / opened / closed / 查不到)选为当前模块;`module-start` 会把该模块的所有 REQ 一次做完。 | ||
| 15 | +> | ||
| 16 | +> **约束**:同一模块的所有 REQ 必须**连续排列**。允许打破依赖拓扑(如环依赖、业务必须先做),但必须在「备注」列写明原因。 | ||
| 17 | + | ||
| 18 | +| # | REQ | 所属模块 | 选中理由 | 备注 | | ||
| 19 | +|---|-----|---------|---------|------| | ||
| 20 | +| 1 | **REQ-MOD-001** | module_mod | 所属模块无依赖,基础模块;同模块第一个 REQ | — | | ||
| 21 | +| 2 | **REQ-MOD-002** | module_mod | 同模块前序 REQ-MOD-001 已在前 | — | | ||
| 22 | +| 3 | **REQ-MOD-003** | module_mod | 同模块前序 REQ-MOD-001/002 已在前 | — | | ||
| 23 | +| 4 | **REQ-MOD-004** | module_mod | 同模块前序 REQ-MOD-001~003 已在前 | — | | ||
| 24 | +| 5 | **REQ-USR-001** | module_usr | 所属模块无依赖;module_mod 已在前 | — | | ||
| 25 | +| 6 | **REQ-USR-002** | module_usr | 同模块前序 REQ-USR-001 已在前 | — | | ||
| 26 | +| 7 | **REQ-USR-003** | module_usr | 同模块前序 REQ-USR-001/002 已在前 | — | | ||
| 27 | +| 8 | **REQ-USR-004** | module_usr | 同模块前序 REQ-USR-001 已在前;登录依赖已存在用户记录 | — | | ||
| 28 | + | ||
| 29 | +## 三、关键说明 | ||
| 30 | + | ||
| 31 | +- **模块顺序**:MOD 与 USR 模块互无 schema 级依赖,按 `module_id` 字母序排(module_mod → module_usr)。任一模块均可作为第一个被开发的模块;本清单选 module_mod 在前以保持稳定的字母序,避免后续追加模块时出现行序漂移。 | ||
| 32 | +- **REQ 顺序**:每模块内部按 CRUD + 业务流程的自然依赖排(增 → 改 → 删 → 查;登录置最后,依赖已有用户记录)。无环依赖。 | ||
| 33 | +- **基础设施 REQ**:本期未单列基础设施 REQ(鉴权拦截器 / 全局异常处理器 / 统一响应封装等横切组件由 `module_usr` 的 REQ-USR-004 触发首次落地,并复用到其他模块)。 | ||
| 34 | +- **数据准备**:REQ-USR-001 实施前需在 `tStaff` / `tPermissionCategory` 中预置基础字典数据(业务运营提供,不在本计划中作为独立 REQ)。 |
docs/03-数据库设计文档.md
0 → 100644
| 1 | +++ a/docs/03-数据库设计文档.md | ||
| 1 | +# 03-数据库设计文档 | ||
| 2 | + | ||
| 3 | +Schema: `xlyweberp_vibe_erp_test` | ||
| 4 | +Migration 清单: `sql/migrations/V*.sql`(由 Flyway 顺序 apply) | ||
| 5 | +生成方式: 由 A3 `db-design-gen` 基于 `docs/01-需求清单/<module>/REQ-*.md` REQ 卡片正向设计生成(schema SSoT)。 | ||
| 6 | + | ||
| 7 | +## 项目标准列约定 | ||
| 8 | + | ||
| 9 | +下文每张业务表的字段清单都自动包含以下 5 个标准列(匈牙利前缀 `i` int / `s` varchar / `t` datetime)。渲染时由 `docs-03-table-template.md` 模板内置原样输出。 | ||
| 10 | + | ||
| 11 | +| 列名 | 类型 | 可空 | 主键 | 说明 | | ||
| 12 | +|---|---|---|---|---| | ||
| 13 | +| `iIncrement` | int | 否 | 是 | 整数主键 ID(自增方式由实现决定:DB `AUTO_INCREMENT` 或应用 / 触发器分配) | | ||
| 14 | +| `sId` | varchar(100) | 是 | — | 业务 ID(对外暴露的字符串标识,如 UUID / 人类可读编号) | | ||
| 15 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离) | | ||
| 16 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离) | | ||
| 17 | +| `tCreateDate` | datetime | 否 | — | 记录创建时间 | | ||
| 18 | + | ||
| 19 | +字典 / 辅助表如有豁免,在该表业务注记里注明豁免原因。 | ||
| 20 | + | ||
| 21 | +## ER 关系概览 | ||
| 22 | + | ||
| 23 | +本期共 5 张业务表,分布在 USR / MOD 两个模块: | ||
| 24 | + | ||
| 25 | +- **`tUser`** 是用户账户主表,可选关联 `tStaff`(员工);与 `tPermissionCategory` 通过 `tUserPermission` 形成多对多权限授权关系。登录失败计数走 Redis,不入库。 | ||
| 26 | +- **`tStaff`** 是职员维度表,作为 `tUser` 的员工来源(员工名 / 部门下拉)。 | ||
| 27 | +- **`tPermissionCategory`** 是权限分类树(自引用 `iParentId`),由 `tUserPermission` 关联到具体用户。 | ||
| 28 | +- **`tUserPermission`** 是 `tUser` × `tPermissionCategory` 的关联表(多对多)。 | ||
| 29 | +- **`tModule`** 是 ERP 业务模块定义表(自引用 `iParentId` 形成模块树),与本期其他表无外键关系。 | ||
| 30 | + | ||
| 31 | +软删除统一使用 `bDeleted` / `tDeletedDate` / `sDeletedBy` 三件套;查询接口默认过滤 `bDeleted = 0`。 | ||
| 32 | + | ||
| 33 | +## 表清单 | ||
| 34 | + | ||
| 35 | +- `tUser` — 系统用户账户与登录凭据 | ||
| 36 | +- `tStaff` — 职员维度(员工名 / 部门 / 编号) | ||
| 37 | +- `tPermissionCategory` — 权限分类树 | ||
| 38 | +- `tUserPermission` — 用户与权限分类的关联 | ||
| 39 | +- `tModule` — ERP 业务模块元数据树 | ||
| 40 | + | ||
| 41 | +--- | ||
| 42 | + | ||
| 43 | +## `tUser` — 系统用户账户与登录凭据 | ||
| 44 | + | ||
| 45 | +### 字段 | ||
| 46 | + | ||
| 47 | +| 字段 | 类型 | Nullable | 默认 | 业务含义 | | ||
| 48 | +|---|---|---|---|---| | ||
| 49 | +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | | ||
| 50 | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | | ||
| 51 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | | ||
| 52 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | | ||
| 53 | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | | ||
| 54 | +| `sUserNo` | varchar(50) | 否 | — | 用户号;系统内唯一 | | ||
| 55 | +| `sUserName` | varchar(50) | 否 | — | 用户名(登录账号);系统内唯一 | | ||
| 56 | +| `iStaffId` | int | 是 | NULL | 关联职员 ID(可选,外键 → `tStaff.iIncrement`) | | ||
| 57 | +| `sUserType` | varchar(20) | 否 | `普通用户` | 用户类型;枚举:`普通用户` / `超级管理员` | | ||
| 58 | +| `sLanguage` | varchar(10) | 否 | `zh` | 语言偏好;枚举:`zh` / `en` / `zh-TW` | | ||
| 59 | +| `bCanModifyDocs` | bit(1) | 否 | 0 | 单据修改权限;0 否 / 1 是 | | ||
| 60 | +| `sPasswordHash` | varchar(255) | 否 | — | 密码哈希值(BCrypt 等强哈希算法),新增默认初始密码 `666666` 的哈希 | | ||
| 61 | +| `tLastLoginDate` | datetime | 是 | NULL | 最后登录时间 | | ||
| 62 | +| `sCreatedBy` | varchar(50) | 是 | — | 制单人(创建用户的操作员用户号) | | ||
| 63 | +| `bDeleted` | bit(1) | 否 | 0 | 软删除标记;0 有效 / 1 已作废 | | ||
| 64 | +| `tDeletedDate` | datetime | 是 | NULL | 软删除时间 | | ||
| 65 | +| `sDeletedBy` | varchar(50) | 是 | NULL | 软删除操作人 | | ||
| 66 | + | ||
| 67 | +### 索引 | ||
| 68 | + | ||
| 69 | +- `uk_user_no` (UNIQUE): (`sUserNo`) | ||
| 70 | +- `uk_user_name` (UNIQUE): (`sUserName`) | ||
| 71 | +- `idx_staff_id` (NORMAL): (`iStaffId`) | ||
| 72 | +- `idx_brand_subsidiary` (NORMAL): (`sBrandsId`, `sSubsidiaryId`) | ||
| 73 | +- `idx_deleted_login` (NORMAL): (`bDeleted`, `tLastLoginDate`) | ||
| 74 | + | ||
| 75 | +### 外键 | ||
| 76 | + | ||
| 77 | +- `fk_user_staff`: `iStaffId` → `tStaff.iIncrement` (ON DELETE SET NULL, ON UPDATE CASCADE) | ||
| 78 | + | ||
| 79 | +### 业务注记 | ||
| 80 | + | ||
| 81 | +- 用户名 / 用户号在系统内全局唯一(含已软删除记录是否计入唯一性,需在 service 层用部分唯一索引或代码校验决定,本设计取代码校验,唯一索引仅约束未删除部分由应用层保证)。 | ||
| 82 | +- 密码哈希存储;登录失败次数 / 临时锁定不入库,由 Redis 维护。 | ||
| 83 | +- REQ-USR-003 查询接口的「作废」字段直接映射 `bDeleted`;「制单日期」即 `tCreateDate`;「制单人」即 `sCreatedBy`。 | ||
| 84 | +- 未关联职员的用户允许 `iStaffId = NULL`(如系统管理员账号)。 | ||
| 85 | + | ||
| 86 | +--- | ||
| 87 | + | ||
| 88 | +## `tStaff` — 职员维度(员工名 / 部门 / 编号) | ||
| 89 | + | ||
| 90 | +### 字段 | ||
| 91 | + | ||
| 92 | +| 字段 | 类型 | Nullable | 默认 | 业务含义 | | ||
| 93 | +|---|---|---|---|---| | ||
| 94 | +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | | ||
| 95 | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | | ||
| 96 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | | ||
| 97 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | | ||
| 98 | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | | ||
| 99 | +| `sStaffNo` | varchar(50) | 是 | — | 职员编号;系统内唯一 | | ||
| 100 | +| `sStaffName` | varchar(50) | 否 | — | 职员姓名 | | ||
| 101 | +| `sDepartment` | varchar(100) | 是 | NULL | 所属部门(本期暂用字符串,未来如需独立 `tDepartment` 字典表再另行重构) | | ||
| 102 | +| `sCreatedBy` | varchar(50) | 是 | — | 制单人 | | ||
| 103 | +| `bDeleted` | bit(1) | 否 | 0 | 软删除标记 | | ||
| 104 | +| `tDeletedDate` | datetime | 是 | NULL | 软删除时间 | | ||
| 105 | +| `sDeletedBy` | varchar(50) | 是 | NULL | 软删除操作人 | | ||
| 106 | + | ||
| 107 | +### 索引 | ||
| 108 | + | ||
| 109 | +- `uk_staff_no` (UNIQUE): (`sStaffNo`) | ||
| 110 | +- `idx_staff_name` (NORMAL): (`sStaffName`) | ||
| 111 | +- `idx_department` (NORMAL): (`sDepartment`) | ||
| 112 | + | ||
| 113 | +### 外键 | ||
| 114 | + | ||
| 115 | +(无) | ||
| 116 | + | ||
| 117 | +### 业务注记 | ||
| 118 | + | ||
| 119 | +- USR 模块通过 `tUser.iStaffId` 引用本表;REQ-USR-003 列表的「员工名 / 部门」均来自本表。 | ||
| 120 | +- 部门当前以字符串保存,便于本期快速落地;后续若需独立部门字典表(如审批流引用部门),改为 `iDepartmentId` 外键。 | ||
| 121 | + | ||
| 122 | +--- | ||
| 123 | + | ||
| 124 | +## `tPermissionCategory` — 权限分类树 | ||
| 125 | + | ||
| 126 | +### 字段 | ||
| 127 | + | ||
| 128 | +| 字段 | 类型 | Nullable | 默认 | 业务含义 | | ||
| 129 | +|---|---|---|---|---| | ||
| 130 | +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | | ||
| 131 | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | | ||
| 132 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | | ||
| 133 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | | ||
| 134 | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | | ||
| 135 | +| `sCategoryCode` | varchar(50) | 否 | — | 权限分类编码;系统内唯一 | | ||
| 136 | +| `sCategoryName` | varchar(100) | 否 | — | 权限分类名称(界面展示) | | ||
| 137 | +| `iParentId` | int | 是 | NULL | 父分类 ID(自引用,根节点为 NULL) | | ||
| 138 | +| `iSortOrder` | int | 否 | 0 | 同级排序号 | | ||
| 139 | +| `sCreatedBy` | varchar(50) | 是 | — | 制单人 | | ||
| 140 | +| `bDeleted` | bit(1) | 否 | 0 | 软删除标记 | | ||
| 141 | +| `tDeletedDate` | datetime | 是 | NULL | 软删除时间 | | ||
| 142 | +| `sDeletedBy` | varchar(50) | 是 | NULL | 软删除操作人 | | ||
| 143 | + | ||
| 144 | +### 索引 | ||
| 145 | + | ||
| 146 | +- `uk_category_code` (UNIQUE): (`sCategoryCode`) | ||
| 147 | +- `idx_parent` (NORMAL): (`iParentId`) | ||
| 148 | + | ||
| 149 | +### 外键 | ||
| 150 | + | ||
| 151 | +- `fk_category_parent`: `iParentId` → `tPermissionCategory.iIncrement` (ON DELETE RESTRICT, ON UPDATE CASCADE) | ||
| 152 | + | ||
| 153 | +### 业务注记 | ||
| 154 | + | ||
| 155 | +- REQ-USR-001 / 002 表 2「权限组 - 权限分类」下拉来源即本表(`bDeleted = 0` 过滤)。 | ||
| 156 | +- 自引用形成权限分类树;删除前须确保无子分类、无 `tUserPermission` 引用。 | ||
| 157 | + | ||
| 158 | +--- | ||
| 159 | + | ||
| 160 | +## `tUserPermission` — 用户与权限分类关联 | ||
| 161 | + | ||
| 162 | +### 字段 | ||
| 163 | + | ||
| 164 | +| 字段 | 类型 | Nullable | 默认 | 业务含义 | | ||
| 165 | +|---|---|---|---|---| | ||
| 166 | +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | | ||
| 167 | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | | ||
| 168 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | | ||
| 169 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | | ||
| 170 | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | | ||
| 171 | +| `iUserId` | int | 否 | — | 关联用户 ID(外键 → `tUser.iIncrement`) | | ||
| 172 | +| `iCategoryId` | int | 否 | — | 关联权限分类 ID(外键 → `tPermissionCategory.iIncrement`) | | ||
| 173 | +| `sCreatedBy` | varchar(50) | 是 | — | 授权操作人 | | ||
| 174 | + | ||
| 175 | +### 索引 | ||
| 176 | + | ||
| 177 | +- `uk_user_category` (UNIQUE): (`iUserId`, `iCategoryId`) | ||
| 178 | +- `idx_category` (NORMAL): (`iCategoryId`) | ||
| 179 | + | ||
| 180 | +### 外键 | ||
| 181 | + | ||
| 182 | +- `fk_up_user`: `iUserId` → `tUser.iIncrement` (ON DELETE CASCADE, ON UPDATE CASCADE) | ||
| 183 | +- `fk_up_category`: `iCategoryId` → `tPermissionCategory.iIncrement` (ON DELETE RESTRICT, ON UPDATE CASCADE) | ||
| 184 | + | ||
| 185 | +### 业务注记 | ||
| 186 | + | ||
| 187 | +- 关联表保留标准列以保持设计一致性,无独立软删除字段——授权撤销直接物理删除关联行(CASCADE 也会随用户删除而清理)。 | ||
| 188 | +- REQ-USR-001 / 002 提交时按表 2 选中行重建本表 `iUserId` 下的关联(先删后插或差异同步)。 | ||
| 189 | + | ||
| 190 | +--- | ||
| 191 | + | ||
| 192 | +## `tModule` — ERP 业务模块元数据树 | ||
| 193 | + | ||
| 194 | +### 字段 | ||
| 195 | + | ||
| 196 | +| 字段 | 类型 | Nullable | 默认 | 业务含义 | | ||
| 197 | +|---|---|---|---|---| | ||
| 198 | +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | | ||
| 199 | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | | ||
| 200 | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | | ||
| 201 | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | | ||
| 202 | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | | ||
| 203 | +| `sDisplayType` | varchar(20) | 否 | `手机端` | 显示类型;枚举:`手机端` / `前端业务` / `系统配置` / `接口` | | ||
| 204 | +| `sProcedureName` | varchar(100) | 否 | — | 存储过程(审核)名称;系统内唯一 | | ||
| 205 | +| `sModuleType` | varchar(50) | 否 | — | 模块类型(本期按自由文本处理,VARCHAR(50);如未来收敛到枚举再加 CHECK 约束) | | ||
| 206 | +| `sManageDeptEn` | varchar(50) | 否 | — | 管理部门英文标识 | | ||
| 207 | +| `bShowPermission` | bit(1) | 否 | 0 | 权限是否显示;0 否 / 1 是 | | ||
| 208 | +| `sModuleNameZh` | varchar(100) | 否 | — | 界面名称(中文,模糊查询用) | | ||
| 209 | +| `iParentId` | int | 是 | NULL | 父模块 ID(自引用,根节点为 NULL) | | ||
| 210 | +| `iSortOrder` | int | 否 | 0 | 同级排序号 | | ||
| 211 | +| `sCreatedBy` | varchar(50) | 是 | — | 制单人 | | ||
| 212 | +| `bDeleted` | bit(1) | 否 | 0 | 软删除标记 | | ||
| 213 | +| `tDeletedDate` | datetime | 是 | NULL | 软删除时间 | | ||
| 214 | +| `sDeletedBy` | varchar(50) | 是 | NULL | 软删除操作人 | | ||
| 215 | + | ||
| 216 | +### 索引 | ||
| 217 | + | ||
| 218 | +- `uk_procedure_name` (UNIQUE): (`sProcedureName`) | ||
| 219 | +- `idx_module_name_zh` (NORMAL): (`sModuleNameZh`) | ||
| 220 | +- `idx_parent` (NORMAL): (`iParentId`) | ||
| 221 | +- `idx_display_type` (NORMAL): (`sDisplayType`) | ||
| 222 | + | ||
| 223 | +### 外键 | ||
| 224 | + | ||
| 225 | +- `fk_module_parent`: `iParentId` → `tModule.iIncrement` (ON DELETE RESTRICT, ON UPDATE CASCADE) | ||
| 226 | + | ||
| 227 | +### 业务注记 | ||
| 228 | + | ||
| 229 | +- REQ-MOD-004 查询按 `sModuleNameZh` 模糊匹配(`LIKE '%关键字%'`,`bDeleted = 0` 过滤),结果按 `iParentId` 拼装树形。 | ||
| 230 | +- REQ-MOD-003 删除策略:仅软删除(写入 `bDeleted` / `tDeletedDate` / `sDeletedBy`);service 层先校验「无未删除子模块」「未被外部业务引用」再执行。 | ||
| 231 | +- `sProcedureName` 假定系统内唯一,若实际允许同名请在审阅时移除该唯一索引。 |
docs/04-技术规范.md
0 → 100644
| 1 | +++ a/docs/04-技术规范.md | ||
| 1 | +# 04-技术规范 | ||
| 2 | + | ||
| 3 | +## 零、技术栈总览 | ||
| 4 | + | ||
| 5 | +| 分层模块 | 技术 | 版本要求 | 说明 | | ||
| 6 | +|---|---|---|---| | ||
| 7 | +| 前端基础框架 | React | 18.x | 构建前端应用 | | ||
| 8 | +| 前端 UI 组件 | Ant Design | 5.x | 页面组件与交互控件 | | ||
| 9 | +| 前端状态管理 | Redux Toolkit | 最新稳定版 | 管理全局状态 | | ||
| 10 | +| 前端路由管理 | React Router | v6 | 页面路由与导航 | | ||
| 11 | +| 前端工程化构建 | Vite | 最新稳定版 | 前端开发与打包构建 | | ||
| 12 | +| 前端接口通信 | Axios | 最新稳定版 | 调用后端 API | | ||
| 13 | +| 后端基础框架 | Spring Boot | 3.x | 构建后端服务 | | ||
| 14 | +| 后端数据访问 | MyBatis-Plus | 最新稳定版 | 数据库访问与 ORM 增强 | | ||
| 15 | +| 工作流引擎 | Activiti | 6.x | 审批流、流程流转 | | ||
| 16 | +| 缓存服务 | Redis | 最新稳定版 | 缓存、会话、分布式能力 | | ||
| 17 | +| 报表打印 | JXLS | 2.8.1 | 基于 Excel 模板生成报表 | | ||
| 18 | +| Excel 导入导出 | EasyExcel | 4.0.3 | Excel 数据导入导出 | | ||
| 19 | +| 关系型数据库 | MySQL | 8.x | 核心业务数据存储 | | ||
| 20 | +| 数据库 schema 迁移 | Flyway (`flyway-core` + `flyway-mysql`) | 10.x / 最新稳定版 | `sql/migrations/V_n__*.sql` 顺序 apply;Spring Boot 启动时自动应用 | | ||
| 21 | +| 接口风格 | RESTful API | 统一规范 | 前后端接口设计规范 | | ||
| 22 | +| 权限认证 | Spring Security / JWT | 最新稳定版 | 登录认证、权限控制 | | ||
| 23 | +| API 文档 | OpenAPI / Swagger | 最新稳定版 | 接口文档与调试 | | ||
| 24 | +| 项目构建管理 | Maven | 3.9.x | Java 项目依赖与构建 | | ||
| 25 | +| JDK 运行环境 | Java | 17 / 21 | Spring Boot 3 推荐版本 | | ||
| 26 | +| 部署容器 | Docker | 最新稳定版 | 容器化部署 | | ||
| 27 | +| Web 服务器 / 反向代理 | Nginx | 最新稳定版 | 前端托管、反向代理、负载分发 | | ||
| 28 | +| 日志管理 | Logback | 默认集成 / 最新稳定版 | 应用日志输出 | | ||
| 29 | +| 对象映射工具 | MapStruct | 最新稳定版 | DTO / VO / Entity 转换 | | ||
| 30 | +| 工具类库 | Hutool / Apache Commons | 最新稳定版 | 常用工具方法支持 | | ||
| 31 | + | ||
| 32 | +> 本表由 scope-lock 锁定。后续所有规范基于此表推导。 | ||
| 33 | + | ||
| 34 | +## 一、后端规范 | ||
| 35 | + | ||
| 36 | +### 1.1 分层结构 | ||
| 37 | + | ||
| 38 | +| 层 | 职责 | | ||
| 39 | +|---|---| | ||
| 40 | +| `controller` | REST 入口,参数绑定 / 校验 / 调用 service,**不写业务逻辑** | | ||
| 41 | +| `service` | 业务编排,事务边界,跨模块调用通过 service 接口完成 | | ||
| 42 | +| `mapper` | MyBatis-Plus 接口 + XML,仅做 CRUD,不写业务规则 | | ||
| 43 | +| `entity` | 与表 1:1 的 PO,仅作 ORM 映射 | | ||
| 44 | +| `dto` / `vo` | 入参 DTO(Controller ↔ Service) / 出参 VO(Service → 前端) | | ||
| 45 | +| `common` | 统一响应 / 全局异常 / 工具方法 | | ||
| 46 | +| `config` | Spring 配置类(Security / Redis / Swagger / Web) | | ||
| 47 | + | ||
| 48 | +### 1.2 命名约定 | ||
| 49 | + | ||
| 50 | +- **包名**:全小写,根包 `com.xly.erp`,业务模块下按 `module.<mod>.<layer>` 划分。 | ||
| 51 | +- **类名**:大驼峰,如 `UserController` / `UserServiceImpl` / `UserMapper`。 | ||
| 52 | +- **方法名**:小驼峰动宾式,如 `createUser` / `pageUsers`。 | ||
| 53 | +- **常量**:全大写下划线,如 `MAX_PAGE_SIZE` / `JWT_TOKEN_HEADER`。 | ||
| 54 | +- 示例: | ||
| 55 | + - `<root>.module.usr.controller.UserController#createUser` | ||
| 56 | + - `<root>.module.mod.service.ModuleService#pageModules` | ||
| 57 | + | ||
| 58 | +### 1.3 统一响应格式 | ||
| 59 | + | ||
| 60 | +```json | ||
| 61 | +{ | ||
| 62 | + "code": "0", | ||
| 63 | + "message": "ok", | ||
| 64 | + "data": { /* 业务数据 */ } | ||
| 65 | +} | ||
| 66 | +``` | ||
| 67 | + | ||
| 68 | +- 成功 `code=0`;失败按段位划分: | ||
| 69 | + - `1xxx` 通用错误(参数 / 鉴权 / 权限) | ||
| 70 | + - `2xxx` USR 模块业务错误 | ||
| 71 | + - `3xxx` MOD 模块业务错误 | ||
| 72 | + - 后续模块按段位顺延,由 `downstream-gen` 在 docs/05 中登记。 | ||
| 73 | + | ||
| 74 | +### 1.4 异常处理 | ||
| 75 | + | ||
| 76 | +- 使用 `@RestControllerAdvice` 注册全局异常处理器,统一捕获 `BizException` / `BindException` / `Exception`。 | ||
| 77 | +- 业务异常抛 `BizException(code, message)`;参数校验由 Spring `@Valid` 触发。 | ||
| 78 | +- **接口响应禁止回显后端异常堆栈**——堆栈只入 Logback 日志,响应只返回友好错误码 + 文案。 | ||
| 79 | + | ||
| 80 | +### 1.5 事务 | ||
| 81 | + | ||
| 82 | +- 事务边界统一在 `service` 层,使用 `@Transactional(rollbackFor = Exception.class)`。 | ||
| 83 | +- **禁止跨服务远程调用包在事务内**;本项目暂无远程依赖,跨模块调用通过本地 service 接口同步完成。 | ||
| 84 | +- 长耗时操作(导入导出、批处理)拆分为多个短事务,必要时入异步任务。 | ||
| 85 | + | ||
| 86 | +### 1.6 认证 | ||
| 87 | + | ||
| 88 | +- 基于 Spring Security + JWT 无状态认证: | ||
| 89 | + - 登录返回 `accessToken`(HS256,过期 2 小时)+ `refreshToken`(过期 7 天)。 | ||
| 90 | + - `accessToken` 通过 `Authorization: Bearer <token>` 请求头携带。 | ||
| 91 | + - `refreshToken` 仅用于换新 `accessToken`,存于 Redis 用于黑名单 / 撤销。 | ||
| 92 | +- JWT 密钥放 `.env.local`,启动期注入,**禁止硬编码**。 | ||
| 93 | + | ||
| 94 | +## 二、前端规范 | ||
| 95 | + | ||
| 96 | +### 2.1 目录约定 | ||
| 97 | + | ||
| 98 | +| 目录 | 职责 | | ||
| 99 | +|---|---| | ||
| 100 | +| `src/api/` | Axios 封装 + 模块接口;**前端禁止直接写 SQL / 操作 DB**,所有数据访问走 api/ 层 | | ||
| 101 | +| `src/components/` | 通用组件(UI 组合、无业务) | | ||
| 102 | +| `src/pages/` | 业务页面(按模块代码 usr / mod 分子目录) | | ||
| 103 | +| `src/store/` | Redux Toolkit slices | | ||
| 104 | +| `src/hooks/` | 通用 hooks | | ||
| 105 | +| `src/utils/` | 纯函数工具(日期 / 金额 / 校验) | | ||
| 106 | +| `src/router/` | React Router 配置 | | ||
| 107 | +| `src/styles/` | 全局样式 + Design Token | | ||
| 108 | + | ||
| 109 | +### 2.2 状态管理 | ||
| 110 | + | ||
| 111 | +- 使用 Redux Toolkit + RTK Query(按需)管理服务端数据缓存。 | ||
| 112 | +- 全局状态:登录用户信息、菜单树、权限码集合、全局加载态。 | ||
| 113 | +- 局部 / 短生命周期状态:放组件 `useState` / `useReducer`,不进 store。 | ||
| 114 | +- 服务端数据:列表 / 详情等"远程状态"放 `store/api`(RTK Query slice)或 React Query 替代。 | ||
| 115 | + | ||
| 116 | +### 2.3 请求封装 | ||
| 117 | + | ||
| 118 | +- `src/api/request.ts` 单例 Axios,注册: | ||
| 119 | + - **请求拦截器**:注入 `Authorization` 头;请求超时默认 15 s。 | ||
| 120 | + - **响应拦截器**:剥离 `data` 字段;遇 401 触发 token 刷新或跳登录;业务错误码统一抛出。 | ||
| 121 | +- 不在业务代码里直接 `import axios`,统一走 `request.get/post`。 | ||
| 122 | + | ||
| 123 | +### 2.4 错误处理 | ||
| 124 | + | ||
| 125 | +- **网络错误**(无 response):`message.error("网络异常,请检查连接")`,不重试。 | ||
| 126 | +- **业务错误**(response.code !== 0):`message.error(response.message)`;权限类错误(401 / 403)跳登录或 403 页。 | ||
| 127 | +- **页面级错误**(路由级 `ErrorBoundary`):渲染 `Result` 组件,提供"返回首页"链接。 | ||
| 128 | + | ||
| 129 | +### 2.5 样式与主题 | ||
| 130 | + | ||
| 131 | +- CSS 变量命名:`--color-<scope>-<role>-<state>` | ||
| 132 | + - `scope` ∈ {`form`, `table-row`, `button`, `panel`, ...} | ||
| 133 | + - `role` ∈ {`bg`, `fg`, `border`} | ||
| 134 | + - `state` ∈ {`edit`, `readonly`, `hover`, `selected`, `disabled`} | ||
| 135 | +- 文件位置:`src/styles/tokens.css`,由 skeleton-gen 生成空骨架,色值由 docs/06 § 四锁定后填入。 | ||
| 136 | +- 组件样式中只用 `var(--color-xxx)`,**禁止硬编码 hex / rgba**。 | ||
| 137 | +- 与 Ant Design ConfigProvider 对接:在 App 根读取 `tokens.css` 变量映射到 `theme.token`(如 `colorPrimary` 取自 `getComputedStyle(document.documentElement).getPropertyValue('--color-primary')`)。 | ||
| 138 | +- 具体 token 表见 `docs/06 § 四`。 | ||
| 139 | + | ||
| 140 | +## 三、共同约定 | ||
| 141 | + | ||
| 142 | +### 3.1 Git 提交 | ||
| 143 | + | ||
| 144 | +`<type>(<scope>): <subject> REQ-XXX-NNN` | ||
| 145 | + | ||
| 146 | +### 3.2 分页查询 | ||
| 147 | + | ||
| 148 | +- **后端入参**:`PageRequest { pageNum: int (≥1), pageSize: int (1-100), keyword?: string, sort?: string }` | ||
| 149 | +- **后端出参**:`PageResult<T> { total: long, list: List<T>, pageNum, pageSize }` | ||
| 150 | +- **前端**:使用 Ant Design `Table` 受控分页,`current / pageSize` 与后端 `pageNum / pageSize` 对齐。 | ||
| 151 | + | ||
| 152 | +### 3.3 日期与金额 | ||
| 153 | + | ||
| 154 | +- **后端日期**:`LocalDateTime`(数据库 `DATETIME`),统一序列化为 ISO 8601(`yyyy-MM-dd'T'HH:mm:ss`)。 | ||
| 155 | +- **后端金额**:`BigDecimal`,数据库 `DECIMAL(18,4)`,业务展示精度由前端统一 `utils/money.ts` 处理。 | ||
| 156 | +- **前端展示**:日期使用 `dayjs` 格式化;金额使用 `toFixed(2)` + 千分位分隔。 | ||
| 157 | + | ||
| 158 | +### 3.4 数据访问规约 | ||
| 159 | + | ||
| 160 | +- SELECT 字段**显式列举**,禁止 `SELECT *`。 | ||
| 161 | +- 循环中**不得执行 DB 查询**(N+1 反模式),改用批量查 / `IN` 子句 / `JOIN`。 | ||
| 162 | +- Mapper XML 字段名 / 表名使用 `<sql id="Base_Column_List">` 复用,避免散落字符串拼接。 | ||
| 163 | +- 使用 MyBatis-Plus 的 `LambdaQueryWrapper` 时,字段引用通过方法引用(如 `User::getId`),不允许直接传字符串字段名。 | ||
| 164 | + | ||
| 165 | +### 3.5 配置与安全 | ||
| 166 | + | ||
| 167 | +- **配置**:DB 连接 / Redis 地址 / JWT 密钥 / 第三方 URL 一律放 `application.yml` 占位 + `.env.local` 真值,**代码里禁止硬编码**。 | ||
| 168 | +- **前端安全**:`localStorage` **不存敏感信息**(token / 身份 / 个人数据),推荐 HttpOnly Cookie 或 内存 + refresh token 模式。 | ||
| 169 | +- 接口响应禁止回显后端异常堆栈(与 § 1.4 一致)。 |
docs/05-API接口契约.md
0 → 100644
| 1 | +++ a/docs/05-API接口契约.md | ||
| 1 | +# 05-API接口契约 | ||
| 2 | + | ||
| 3 | +BasePath: `/api` | ||
| 4 | +端口: `8080` | ||
| 5 | + | ||
| 6 | +## 全局约定 | ||
| 7 | + | ||
| 8 | +### 响应格式 | ||
| 9 | +```json | ||
| 10 | +{"code": 200, "message": "操作成功", "data": {}, "timestamp": 1700000000000} | ||
| 11 | +``` | ||
| 12 | + | ||
| 13 | +### 错误码 | ||
| 14 | +| 范围 | 含义 | | ||
| 15 | +|---|---| | ||
| 16 | +| 200 | 成功 | | ||
| 17 | +| 400xx | 客户端参数错误 | | ||
| 18 | +| 401xx | 认证/授权错误 | | ||
| 19 | +| 403xx | 权限不足 | | ||
| 20 | +| 404xx | 资源不存在 | | ||
| 21 | +| 500xx | 服务端内部错误 | | ||
| 22 | + | ||
| 23 | +### 鉴权 | ||
| 24 | + | ||
| 25 | +除登录接口(`POST /api/auth/login`)外,所有接口要求 `Authorization: Bearer <accessToken>` 请求头。token 由 `POST /api/auth/login` 签发,HS256,有效期 2 小时;过期返回 `40101`,前端需用 refreshToken 换新或重新登录。 | ||
| 26 | + | ||
| 27 | +### 分页参数 | ||
| 28 | + | ||
| 29 | +- 入参(query):`pageNum`(≥1,默认 1)、`pageSize`(1~100,默认 20)、`keyword`(可选模糊查询)、`sort`(可选,形如 `field,asc|desc`)。 | ||
| 30 | +- 出参(data):`{ "total": 0, "list": [], "pageNum": 1, "pageSize": 20 }`。 | ||
| 31 | + | ||
| 32 | +## 接口清单 | ||
| 33 | + | ||
| 34 | +(各模块接口段落见下方,由 `downstream-gen` 按 REQ 填入) | ||
| 35 | + | ||
| 36 | +--- | ||
| 37 | + | ||
| 38 | +## module_mod 模块管理 | ||
| 39 | + | ||
| 40 | +### REQ-MOD-001 模块新增 | ||
| 41 | + | ||
| 42 | +- **Method**: POST | ||
| 43 | +- **Path**: `/api/modules` | ||
| 44 | +- **Auth**: 需要 | ||
| 45 | +- **Permission**: `MOD:CREATE` | ||
| 46 | +- **请求**: JSON body:`sDisplayType`(必填,枚举:手机端 / 前端业务 / 系统配置 / 接口)、`sProcedureName`(必填,唯一)、`sModuleType`(必填)、`sManageDeptEn`(必填)、`bShowPermission`(可选,默认 false)、`sModuleNameZh`(必填)、`iParentId`(可选)、`iSortOrder`(可选,默认 0) | ||
| 47 | +- **响应**: `data.iIncrement` 新模块主键 + 完整模块对象(VO) | ||
| 48 | + | ||
| 49 | +#### 错误码 | ||
| 50 | +- `40010` — 参数缺失或格式错误 | ||
| 51 | +- `40911` — `sProcedureName` 已存在(唯一性冲突) | ||
| 52 | +- `40411` — `iParentId` 指向的父模块不存在或已删除 | ||
| 53 | + | ||
| 54 | +--- | ||
| 55 | + | ||
| 56 | +### REQ-MOD-002 模块修改 | ||
| 57 | + | ||
| 58 | +- **Method**: PUT | ||
| 59 | +- **Path**: `/api/modules/{id}` | ||
| 60 | +- **Auth**: 需要 | ||
| 61 | +- **Permission**: `MOD:UPDATE` | ||
| 62 | +- **请求**: 路径参数 `id` = `iIncrement`;JSON body 同新增(`sProcedureName` 不可改),未提供字段保持原值 | ||
| 63 | +- **响应**: `data` = 更新后的模块 VO | ||
| 64 | + | ||
| 65 | +#### 错误码 | ||
| 66 | +- `40010` — 参数缺失或格式错误 | ||
| 67 | +- `40421` — 模块不存在或已删除 | ||
| 68 | +- `40921` — 同级名称冲突或不能将自身/后代设为父模块 | ||
| 69 | +- `40911` — `sProcedureName` 冲突(如尝试修改) | ||
| 70 | + | ||
| 71 | +--- | ||
| 72 | + | ||
| 73 | +### REQ-MOD-003 模块删除 | ||
| 74 | + | ||
| 75 | +- **Method**: DELETE | ||
| 76 | +- **Path**: `/api/modules/{id}` | ||
| 77 | +- **Auth**: 需要 | ||
| 78 | +- **Permission**: `MOD:DELETE` | ||
| 79 | +- **请求**: 路径参数 `id` = `iIncrement` | ||
| 80 | +- **响应**: `data` = `{ "iIncrement": <id>, "bDeleted": 1 }`(软删除标记) | ||
| 81 | + | ||
| 82 | +#### 错误码 | ||
| 83 | +- `40421` — 模块不存在或已被删除 | ||
| 84 | +- `40912` — 存在子模块或外部业务引用,禁止删除(响应附 `data.references`) | ||
| 85 | + | ||
| 86 | +--- | ||
| 87 | + | ||
| 88 | +### REQ-MOD-004 模块查询 | ||
| 89 | + | ||
| 90 | +- **Method**: GET | ||
| 91 | +- **Path**: `/api/modules` | ||
| 92 | +- **Auth**: 需要 | ||
| 93 | +- **Permission**: `MOD:READ` | ||
| 94 | +- **请求**: query 参数 `keyword`(可选,对 `sModuleNameZh` 模糊匹配);返回完整树而非分页 | ||
| 95 | +- **响应**: `data` = 模块树数组,节点结构:`{ iIncrement, sModuleNameZh, sDisplayType, sManageDeptEn, iParentId, iSortOrder, children: [] }` | ||
| 96 | + | ||
| 97 | +#### 错误码 | ||
| 98 | +- `40010` — `keyword` 长度超限 | ||
| 99 | + | ||
| 100 | +--- | ||
| 101 | + | ||
| 102 | +## module_usr 用户管理 | ||
| 103 | + | ||
| 104 | +### REQ-USR-001 用户新增 | ||
| 105 | + | ||
| 106 | +- **Method**: POST | ||
| 107 | +- **Path**: `/api/users` | ||
| 108 | +- **Auth**: 需要 | ||
| 109 | +- **Permission**: `USR:CREATE` | ||
| 110 | +- **请求**: JSON body:`sUserNo`(必填)、`sUserName`(必填)、`iStaffId`(可选)、`sUserType`(必填)、`sLanguage`(必填)、`bCanModifyDocs`(可选,默认 false)、`permissionCategoryIds`(数组,对应 REQ-USR-001 表 2 选中行写入 `tUserPermission`);密码不在请求中传,后端使用初始密码 `666666` 经 BCrypt 哈希后落库 | ||
| 111 | +- **响应**: `data.iIncrement` 新用户主键 + 用户 VO(不含密码哈希) | ||
| 112 | + | ||
| 113 | +#### 错误码 | ||
| 114 | +- `40020` — 参数缺失或格式错误 | ||
| 115 | +- `40921` — `sUserName` 或 `sUserNo` 唯一性冲突 | ||
| 116 | +- `40421` — `iStaffId` 不存在或已软删除 | ||
| 117 | +- `40422` — `permissionCategoryIds` 含不存在的权限分类 | ||
| 118 | + | ||
| 119 | +--- | ||
| 120 | + | ||
| 121 | +### REQ-USR-002 用户修改 | ||
| 122 | + | ||
| 123 | +- **Method**: PUT | ||
| 124 | +- **Path**: `/api/users/{id}` | ||
| 125 | +- **Auth**: 需要 | ||
| 126 | +- **Permission**: `USR:UPDATE` | ||
| 127 | +- **请求**: 路径参数 `id` = `iIncrement`;JSON body 同新增(`sUserNo` 不可改);`permissionCategoryIds` 重新覆盖当前权限关联(先删后插) | ||
| 128 | +- **响应**: `data` = 更新后的用户 VO | ||
| 129 | + | ||
| 130 | +#### 错误码 | ||
| 131 | +- `40020` — 参数缺失或格式错误 | ||
| 132 | +- `40431` — 用户不存在或已软删除 | ||
| 133 | +- `40931` — `sUserName` 唯一性冲突 | ||
| 134 | +- `40331` — 当前操作员无权变更目标角色 | ||
| 135 | + | ||
| 136 | +--- | ||
| 137 | + | ||
| 138 | +### REQ-USR-003 用户查询 | ||
| 139 | + | ||
| 140 | +- **Method**: GET | ||
| 141 | +- **Path**: `/api/users` | ||
| 142 | +- **Auth**: 需要 | ||
| 143 | +- **Permission**: `USR:READ` | ||
| 144 | +- **请求**: query 参数:`pageNum` / `pageSize`(标准分页)、`queryField`(枚举:`username` / `staffname` / `userno` / `department` / `usertype` / `deleted` / `lastLoginDate` / `createdBy`)、`matchType`(`contains` / `notContains` / `equals`)、`queryValue`(可空) | ||
| 145 | +- **响应**: `data` = `PageResult<UserListVO>`,VO 字段:`iIncrement`、`sUserName`、`sStaffName`(join `tStaff`)、`sUserNo`、`sDepartment`(join `tStaff`)、`sUserType`、`sLanguage`、`bDeleted`、`tLastLoginDate`、`sCreatedBy`、`tCreateDate` | ||
| 146 | + | ||
| 147 | +#### 错误码 | ||
| 148 | +- `40020` — `queryField` / `matchType` 非枚举值 | ||
| 149 | +- `40021` — `pageSize` 超出 1~100 | ||
| 150 | + | ||
| 151 | +--- | ||
| 152 | + | ||
| 153 | +### REQ-USR-004 用户登录 | ||
| 154 | + | ||
| 155 | +- **Method**: POST | ||
| 156 | +- **Path**: `/api/auth/login` | ||
| 157 | +- **Auth**: 不需要 | ||
| 158 | +- **Permission**: 无 | ||
| 159 | +- **请求**: JSON body:`sUserName`(必填)、`sPassword`(必填,明文,HTTPS 传输)、`sVersion`(必填,枚举:`standard`) | ||
| 160 | +- **响应**: `data` = `{ "accessToken": "...", "refreshToken": "...", "expiresIn": 7200, "user": { "iIncrement", "sUserNo", "sUserName", "sUserType", "sLanguage" } }`;登录成功同时回写 `tUser.tLastLoginDate` | ||
| 161 | + | ||
| 162 | +#### 错误码 | ||
| 163 | +- `40101` — 用户名或密码错误(统一提示,不区分原因) | ||
| 164 | +- `40102` — 账号已禁用 / 已软删除 | ||
| 165 | +- `40301` — 账号被临时锁定(响应附 `data.cooldownSeconds` 剩余秒数) | ||
| 166 | +- `40020` — 参数缺失或格式错误 |
docs/06-UI交互规范.md
0 → 100644
| 1 | +++ a/docs/06-UI交互规范.md | ||
| 1 | +# 06-UI交互规范 | ||
| 2 | + | ||
| 3 | +## 一、整体布局 | ||
| 4 | + | ||
| 5 | +### 1.1 页面框架 | ||
| 6 | + | ||
| 7 | +采用 Ant Design 5.x 的 `Layout` 组件构建整体骨架,分三段: | ||
| 8 | + | ||
| 9 | +- **Header(顶部导航)**:左侧 Logo + 系统名("小羚羊 ERP"),右侧用户头像下拉(个人信息 / 修改密码 / 退出登录)。 | ||
| 10 | +- **Sider(侧边菜单)**:固定左侧,使用 `Menu` 组件以树形展示由 MOD 模块管理维护的功能菜单,支持折叠。 | ||
| 11 | +- **Content(主区域)**:路由出口,渲染各业务页面,外包 `Breadcrumb` + `PageHeader` 上下文容器。 | ||
| 12 | + | ||
| 13 | +整体使用 `ConfigProvider` 注入主题与中文 `zh_CN` locale。 | ||
| 14 | + | ||
| 15 | +### 1.2 布局参数 | ||
| 16 | + | ||
| 17 | +| 项 | 默认值 | 说明 | | ||
| 18 | +|---|---|---| | ||
| 19 | +| Header 高度 | 56 px | 紧凑信息密度,留出更多内容区 | | ||
| 20 | +| Sidebar 展开宽度 | 220 px | 菜单文案完整可见 | | ||
| 21 | +| Sidebar 折叠宽度 | 56 px | 仅图标 | | ||
| 22 | +| Content 内边距 | 16 px | 与 Card / Table 的内边距对齐 | | ||
| 23 | +| 最小分辨率 | 1366 × 768 | ERP 桌面端为主,不做移动端适配 | | ||
| 24 | + | ||
| 25 | +## 二、标准页面类型 | ||
| 26 | + | ||
| 27 | +基于 USR / MOD 模块均为典型主数据维护场景,列表 / 表单 / 详情 三类页面足以覆盖;用户与模块均为扁平或浅层树结构,再补一个树形管理页用于 MOD。 | ||
| 28 | + | ||
| 29 | +### 2.1 列表页 | ||
| 30 | + | ||
| 31 | +自上而下三段: | ||
| 32 | + | ||
| 33 | +- **顶部操作区**:左侧批量动作(启用 / 停用 / 删除),右侧主操作("新增 X")。 | ||
| 34 | +- **搜索栏**:`Form` 行内布局,含关键字 + 1~2 个枚举筛选;提交触发请求,"重置"清空。 | ||
| 35 | +- **表格 + 分页**:`Table` + 服务端分页(`Pagination`),列尾固定 `操作` 列(编辑 / 详情 / 删除)。 | ||
| 36 | +- 删除等危险操作需 `Modal.confirm` 二次确认。 | ||
| 37 | + | ||
| 38 | +### 2.2 表单页 | ||
| 39 | + | ||
| 40 | +新建 / 编辑统一使用右侧 `Drawer`(宽度 600 px),不跳页: | ||
| 41 | + | ||
| 42 | +- 字段使用 Ant Design `Form` + `Form.Item`,校验时机 `onBlur`,提交前再 `validateFields` 全量校验。 | ||
| 43 | +- 提交按钮固定在 Drawer 右下角,主按钮"保存"在右、"取消"在左。 | ||
| 44 | +- 提交期间按钮 `loading=true`,避免重复提交。 | ||
| 45 | + | ||
| 46 | +### 2.3 详情页 | ||
| 47 | + | ||
| 48 | +使用 `Descriptions`(`bordered + column={2}`)展示主数据,必要时 `Tabs` 切分"基本信息 / 关联资源 / 操作日志"等子页签;详情页只读,编辑动作回到 Drawer 表单。 | ||
| 49 | + | ||
| 50 | +### 2.4 树形管理页 | ||
| 51 | + | ||
| 52 | +仅 MOD 模块管理使用:左侧 `Tree` 展示模块层级,右侧 `Descriptions` + `Form` 展示选中节点详情;新增子模块使用节点上的"+"按钮直接打开 Drawer。 | ||
| 53 | + | ||
| 54 | +## 三、通用交互规则 | ||
| 55 | + | ||
| 56 | +### 3.1 操作反馈 | ||
| 57 | + | ||
| 58 | +- **成功**:`message.success`,文案 "<动作>成功",自动消失 2 秒。 | ||
| 59 | +- **失败**:`message.error` 或 `Modal.error`,文案优先取后端返回的 `message`,缺省"操作失败,请稍后重试"。 | ||
| 60 | +- **危险操作**:删除 / 停用使用 `Modal.confirm` 二次确认,确认按钮使用 `danger` 色。 | ||
| 61 | +- **长耗时按钮**:超过 300 ms 的请求按钮 `loading=true`,禁用重复点击。 | ||
| 62 | + | ||
| 63 | +### 3.2 数据展示 | ||
| 64 | + | ||
| 65 | +- **空状态**:`Empty` 组件,文案统一 "暂无数据"。 | ||
| 66 | +- **加载**:列表整体 `Spin`;局部加载使用 Skeleton。 | ||
| 67 | +- **异常**:请求失败统一 `Result` 组件 + "重试"按钮,错误码 / 错误信息以小字置于副标题。 | ||
| 68 | + | ||
| 69 | +### 3.3 权限控制(前端) | ||
| 70 | + | ||
| 71 | +- **菜单级**:登录返回的菜单树驱动 `Sider Menu`,未授权模块不渲染。 | ||
| 72 | +- **按钮级**:自定义 `<Authorized code="XXX">` 包装组件,按权限码控制按钮可见 / 禁用。 | ||
| 73 | +- **路由级**:`<RequireAuth>` 高阶组件,未授权路由重定向到 403 页。 | ||
| 74 | +- 所有权限码与后端 RBAC 对齐,前端不做信任决策,仅做展示控制。 | ||
| 75 | + | ||
| 76 | +## 四、Design Tokens | ||
| 77 | + | ||
| 78 | +### 4.1 全局调色板 | ||
| 79 | + | ||
| 80 | +与 Ant Design 5.x 主题 token 对齐,统一以 CSS 变量定义于 `src/styles/tokens.css`,并通过 `ConfigProvider.theme.token` 映射。 | ||
| 81 | + | ||
| 82 | +| 语义 | 变量名 | 默认值 | 用途 | | ||
| 83 | +|---|---|---|---| | ||
| 84 | +| 主色 | `--color-primary` | `#1677ff` | 主按钮 / 链接 / 高亮 | | ||
| 85 | +| 成功 | `--color-success` | `#52c41a` | 成功消息 / 正向状态标签 | | ||
| 86 | +| 警告 | `--color-warning` | `#faad14` | 提醒类反馈 | | ||
| 87 | +| 错误 | `--color-error` | `#ff4d4f` | 错误消息 / 危险按钮 | | ||
| 88 | +| 主文字 | `--color-text` | `rgba(0,0,0,0.88)` | 正文文字 | | ||
| 89 | +| 次文字 | `--color-text-secondary` | `rgba(0,0,0,0.65)` | 辅助说明 | | ||
| 90 | +| 边框 | `--color-border` | `#d9d9d9` | 表单边框 / 分割线 | | ||
| 91 | +| 背景 | `--color-bg` | `#ffffff` | 主体背景 | | ||
| 92 | +| 容器背景 | `--color-bg-container` | `#fafafa` | Card / Sider 等容器 | | ||
| 93 | + | ||
| 94 | +### 4.2 组件级状态色 | ||
| 95 | + | ||
| 96 | +| 序号 | 组件 | 编辑 bg | 只读 bg | 悬浮 bg | 编辑 fg | 只读 fg | 悬浮 fg | 备注 | | ||
| 97 | +|---|---|---|---|---|---|---|---|---| | ||
| 98 | +| 1 | Form Input | `var(--color-bg)` | `var(--color-bg-container)` | `var(--color-bg)` | `var(--color-text)` | `var(--color-text-secondary)` | `var(--color-text)` | 只读使用 `disabled` 样式 | | ||
| 99 | +| 2 | Table Row | `var(--color-bg)` | `var(--color-bg)` | `var(--color-row-hover)` | `var(--color-text)` | `var(--color-text)` | `var(--color-text)` | 选中行另用 `--color-row-selected` | | ||
| 100 | +| 3 | Button Primary | `var(--color-primary)` | — | `var(--color-primary-hover)` | `#fff` | — | `#fff` | 危险按钮用 `--color-error` 系 | | ||
| 101 | + | ||
| 102 | +**Token 默认值**: | ||
| 103 | + | ||
| 104 | +| Token | 默认值 | | ||
| 105 | +|---|---| | ||
| 106 | +| `--color-row-hover` | `#f5f5f5` | | ||
| 107 | +| `--color-row-selected` | `#e6f4ff` | | ||
| 108 | +| `--color-primary-hover` | `#4096ff` | | ||
| 109 | + | ||
| 110 | +### 4.3 引用约定 | ||
| 111 | + | ||
| 112 | +- 组件样式只用 `var(--color-xxx)`,禁止硬编码 hex / rgba。 | ||
| 113 | +- 新增 token 须先登记到 § 4.1 / 4.2 再补 `tokens.css`。 | ||
| 114 | +- 修改色值只改 `tokens.css` 一处,不允许组件覆盖。 | ||
| 115 | + | ||
| 116 | +## 五、页面清单 | ||
| 117 | + | ||
| 118 | +### module_mod 模块管理 | ||
| 119 | + | ||
| 120 | +- **模块列表 / 树形管理** (`/mod/list`) | ||
| 121 | + - 类型: 树形管理页(左 Tree 右 Form/Descriptions) | ||
| 122 | + - 对应 REQ: REQ-MOD-001 / REQ-MOD-002 / REQ-MOD-003 / REQ-MOD-004 | ||
| 123 | + - 入口菜单: 系统配置 → 模块管理 | ||
| 124 | + - 主要交互: 关键字搜索 `sModuleNameZh` 模糊匹配;左侧 Tree 选中节点 → 右侧 Descriptions 展示详情;节点上 "+" 打开 Drawer 新增子模块;"编辑" 按钮打开 Drawer 修改;"删除" 按钮触发 Modal.confirm 二次确认软删除(存在子节点时禁用并提示) | ||
| 125 | + | ||
| 126 | +### module_usr 用户管理 | ||
| 127 | + | ||
| 128 | +- **用户列表** (`/usr/list`) | ||
| 129 | + - 类型: 列表页 | ||
| 130 | + - 对应 REQ: REQ-USR-003(查询)/ REQ-USR-001(新增入口)/ REQ-USR-002(编辑入口) | ||
| 131 | + - 入口菜单: 系统配置 → 用户管理 | ||
| 132 | + - 主要交互: 顶部搜索栏(查询字段下拉 + 匹配方式下拉 + 查询值文本框);右上 "新增用户" 主按钮;表格列尾 "编辑 / 详情" 操作;分页器(20/页 默认) | ||
| 133 | + | ||
| 134 | +- **用户表单(新增 / 编辑)** (`/usr/list?drawer=user`) | ||
| 135 | + - 类型: 表单页(Drawer 形式,与列表页同路由用 query 控制开合) | ||
| 136 | + - 对应 REQ: REQ-USR-001 / REQ-USR-002 | ||
| 137 | + - 入口菜单: 列表页"新增用户"按钮 / 列表行"编辑"按钮 | ||
| 138 | + - 主要交互: 基本字段表单(用户号 / 用户名 / 员工 / 类型 / 语言 / 单据修改权限)+ 权限组多选 Table;保存触发后端校验,唯一性冲突在字段下方红字提示;初始密码不在表单展示,由后端默认 `666666` | ||
| 139 | + | ||
| 140 | +- **登录页** (`/login`) | ||
| 141 | + - 类型: 表单页(独立全屏布局,不走 Layout 框架) | ||
| 142 | + - 对应 REQ: REQ-USR-004 | ||
| 143 | + - 入口菜单: 未登录时所有路由重定向到此 | ||
| 144 | + - 主要交互: 用户名 + 密码(星号显示)+ 版本下拉;提交登录返回 token 写入内存 / Cookie;连续失败锁定后展示剩余冷却时间并禁用按钮 |
docs/07-环境配置.md
0 → 100644
| 1 | +++ a/docs/07-环境配置.md | ||
| 1 | +# 07-环境配置 | ||
| 2 | + | ||
| 3 | +## 一、依赖清单 | ||
| 4 | + | ||
| 5 | +| 层 | 依赖 | 版本 | 说明 | | ||
| 6 | +|---|---|---|---| | ||
| 7 | +| 运行时 | JDK | Java 17 / 21 | Spring Boot 3 推荐版本 | | ||
| 8 | +| 运行时 | MySQL | 8.x | 关系型数据库 | | ||
| 9 | +| 运行时 | Redis | 最新稳定版 | 缓存 / 会话 | | ||
| 10 | +| 运行时 | Node.js | 20 LTS | 前端构建运行时 | | ||
| 11 | +| 运行时 | Nginx | 最新稳定版 | 前端托管 / 反向代理 | | ||
| 12 | +| 构建 | Maven | 3.9.x | 后端依赖与构建 | | ||
| 13 | +| 构建 | Vite | 最新稳定版 | 前端开发与打包 | | ||
| 14 | +| 构建 | npm / pnpm | 最新稳定版 | 前端依赖管理 | | ||
| 15 | +| 容器 | Docker | 最新稳定版 | 容器化部署 | | ||
| 16 | +| 容器 | Docker Compose | 最新稳定版 | 本地多服务编排 | | ||
| 17 | +| CLI | git | 最新稳定版 | 源码管理 | | ||
| 18 | +| CLI | mysql-client | 8.x | 命令行连接数据库 | | ||
| 19 | +| CLI | redis-cli | 最新稳定版 | 命令行操作 Redis | | ||
| 20 | +| CLI | glab | 最新稳定版 | 创建 / 管理 GitLab MR | | ||
| 21 | + | ||
| 22 | +## 二、端口约定 | ||
| 23 | + | ||
| 24 | +| 服务 | 端口 | 说明 | | ||
| 25 | +|---|---|---| | ||
| 26 | +| 后端 HTTP | 8080 | Spring Boot 默认端口 | | ||
| 27 | +| 前端 dev | 5173 | Vite 默认端口 | | ||
| 28 | +| MySQL | 3306 | 数据库 | | ||
| 29 | +| Redis | 6379 | 缓存 | | ||
| 30 | +| Nginx | 80 / 443 | 反向代理 / HTTPS | | ||
| 31 | + | ||
| 32 | +## 三、环境变量 | ||
| 33 | + | ||
| 34 | +运行时凭据(数据库连接、Redis 连接、JWT 密钥等)全部放在仓库根的 `.env.local`,不入 git。 | ||
| 35 | +字段清单与占位符见该文件,真实值由开发者本地填写。 | ||
| 36 | + | ||
| 37 | +## 四、常用命令 | ||
| 38 | + | ||
| 39 | +| 命令 | 说明 | | ||
| 40 | +|---|---| | ||
| 41 | +| `cd backend && mvn spring-boot:run` | 启动后端服务 | | ||
| 42 | +| `cd frontend && npm run dev` | 启动前端 dev 服务 | | ||
| 43 | +| `cd backend && mvn clean package -DskipTests` | 后端打包 | | ||
| 44 | +| `cd frontend && npm run build` | 前端打包 | | ||
| 45 | +| `bash scripts/test.sh` | 全量测试(后端 + 前端 + E2E) | | ||
| 46 | +| `bash scripts/setup-test-db.sh` | 重置测试数据库(DROP + CREATE) | | ||
| 47 | +| `glab mr create` | 创建 GitLab MR | | ||
| 48 | +| `git push` | 推送(pre-push 钩子会预跑后端测试) | |
docs/08-模块任务管理.md
0 → 100644
| 1 | +++ a/docs/08-模块任务管理.md | ||
| 1 | +# 08-工作流进度 | ||
| 2 | + | ||
| 3 | +> 全流程进度跟踪。CC 每完成一项产出就勾选一项。 | ||
| 4 | +> - **§ 一 Plan(A0~A5)**:`plan-start` 找第一个未勾 A 子项分发到对应 skill | ||
| 5 | +> - **§ 二 Coding(模块)**:分发以 `docs/02-开发计划.md § 二 开发顺序清单` 为准;`coding-start` 按 docs/02 顺序扫描,对每个 REQ 所属模块查询本 § 二的 `MR:` 字段 + GitLab API `state`,找第一个非 merged 模块分发。本 § 二 行序无语义,仅作模块元数据表 | ||
| 6 | + | ||
| 7 | +## 一、Plan 阶段(一次性) | ||
| 8 | + | ||
| 9 | +- [x] A0 项目初始化 — project-init | ||
| 10 | + - [x] 依赖检查通过 | ||
| 11 | + - [x] 项目文件骨架已创建(CLAUDE.md + docs/01-需求清单/index.md + docs/04-技术规范.md) | ||
| 12 | + - [x] Git 已初始化 | ||
| 13 | + | ||
| 14 | +- [x] A1 范围锁定 — scope-lock | ||
| 15 | + - [x] 项目概述已填写(CLAUDE.md § 🎯 项目概述) | ||
| 16 | + - [x] 技术栈已确认(docs/04 § 零) | ||
| 17 | + - [x] 需求清单索引已填写(docs/01-需求清单/index.md) | ||
| 18 | + - [x] REQ 卡片骨架已生成(docs/01-需求清单/<module>/REQ-*.md,业务内容留待人工填写) | ||
| 19 | + | ||
| 20 | +- [x] A2 骨架生成 — skeleton-gen | ||
| 21 | + - [x] 架构文档已生成(docs/04 § 一+、docs/06、docs/07、docs/09) | ||
| 22 | + - [x] 工具脚本已生成(scripts/*.sh、.githooks/pre-push、.env.local) | ||
| 23 | + - [x] .gitignore 已配置 | ||
| 24 | + | ||
| 25 | +- [x] A3 DB 设计 + REQ 回填 — db-design-gen | ||
| 26 | + - [x] docs/03-数据库设计文档.md 已生成 | ||
| 27 | + - [x] docs/01 各 REQ 卡片"依赖表" + 模块头"涉及表" 已回填 | ||
| 28 | + | ||
| 29 | +- [x] A4 DB 初始化 — db-init | ||
| 30 | + - [x] sql/migrations/V1__initial_schema.sql 已生成 | ||
| 31 | + - [x] DDL 与 docs/03 全量一致 | ||
| 32 | + - [x] .env.local 凭据已验证(mysql -e "SELECT 1" OK) | ||
| 33 | + - [x] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行 | ||
| 34 | + - [x] SHOW TABLES 行数 == docs/03 表数量 | ||
| 35 | + | ||
| 36 | +- [x] A5 下游文档生成 — downstream-gen | ||
| 37 | + - [x] docs/02 开发计划已生成 | ||
| 38 | + - [x] docs/05 API 契约已生成 | ||
| 39 | + - [x] docs/06 § 五 页面清单已填入 | ||
| 40 | + - [x] docs/10 验收清单已生成 | ||
| 41 | + - [x] 下方模块列表已填入 | ||
| 42 | + - [x] REQ 卡片依赖接口已回填 | ||
| 43 | + | ||
| 44 | +## 二、Coding 阶段(按模块循环) | ||
| 45 | + | ||
| 46 | +(A5 填入后,每行一个模块。每个模块的 `MR:` 字段在 `—` 和 `!<iid>` 之间变化,完成由 GitLab API `state=merged` 判定。`coding-start` 每次按 docs/02 REQ 序扫每模块的 MR state 决定派发。) | ||
| 47 | + | ||
| 48 | +<!-- 模块格式示例(由 A5 downstream-gen 追加;功能子项由 feature-review 在 approve 时勾选): | ||
| 49 | +- module_0 系统管理 | ||
| 50 | + - 依赖: — | ||
| 51 | + - 路径: backend/module/sys/, frontend/pages/sys/ | ||
| 52 | + - MR: — | ||
| 53 | + - 功能: | ||
| 54 | + - [ ] REQ-SYS-001 用户登录 | ||
| 55 | + - [ ] REQ-SYS-002 用户注册 | ||
| 56 | +--> | ||
| 57 | + | ||
| 58 | +- module_mod 模块管理 | ||
| 59 | + - 依赖: — | ||
| 60 | + - 路径: backend/src/main/java/com/xly/erp/module/mod/, frontend/src/pages/mod/ | ||
| 61 | + - MR: — | ||
| 62 | + - 功能: | ||
| 63 | + - [ ] REQ-MOD-001 模块新增 | ||
| 64 | + - [ ] REQ-MOD-002 模块修改 | ||
| 65 | + - [ ] REQ-MOD-003 模块删除 | ||
| 66 | + - [ ] REQ-MOD-004 模块查询 | ||
| 67 | + | ||
| 68 | +- module_usr 用户管理 | ||
| 69 | + - 依赖: — | ||
| 70 | + - 路径: backend/src/main/java/com/xly/erp/module/usr/, frontend/src/pages/usr/ | ||
| 71 | + - MR: — | ||
| 72 | + - 功能: | ||
| 73 | + - [ ] REQ-USR-001 用户新增 | ||
| 74 | + - [ ] REQ-USR-002 用户修改 | ||
| 75 | + - [ ] REQ-USR-003 用户查询 | ||
| 76 | + - [ ] REQ-USR-004 用户登录 |
docs/09-项目目录结构.md
0 → 100644
| 1 | +++ a/docs/09-项目目录结构.md | ||
| 1 | +# 09-项目目录结构 | ||
| 2 | + | ||
| 3 | +## 一、仓库顶层 | ||
| 4 | + | ||
| 5 | +``` | ||
| 6 | +. | ||
| 7 | +├── CLAUDE.md # Claude Code 主指令 | ||
| 8 | +├── README.md | ||
| 9 | +├── .env.local # 本地凭据,不入 git | ||
| 10 | +├── .gitignore | ||
| 11 | +├── .githooks/ | ||
| 12 | +│ └── pre-push # 推送前测试闸门 | ||
| 13 | +├── scripts/ | ||
| 14 | +│ ├── test.sh # 全量测试 | ||
| 15 | +│ └── setup-test-db.sh # 重置测试数据库 | ||
| 16 | +├── sql/ | ||
| 17 | +│ └── migrations/ # Flyway V_n__*.sql | ||
| 18 | +├── docs/ # 项目文档(见 § 四) | ||
| 19 | +├── backend/ # 后端 Spring Boot 模块 | ||
| 20 | +└── frontend/ # 前端 Vite + React 模块 | ||
| 21 | +``` | ||
| 22 | + | ||
| 23 | +## 二、后端目录 | ||
| 24 | + | ||
| 25 | +根包名 `com.xly.erp`,本节以 `<root>` 代指。 | ||
| 26 | + | ||
| 27 | +``` | ||
| 28 | +backend/ | ||
| 29 | +├── pom.xml | ||
| 30 | +├── src/ | ||
| 31 | +│ ├── main/ | ||
| 32 | +│ │ ├── java/<root>/ | ||
| 33 | +│ │ │ ├── ErpApplication.java # Spring Boot 启动类 | ||
| 34 | +│ │ │ ├── common/ # 统一响应 / 异常 / 工具 | ||
| 35 | +│ │ │ │ ├── response/ # ApiResponse / ErrorCode | ||
| 36 | +│ │ │ │ ├── exception/ # 全局异常处理器 | ||
| 37 | +│ │ │ │ └── util/ # Hutool 二次封装等 | ||
| 38 | +│ │ │ ├── config/ # SecurityConfig / RedisConfig / SwaggerConfig | ||
| 39 | +│ │ │ ├── security/ # JWT 过滤器 / RBAC | ||
| 40 | +│ │ │ └── module/ | ||
| 41 | +│ │ │ ├── usr/ # USR 用户管理 | ||
| 42 | +│ │ │ │ ├── controller/ | ||
| 43 | +│ │ │ │ ├── service/ | ||
| 44 | +│ │ │ │ ├── mapper/ | ||
| 45 | +│ │ │ │ ├── entity/ | ||
| 46 | +│ │ │ │ ├── dto/ | ||
| 47 | +│ │ │ │ └── vo/ | ||
| 48 | +│ │ │ └── mod/ # MOD 模块管理 | ||
| 49 | +│ │ │ ├── controller/ | ||
| 50 | +│ │ │ ├── service/ | ||
| 51 | +│ │ │ ├── mapper/ | ||
| 52 | +│ │ │ ├── entity/ | ||
| 53 | +│ │ │ ├── dto/ | ||
| 54 | +│ │ │ └── vo/ | ||
| 55 | +│ │ └── resources/ | ||
| 56 | +│ │ ├── application.yml | ||
| 57 | +│ │ ├── application-dev.yml | ||
| 58 | +│ │ ├── logback-spring.xml | ||
| 59 | +│ │ └── mapper/ # MyBatis-Plus XML | ||
| 60 | +│ └── test/ | ||
| 61 | +│ └── java/<root>/ # 单元 / 集成测试 | ||
| 62 | +└── target/ # 构建产物,git 忽略 | ||
| 63 | +``` | ||
| 64 | + | ||
| 65 | +## 三、前端目录 | ||
| 66 | + | ||
| 67 | +``` | ||
| 68 | +frontend/ | ||
| 69 | +├── package.json | ||
| 70 | +├── vite.config.ts | ||
| 71 | +├── index.html | ||
| 72 | +├── src/ | ||
| 73 | +│ ├── main.tsx # 入口 | ||
| 74 | +│ ├── App.tsx # 路由总配置 | ||
| 75 | +│ ├── api/ # axios 封装 + 各模块接口 | ||
| 76 | +│ │ ├── request.ts | ||
| 77 | +│ │ ├── usr.ts | ||
| 78 | +│ │ └── mod.ts | ||
| 79 | +│ ├── components/ # 通用组件 | ||
| 80 | +│ ├── pages/ | ||
| 81 | +│ │ ├── usr/ # USR 页面 | ||
| 82 | +│ │ └── mod/ # MOD 页面 | ||
| 83 | +│ ├── store/ # Redux Toolkit slices | ||
| 84 | +│ ├── hooks/ | ||
| 85 | +│ ├── utils/ | ||
| 86 | +│ ├── styles/ | ||
| 87 | +│ │ ├── tokens.css # Design Token CSS 变量 | ||
| 88 | +│ │ └── index.css | ||
| 89 | +│ └── router/ # React Router 配置 | ||
| 90 | +└── public/ | ||
| 91 | +``` | ||
| 92 | + | ||
| 93 | +## 四、docs/ 结构 | ||
| 94 | + | ||
| 95 | +``` | ||
| 96 | +docs/ | ||
| 97 | +├── 01-需求清单/ # 每模块一子目录(_module.md 模块头 + REQ-*.md 卡片) | ||
| 98 | +├── 02-开发计划.md | ||
| 99 | +├── 03-数据库设计文档.md | ||
| 100 | +├── 04-技术规范.md | ||
| 101 | +├── 05-API接口契约.md | ||
| 102 | +├── 06-UI交互规范.md | ||
| 103 | +├── 07-环境配置.md | ||
| 104 | +├── 08-模块任务管理.md | ||
| 105 | +├── 09-项目目录结构.md | ||
| 106 | +├── 10-验收检查清单.md | ||
| 107 | +└── superpowers/ # CC 运行时产物 | ||
| 108 | +``` | ||
| 109 | + | ||
| 110 | +## 五、命名与放置约定 | ||
| 111 | + | ||
| 112 | +- **根包名**:`com.xly.erp`,所有 Java 类位于此包及其子包下。 | ||
| 113 | +- **前端命名空间**:以 `@/` 别名映射 `frontend/src/`(在 `vite.config.ts` 中配置)。 | ||
| 114 | +- **Controller**:`<root>.module.<mod>.controller.XxxController`,REST 入口;不包含业务逻辑。 | ||
| 115 | +- **Service**:`<root>.module.<mod>.service.XxxService` + `service.impl.XxxServiceImpl`,业务编排,事务边界。 | ||
| 116 | +- **Mapper**:`<root>.module.<mod>.mapper.XxxMapper`(接口)+ `resources/mapper/<mod>/XxxMapper.xml`(XML)。 | ||
| 117 | +- **Entity**:`<root>.module.<mod>.entity.Xxx`,1:1 映射数据库表。 | ||
| 118 | +- **DTO**:`<root>.module.<mod>.dto.XxxDTO`,入参传输对象(Controller ↔ Service)。 | ||
| 119 | +- **VO**:`<root>.module.<mod>.vo.XxxVO`,出参视图对象(Service → Controller → 前端)。 | ||
| 120 | +- **前端组件**:`frontend/src/components/<域>/XxxComponent.tsx`,复用粒度。 | ||
| 121 | +- **前端页面**:`frontend/src/pages/<mod>/<功能>.tsx`,与 REQ 卡片功能一一对应。 | ||
| 122 | +- **API 模块**:`frontend/src/api/<mod>.ts` 一个文件汇集该模块所有后端接口调用。 |
docs/10-验收检查清单.md
0 → 100644
| 1 | +++ a/docs/10-验收检查清单.md | ||
| 1 | +# 10-验收检查清单 | ||
| 2 | + | ||
| 3 | +通用验收项(全项目适用): | ||
| 4 | + | ||
| 5 | +- [ ] `scripts/test.sh` 本地全绿 | ||
| 6 | +- [ ] 所有 schema 改动都有对应 `sql/migrations/V_n__<desc>.sql` | ||
| 7 | +- [ ] 所有新接口在 `docs/05` 中有契约定义 | ||
| 8 | +- [ ] 所有新功能代码注释含 REQ-XXX-NNN | ||
| 9 | +- [ ] 统一响应格式 `{code, message, data, timestamp}` | ||
| 10 | +- [ ] 异常走全局处理器,不暴露堆栈到前端 | ||
| 11 | +- [ ] 前端不存敏感信息到 localStorage | ||
| 12 | + | ||
| 13 | +> 本文档仅维护项目级验收 SOP。粒度更细的验收信息分散在: | ||
| 14 | +> - **每 REQ 的业务验收点**:`docs/01-需求清单/<module>/<req_id>.md § 验收` | ||
| 15 | +> - **每模块的实测验收(数据 / UI / 自动化用例位置)**:由 B 阶段 `module-report` 在该模块完成时填入模块完成报告 | ||
| 16 | +> - **REQ 开发进度(feature-review approve 状态)**:`docs/08 § 二` |
scripts/setup-test-db.sh
0 → 100755
| 1 | +++ a/scripts/setup-test-db.sh | ||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# scripts/setup-test-db.sh — 数据库重置脚本:drop + create 空库。 | ||
| 3 | +# schema apply 由 Flyway 在 Spring Boot 启动时自动处理(见 docs/04 技术栈 + sql/migrations/V*.sql)。 | ||
| 4 | +# seed 数据由测试框架负责(Spring @Sql / Flyway R__seed.sql / data.sql)。 | ||
| 5 | +# | ||
| 6 | +# 使用场景: | ||
| 7 | +# - scripts/test.sh 开头:清空库,让 Spring 启动时 Flyway 从 V1 开始重放所有 migration | ||
| 8 | +# - scripts/test.sh 结尾:清空库,避免测试遗留污染下次运行 | ||
| 9 | +# - 手动调试时:reset 到零状态 | ||
| 10 | +# | ||
| 11 | +# 防护:本脚本只允许在本地 host + 测试库名上执行;非预期目标会被拒绝, | ||
| 12 | +# 避免 .env.local 误指向 staging/prod 时触发不可逆 DROP。 | ||
| 13 | + | ||
| 14 | +set -euo pipefail | ||
| 15 | + | ||
| 16 | +ENV_FILE="$(dirname "$0")/../.env.local" | ||
| 17 | +[ -f "$ENV_FILE" ] || { echo "[setup-test-db] ⚠️ .env.local 不存在($ENV_FILE)" >&2; exit 1; } | ||
| 18 | + | ||
| 19 | +# 用 set -a 加载,让 KEY=VALUE 导出为环境变量;密码中含特殊字符时 .env.local 请用单引号包裹 | ||
| 20 | +set -a; . "$ENV_FILE"; set +a | ||
| 21 | + | ||
| 22 | +# 防护 1:默认只允许本地 host(localhost / 127.0.0.1 / ::1)。 | ||
| 23 | +# 若要为本项目额外允许某些远程 host(如公司测试 MySQL),在 .env.local 里设: | ||
| 24 | +# TEST_DB_ALLOWED_HOSTS="118.178.19.35 test-mysql.internal" # 空格或逗号分隔 | ||
| 25 | +# 被列入者可直接 DROP CREATE,不再需要 TEST_DB_ALLOW_REMOTE=1。 | ||
| 26 | +ALLOWED_HOSTS="localhost 127.0.0.1 ::1 ${TEST_DB_ALLOWED_HOSTS//,/ }" | ||
| 27 | +host_allowed=0 | ||
| 28 | +for h in $ALLOWED_HOSTS; do | ||
| 29 | + [ "${DB_HOST:-}" = "$h" ] && { host_allowed=1; break; } | ||
| 30 | +done | ||
| 31 | +if [ "$host_allowed" -ne 1 ]; then | ||
| 32 | + echo "[setup-test-db] ⚠️ 拒绝在非白名单 host (${DB_HOST}) 上执行 DROP DATABASE" >&2 | ||
| 33 | + echo " 当前白名单:${ALLOWED_HOSTS}" >&2 | ||
| 34 | + echo " 加入 host:在 .env.local 追加 TEST_DB_ALLOWED_HOSTS=\"<host1> <host2>\"" >&2 | ||
| 35 | + echo " 一次性绕过:TEST_DB_ALLOW_REMOTE=1 $0" >&2 | ||
| 36 | + [ "${TEST_DB_ALLOW_REMOTE:-0}" = "1" ] || exit 1 | ||
| 37 | +fi | ||
| 38 | + | ||
| 39 | +# 防护 2:schema 名需像测试/开发库(含 test / _dev / _local),否则要求显式确认 | ||
| 40 | +case "${DB_SCHEMA:-}" in | ||
| 41 | + *test*|*_dev|*_local|*_ci) | ||
| 42 | + ;; | ||
| 43 | + *) | ||
| 44 | + echo "[setup-test-db] ⚠️ schema '${DB_SCHEMA}' 不像测试库(期望命名含 test / _dev / _local / _ci)" >&2 | ||
| 45 | + echo " 如确为期望行为,请显式声明:TEST_DB_ALLOW_PROD_NAME=1 $0" >&2 | ||
| 46 | + [ "${TEST_DB_ALLOW_PROD_NAME:-0}" = "1" ] || exit 1 | ||
| 47 | + ;; | ||
| 48 | +esac | ||
| 49 | + | ||
| 50 | +# 防护 3:显式 banner,让人看见自己在 drop 什么;远程 host 额外提示白名单内容 | ||
| 51 | +echo "[setup-test-db] 即将 DROP + CREATE \`${DB_SCHEMA}\` on ${DB_HOST}:${DB_PORT}" | ||
| 52 | +case "${DB_HOST:-}" in | ||
| 53 | + localhost|127.0.0.1|::1) ;; | ||
| 54 | + *) | ||
| 55 | + echo "[setup-test-db] ⚠️ 目标是 **远程** host(已在 TEST_DB_ALLOWED_HOSTS 白名单中,每次 test.sh 都会 DROP)" | ||
| 56 | + echo "[setup-test-db] 当前白名单: ${ALLOWED_HOSTS}" | ||
| 57 | + echo "[setup-test-db] 若不希望每次自动 DROP,从 .env.local 的 TEST_DB_ALLOWED_HOSTS 删掉此 host" | ||
| 58 | + ;; | ||
| 59 | +esac | ||
| 60 | + | ||
| 61 | +MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}" | ||
| 62 | + | ||
| 63 | +$MYSQL_CMD -e "DROP DATABASE IF EXISTS \`${DB_SCHEMA}\`; CREATE DATABASE \`${DB_SCHEMA}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" | ||
| 64 | + | ||
| 65 | +echo "[setup-test-db] done — schema will be applied by Flyway when Spring Boot starts" |
scripts/test.sh
0 → 100755
| 1 | +++ a/scripts/test.sh | ||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# scripts/test.sh —— 合并到默认分支(main / master)前的测试闸门。 | ||
| 3 | +# 顺序:detect → setup-db → build → lint → unit+integration → e2e → reset-db | ||
| 4 | +# 由 .githooks/pre-push 和 test-gate skill(通过子会话)调用。 | ||
| 5 | + | ||
| 6 | +set -euo pipefail | ||
| 7 | + | ||
| 8 | +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | ||
| 9 | +cd "$PROJECT_ROOT" | ||
| 10 | + | ||
| 11 | +# Stack detection (runtime, mode-agnostic) | ||
| 12 | +HAS_BACKEND=0; [ -d backend ] && HAS_BACKEND=1 | ||
| 13 | +HAS_FRONTEND=0; [ -d frontend ] && HAS_FRONTEND=1 | ||
| 14 | +if [ $HAS_BACKEND -eq 0 ] && [ $HAS_FRONTEND -eq 0 ]; then | ||
| 15 | + echo "[test.sh] FATAL: neither backend/ nor frontend/ exists" >&2 | ||
| 16 | + exit 1 | ||
| 17 | +fi | ||
| 18 | + | ||
| 19 | +echo "[test.sh] 1/6 setup test db" | ||
| 20 | +./scripts/setup-test-db.sh | ||
| 21 | + | ||
| 22 | +echo "[test.sh] 2/6 build" | ||
| 23 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && mvn -B clean package -DskipTests); else echo "[test.sh] skip backend build"; fi | ||
| 24 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && npm run build); else echo "[test.sh] skip frontend build"; fi | ||
| 25 | + | ||
| 26 | +echo "[test.sh] 3/6 lint" | ||
| 27 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && mvn -B compile); else echo "[test.sh] skip backend lint"; fi | ||
| 28 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && npm run lint); else echo "[test.sh] skip frontend lint"; fi | ||
| 29 | + | ||
| 30 | +echo "[test.sh] 4/6 unit + integration" | ||
| 31 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && mvn -B test); else echo "[test.sh] skip backend test"; fi | ||
| 32 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && npm test -- --run); else echo "[test.sh] skip frontend test"; fi | ||
| 33 | + | ||
| 34 | +echo "[test.sh] 5/6 E2E" | ||
| 35 | +echo "[test.sh] e2e 略" | ||
| 36 | + | ||
| 37 | +echo "[test.sh] 6/6 reset test db" | ||
| 38 | +./scripts/setup-test-db.sh | ||
| 39 | + | ||
| 40 | +echo "[test.sh] GREEN" |
sql/migrations/.gitkeep
0 → 100644
| 1 | +++ a/sql/migrations/.gitkeep |
sql/migrations/V1__initial_schema.sql
0 → 100644
| 1 | +++ a/sql/migrations/V1__initial_schema.sql | ||
| 1 | +-- Flyway migration V1 — initial schema for 小羚羊 | ||
| 2 | +-- Generated: 2026-05-06T08:30:00Z | ||
| 3 | +-- Source: 由 A4 db-init 从 docs/03-数据库设计文档.md 翻译生成(schema SSoT 是 docs/03) | ||
| 4 | +-- This is the FIRST migration; subsequent schema changes must be written as new files sql/migrations/V2__<desc>.sql, V3__... etc. | ||
| 5 | +-- Apply: Flyway runs this automatically at Spring Boot startup. | ||
| 6 | +-- Do not hand-edit this file after it is committed; write a new migration instead. | ||
| 7 | + | ||
| 8 | +-- ============================================================ | ||
| 9 | +-- 1) tUser — 系统用户账户与登录凭据 | ||
| 10 | +-- ============================================================ | ||
| 11 | +CREATE TABLE `tUser` ( | ||
| 12 | + `iIncrement` int NOT NULL AUTO_INCREMENT COMMENT '整数主键 ID(标准列)', | ||
| 13 | + `sId` varchar(100) NULL COMMENT '业务 ID(标准列)', | ||
| 14 | + `sBrandsId` varchar(100) NULL COMMENT '品牌 ID(多租户隔离,标准列)', | ||
| 15 | + `sSubsidiaryId` varchar(100) NULL COMMENT '子公司 ID(组织层级隔离,标准列)', | ||
| 16 | + `tCreateDate` datetime NOT NULL COMMENT '创建时间(标准列)', | ||
| 17 | + `sUserNo` varchar(50) NOT NULL COMMENT '用户号;系统内唯一', | ||
| 18 | + `sUserName` varchar(50) NOT NULL COMMENT '用户名(登录账号);系统内唯一', | ||
| 19 | + `iStaffId` int NULL DEFAULT NULL COMMENT '关联职员 ID(可选,外键 → tStaff.iIncrement)', | ||
| 20 | + `sUserType` varchar(20) NOT NULL DEFAULT '普通用户' COMMENT '用户类型;枚举:普通用户 / 超级管理员', | ||
| 21 | + `sLanguage` varchar(10) NOT NULL DEFAULT 'zh' COMMENT '语言偏好;枚举:zh / en / zh-TW', | ||
| 22 | + `bCanModifyDocs` bit(1) NOT NULL DEFAULT b'0' COMMENT '单据修改权限;0 否 / 1 是', | ||
| 23 | + `sPasswordHash` varchar(255) NOT NULL COMMENT '密码哈希值(BCrypt 等强哈希算法),新增默认初始密码 666666 的哈希', | ||
| 24 | + `tLastLoginDate` datetime NULL DEFAULT NULL COMMENT '最后登录时间', | ||
| 25 | + `sCreatedBy` varchar(50) NULL COMMENT '制单人(创建用户的操作员用户号)', | ||
| 26 | + `bDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '软删除标记;0 有效 / 1 已作废', | ||
| 27 | + `tDeletedDate` datetime NULL DEFAULT NULL COMMENT '软删除时间', | ||
| 28 | + `sDeletedBy` varchar(50) NULL DEFAULT NULL COMMENT '软删除操作人', | ||
| 29 | + PRIMARY KEY (`iIncrement`) | ||
| 30 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统用户账户与登录凭据'; | ||
| 31 | + | ||
| 32 | +-- ============================================================ | ||
| 33 | +-- 2) tStaff — 职员维度(员工名 / 部门 / 编号) | ||
| 34 | +-- ============================================================ | ||
| 35 | +CREATE TABLE `tStaff` ( | ||
| 36 | + `iIncrement` int NOT NULL AUTO_INCREMENT COMMENT '整数主键 ID(标准列)', | ||
| 37 | + `sId` varchar(100) NULL COMMENT '业务 ID(标准列)', | ||
| 38 | + `sBrandsId` varchar(100) NULL COMMENT '品牌 ID(多租户隔离,标准列)', | ||
| 39 | + `sSubsidiaryId` varchar(100) NULL COMMENT '子公司 ID(组织层级隔离,标准列)', | ||
| 40 | + `tCreateDate` datetime NOT NULL COMMENT '创建时间(标准列)', | ||
| 41 | + `sStaffNo` varchar(50) NULL COMMENT '职员编号;系统内唯一', | ||
| 42 | + `sStaffName` varchar(50) NOT NULL COMMENT '职员姓名', | ||
| 43 | + `sDepartment` varchar(100) NULL DEFAULT NULL COMMENT '所属部门(本期暂用字符串,未来如需独立 tDepartment 字典表再另行重构)', | ||
| 44 | + `sCreatedBy` varchar(50) NULL COMMENT '制单人', | ||
| 45 | + `bDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '软删除标记', | ||
| 46 | + `tDeletedDate` datetime NULL DEFAULT NULL COMMENT '软删除时间', | ||
| 47 | + `sDeletedBy` varchar(50) NULL DEFAULT NULL COMMENT '软删除操作人', | ||
| 48 | + PRIMARY KEY (`iIncrement`) | ||
| 49 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='职员维度(员工名 / 部门 / 编号)'; | ||
| 50 | + | ||
| 51 | +-- ============================================================ | ||
| 52 | +-- 3) tPermissionCategory — 权限分类树 | ||
| 53 | +-- ============================================================ | ||
| 54 | +CREATE TABLE `tPermissionCategory` ( | ||
| 55 | + `iIncrement` int NOT NULL AUTO_INCREMENT COMMENT '整数主键 ID(标准列)', | ||
| 56 | + `sId` varchar(100) NULL COMMENT '业务 ID(标准列)', | ||
| 57 | + `sBrandsId` varchar(100) NULL COMMENT '品牌 ID(多租户隔离,标准列)', | ||
| 58 | + `sSubsidiaryId` varchar(100) NULL COMMENT '子公司 ID(组织层级隔离,标准列)', | ||
| 59 | + `tCreateDate` datetime NOT NULL COMMENT '创建时间(标准列)', | ||
| 60 | + `sCategoryCode` varchar(50) NOT NULL COMMENT '权限分类编码;系统内唯一', | ||
| 61 | + `sCategoryName` varchar(100) NOT NULL COMMENT '权限分类名称(界面展示)', | ||
| 62 | + `iParentId` int NULL DEFAULT NULL COMMENT '父分类 ID(自引用,根节点为 NULL)', | ||
| 63 | + `iSortOrder` int NOT NULL DEFAULT 0 COMMENT '同级排序号', | ||
| 64 | + `sCreatedBy` varchar(50) NULL COMMENT '制单人', | ||
| 65 | + `bDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '软删除标记', | ||
| 66 | + `tDeletedDate` datetime NULL DEFAULT NULL COMMENT '软删除时间', | ||
| 67 | + `sDeletedBy` varchar(50) NULL DEFAULT NULL COMMENT '软删除操作人', | ||
| 68 | + PRIMARY KEY (`iIncrement`) | ||
| 69 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='权限分类树'; | ||
| 70 | + | ||
| 71 | +-- ============================================================ | ||
| 72 | +-- 4) tUserPermission — 用户与权限分类关联 | ||
| 73 | +-- ============================================================ | ||
| 74 | +CREATE TABLE `tUserPermission` ( | ||
| 75 | + `iIncrement` int NOT NULL AUTO_INCREMENT COMMENT '整数主键 ID(标准列)', | ||
| 76 | + `sId` varchar(100) NULL COMMENT '业务 ID(标准列)', | ||
| 77 | + `sBrandsId` varchar(100) NULL COMMENT '品牌 ID(多租户隔离,标准列)', | ||
| 78 | + `sSubsidiaryId` varchar(100) NULL COMMENT '子公司 ID(组织层级隔离,标准列)', | ||
| 79 | + `tCreateDate` datetime NOT NULL COMMENT '创建时间(标准列)', | ||
| 80 | + `iUserId` int NOT NULL COMMENT '关联用户 ID(外键 → tUser.iIncrement)', | ||
| 81 | + `iCategoryId` int NOT NULL COMMENT '关联权限分类 ID(外键 → tPermissionCategory.iIncrement)', | ||
| 82 | + `sCreatedBy` varchar(50) NULL COMMENT '授权操作人', | ||
| 83 | + PRIMARY KEY (`iIncrement`) | ||
| 84 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户与权限分类关联'; | ||
| 85 | + | ||
| 86 | +-- ============================================================ | ||
| 87 | +-- 5) tModule — ERP 业务模块元数据树 | ||
| 88 | +-- ============================================================ | ||
| 89 | +CREATE TABLE `tModule` ( | ||
| 90 | + `iIncrement` int NOT NULL AUTO_INCREMENT COMMENT '整数主键 ID(标准列)', | ||
| 91 | + `sId` varchar(100) NULL COMMENT '业务 ID(标准列)', | ||
| 92 | + `sBrandsId` varchar(100) NULL COMMENT '品牌 ID(多租户隔离,标准列)', | ||
| 93 | + `sSubsidiaryId` varchar(100) NULL COMMENT '子公司 ID(组织层级隔离,标准列)', | ||
| 94 | + `tCreateDate` datetime NOT NULL COMMENT '创建时间(标准列)', | ||
| 95 | + `sDisplayType` varchar(20) NOT NULL DEFAULT '手机端' COMMENT '显示类型;枚举:手机端 / 前端业务 / 系统配置 / 接口', | ||
| 96 | + `sProcedureName` varchar(100) NOT NULL COMMENT '存储过程(审核)名称;系统内唯一', | ||
| 97 | + `sModuleType` varchar(50) NOT NULL COMMENT '模块类型(本期按自由文本处理,VARCHAR(50);如未来收敛到枚举再加 CHECK 约束)', | ||
| 98 | + `sManageDeptEn` varchar(50) NOT NULL COMMENT '管理部门英文标识', | ||
| 99 | + `bShowPermission` bit(1) NOT NULL DEFAULT b'0' COMMENT '权限是否显示;0 否 / 1 是', | ||
| 100 | + `sModuleNameZh` varchar(100) NOT NULL COMMENT '界面名称(中文,模糊查询用)', | ||
| 101 | + `iParentId` int NULL DEFAULT NULL COMMENT '父模块 ID(自引用,根节点为 NULL)', | ||
| 102 | + `iSortOrder` int NOT NULL DEFAULT 0 COMMENT '同级排序号', | ||
| 103 | + `sCreatedBy` varchar(50) NULL COMMENT '制单人', | ||
| 104 | + `bDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '软删除标记', | ||
| 105 | + `tDeletedDate` datetime NULL DEFAULT NULL COMMENT '软删除时间', | ||
| 106 | + `sDeletedBy` varchar(50) NULL DEFAULT NULL COMMENT '软删除操作人', | ||
| 107 | + PRIMARY KEY (`iIncrement`) | ||
| 108 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ERP 业务模块元数据树'; | ||
| 109 | + | ||
| 110 | +-- ============================================================ | ||
| 111 | +-- 索引 | ||
| 112 | +-- ============================================================ | ||
| 113 | +-- tUser | ||
| 114 | +CREATE UNIQUE INDEX `uk_user_no` ON `tUser` (`sUserNo`); | ||
| 115 | +CREATE UNIQUE INDEX `uk_user_name` ON `tUser` (`sUserName`); | ||
| 116 | +CREATE INDEX `idx_staff_id` ON `tUser` (`iStaffId`); | ||
| 117 | +CREATE INDEX `idx_brand_subsidiary` ON `tUser` (`sBrandsId`, `sSubsidiaryId`); | ||
| 118 | +CREATE INDEX `idx_deleted_login` ON `tUser` (`bDeleted`, `tLastLoginDate`); | ||
| 119 | + | ||
| 120 | +-- tStaff | ||
| 121 | +CREATE UNIQUE INDEX `uk_staff_no` ON `tStaff` (`sStaffNo`); | ||
| 122 | +CREATE INDEX `idx_staff_name` ON `tStaff` (`sStaffName`); | ||
| 123 | +CREATE INDEX `idx_department` ON `tStaff` (`sDepartment`); | ||
| 124 | + | ||
| 125 | +-- tPermissionCategory | ||
| 126 | +CREATE UNIQUE INDEX `uk_category_code` ON `tPermissionCategory` (`sCategoryCode`); | ||
| 127 | +CREATE INDEX `idx_parent` ON `tPermissionCategory` (`iParentId`); | ||
| 128 | + | ||
| 129 | +-- tUserPermission | ||
| 130 | +CREATE UNIQUE INDEX `uk_user_category` ON `tUserPermission` (`iUserId`, `iCategoryId`); | ||
| 131 | +CREATE INDEX `idx_category` ON `tUserPermission` (`iCategoryId`); | ||
| 132 | + | ||
| 133 | +-- tModule | ||
| 134 | +CREATE UNIQUE INDEX `uk_procedure_name` ON `tModule` (`sProcedureName`); | ||
| 135 | +CREATE INDEX `idx_module_name_zh` ON `tModule` (`sModuleNameZh`); | ||
| 136 | +CREATE INDEX `idx_parent` ON `tModule` (`iParentId`); | ||
| 137 | +CREATE INDEX `idx_display_type` ON `tModule` (`sDisplayType`); | ||
| 138 | + | ||
| 139 | +-- ============================================================ | ||
| 140 | +-- 外键 | ||
| 141 | +-- ============================================================ | ||
| 142 | +ALTER TABLE `tUser` | ||
| 143 | + ADD CONSTRAINT `fk_user_staff` | ||
| 144 | + FOREIGN KEY (`iStaffId`) REFERENCES `tStaff` (`iIncrement`) | ||
| 145 | + ON DELETE SET NULL ON UPDATE CASCADE; | ||
| 146 | + | ||
| 147 | +ALTER TABLE `tPermissionCategory` | ||
| 148 | + ADD CONSTRAINT `fk_category_parent` | ||
| 149 | + FOREIGN KEY (`iParentId`) REFERENCES `tPermissionCategory` (`iIncrement`) | ||
| 150 | + ON DELETE RESTRICT ON UPDATE CASCADE; | ||
| 151 | + | ||
| 152 | +ALTER TABLE `tUserPermission` | ||
| 153 | + ADD CONSTRAINT `fk_up_user` | ||
| 154 | + FOREIGN KEY (`iUserId`) REFERENCES `tUser` (`iIncrement`) | ||
| 155 | + ON DELETE CASCADE ON UPDATE CASCADE; | ||
| 156 | + | ||
| 157 | +ALTER TABLE `tUserPermission` | ||
| 158 | + ADD CONSTRAINT `fk_up_category` | ||
| 159 | + FOREIGN KEY (`iCategoryId`) REFERENCES `tPermissionCategory` (`iIncrement`) | ||
| 160 | + ON DELETE RESTRICT ON UPDATE CASCADE; | ||
| 161 | + | ||
| 162 | +ALTER TABLE `tModule` | ||
| 163 | + ADD CONSTRAINT `fk_module_parent` | ||
| 164 | + FOREIGN KEY (`iParentId`) REFERENCES `tModule` (`iIncrement`) | ||
| 165 | + ON DELETE RESTRICT ON UPDATE CASCADE; |
src/styles/tokens.css
0 → 100644
| 1 | +++ a/src/styles/tokens.css | ||
| 1 | +/* | ||
| 2 | + * src/styles/tokens.css — Design Tokens | ||
| 3 | + * 命名规范见 docs/04-技术规范.md § 2.5 | ||
| 4 | + * 色值锁定见 docs/06-UI交互规范.md § 四 | ||
| 5 | + * | ||
| 6 | + * 命名格式:--color-<scope>-<role>-<state> | ||
| 7 | + * <scope> 组件域:form / table-row / table-header / ... | ||
| 8 | + * <role> 作用:bg(背景)/ fg(前景/字体)/ border | ||
| 9 | + * <state> 状态:edit / readonly / hover / selected(无状态时省略) | ||
| 10 | + * | ||
| 11 | + * 约束: | ||
| 12 | + * - 组件样式中只用 var(--color-xxx),禁止硬编码 hex / rgba | ||
| 13 | + * - 修改色值只改本文件,不允许在组件级覆盖 | ||
| 14 | + * - 新增 token 须先登记到 docs/06 § 4.1 / 4.2,再补到此处 | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +:root { | ||
| 18 | + /* === 1. 全局调色板(与 Ant Design 主题对齐) === */ | ||
| 19 | + --color-primary: #1890ff; | ||
| 20 | + --color-success: #52c41a; | ||
| 21 | + --color-warning: #faad14; | ||
| 22 | + --color-error: #ff4d4f; | ||
| 23 | + --color-text: rgba(0, 0, 0, 0.85); | ||
| 24 | + --color-text-secondary: rgba(0, 0, 0, 0.45); | ||
| 25 | + --color-border: #d9d9d9; | ||
| 26 | + --color-bg-base: #f0f2f5; | ||
| 27 | + | ||
| 28 | + /* === 2. 组件级状态色(与 docs/06 § 4.2 一一对应) === */ | ||
| 29 | + | ||
| 30 | + /* form:输入框 / 备注框 / 时间框 / 下拉框共用 */ | ||
| 31 | + --color-form-bg-edit: #ffffff; | ||
| 32 | + --color-form-bg-readonly: #f1f2f8; | ||
| 33 | + --color-form-bg-hover: #f5f5f5; /* 仅下拉框使用 */ | ||
| 34 | + --color-form-fg: #000000; | ||
| 35 | + | ||
| 36 | + /* table */ | ||
| 37 | + --color-table-row-bg-selected: #86d5fb; | ||
| 38 | + --color-table-row-bg-hover: #fff7e6; | ||
| 39 | + --color-table-row-bg-readonly: #f1f2f8; /* = rgb(241, 242, 248) */ | ||
| 40 | + --color-table-row-fg: #000000; | ||
| 41 | + --color-table-header-bg: #f5f5f5; | ||
| 42 | + --color-table-header-fg: rgba(0, 0, 0, 0.85); /* = #000000D9 */ | ||
| 43 | +} |