Files
ERP/.claude/wf-w5.js
QiufengandClaude Opus 4.8 5e51dc3f56 SNAPSHOT W7 已部署稳定态 — 凯迪ERP+OA一体化平台 (MET 73.3%)
恢复点(restore point)。别人改崩后可 git reset --hard 回到此提交。

== 此快照内容 ==
- 后端 oa-backend: 734 控制器 / 711 实体 (Spring Boot 3.2.5 + SQLite, 端口8091)
- 前端 modern-ui/app: Vue3+Vite, 约700页 (构建产物已在 oa-backend/src/main/resources/static)
- 数据库 oa-backend/data/oa.db: 含全部演示数据 (强制入库, 6.6MB)
- 交接文档 go.md + go-code-reference/endpoints/entities/database.md
- 多代理建设脚本 .claude/wf-*.js

== 状态 ==
- 对 凯迪科技ERP_20260507.xlsx 合规 MET ~73.3% (PARTIAL 75: 34可建+6种子/bug+35外部硬天花板)
- 安全: 5轮红队+5轮复检, default-deny分级鉴权, 连续零可利用
- W3~W7 累计补完436缺口; W8末轮(40缺口)为半成品(源码树可编译但未集成)
- 运行: cd oa-backend; java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091; admin/123456

== 排除(gitignore, 可再生) ==
node_modules / oa-backend/build / .jdks / *.log / Backup-ERP-* / 弃用的OFBiz核心(只保留modern-ui)
完整文件夹备份见同目录 Backup-ERP-20260615-191517/ (含上述全部, 仅缺 node_modules)

时间戳: 20260615-191517

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:19:15 +08:00

106 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export const meta = {
name: 'erp-w5-buildable',
description: 'W529机构并行把剩余72个可建PARTIAL缺口推向MET(补状态机/聚合/联动/调度/自动取数;跳过需外部系统的23个)',
phases: [{ title: '补可建缺口W5' }],
}
const COMMON = `黄金模板:web/FinancingController.java、web/DevProjectController.java(状态机+审批自动展开)、聚合 web/FundPoolController.java、调度 @Scheduled、前端 oa/pages/payment/repayment.vue。
硬约束:①金额BigDecimal+com.kaidi.oa.common.Money(无裸double);②日期String+LocalDate/ChronoUnit;③控制器@RestController @RequestMapping("/api/oa/<资源>"),ApiResp.ok(),record请求体,NotFoundException/ApiException(400/409中文),状态迁移/级联@Transactional;④仓库extends JpaRepository派生查询;⑤前端真页接真后端(import { http },已解包ApiResp.data,token键oa.token)。
**⚠️W3量产5大崩溃坑——本轮绝不再犯(逐条自查)**:
[后端]①**Map.of()最多10对键值**(20参)——超过就编译失败"找不到合适的方法";多字段Map一律用 new java.util.LinkedHashMap<>() 逐.put()。
②**SQLite保留字绝不做列名/字段名**:references/primary/order/group/key/values/index/table/column/check/default/constraint/references/desc/asc等;若业务字段必须叫这名,Java字段可留但**必须**加@Column(name="安全别名")映射物理列(并import jakarta.persistence.Column),否则建表DDL崩"near xxx: syntax error"。
③**派生查询禁用ContainingIgnoreCase**(Hibernate6社区SQLite方言upper()类型校验崩,启动即挂)——一律用 Containing(SQLite的LIKE对ASCII本就大小写不敏感,中文无大小写,功能等价)。
[前端]④**el-table-column插槽**:写 #default="{ row }" 或 #default="{ row }: { row: any }";**绝不写 typeof X[0] / typeof X.value[0] 注解**(vue-tsc过但vite编译/类型崩),绝不套外层括号。
⑤**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 }"。
⑥**图标必须真存在于@element-plus/icons-vue**(MobilePhone不存在→用Iphone;不确定就用 Document/List/Money/DataLine 等常见图标);http删除用 http.del() 不是 http.delete()。
⑦**具名插槽「<template #footer>」/「#header」/「#default」必须是组件(el-dialog/el-card/el-table-column)的直接子节点,绝不能嵌在「<div>」等HTML元素里**(否则vite vue编译器崩"Cannot read properties of undefined reading type",整个build失败)——dialog里要按状态切换footer时,footer提到dialog直属用「<template v-if>」切换,别把footer放进「<div v-if>」。
⑧**http参数形态**:「http.get<T>(url, query)」第2参直接就是query对象(「Record<string,string|number|boolean|null|undefined>」),**不是**「{ params: {...} }」;「http.post<T>(url, body, opts)」的opts用「query」键不是「params」;声明query变量用「const params: Record<string, any> = {}」(别用unknown)。模板里「Promise」/「window」不在Vue全局白名单,要用就在「<script setup>」定义函数包装再调。
⑨无emoji、无渐变(纯色蓝#2f6fed);类名表名加域前缀,grep确认与现有~623控制器/614实体零撞名。**优先新建实体;若必须给既有实体加字段,在summary注明"既有实体X加字段Y"(SQLite ddl-update不可靠加列,集成时统一schema-sync)**。
**关键纪律**:①只新建文件或改你机构自己的页;共享文件(AuthInterceptor/oaModules/TriggerRuleEngine/WorkflowService)不自改,放sharedFileSnippets;②不运行gradle/npm;③加法式不破坏现有链;④逐文件自审Java语法(import齐全[@PathVariable/@RequestBody/@Column/domain类]、ApiResp用record访问器.data()非.getData()、括号配平、record字段一致、实体setter存在、Map.of≤10对)与Vue语法(插槽不套外括号无typeof、v-model成员表达式、http.del、图标存在)。`
const RESULT = {
type: 'object', additionalProperties: false,
properties: {
unit: { type: 'string' },
backendFiles: { type: 'array', items: { type: 'string' } },
frontendFiles: { type: 'array', items: { type: 'string' } },
authSensitiveRead: { type: 'array', items: { type: 'string' } },
navEntries: { type: 'array', items: {
type: 'object', additionalProperties: false,
properties: { moduleId: { type: 'string' }, items: { type: 'array', items: {
type: 'object', additionalProperties: false,
properties: { key: { type: 'string' }, label: { type: 'string' }, kind: { type: 'string' }, path: { type: 'string' } },
required: ['key', 'label', 'kind', 'path'] } } },
required: ['moduleId', 'items'] } },
sharedFileSnippets: { type: 'array', items: {
type: 'object', additionalProperties: false,
properties: { file: { type: 'string' }, anchor: { type: 'string' }, change: { type: 'string' }, why: { type: 'string' } },
required: ['file', 'anchor', 'change', 'why'] } },
closedGaps: { type: 'array', items: { type: 'string' } },
skippedExternal: { type: 'array', items: { type: 'string' }, description: '本轮判定需外部系统而跳过的缺口' },
summary: { type: 'string' },
},
required: ['unit', 'backendFiles', 'frontendFiles', 'authSensitiveRead', 'navEntries', 'sharedFileSnippets', 'closedGaps', 'skippedExternal', 'summary'],
}
const GAPS = '/Users/qiu/Desktop/ERP/requirements/_xlsx_audit_w4post.json'
const SLICE = '/Users/qiu/Desktop/ERP/requirements/_req_slices'
const UNITS = [
['创新研发中心','产品开发部','rd','01_创新研发中心__产品开发部'],
['创新研发中心','申报服务部','rd','02_创新研发中心__申报服务部'],
['创新研发中心','知识产权部','rd','03_创新研发中心__知识产权部'],
['创新研发中心','实验室','lab','04_创新研发中心__实验室'],
['设计研究中心','咨询可研院','design','05_设计研究中心__咨询可研院'],
['设计研究中心','规划设计部','design','06_设计研究中心__规划设计部'],
['设计研究中心','工程监理部','supervision','07_设计研究中心__工程监理部'],
['制造管理中心','环保设备制造中心','mfg','08_制造管理中心__环保设备制造中心'],
['制造管理中心','生物质肥料制造中心','mfg','09_制造管理中心__生物质肥料制造中心'],
['工程管理中心','质安部','ehs','10_工程管理中心__质安部'],
['工程管理中心','资料室','archive','11_工程管理中心__资料室'],
['工程管理中心','专利工法办','rd','12_工程管理中心__专利工法办'],
['工程管理中心','成本控制部','budget','13_工程管理中心__成本控制部'],
['运营管理中心','城镇污水运营中心','ops','14_运营管理中心__城镇污水运营中心'],
['运营管理中心','工业废水运营中心','ops','15_运营管理中心__工业废水运营中心'],
['市场部','经营部','crm','16_市场部__经营部'],
['市场部','办事处','crm','17_市场部__办事处'],
['市场部','分公司','crm','18_市场部__分公司'],
['支付中心','结算中心','payment','19_支付中心__结算中心'],
['支付中心','财务部','finance','20_支付中心__财务部'],
['支付中心','金融办','payment','21_支付中心__金融办'],
['内控部','法务风险部','legal','22_内控部__法务风险部'],
['内控部','审计监察部','audit','23_内控部__审计监察部'],
['品牌推广部','宣传部','culture','24_品牌推广部__宣传部'],
['品牌推广部','项目文化','culture','25_品牌推广部__项目文化'],
['行政','综合部','hr','26_行政__综合部'],
['行政','信息部','itasset','27_行政__信息部'],
['行政','办公室','admin','28_行政__办公室'],
['行政','后勤','admin','29_行政__后勤'],
]
log(`W4${UNITS.length} 机构补94个可建PARTIAL缺口`)
phase('补可建缺口W5')
const results = await parallel(UNITS.map(([dept, sub, mod, fn]) => () =>
agent(
`你是资深全栈工程师,为凯迪 ERP【把这个机构剩余的"可建PARTIAL缺口"推向真正MET】:${dept} / ${sub}
第一步 运行 python3 取本机构的剩余可建缺口:python3 -c "import json;d=json.load(open('${GAPS}'));[print('['+g['severity']+']',g['module'],'',g['gap']) for g in d['buildable'] if '${sub}' in g['area']]"
若上面输出为空,说明本机构无可建缺口,直接返回空结果(closedGaps=[])。
第二步 Read 需求切片 ${SLICE}/${fn}.txt 对照理解。
第三步 grep/读码确认现状(本机构已有很多控制器/实体),针对每条可建缺口补完真实现:缺的结构化实体(如测试计划TestPlan)/状态机端点/甘特或日历聚合API/自动计算逻辑(如标准成本从BOM+工艺路线展开)/跨模块联动写入(如发布BOM到生产)/规则引擎自动触发钩子/调度Job(@Scheduled自动下架/生成)/统计分析端点(上市周期/成功率/三维投入)等。新建实体+仓库+控制器(+服务)与真实前端页,把PARTIAL真正补全到可用。
**优先 high>med>low**。**凡缺口本质需要外部系统的**(真CA电子签/数字证书/区块链存证、BIM碰撞、CAD/Revit解析、AI语义/知识图谱/向量、JPush/APNs推送网关、语音录入、IoT/SCADA、银企直连/税务直连)——放入skippedExternal数组并简述,本轮不做。
前端页挂模块 '${mod}'。
${COMMON}
返回结构化结果(closedGaps列出真正补完到MET的缺口模块名;skippedExternal列出跳过的;导航 moduleId='${mod}')。`,
{ label: `W5:${sub}`, phase: '补可建缺口W5', schema: RESULT, model: 'sonnet' }
)
)).then((a) => a.filter(Boolean))
const closed = results.reduce((s, r) => s + (r.closedGaps || []).length, 0)
const skipped = results.reduce((s, r) => s + (r.skippedExternal || []).length, 0)
const bf = results.reduce((s, r) => s + (r.backendFiles || []).length, 0)
const ff = results.reduce((s, r) => s + (r.frontendFiles || []).length, 0)
log(`W5完成: 补完${closed}缺口 跳过外部${skipped}; 后端文件${bf} 前端页${ff}`)
return { built: results.length, total: UNITS.length, closed, skipped, backendFiles: bf, frontendFiles: ff, results }