Files
ERP/go.md
T

33 KiB
Raw Blame History

凯迪 ERP+OA 一体化平台 —— 完整交接文档(go.md)

本文件是给"没有任何项目记忆"的新工具/新模型看的自包含交接手册。 读完它,你应该能:把项目跑起来、看懂架构、知道做了什么/没做什么、并能继续往下推进。 最后更新:2026-06-15 晚(W7 已部署上线,W8 半成品在源码树未集成)。

💾 备份与恢复点(2026-06-15 建立,别人改崩了照这里恢复)

当前 W7 稳定态做了双保险

① 全量文件夹备份Backup-ERP-20260615-191517/(本项目目录下,2.6GB

  • 含:后端 734 控制器源码 + 前端全部源码 + 数据库 oa-backend/data/oa.db + 可执行 jar + JDK + 5 个 go 文档。
  • 唯一排除:node_modules(可再生,恢复跑 npm install)。详见该夹内 _BACKUP-README.md
  • 恢复:把该文件夹内容拷回覆盖,cd app && npm install,即可。

② Git 恢复点(本项目已 git init):

  • 提交:5e51dc3(完整 5e51dc3f566c85100f2bff3cfc5100df49eeecd2
  • Tagsnapshot-W7-20260615-191517
  • 内容:10584 文件(后端源码 + 前端源码 + oa.db 数据快照 + go 文档 + .claude 脚本);gitignore 掉了 node_modules/build/.jdks/备份夹/弃用的 OFBiz 核心。
  • 恢复命令(改崩后回到此稳定态):
    cd /Users/qiu/Desktop/ERP
    git stash            # 或 git reset --hard 丢弃当前改动
    git checkout snapshot-W7-20260615-191517   # 或 git reset --hard 5e51dc3
    
  • 注意:建 Git 点时移除了 ofbiz-framework 内弃用 OFBiz 上游的两个嵌套 .git(它是死代码、且文件夹备份保留了文件),以便外层 git 能纳入 modern-ui 前端源码。

以后每次改完一批、确认能跑,就再建一个 Git 点git add -A && git add -f oa-backend/data/oa.db && git commit -m "SNAPSHOT <说明>",并打 tag git tag snapshot-<日期>

🌿 分支策略(main 受保护,dev 干活)

  • main = 受保护的稳定线:只存验证过能跑的快照,禁止直接在上面改/提交(有 git hook 拦截)。每个稳定里程碑打 tag。
  • dev = 开发线:接手的人/AI 所有改动都在这里做。改崩了只崩 dev,main 毫发无损。
  • 合并门槛dev 必须「./gradlew compileJava + npm run build + 起服务 + 冒烟登录」全过,才合并回 main:
    git checkout main && git merge dev && git tag snapshot-<日期> && git checkout dev
    
  • 保护机制.githooks/pre-commit(已 git config core.hooksPath .githooks 激活)会拒绝在 main 上提交,提示切 dev。确需向 main 落稳定快照用 git commit --no-verify

    新环境克隆/拷贝后需重新激活一次:git config core.hooksPath .githooks

  • 崩了恢复git checkout mainmain 永远是上一个稳定态)或 git reset --hard snapshot-W7-20260615-191517 或拷回备份夹。
  • 当前所在分支:接手时应在 dev 上工作(git checkout dev)。

📚 配套参考文件(和本文件同目录,务必一起读)

go.md 是主文档(架构+约定+方法论+坑)。还有 4 个从代码精确抽取的细节参考:

  • go-code-reference.md —— 核心基础类逐字 + 一套端到端完整范例(实体/仓库/控制器/Seeder/Vue 页全文)。新建功能照这个抄。
  • go-endpoints.md —— 完整端点目录:734 个控制器 × 5535 个端点(基路径+每个 HTTP 方法+子路径)。查"某功能有没有/在哪个端点"看这个。
  • go-entities.md —— 完整数据模型目录:711 个实体 × 物理表名 + 全字段类型。查数据模型看这个。
  • go-database.md —— 数据库 702 张表的行数(218 有数据 / 484 空)。空表=功能在但缺演示数据,补 Seeder 即可。

0. 一句话项目是什么

把致远 OA 复刻、并扩建成 凯迪科技的 ERP+OA 一体化平台。覆盖 29 个机构/部门,后端约 734 个 REST 控制器、711 个 JPA 实体,前端约 700 个 Vue 页面。权威需求是甲方给的 凯迪科技ERP_20260507.xlsx(已切成 requirements/_req_slices/01-29_*.txt)。当前对该 xlsx 的功能合规率(sonnet 审计口径)MET 73.3%

  • 单人开发、单机部署、演示用。不是真生产系统。
  • 后端语言 JavaSpring Boot),前端 Vue3,数据库 SQLite(单文件)。
  • 旧的 OFBiz 框架(ofbiz-framework/已弃用,只有它下面的 plugins/modern-ui/app/ 这个 Vite 前端在用。
  • oa-backend/ 是当前唯一在用的后端。

1. 技术栈(精确版本)

技术 版本/位置
后端框架 Spring Boot 3.2.5oa-backend/build.gradle
持久化 Spring Data JPA + Hibernate Hibernate 6Spring Boot BOM 管理)
数据库 SQLite org.xerial:sqlite-jdbc:3.45.3.0,方言 org.hibernate.community.dialect.SQLiteDialecthibernate-community-dialects
数据库文件 oa-backend/data/oa.db 单文件,约 6.6MB含全部演示数据,别删
JDK OpenJDK 17.0.19+10 /Users/qiu/Desktop/ERP/.jdks/jdk-17.0.19+10/Contents/Home(项目自带,必须用这个)
构建工具 Gradlewrapper oa-backend/gradlew
前端框架 Vue 3 + TypeScript ofbiz-framework/plugins/modern-ui/app/
前端构建 Vite npm run build = vue-tsc --noEmit && vite build
UI 库 Element Plus 图标用 @element-plus/icons-vue
隧道 ngrok 固定域名 resonant-elated-launder.ngrok-free.dev → 8091

2. 仓库结构(关键路径)

/Users/qiu/Desktop/ERP/
├── go.md                         ← 本文件
├── .jdks/jdk-17.0.19+10/...      ← 必用的 JDK17
├── oa-backend/                   ← 后端(当前唯一在用)
│   ├── build.gradle              ← group=com.kaidi version=0.1.0
│   ├── data/oa.db                ← SQLite 数据库(含演示数据!)
│   ├── build/libs/oa-backend-0.1.0.jar  ← 构建产物(约 77MB,含打包的前端静态)
│   └── src/main/
│       ├── resources/
│       │   ├── application.yml    ← 配置(端口/数据源/ddl-auto)
│       │   └── static/            ← 前端构建产物落地处(vite 直写到这里)
│       └── java/com/kaidi/oa/
│           ├── OaBackendApplication.java   ← 启动类
│           ├── common/            ← ApiResp / Money / MoneyParser 等基础类
│           ├── config/            ← AuthInterceptor / CorsConfig 等
│           ├── domain/            ← 711 个 JPA 实体(@Entity
│           ├── repository/        ← Spring Data 仓库接口
│           ├── web/               ← 734 个 @RestController
│           ├── service/           ← TriggerRuleEngine / WorkflowService 等
│           └── seed/              ← 40 个 DataSeederCommandLineRunner,启动播种演示数据)
├── ofbiz-framework/plugins/modern-ui/app/   ← 前端(唯一在用)
│   ├── package.json              ← scripts.build
│   ├── vite.config.ts            ← outDir 指向后端 static(见下)
│   └── src/
│       ├── data/oaModules.ts     ← 【单一数据源】导航 + 路由自动发现
│       └── oa/
│           ├── api/http.ts        ← HTTP 客户端(封装 fetch)
│           └── pages/<module>/<key>.vue  ← 所有业务页面
├── requirements/                 ← 需求与审计产物
│   ├── Request.MD                ← 甲方原始需求分析
│   ├── _req_slices/01-29_*.txt   ← xlsx 切成的 29 份机构需求(审计/建设 agent 读这个)
│   ├── _xlsx_audit_w7post.json   ← 最近一次审计后的"可建缺口"清单
│   └── XLSX-COMPLIANCE-AUDIT.md  ← 合规审计报告
└── .claude/wf-*.js               ← 多代理建设/审计 workflow 脚本(W3~W8 + audit-lean

3. 【最重要】如何启动运行

3.1 端口说明(坑)

application.yml 里默认端口是 8090,但 8090 被本机的 Qhost PHP 占了,所以我们一律用命令行参数 --server.port=8091 覆盖,实际跑在 8091。后端 API 前缀是 /api/oa/*

3.2 启动后端(最快:直接跑现成 jar)

现成 jar 就是 W7 构建产物,能直接跑,不用重新构建:

cd /Users/qiu/Desktop/ERP/oa-backend
export JAVA_HOME=/Users/qiu/Desktop/ERP/.jdks/jdk-17.0.19+10/Contents/Home
"$JAVA_HOME/bin/java" -jar build/libs/oa-backend-0.1.0.jar --server.port=8091
# 后台跑则用 nohup ... > /tmp/oa8091.log 2>&1 &

启动约 15 秒,看到日志 Started OaBackendApplication in ~15s 即就绪。

只杀 8091 自己的进程,绝不 pkill/killall java(机器上可能有别的 java):

kill $(lsof -nP -iTCP:8091 -sTCP:LISTEN -t)   # 优雅停
# 不行再 kill -9 $(lsof -nP -iTCP:8091 -sTCP:LISTEN -t)

3.3 重新构建后端(改了 Java 代码后)

cd /Users/qiu/Desktop/ERP/oa-backend
export JAVA_HOME=/Users/qiu/Desktop/ERP/.jdks/jdk-17.0.19+10/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
./gradlew compileJava -q     # 只检查编译错(快)
./gradlew bootJar -q         # 出可执行 jar(约 30s,会把 static/ 一起打进 jar

注意:bootJar 会把 src/main/resources/static/(前端产物)打进 jar。所以改了前端要先 npm run build(写入 static),再 bootJar,否则 jar 里是旧前端。

3.4 构建前端(改了 Vue 代码后)

cd /Users/qiu/Desktop/ERP/ofbiz-framework/plugins/modern-ui/app
export NODE_OPTIONS="--max-old-space-size=8192"   # 项目大了,不加会 OOM
npm run build     # = vue-tsc --noEmit && vite build;产物直接写到后端 static/

vite 的 outDir 直接指向 ../../../../oa-backend/src/main/resources/staticemptyOutDir:true——所以构建会清空并重写后端 static,没有独立的 dist 目录,别手动 rm static

3.5 完整发布流程(前后端都改了)

1. cd app && NODE_OPTIONS=--max-old-space-size=8192 npm run build   # 前端→后端static
2. cd oa-backend && ./gradlew bootJar -q                            # 打包(含新static)
3. kill $(lsof -nP -iTCP:8091 -sTCP:LISTEN -t)                      # 停旧
4. java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091     # 起新
5. 冒烟:curl 登录 + 抽测端点(见 3.7)

3.6 前端开发热更新(可选,调样式时方便)

cd app && npm run dev     # vite dev server 在 :5175,代理到后端
# 访问 http://localhost:5175

演示链接走的是后端 static(8091),不是 5175

3.7 登录账号 + 冒烟测试

  • 账号:admin / 123456
  • 登录接口:
TOK=$(curl -s -X POST http://127.0.0.1:8091/api/oa/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"loginName":"admin","password":"123456"}' \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['token'])")
# 带 token 调业务接口:
curl http://127.0.0.1:8091/api/oa/dev-projects -H "Authorization: Bearer $TOK"

3.8 演示链接(ngrok 公网)

ngrok 已在跑,把公网域名映射到本机 8091:

# 若隧道断了,重启:
ngrok http --url=resonant-elated-launder.ngrok-free.dev 8091

公网地址:https://resonant-elated-launder.ngrok-free.dev/

  • 前端是 SPA,由后端 static 提供,访问根路径即出登录页。
  • 调 API 时前端会自动带 header ngrok-skip-browser-warning(绕过 ngrok 警告页)。

4. 架构与核心约定(改代码前必读)

4.1 后端响应包装 ApiRespcommon/ApiResp.java22 行)

是 record,不是普通类

public record ApiResp<T>(int code, String message, T data) {
    public static <T> ApiResp<T> ok(T data) { return new ApiResp<>(0, "ok", data); }
}
  • 取数据用访问器 .data() 不是 .getData()record 语法)。
  • code=0 表示成功;异常由 GlobalExceptionHandler 统一包装(400/404/409 等带中文 message)。

4.2 金额一律 BigDecimal + Money 工具(common/Money.java81 行)

  • 所有"钱"字段用 BigDecimal禁止裸 double 算钱
  • Money 提供 of/add/sub/nz/gt/lte0/ZEROscale=2、HALF_UP。nz(x) 把 null 当 0注意有的重载要 2 个参数)。
  • MoneyParser.parse(String)common/MoneyParser.java):解析"万"→×10000,保留科学计数 e/E。
  • 科学量/数量(不是钱)可以用 double。

4.3 鉴权 AuthInterceptorconfig/AuthInterceptor.java535 行)—— default-deny 分级

这是安全核心,新加控制器默认就受保护(默认分支要求 ADMIN/APPROVER)。几个白/黑名单:

  • ADMIN_PREFIXES:仅 ADMIN 可访问的前缀。
  • FINANCE_PREFIXES:财务相关,写需 ADMIN/APPROVER。
  • SENSITIVE_READ_PREFIXES敏感读前缀(读也要 ADMIN/APPROVER)。新建任何返回金额/PII/机密聚合的读端点,必须把它的 /api/oa/xxx 前缀加进这个 List,否则普通 USER 能读到敏感数据(虽然 default-deny 会兜底,但显式登记才规范)。
  • SELF_SERVICE_WRITE_PREFIXES:个人/协作类(favorites/schedule/blogs/files/messages/form-instances 等)任意已登录角色可写,靠控制器内对象级属主校验。
  • 路径规范化防绕过;停用用户即失效;登录限流;WS 握手鉴权。

集成新一批控制器时,用脚本把新端点的敏感读前缀合并进 SENSITIVE_READ_PREFIXES(见第 8 节"集成六步")。

4.4 前端导航+路由:oaModules.ts单一数据源

src/data/oaModules.ts 里每个模块 { id, label, icon, path, children:[{key,label,kind,path}] }

  • 路由由 import.meta.glob 自动发现:页面文件放 pages/<module>/<key>.vuenav 里写对应 {key, path:'/<module>/<key>'} 就能访问,不用改 router
  • kind 只能是这几个枚举之一:list | form | detail | portal | tree | calendar | board | settings | report没有 'page'(写了 kind:'page' 会 vue-tsc 报错,常见坑)。
  • 29 个模块 idorgview collab appdev report goal meeting knowledge doccollab culture hr masterdata contract payment sealcenter archive intel crm bidding ehs rd budget datacenter ops mfg finance lab design supervision audit itasset admin legal mobile

4.5 前端 HTTP 客户端:http.tssrc/oa/api/http.ts

http.get<T>(path, query?, opts?)   // 第2参直接是 query 对象,不是 {params}
http.post<T>(path, body?, opts?)   // opts.query 放查询参数(不是 params
http.put / http.patch / http.del   // 删除是 http.del() 不是 http.delete()
  • 自动解包 ApiResp.datatoken 存 localStorage 键 oa.token,自动带 Authorization: Bearerngrok-skip-browser-warning

4.6 审批工作流:WorkflowService2091 行)

  • submitadvance 推进;合法动作:同意 / 办结 / 退回 / 转交 / 加签没有"通过")。
  • FormInstance 生命周期:submit → advance → 办结时触发 TriggerRuleEngine.fire()
  • 支持并行审批、组织级分派、多办理人、AND/OR 条件。

4.7 联动规则引擎:TriggerRuleEngine771 行)

  • fire(FormInstance, Instant)7 条硬编码业务链 + 可配置规则 applyConfiguredRulesRuleConfig 表)。
  • 幂等:靠 AutomationLog(instanceId, ruleKey) 唯一约束 + existsByInstanceIdAndRuleKey同一实例多条规则要用不同 ruleKey,失败态也要专属 ruleKey 不撞键。

4.8 设置键值表 OaSetting(通用持久化)

/api/oa/settings/{key} 是一个键值表,让任意 mock 列表/配置页零后端改动就能持久化。很多"草稿/凭证 JSON"暂存这里。


5. 29 机构/业务域清单

按 xlsx 的机构划分(requirements/_req_slices/ 里一一对应):

  1. 创新研发中心:产品开发部、申报服务部、知识产权部、实验室
  2. 设计研究中心:咨询可研院、规划设计部、工程监理部
  3. 制造管理中心:环保设备制造中心、生物质肥料制造中心
  4. 工程管理中心:质安部、资料室、专利工法办、成本控制部
  5. 运营管理中心:城镇污水运营中心、工业废水运营中心
  6. 市场部:经营部、办事处、分公司
  7. 支付中心:结算中心、财务部、金融办
  8. 内控部:法务风险部、审计监察部
  9. 品牌推广部:宣传部、项目文化
  10. 行政:综合部、信息部、办公室、后勤

外加平台级模块:组织视图、协作、审批流、报表、目标、会议、知识库、文档协作、印章中心、档案、商业智能、CRM、招投标、合同、主数据、移动端等(见 4.4 的 33 个 module id)。


6. 数据 / 账号 / 种子

  • 数据库:oa-backend/data/oa.dbSQLite含全部演示数据)。data/ 下有多个 .bak* 备份。
  • ddl-auto: update:启动时 Hibernate 自动建/补表。注意大坑SQLite 的 update 模式不可靠地给既有表 ALTER 加列(见第 9 节)。
  • 启动时跑 40 个 seed/*Seeder.java@Order(n) 排序的 CommandLineRunner),播种演示数据。多数 Seeder 有 if (repo.count()>0) return 去重守卫。
  • 账号:admin / 123456(密码已哈希存储,PasswordUtil)。

7. 已完成的工作(做了什么)

7.1 演进历程(控制器数量)

90 → P0-P3 修复+从0建5域 → W122机构深水)→ W2 → W3(553) → W4(623) → W5(668) → W6(695) → W7(717,已部署) → W8(734,半成品未集成)。

7.2 合规率轨迹(对 xlsx,sonnet 审计口径)

MET1.4% → 16.5% → 39.6% → 58.6% → 66.0% → 74.4% → 73.3%W7 后全量审计)

  • LOGIC_GAP=0(无"有页面缺关键逻辑")、MISSING≈0(几乎每个模块都有实现)。
  • 累计补完约 436 个功能缺口W3~W7156+99+76+51+54)。
  • 审计有判定方差:±10 MET 属正常(29 个独立 sonnet agent 每轮主观判 MET/PARTIAL),所以 74.4→73.3 不是真回归。真实 MET 在 70~75%

7.3 安全加固(5 轮红队 + 5 轮复检,已收敛到连续零可利用)

default-deny 分级鉴权、审批对象级鉴权(办理人+自批闸)、文件删除属主校验、停用即失效、WS 鉴权、登录限流/枚举/计时防护、SQLite 约束→409、批量赋值收口、PII 脱敏、分页上限、调度事务、金额 BigDecimal。详见记忆 oa-security-hardening.md

7.4 贯穿能力

可配置规则引擎、统一审计日志(SHA256 哈希链防篡改)、外部对接框架(占位)、金额精度、移动端响应式(整站手机自适应)。

7.5 产出文档

  • requirements/XLSX-COMPLIANCE-AUDIT.mdOA-PARITY-AUDIT.md:审计报告
  • 《凯迪ERP系统使用教程》PDF、《技术文档》PDF(早期产出)
  • oa-backend/API.mdoa-backend/SECURITY.md

8. 怎么继续往下推进(多代理建设方法论)

整个项目是用 Claude Code 的 Workflow(多代理并行) 推进的。方法论是一个闭环:审计 → 分类 → 建设(W轮) → 集成 → 再审。脚本都在 .claude/wf-*.js

8.1 审计(量化现状 + 找缺口)

.claude/wf-audit-lean.js29 个 sonnet agent 并行,每个读一份 _req_slices/NN_*.txt,grep/读码/活体取证,对每个功能模块判 MET/PARTIAL/LOGIC_GAP/MISSING + 证据 + 缺口 + severity。输出汇总 counts + gaps 数组。

用 sonnet(不是 Opus),省钱够用。

8.2 分类(可建 vs 需外部)

拿审计 gaps,按关键词把 PARTIAL 分成:

  • 可建(内部逻辑:状态机/聚合 API/联动/调度/自动取数/补种子数据/小 bug)
  • 需外部(真 CA 电子签、BIM、CAD/Revit、AI-LLM、IoT/SCADA、APNs/FCM 推送、企查查/incoPat 等商用/政府 API)——这些没有外部系统到不了 MET,是硬天花板

8.3 建设(一轮 W 攻可建缺口)

.claude/wf-w8.js(最新模板):29 agent 并行,每个 agent

  1. python 过滤出本机构的可建缺口
  2. 读需求切片对照
  3. grep/读码确认现状,新建 实体+仓库+控制器(+服务)+真实 Vue 页,把 PARTIAL 推向 MET
  4. 跳过需外部系统的(放 skippedExternal
  5. 返回结构化结果:backendFiles/frontendFiles/authSensitiveRead/navEntries/sharedFileSnippets/closedGaps/skippedExternal/summary

纪律:只新建文件或改自己机构的页,不自改共享文件AuthInterceptor/oaModules/TriggerRuleEngine/WorkflowService)——把改动放 sharedFileSnippets 回传,集成时统一合。

8.4 集成六步(W 轮跑完后,主循环做)

  1. 解析输出 → 收集 authSensitiveRead + navEntriesidempotent 合并AuthInterceptor.SENSITIVE_READ_PREFIXESoaModules.ts 各模块 children(只加 key 不存在的)。
  2. ./gradlew compileJava 修后端编译错(常见坑见第 9 节)。
  3. vue-tsc 修前端类型错 + 跑 slot-in-div 扫描器(见 9.7)。
  4. NODE_OPTIONS=--max-old-space-size=8192 npm run buildvite 直写 static)。
  5. schema-sync(关键,防 ddl-update 漏列):见 9.6。
  6. bootJar → 只杀 8091 LISTEN pid → 起新 jar → 冒烟。然后回到 8.1 再审。

8.5 卡死处置

Workflow 偶尔会静默卡死(jsonl 长时间不写入、不发完成通知)。判定:agent 的 .jsonl 文件 >150s 无写入。处置:直接 Workflow({scriptPath, resumeFromRunId}) 重跑——已完成的 agent 走缓存秒回,卡住的重跑。


9. 血泪坑大全(踩过的所有坑,照着避

9.1 Map.of() 最多 10 对键值

超过 10 对(20 参)编译失败"找不到合适的方法"。多字段 Map 一律 new java.util.LinkedHashMap<>().put()

9.2 SQLite 保留字不能做列名

references/primary/order/group/key/values/index/table 等做字段名 → 建表 DDL 崩 near "xxx": syntax error。Java 字段名可留(它们不是 Java 关键字),但必须加 @Column(name="安全别名") 映射物理列(并 import jakarta.persistence.Column)。

9.3 禁用 ContainingIgnoreCase 派生查询

Hibernate6 社区 SQLite 方言对 upper() 类型校验失败,启动即挂。一律用 ContainingSQLite 的 LIKE 对 ASCII 本就大小写不敏感,中文无大小写,功能等价)。

9.4 派生查询属性名必须和实体字段精确一致

findByProjectCodeContaining 但实体字段叫 projectCodes(复数)→ Spring 启动期才报 No property 'projectCode'。编译查不出,只有启动才暴露,逐个修。别自写 scanner 校验Or/AndorderId/orgUnit/workOrderId 等字段的子串,Spring 用最长匹配不会拆,自写扫描全是假阳性)——靠 Spring 启动校验权威。

9.5 Java 字符串里禁止未转义的中文/全角引号嵌套

"秉承"诚信"的价值观""针对"%s"" 这种内嵌 " 会断字符串 → 编译错。内层引号换成 「」这是 sonnet 量产代码最高频的编译坑,集成时 grep 一遍。

9.6 SQLite ddl-auto=update 不可靠地给既有表加列 →【schema-sync】

已存在的实体加字段后,update 模式经常没把列 ALTER 进既有表(boolean 列尤其) → 该实体 SELECT 找不到列、全 500、级联拖垮依赖它的端点。新建的表 CREATE 没问题,只有 ALTER 既有表不可靠。

权威修法(schema-sync,已成集成标准步骤)

# 1. 让 Hibernate 导出全量权威 CREATE DDL(临时端口,不动库)
java -jar build/libs/oa-backend-0.1.0.jar --server.port=8099 \
  --spring.jpa.hibernate.ddl-auto=none \
  --spring.jpa.properties.jakarta.persistence.schema-generation.scripts.action=create \
  --spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-target=/tmp/schema.sql
# 2. python 解析 schema.sql 每表列名(权威,别自写 snake_case 推导)diff 活库 PRAGMA table_info
#    对既有表缺的列 ALTER TABLE ADD COLUMNNOT NULL 列要带 default 0 或 '',否则 SQLite 拒绝加)

diff/ALTER 的 python 逻辑见 .claude 历史脚本或本文件第 10 节。手动 ALTER 时 boolean 关键字没问题(是 Hibernate update 模式自己不可靠,不是关键字问题)。

9.7 Vue:具名插槽 <template #footer/#header/#default> 必须是组件直接子节点

绝不能嵌在 <div> 等 HTML 元素里,否则 vite 的 vue 编译器崩 Cannot read properties of undefined (reading 'type')整个 build 失败且只报一个文件。dialog 里按状态切 footer 时,footer 提到 <el-dialog> 直属用 <template v-if> 切换,别放进 <div v-if>

集成时跑这个扫描器揪出来(标签栈定位具名插槽的直接父级是否 HTML 元素):见第 10 节脚本。

9.8 Vue 其它坑

  • v-model 必须是可写成员表达式,绝不 v-model="!!expr"v-model="fn()"vite 崩 "must be a valid JavaScript member expression");要派生可见性用 :model-value="!!x" @update:model-value="(v:boolean)=>{ if(!v) x=null }"
  • el-table-column 插槽写 #default="{ row }"#default="{ row }: { row: any }"绝不 typeof X[0] / typeof X.value[0] 注解vue-tsc 过但 vite/类型崩),绝不套外层括号。
  • 属性值里禁止内嵌双引号:title="...勾选"强制扣减"放行..." → vite 崩 "Attribute name cannot contain U+0022",换 「」
  • 模板里 Promise / window 不在 Vue 全局白名单,要用就在 <script setup> 定义函数包装再调(如 const openUrl = (u:string)=>window.open(u,'_blank'))。
  • 图标必须真存在于 @element-plus/icons-vueMobilePhone 不存在 → 用 Iphone;不确定就用 Document/List/Money/DataLine)。
  • http 删除用 http.del() 不是 http.delete()query 参数直接传对象,不是 {params:{...}}
  • kind: 'page' 非法(见 4.4)。
  • query 变量声明 Record<string, any> 别用 unknown(传 http.get 会类型错)。

9.9 构建/运行其它坑

  • 前端项目大了,npm run build 不加 NODE_OPTIONS=--max-old-space-size=8192OOM"Ineffective mark-compacts near heap limit")。
  • vite outDir 直写后端 static + emptyOutDir别手动 rm static(删了得重新 npm run build 重生)。
  • 改前端后必须 npm run buildbootJar,否则 jar 里是旧前端。
  • 子资源控制器:@RequestMapping("/api/oa/xxx") 的 base 路径没有 GET 时,GET /api/oa/xxx 返 404 是正常的(它的 GET 在子路径如 /events /summary)——别据此判 MISSING。
  • Workflow 脚本的 COMMON 模板串(反引号字符串)里绝不能用反引号包内联代码(会提前闭合模板串解析失败),用 「」
  • Workflow agent 用便宜模型(sonnet),别默认继承 Opus(撞 token 限的教训)。
  • 多 agent 因 area 过滤宽松可能抢做同一跨单元缺口产生重复 Seeder/页面(W7 踩过)——给 agent 加"只做本机构、过滤空就返空、不替邻居做"的强约束。

10. 还没做的 / 未完成(重点

10.1 W8 半成品(源码树里,未集成

W8(末轮内部推进,攻 40 个 build+seed/bug 缺口)跑到一半静默卡死,已落盘约 17 个新控制器 + 14 个新实体到源码树。当前状态

  • 后端源码能编译734 控制器/711 实体,compileJava 0 错)——W8 新文件是自包含的。
  • 但 W8 没集成:① 它的新端点没合进 AuthInterceptor.SENSITIVE_READ_PREFIXES;② 导航没合进 oaModules.ts;③ 前端没 vue-tsc 检查/没 build;④ schema-sync 没跑(若 W8 给既有实体加了字段,会有 9.6 的漏列问题)。
  • 运行中的 jarbuild/libs/oa-backend-0.1.0.jar,时间戳 06-15 17:40)是 W7,不含 W8。

怎么处理 W8(二选一):

  • (A) 完成 W8 集成Workflow({scriptPath:".claude/wf-w8.js", resumeFromRunId:"wf_2c4b1474-f9c"}) 把卡死的补完 → 然后走第 8.4 集成六步。
  • (B) 放弃 W8、回到干净 W7:用 git 或手动把 W8 新增文件移走,或直接基于现有 W7 jar 运行(W8 文件留着不集成也不影响 W7 jar 运行)。

10.2 剩余缺口(W7 审计后 75 个 PARTIAL

分类:约 34 可建 + 6 种子/小bug + 35 需外部硬天花板

  • 可建(W8 在攻的):补字段(minStock/maxStock、MeetingMinute.supervisionProjectId)、接审批流(合同变更走 TriggerRuleEngine)、补种子数据(BomVarianceDetail/rd-eboms/HrLaborContract 表空)、建前端页(看板/拓扑/报表汇总)、修小 bugQhseCrossDeptSyncController 活体 404 路径不通、EVM 端点 path-variable vs query-param 不一致、/alerts/price-alerts)。详单见 requirements/_xlsx_audit_w7post.json

  • 需外部硬天花板(永远到不了 MET,除非买/接外部系统)

    • 真 CA 电子签 / 数字证书 / 可信时间戳(《电子签名法》级)
    • BIM 碰撞检测 / Navisworks / Revit
    • CAD/AutoCAD/SketchUp 文件解析、在线协同编辑(OnlyOffice/WOPI/CRDT/WebSocket
    • AI-LLM 能力(AI 审合同条款、知识图谱/语义检索)——需真 LLM 接入
    • IoT/SCADA/PLC 实时采集、加药泵/地磅/流量计联动、远程运维
    • APNs/FCM/极光/短信/企业微信 推送网关
    • OCR/ES/Lucene 全文检索引擎、PDF/A 转换、服务端水印
    • 商用/政府 API:企查查/天眼查/征信、裁判文书网、北大法宝、incoPat/智慧芽、国知局、国发平台/省级在线监测、LIMS、银企直连、税务/社保局
    • 条码/二维码/RFID/GPS 等硬件集成

    这些缺口已经是"框架就绪 PARTIAL":实体/控制器/前端入口都建好了,只差真实外部系统的对接。内部再写多少代码都到不了 MET,这是数学上的上限,不是没干。

10.3 一句话现实

内部可建的已基本榨干(MET ~73%)。要继续往 80%+ 推,就把 10.2 的"可建+种子/bug"约 40 个做完(即完成 W8)。90%+ 需要采购/对接外部系统,纯写代码到不了。


11. 关键文件速查表

要找什么 文件
后端启动类 oa-backend/src/main/java/com/kaidi/oa/OaBackendApplication.java
后端配置(端口/库/ddl oa-backend/src/main/resources/application.yml
响应包装 common/ApiResp.java
金额工具 common/Money.javacommon/MoneyParser.java
鉴权(安全核心) config/AuthInterceptor.java535 行)
审批引擎 service/WorkflowService.java2091 行)
联动规则引擎 service/TriggerRuleEngine.java771 行)
登录 web/AuthController.java
所有控制器 web/*.java734 个)
所有实体 domain/*.java711 个)
演示数据播种 seed/*Seeder.java40 个)
前端导航+路由源 app/src/data/oaModules.ts
前端 HTTP 客户端 app/src/oa/api/http.ts
前端页面 app/src/oa/pages/<module>/<key>.vue
vite 配置(outDir app/vite.config.ts
需求切片(审计/建设读) requirements/_req_slices/01-29_*.txt
最近审计可建缺口 requirements/_xlsx_audit_w7post.json
多代理建设脚本 .claude/wf-w8.js(最新建设模板)、.claude/wf-audit-lean.js(审计)

12. 给接手者的 TL;DR

  1. 跑起来
    cd /Users/qiu/Desktop/ERP/oa-backend
    export JAVA_HOME=/Users/qiu/Desktop/ERP/.jdks/jdk-17.0.19+10/Contents/Home
    "$JAVA_HOME/bin/java" -jar build/libs/oa-backend-0.1.0.jar --server.port=8091
    
    浏览器开 http://127.0.0.1:8091(或 ngrok 公网域名 https://resonant-elated-launder.ngrok-free.dev/),登录 admin/123456
  2. 现状W7 已部署,734 控制器/711 实体,对 xlsx 合规 MET ~73%,安全已加固,0 LOGIC_GAP/MISSING。
  3. 想继续:审计 → 分类 → 建设(W轮) → 集成六步 → 再审(第 8 节)。下一步具体就是完成 W8resume wf_2c4b1474-f9c 或重跑 .claude/wf-w8.js)把那 40 个可建/种子/bug 做完,能到 ~80%。
  4. 改代码必看第 9 节坑表(尤其 Map.of≤10、SQLite 保留字/漏列 schema-sync、中文引号嵌套、Vue 插槽嵌 div、kind 没有 'page')。
  5. 到不了 100%:剩 ~35 个缺口需真 CA/BIM/CAD/AI-LLM/IoT/商用 API 等外部系统,纯写代码无解,已是框架就绪 PARTIAL。
  6. 铁律:绝不用 emoji(图标走 @element-plus/icons-vue)、绝不用渐变色(纯色蓝 #2f6fed)、只杀 8091 自己的 pid 绝不 pkill java、金额一律 BigDecimal、新控制器记得登记进 AuthInterceptor 敏感读。