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>
This commit is contained in:
Qiufeng
2026-06-15 19:19:15 +08:00
co-authored by Claude Opus 4.8
commit 5e51dc3f56
10584 changed files with 2501339 additions and 0 deletions
@@ -0,0 +1,95 @@
package com.kaidi.oa.seed;
import com.kaidi.oa.common.PasswordUtil;
import com.kaidi.oa.domain.SysDept;
import com.kaidi.oa.domain.SysUser;
import com.kaidi.oa.repository.SysDeptRepository;
import com.kaidi.oa.repository.SysUserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* Enriches the seed org with users matching the approval templates' common role
* labels (董事长 / 总经理 / 成控总监 / 各成本会计 / 出纳 / 法务经理 / 保密员 / 资料员 …).
*
* Why: workflow nodes carry role labels; with only 6 demo users NodeAssigneeResolver
* dumped most approvals on admin. Seeding the matching real users lets the resolver
* route each node to a distinct person, so multi-user 待办 is realistic.
*
* Idempotent + non-destructive: runs after the base seeder, only adds users that are
* missing (guard on a sentinel login). All accounts use password 123456.
*/
@Component
@Order(2)
public class OrgEnrichmentSeeder implements CommandLineRunner {
private final SysUserRepository userRepo;
private final SysDeptRepository deptRepo;
public OrgEnrichmentSeeder(SysUserRepository userRepo, SysDeptRepository deptRepo) {
this.userRepo = userRepo;
this.deptRepo = deptRepo;
}
/** login, displayName, dept name, title — title/name tuned to match template node labels. */
private static final String[][] ROLE_USERS = {
{"zhoujg", "周建国", "开迪集团", "董事长"},
{"sungq", "孙国强", "开迪集团", "总经理"},
{"zhangyun", "张运", "开迪集团", "董事长助理"},
{"chenfr", "陈法人", "开迪集团", "法人"},
{"zhaocz", "赵成志", "成本控制部", "成控总监"},
{"zhengqt", "郑前台", "成本控制部", "成控前台"},
{"sunzh", "孙志华", "财务部", "财务文员"},
{"qiancn", "钱晨", "资金结算部", "制单出纳"},
{"guolin", "郭琳", "资金结算部", "结算会计"},
{"qiantcc", "钱总成", "财务部", "总公司成本会计"},
{"sunfcc", "孙分成", "成本控制部", "分公司成本会计"},
{"zhoujtc", "周集成", "财务部", "集团成本会计"},
{"lijm", "李剑美", "内控部", "保密员"},
{"zhaofw", "赵法", "法务风险部", "法务部经理"},
{"wuzly", "吴资料", "资料室", "集团公司资料员"}
};
@Override
public void run(String... args) {
// Sentinel: if the first enrichment user already exists, assume done.
if (userRepo.findByLoginName("zhoujg").isPresent()) {
return;
}
// base org must exist (admin seeded by DataSeeder @Order(1)); else nothing to attach to.
if (userRepo.findByLoginName("admin").isEmpty()) {
return;
}
Map<String, Long> deptIdByName = new HashMap<>();
for (SysDept d : deptRepo.findAll()) {
deptIdByName.putIfAbsent(d.getName(), d.getId());
}
int idx = 6;
for (String[] r : ROLE_USERS) {
if (userRepo.findByLoginName(r[0]).isPresent()) {
continue;
}
// displayName 唯一护栏:对象级鉴权/审批办理人按 displayName 解析到稳定 userId
// 重名会让"同名/改名重用"互相冒充。种子也绝不播出重名(与 UserController 的唯一约束同口径),
// 让"唯一 displayName"在全新重建库上同样成立。
if (!userRepo.findByDisplayName(r[1]).isEmpty()) {
continue;
}
SysUser u = new SysUser();
u.setLoginName(r[0]);
u.setDisplayName(r[1]);
u.setDeptId(deptIdByName.get(r[2]));
u.setTitle(r[3]);
u.setPhone(String.format("138%08d", idx));
u.setEmail(r[0] + "@kaidi.com");
u.setPassword(PasswordUtil.hash("123456"));
u.setEnabled(true);
userRepo.save(u);
idx++;
}
}
}