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,121 @@
package com.kaidi.oa.seed;
import com.kaidi.oa.domain.LabElnAccessLog;
import com.kaidi.oa.repository.LabElnAccessLogRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
/**
* 实验室 ELN / 报告读取访问日志演示数据(质量管理与合规 §6 审计追踪)。
*
* 覆盖全部动作类型(登录/ELN查看/ELN导出/报告查看/报告导出/原始数据查看/原始数据导出/审批查看),
* 涉及多名实验人员、多时间段,让 eln-audit.vue 活体展示完整审计轨迹。
*
* @Order(205) 在所有主数据 Seeder 之后、幂等(count > 0 则跳过)。
*/
@Component
@Order(205)
public class LabElnAccessLogSeeder implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(LabElnAccessLogSeeder.class);
private final LabElnAccessLogRepository repo;
public LabElnAccessLogSeeder(LabElnAccessLogRepository repo) {
this.repo = repo;
}
@Override
public void run(String... args) {
if (repo.count() > 0) {
return;
}
log.info("Seeding LabElnAccessLog demo data...");
Instant base = Instant.now().minus(7, ChronoUnit.DAYS);
// Day -7:登录审计
seed(null, null, "登录", "李化验", "质量检测部", "192.168.1.45",
"用户登录实验室系统", base);
seed(null, null, "登录", "王化验", "质量检测部", "192.168.1.47",
"用户登录实验室系统", base.plus(10, ChronoUnit.MINUTES));
seed(null, null, "登录", "陈检测", "质量检测部", "192.168.1.52",
"用户登录实验室系统", base.plus(25, ChronoUnit.MINUTES));
// Day -7ELN 查看
seed(1L, "ELN-001 COD分析实验记录", "ELN查看", "李化验", "质量检测部", "192.168.1.45",
"查看 ELN-001 COD分析实验记录", base.plus(30, ChronoUnit.MINUTES));
seed(2L, "ELN-002 氨氮检测实验记录", "ELN查看", "王化验", "质量检测部", "192.168.1.47",
"查看 ELN-002 氨氮检测实验记录", base.plus(35, ChronoUnit.MINUTES));
// Day -6ELN 导出
seed(1L, "ELN-001 COD分析实验记录", "ELN导出", "李化验", "质量检测部", "192.168.1.45",
"导出 ELN-001 PDF,用于报告附件", base.plus(1, ChronoUnit.DAYS));
seed(3L, "ELN-003 总磷TP检测记录", "ELN导出", "陈检测", "质量检测部", "192.168.1.52",
"导出 ELN-003 Excel,用于数据汇总", base.plus(1, ChronoUnit.DAYS).plus(2, ChronoUnit.HOURS));
// Day -5:报告查看
seed(null, "检测报告 JC-2026-05-001", "报告查看", "张主任", "质量管理部", "192.168.1.60",
"查看检测报告 JC-2026-05-001COD/氨氮汇总)", base.plus(2, ChronoUnit.DAYS));
seed(null, "检测报告 JC-2026-05-002", "报告查看", "王化验", "质量检测部", "192.168.1.47",
"查看检测报告 JC-2026-05-002(总磷/总氮)", base.plus(2, ChronoUnit.DAYS).plus(1, ChronoUnit.HOURS));
// Day -4:报告导出
seed(null, "检测报告 JC-2026-05-001", "报告导出", "张主任", "质量管理部", "192.168.1.60",
"导出报告 JC-2026-05-001 PDF(送客户)", base.plus(3, ChronoUnit.DAYS));
seed(null, "检测报告 JC-2026-05-003", "报告导出", "李化验", "质量检测部", "192.168.1.45",
"导出报告 JC-2026-05-003 PDF(归档)", base.plus(3, ChronoUnit.DAYS).plus(3, ChronoUnit.HOURS));
// Day -3:原始数据查看
seed(2L, "ELN-002 氨氮检测实验记录", "原始数据查看", "陈检测", "质量检测部", "192.168.1.52",
"查看 ELN-002 原始色谱数据", base.plus(4, ChronoUnit.DAYS));
seed(4L, "ELN-004 pH值检测记录", "原始数据查看", "李化验", "质量检测部", "192.168.1.45",
"查看 ELN-004 原始pH计读数", base.plus(4, ChronoUnit.DAYS).plus(2, ChronoUnit.HOURS));
// Day -2:原始数据导出
seed(2L, "ELN-002 氨氮检测实验记录", "原始数据导出", "陈检测", "质量检测部", "192.168.1.52",
"导出 ELN-002 原始数据CSV(归档备查)", base.plus(5, ChronoUnit.DAYS));
seed(5L, "ELN-005 悬浮物SS检测记录", "原始数据导出", "王化验", "质量检测部", "192.168.1.47",
"导出 ELN-005 原始数据ExcelOOS偏差调查)", base.plus(5, ChronoUnit.DAYS).plus(1, ChronoUnit.HOURS));
// Day -1:审批查看
seed(null, "审批流 RPT-APR-2026-06", "审批查看", "张主任", "质量管理部", "192.168.1.60",
"查看报告审批进度(检测人→复核人→批准人)", base.plus(6, ChronoUnit.DAYS));
seed(null, "审批流 RPT-APR-2026-07", "审批查看", "李主任", "质量管理部", "192.168.1.62",
"查看报告审批进度(已批准待签发)", base.plus(6, ChronoUnit.DAYS).plus(2, ChronoUnit.HOURS));
// 今日:各类操作
seed(null, null, "登录", "李化验", "质量检测部", "192.168.1.45",
"用户登录实验室系统", Instant.now().minus(2, ChronoUnit.HOURS));
seed(6L, "ELN-006 总氮TN检测记录", "ELN查看", "李化验", "质量检测部", "192.168.1.45",
"查看 ELN-006 总氮TN检测记录", Instant.now().minus(90, ChronoUnit.MINUTES));
seed(6L, "ELN-006 总氮TN检测记录", "ELN导出", "李化验", "质量检测部", "192.168.1.45",
"导出 ELN-006 PDF附于检测报告", Instant.now().minus(60, ChronoUnit.MINUTES));
log.info("LabElnAccessLog demo data seeded: {} records.", repo.count());
}
private void seed(Long elnId, String elnRef, String action,
String operator, String dept, String ip,
String detail, Instant occurredInstant) {
LabElnAccessLog r = new LabElnAccessLog();
r.setElnId(elnId);
r.setElnRef(elnRef);
r.setAction(action);
r.setOperator(operator);
r.setOperatorDept(dept);
r.setIp(ip);
r.setDetail(detail);
// 格式化操作时间字符串
java.time.LocalDateTime ldt = java.time.LocalDateTime.ofInstant(occurredInstant, java.time.ZoneId.systemDefault());
r.setOccurredAt(ldt.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
r.setCreatedAt(occurredInstant);
repo.save(r);
}
}