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,360 @@
package com.kaidi.oa.seed;
import com.kaidi.oa.domain.Archive;
import com.kaidi.oa.domain.ArchiveVersion;
import com.kaidi.oa.repository.ArchiveRepository;
import com.kaidi.oa.repository.ArchiveVersionRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
/**
* 资料室·Module 8「与其它部门的接口要求」— 跨模块归档链演示种子数据。
*
* <p>补齐 integration-dashboard 六条「未激活」链路(activeChains 从 4/10 → 10/10):
* <ul>
* <li>诉讼(法务风险部 legal.toArchive</li>
* <li>财务(财务部 finance.toArchive</li>
* <li>人事(人力资源部 hr.toArchive</li>
* <li>知识产权(知识产权部 ip.toArchive</li>
* <li>质量(质安部 quality.toArchive</li>
* <li>图纸档案(工程监理 supervision.drawing+ ArchiveVersion 版本历史</li>
* </ul>
*
* <p>幂等:同 sourceType 在 archive 表已有记录则跳过整批(count 检查)。
* @Order(205) 在所有基础 Seeder 后运行。
*/
@Component
@Order(205)
public class ArchiveCrossModuleSeeder implements CommandLineRunner {
private final ArchiveRepository archiveRepo;
private final ArchiveVersionRepository versionRepo;
public ArchiveCrossModuleSeeder(ArchiveRepository archiveRepo,
ArchiveVersionRepository versionRepo) {
this.archiveRepo = archiveRepo;
this.versionRepo = versionRepo;
}
@Override
public void run(String... args) {
seedLitigationArchives();
seedFinanceArchives();
seedHrArchives();
seedIpArchives();
seedQualityArchives();
seedDrawingArchives();
}
// =========================================================================
// 1. 诉讼文书归档(法务风险部 legal.toArchive
// =========================================================================
private void seedLitigationArchives() {
long existing = archiveRepo.findAll().stream()
.filter(a -> "诉讼".equals(a.getSourceType()))
.count();
if (existing > 0) return;
archive("合同档案", "靖州供水项目工程款争议仲裁文书", "诉讼", 1L,
"靖州供水仲裁申请书及答辩材料.pdf", "pdf", 1_840_000L,
"法务专员-王芳", "2026-01-15",
"诉讼,仲裁,合同纠纷,靖州供水,已结案",
"靖州供水信息化建设合同工程款争议仲裁案,申请仲裁→调解→撤案,文书已归档至资料室。",
"机密");
archive("合同档案", "供水设备分包方违约起诉状及判决书", "诉讼", 2L,
"供水设备分包违约诉讼案(完整卷宗).pdf", "pdf", 3_260_000L,
"法务专员-王芳", "2025-11-20",
"诉讼,违约,分包,供水设备,已判决",
"供水调度监控设备分包合同违约诉讼,一审判决我方胜诉,文书卷宗完整归档。",
"机密");
archive("合同档案", "江山春锦项目咨询费结算纠纷调解协议", "诉讼", 3L,
"江山春锦调解协议书.pdf", "pdf", 680_000L,
"法务部主任-张伟", "2026-03-05",
"诉讼,调解协议,咨询费,江山春锦,已结案",
"江山春锦项目工程咨询费结算纠纷经调解达成协议,调解书及结案证明归档。",
"机密");
}
// =========================================================================
// 2. 会计档案年度归档(财务部 finance.toArchive
// =========================================================================
private void seedFinanceArchives() {
long existing = archiveRepo.findAll().stream()
.filter(a -> "财务".equals(a.getSourceType()))
.count();
if (existing > 0) return;
archive("财务档案", "2025年度会计档案(凭证/账簿/报表)", "财务", 2025L,
"2025年度会计档案汇编.pdf", "pdf", 12_480_000L,
"财务总监-刘洋", "2026-04-30",
"年度:2025,类型:会计档案,保存期限:30年,会计凭证,账簿,财务报表",
"2025年度财务档案,含记账凭证、总账/明细账、资产负债表/利润表/现金流量表,依《会计档案管理办法》保存30年。",
"机密");
archive("财务档案", "2024年度会计档案(凭证/账簿/报表)", "财务", 2024L,
"2024年度会计档案汇编.pdf", "pdf", 10_920_000L,
"财务总监-刘洋", "2025-04-30",
"年度:2024,类型:会计档案,保存期限:30年,凭证,账簿,报表",
"2024年度财务档案归档,含年度审计报告及税务申报表副本,永久保存备查。",
"机密");
archive("财务档案", "2026年Q1季度财务快报及纳税申报资料", "财务", 20261L,
"2026Q1财务快报及纳税申报.pdf", "pdf", 2_340_000L,
"财务会计-陈静", "2026-04-15",
"季报,Q1,2026,纳税申报,财务快报",
"2026年第一季度财务快报及增值税/企业所得税季报材料归档。",
"机密");
}
// =========================================================================
// 3. 员工档案归档(人力资源部 hr.toArchive
// =========================================================================
private void seedHrArchives() {
long existing = archiveRepo.findAll().stream()
.filter(a -> a.getSourceType() != null && a.getSourceType().startsWith("人事"))
.count();
if (existing > 0) return;
archive("人事档案", "李娜(EMP001)离职档案", "人事-离职", null,
"EMP001-李娜-离职手续及档案.pdf", "pdf", 560_000L,
"人事专员-王芳", "2026-05-10",
"员工编号:EMP001,员工姓名:李娜,部门:项目管理部,事件:离职",
"项目管理部李娜女士离职手续及人事档案汇编,含离职审批、工作交接记录、薪资结算单。",
"机密");
archive("人事档案", "刘洋(EMP003)晋升档案", "人事-晋升", null,
"EMP003-刘洋-晋升及绩效材料.pdf", "pdf", 340_000L,
"人事专员-王芳", "2026-03-01",
"员工编号:EMP003,员工姓名:刘洋,部门:财务管理部,事件:晋升",
"财务管理部刘洋晋升为财务总监,含晋升审批、绩效评估报告及新岗位协议。",
"机密");
archive("人事档案", "王芳(EMP005)劳动合同续签档案", "人事-续签", null,
"EMP005-王芳-劳动合同续签.pdf", "pdf", 220_000L,
"人事总监-张伟", "2026-01-15",
"员工编号:EMP005,员工姓名:王芳,部门:人力资源部,事件:续签",
"人力资源部王芳劳动合同到期续签,含续签审批、新合同文本(2026-2029)。",
"机密");
archive("人事档案", "陈静(EMP007)入职档案", "人事-入职", null,
"EMP007-陈静-入职材料汇编.pdf", "pdf", 480_000L,
"人事专员-王芳", "2026-02-08",
"员工编号:EMP007,员工姓名:陈静,部门:财务管理部,事件:入职",
"财务管理部陈静入职资料汇编,含录用通知、学历/资质证明、入职登记表、劳动合同。",
"机密");
}
// =========================================================================
// 4. 知识产权证书归档(知识产权部 ip.toArchive
// =========================================================================
private void seedIpArchives() {
long existing = archiveRepo.findAll().stream()
.filter(a -> "知识产权".equals(a.getSourceType()))
.count();
if (existing > 0) return;
archive("知识产权档案", "专利证书:供水智能调度优化方法(ZL202410123456.8", "知识产权", null,
"专利证书-ZL202410123456.8.pdf", "pdf", 860_000L,
"知识产权专员-李娜", "2026-04-20",
"证书编号:ZL202410123456.8,证书类型:专利证书,权利人:湖南凯迪工程科技有限公司,有效期至:2044-04-20",
"供水智能调度优化方法发明专利,授权公告日2026-04-20,权利人凯迪工程,有效期20年。",
"机密");
archive("知识产权档案", "软件著作权证书:凯迪ERP+OA一体化平台V1.02026SR0567890", "知识产权", null,
"软著证书-凯迪ERP-OA-2026SR0567890.pdf", "pdf", 720_000L,
"知识产权专员-李娜", "2026-05-18",
"证书编号:2026SR0567890,证书类型:软件著作权证,权利人:湖南凯迪工程科技有限公司,有效期至:永久",
"凯迪ERP+OA一体化平台V1.0软件著作权登记证书,登记号2026SR0567890,永久保护。",
"机密");
archive("知识产权档案", "商标注册证:凯迪(KAIDI)第30类(3012345678", "知识产权", null,
"商标注册证-凯迪品牌-3012345678.pdf", "pdf", 540_000L,
"知识产权总监-张伟", "2026-01-10",
"证书编号:3012345678,证书类型:商标注册证,权利人:湖南凯迪工程科技有限公司,有效期至:2036-01-10",
"凯迪品牌商标注册证(第30类),有效期10年,到期前12个月提醒续期。",
"内部");
}
// =========================================================================
// 5. 质量记录归档(质安部 quality.toArchive
// =========================================================================
private void seedQualityArchives() {
long existing = archiveRepo.findAll().stream()
.filter(a -> "质量".equals(a.getSourceType()))
.count();
if (existing > 0) return;
archive("质量档案", "检验报告:供水监控设备出厂检验报告(QR2026-001)", "质量", null,
"供水监控设备出厂检验报告-QR2026-001.pdf", "pdf", 2_140_000L,
"质安专员-刘洋", "2026-04-25",
"记录编号:QR2026-001,记录类型:检验报告,项目:靖州供水项目",
"靖州供水调度监控设备出厂检验报告,涵盖硬件功能/电磁兼容/防护等级测试,全项合格。",
"内部");
archive("质量档案", "体系文件:ISO9001质量管理手册(2026版)", "质量", null,
"ISO9001质量管理手册-2026版.pdf", "pdf", 5_680_000L,
"质安总监-张伟", "2026-03-01",
"记录编号:SYS-QM-2026,记录类型:体系文件,项目:公司质量管理体系",
"公司ISO9001:2015质量管理体系手册2026年修订版,通过外部审核,归档保存备查。",
"内部");
archive("质量档案", "安全报告:靖州供水项目施工安全专项检查报告(SR2026-003)", "质量", null,
"靖州供水施工安全检查报告-SR2026-003.pdf", "pdf", 1_360_000L,
"安全专员-王芳", "2026-05-30",
"记录编号:SR2026-003,记录类型:安全报告,项目:靖州供水项目",
"靖州供水项目第三次施工安全专项检查报告,现场整改措施已闭环,报告存档。",
"内部");
archive("质量档案", "特种设备档案:起重机械检验合格证(TSB2026-0078", "质量", null,
"起重机械检验合格证-TSB2026-0078.pdf", "pdf", 680_000L,
"设备管理员-陈静", "2026-02-28",
"记录编号:TSB2026-0078,记录类型:特种设备档案,项目:靖州供水项目",
"靖州施工现场起重机械(额定起重量5t)年度检验合格证,有效期至2027-02-27。",
"内部");
}
// =========================================================================
// 6. 图纸档案归档(工程监理 supervision.drawing+ 版本历史
// =========================================================================
private void seedDrawingArchives() {
long existingDrawings = archiveRepo.findByCategory("图纸档案").size();
if (existingDrawings > 0) return;
// 图纸1:管网总图 V1.0→V2.0V1.0作废,V2.0现行)
Archive d1 = archiveEntity("图纸档案", "靖州供水项目管网系统总图", "图纸档案", 1L,
"靖州供水管网总图-V2.0.pdf", "pdf", 8_640_000L,
"监理工程师-刘洋", "2026-05-20",
"图纸档案,管网总图,靖州供水,现行V2.0",
"靖州县供水管网系统总布置图,含主干管/支管/阀门井/消防栓定位,V2.0为现行版。",
"内部");
d1 = archiveRepo.save(d1);
// V1.0(作废)
ArchiveVersion v1d1 = new ArchiveVersion();
v1d1.setArchiveId(d1.getId());
v1d1.setArchiveTitle(d1.getTitle());
v1d1.setVersionNo("V1.0");
v1d1.setIsCurrent(Boolean.FALSE);
v1d1.setStatus("作废");
v1d1.setChangeNote("初版管网总图,未含北区延伸段,已作废");
v1d1.setAuthor("监理工程师-王芳");
v1d1.setVersionDate("2026-03-10");
v1d1.setCreatedAt(Instant.now());
versionRepo.save(v1d1);
// V2.0(现行)
ArchiveVersion v2d1 = new ArchiveVersion();
v2d1.setArchiveId(d1.getId());
v2d1.setArchiveTitle(d1.getTitle());
v2d1.setVersionNo("V2.0");
v2d1.setIsCurrent(Boolean.TRUE);
v2d1.setStatus("现行");
v2d1.setChangeNote("补充北区管网延伸段(DN200主管×840m),更新阀门井坐标");
v2d1.setAuthor("监理工程师-刘洋");
v2d1.setVersionDate("2026-05-20");
v2d1.setCreatedAt(Instant.now());
versionRepo.save(v2d1);
// 图纸2:调度中心机房平面图 V1.0(现行)
Archive d2 = archiveEntity("图纸档案", "靖州供水调度中心机房平面布置图", "图纸档案", 1L,
"调度中心机房平面图-V1.0.pdf", "pdf", 3_120_000L,
"监理工程师-陈静", "2026-04-08",
"图纸档案,机房平面图,靖州供水,调度中心,现行V1.0",
"供水调度中心机房设备平面布置图,含服务器机柜/UPS/网络设备/弱电桥架,V1.0现行。",
"内部");
d2 = archiveRepo.save(d2);
ArchiveVersion v1d2 = new ArchiveVersion();
v1d2.setArchiveId(d2.getId());
v1d2.setArchiveTitle(d2.getTitle());
v1d2.setVersionNo("V1.0");
v1d2.setIsCurrent(Boolean.TRUE);
v1d2.setStatus("现行");
v1d2.setChangeNote("初版机房平面布置图,经监理审核通过,作为施工及竣工验收依据");
v1d2.setAuthor("监理工程师-陈静");
v1d2.setVersionDate("2026-04-08");
v1d2.setCreatedAt(Instant.now());
versionRepo.save(v1d2);
// 图纸3OA系统网络拓扑图 V1.0→V1.1→V2.0(仅V2.0现行)
Archive d3 = archiveEntity("图纸档案", "凯迪ERP+OA一体化网络拓扑图", "图纸档案", 2L,
"凯迪ERP-OA网络拓扑图-V2.0.pdf", "pdf", 2_280_000L,
"IT运维-张伟", "2026-06-01",
"图纸档案,网络拓扑图,ERP,OA,IT架构,现行V2.0",
"凯迪ERP+OA一体化系统网络拓扑图(物理+逻辑),含边界防火墙/核心交换机/服务器集群,V2.0现行。",
"内部");
d3 = archiveRepo.save(d3);
ArchiveVersion v1d3 = new ArchiveVersion();
v1d3.setArchiveId(d3.getId());
v1d3.setArchiveTitle(d3.getTitle());
v1d3.setVersionNo("V1.0");
v1d3.setIsCurrent(Boolean.FALSE);
v1d3.setStatus("作废");
v1d3.setChangeNote("初版拓扑,无DMZ区,安全审计后作废");
v1d3.setAuthor("IT运维-李娜");
v1d3.setVersionDate("2026-02-15");
v1d3.setCreatedAt(Instant.now());
versionRepo.save(v1d3);
ArchiveVersion v11d3 = new ArchiveVersion();
v11d3.setArchiveId(d3.getId());
v11d3.setArchiveTitle(d3.getTitle());
v11d3.setVersionNo("V1.1");
v11d3.setIsCurrent(Boolean.FALSE);
v11d3.setStatus("作废");
v11d3.setChangeNote("补充DMZ区,增加Web应用防火墙,后发现服务器IP段规划冲突,作废");
v11d3.setAuthor("IT运维-李娜");
v11d3.setVersionDate("2026-04-10");
v11d3.setCreatedAt(Instant.now());
versionRepo.save(v11d3);
ArchiveVersion v2d3 = new ArchiveVersion();
v2d3.setArchiveId(d3.getId());
v2d3.setArchiveTitle(d3.getTitle());
v2d3.setVersionNo("V2.0");
v2d3.setIsCurrent(Boolean.TRUE);
v2d3.setStatus("现行");
v2d3.setChangeNote("重新规划服务器IP段(10.10.0.0/16),完善安全域划分,通过等保测评");
v2d3.setAuthor("IT运维-张伟");
v2d3.setVersionDate("2026-06-01");
v2d3.setCreatedAt(Instant.now());
versionRepo.save(v2d3);
}
// =========================================================================
// helpers
// =========================================================================
private void archive(String category, String title, String sourceType, Long sourceId,
String fileName, String fileType, long fileSize, String uploader,
String archiveDate, String tags, String summary, String accessLevel) {
Archive a = archiveEntity(category, title, sourceType, sourceId,
fileName, fileType, fileSize, uploader, archiveDate, tags, summary, accessLevel);
archiveRepo.save(a);
}
private Archive archiveEntity(String category, String title, String sourceType, Long sourceId,
String fileName, String fileType, long fileSize, String uploader,
String archiveDate, String tags, String summary, String accessLevel) {
Archive a = new Archive();
a.setCategory(category);
a.setTitle(title);
a.setSourceType(sourceType);
a.setSourceId(sourceId);
a.setFileName(fileName);
a.setFileType(fileType);
a.setFileSize(fileSize);
a.setUploader(uploader);
a.setArchiveDate(archiveDate);
a.setTags(tags);
a.setSummary(summary);
a.setAccessLevel(accessLevel);
a.setCreatedAt(Instant.now());
return a;
}
}