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,370 @@
package com.kaidi.oa.seed;
import com.kaidi.oa.domain.EnvDischargePermit;
import com.kaidi.oa.domain.OpsEmergencySupply;
import com.kaidi.oa.domain.WwMsdsFile;
import com.kaidi.oa.domain.WwSamplingTask;
import com.kaidi.oa.repository.EnvDischargePermitRepository;
import com.kaidi.oa.repository.OpsEmergencySupplyRepository;
import com.kaidi.oa.repository.WwMsdsFileRepository;
import com.kaidi.oa.repository.WwSamplingTaskRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
/**
* 工业废水运营中心·缺口演示数据(Gap-7 安全环保+Gap-11 跨部门接口)。
*
* 补完以下"活体返回0条/功能在但无数据演示"缺口:
* 1. WwMsdsFileMSDS 化学品安全技术说明书文件库(8种常用危化品)
* 2. OpsEmergencySupply:应急物资台账种子数据(中和剂/吸附棉/围堰三类)
* 3. EnvDischargePermit:排污许可证合规执行台账(4条:废水+废气+噪声各项指标)
* 4. WwSamplingTask:废水采样任务演示数据(覆盖不同状态和超标场景,供 Gap-11 桥接演示)
*/
@Order(9)
@Component
public class WwOpsSeeder implements CommandLineRunner {
private final WwMsdsFileRepository msdsRepo;
private final OpsEmergencySupplyRepository supplyRepo;
private final EnvDischargePermitRepository permitRepo;
private final WwSamplingTaskRepository samplingRepo;
public WwOpsSeeder(WwMsdsFileRepository msdsRepo,
OpsEmergencySupplyRepository supplyRepo,
EnvDischargePermitRepository permitRepo,
WwSamplingTaskRepository samplingRepo) {
this.msdsRepo = msdsRepo;
this.supplyRepo = supplyRepo;
this.permitRepo = permitRepo;
this.samplingRepo = samplingRepo;
}
@Override
public void run(String... args) {
seedMsdsFiles();
seedEmergencySupplies();
seedDischargePermits();
seedSamplingTasks();
}
// ------------------------------------------------------------------ MSDS 文件库
private void seedMsdsFiles() {
if (msdsRepo.count() > 0) return;
msds("MSDS-001", "硫酸", "7664-93-9", "浓硫酸", "腐蚀品", "I级",
"强腐蚀性,接触皮肤/眼睛造成严重灼伤;吸入蒸气损伤呼吸道",
"泄漏用干沙/干土覆盖吸收,以石灰/纯碱中和;皮肤接触立即用大量清水冲洗15分钟",
"化学防护手套+防化服+防酸护目镜+A型防毒面具",
"阴凉干燥处,与碱类/有机物/金属氧化剂严格分开存放,双人双锁库房",
"2024版", "硫酸MSDS_2024版.pdf", "全站", "东方化工", "2024-01-15", "2025-01-15", "王芳");
msds("MSDS-002", "液碱", "1310-73-2", "氢氧化钠/烧碱", "腐蚀品", "II级",
"强腐蚀性碱,吸入粉尘/液雾刺激呼吸道;皮肤接触引起灼伤",
"泄漏用沙土/干土吸收,以稀盐酸/食用醋中和;皮肤接触立即清水冲洗",
"化学防护手套+防护服+防护眼镜",
"密封储存,与酸类/易燃物分开,防潮防雨",
"2024版", "液碱MSDS_2024版.pdf", "全站", "华正化工", "2024-02-10", "2025-02-10", "刘洋");
msds("MSDS-003", "次氯酸钠", "7681-52-9", "NaClO/漂白液", "氧化剂", "III级",
"强氧化性,与有机物接触可能引起燃烧爆炸;挥发氯气刺激呼吸道和眼睛",
"泄漏用碳酸氢钠(小苏打)中和,通风排气;误入眼睛立即用大量清水冲洗",
"橡胶手套+护目镜+防氯口罩",
"阴凉通风密封储存,远离酸类/有机物/还原剂;避免阳光直射",
"2023版", "次氯酸钠MSDS_2023版.pdf", "全站", "科达化学", "2023-08-01", "2024-08-01", "陈静");
msds("MSDS-004", "双氧水", "7722-84-1", "过氧化氢/H2O2(30%)", "氧化剂", "II级",
"强氧化性,高浓度接触皮肤/眼睛引起灼伤;分解产生氧气,高温易爆",
"泄漏立即用大量水稀释冲洗;燃烧/爆炸风险时撤离并通知消防",
"聚乙烯手套+护目镜+防护面罩",
"低温阴凉储存(避免超过30℃),远离金属催化剂/有机物/强酸,使用专用容器",
"2024版", "双氧水MSDS_2024版.pdf", "全站", "天津大沽化工", "2024-03-01", "2025-03-01", "王芳");
msds("MSDS-005", "亚硫酸氢钠", "7631-90-5", "NaHSO3/保险粉", "还原剂", "III级",
"刺激性,吸入粉尘刺激上呼吸道;与酸接触释放SO2毒气",
"泄漏用干沙覆盖,避免与酸接触;皮肤接触用清水冲洗",
"防尘口罩+手套+护目镜",
"密封干燥处存放,远离酸/氧化剂",
"2023版", "亚硫酸氢钠MSDS_2023版.pdf", "水厂1号", "鑫辉化工", "2023-06-01", "2024-06-01", "刘洋");
msds("MSDS-006", "PAC(聚合氯化铝)", "1327-41-9", "聚铝/净水剂", "刺激品", "IV级",
"粉末吸入刺激呼吸道;溶液接触眼睛轻度刺激",
"泄漏清理,防止进入水体;粉末操作戴口罩",
"防尘口罩+防护眼镜",
"干燥处密封存放,防潮",
"2024版", "PAC絮凝剂MSDS_2024版.pdf", "全站", "广州大源化工", "2024-01-01", "2026-01-01", "陈静");
msds("MSDS-007", "PAM(聚丙烯酰胺)", "9003-05-8", "聚丙烯酰胺/助凝剂", "刺激品", "IV级",
"本身毒性低,但单体丙烯酰胺具神经毒性;粉末吸入刺激呼吸道",
"泄漏立即清除,防止地面湿滑;吸入立即转移到新鲜空气处",
"防尘口罩+手套",
"干燥阴凉处密封存放,远离热源",
"2024版", "PAM助凝剂MSDS_2024版.pdf", "全站", "三达新材料", "2024-02-01", "2026-02-01", "王芳");
msds("MSDS-008", "液氯", "7782-50-5", "氯气(液态)", "毒害品", "I级",
"剧毒,低浓度刺激眼/鼻/喉;高浓度引起肺水肿,严重时危及生命",
"立即疏散至上风向;穿戴全套防化服,用碱液(NaOH溶液)喷洒中和泄漏源;紧急联系消防",
"全密闭防化服+自给式空气呼吸器(SCBA)+防化手套",
"专用密闭储罐,设置安全围堰和泄漏检测,远离火源热源,氯气库房单独存放",
"2024版", "液氯MSDS_2024版.pdf", "水厂1号", "中氯工业", "2024-04-01", "2025-04-01", "刘洋");
}
private void msds(String code, String chemName, String casNo, String alias,
String hazardClass, String hazLevel, String healthHazard,
String emergencyAction, String ppe, String storageReq,
String fileVersion, String fileName, String plant,
String supplier, String publishDate, String reviewDate,
String maintainer) {
WwMsdsFile m = new WwMsdsFile();
m.setMsdsCode(code);
m.setChemName(chemName);
m.setCasNo(casNo);
m.setAlias(alias);
m.setHazardClass(hazardClass);
m.setHazLevel(hazLevel);
m.setHealthHazard(healthHazard);
m.setEmergencyAction(emergencyAction);
m.setPpe(ppe);
m.setStorageReq(storageReq);
m.setFileVersion(fileVersion);
m.setFileName(fileName);
m.setApplicablePlant(plant);
m.setSupplier(supplier);
m.setPublishDate(publishDate);
m.setReviewDate(reviewDate);
m.setStatus("有效");
m.setMaintainer(maintainer);
m.setCreatedAt(Instant.now());
m.setUpdatedAt(Instant.now());
msdsRepo.save(m);
}
// ------------------------------------------------------------------ 应急物资台账
private void seedEmergencySupplies() {
if (supplyRepo.count() > 0) return;
// 石灰(中和剂)- 水厂1号
supply("EMS-IN-001", "石灰(中和剂)", "中和剂", "工业级CaO", "水厂1号",
"危化品库A区01号架", "入库", 500d, "kg", 500d, 200d,
"2026-04-01", "2027-04-01", "王芳", "常备中和应急用料");
// 碳酸钠(中和剂)- 水厂1号
supply("EMS-IN-002", "碳酸钠(中和剂)", "中和剂", "工业纯Na2CO3", "水厂1号",
"危化品库A区02号架", "入库", 200d, "kg", 200d, 80d,
"2026-04-10", "2027-04-10", "王芳", "应急中和备用");
// 吸附棉 - 水厂1号
supply("EMS-IN-003", "吸附棉", "吸附棉", "通用型PP吸附棉40cm×50m", "水厂1号",
"应急物资库B区01号架", "入库", 20d, "", 20d, 8d,
"2026-03-15", "2028-03-15", "刘洋", "化学品泄漏应急吸附");
// 吸附棉 - 水厂2号
supply("EMS-IN-004", "吸附棉", "吸附棉", "通用型PP吸附棉40cm×50m", "水厂2号",
"应急物资库B区01号架", "入库", 15d, "", 15d, 6d,
"2026-03-20", "2028-03-20", "陈静", "化学品泄漏应急吸附");
// 围堰 - 水厂1号(出库演示,测试低库存预警)
supply("EMS-IN-005", "围堰", "围堰", "折叠式防污围堰1m*1m*30cm", "水厂1号",
"应急物资库C区", "入库", 6d, "", 6d, 4d,
"2026-02-01", "2029-02-01", "王芳", "液体泄漏围堵");
// 围堰出库(演示当前库存低于安全库存预警)
supply("EMS-OUT-001", "围堰", "围堰", "折叠式防污围堰1m*1m*30cm", "水厂1号",
"应急物资库C区", "领用出库", 4d, "", 2d, 4d,
"2026-05-10", null, "王芳", "应急预案演练领用");
// 防护装备 - 水厂1号
supply("EMS-IN-006", "防化服", "防护装备", "A级全封闭防化服", "水厂1号",
"应急物资库D区", "入库", 4d, "", 4d, 2d,
"2026-01-15", "2028-01-15", "刘洋", "液氯/强酸应急防护");
// 洗眼液 - 全站
supply("EMS-IN-007", "洗眼液", "洗眼站耗材", "洗眼液500ml/瓶", "水厂1号",
"急救箱", "入库", 24d, "", 24d, 12d,
"2026-04-05", "2027-04-05", "陈静", "腐蚀性化学品接触急救");
}
private void supply(String code, String supplyName, String supplyType, String spec,
String plant, String storageLocation, String opType,
Double quantity, String unit, Double currentStock,
Double safetyStock, String opDate, String expiryDate,
String operator, String remark) {
OpsEmergencySupply s = new OpsEmergencySupply();
s.setCode(code);
s.setSupplyName(supplyName);
s.setSupplyType(supplyType);
s.setSpec(spec);
s.setPlant(plant);
s.setStorageLocation(storageLocation);
s.setOpType(opType);
s.setQuantity(quantity);
s.setUnit(unit);
s.setCurrentStock(currentStock);
s.setSafetyStock(safetyStock);
s.setOpDate(opDate);
s.setExpiryDate(expiryDate);
s.setOperator(operator);
s.setRemark(remark);
s.setCreatedAt(Instant.now());
supplyRepo.save(s);
}
// ------------------------------------------------------------------ 排污许可证台账
private void seedDischargePermits() {
if (permitRepo.count() > 0) return;
// 废水综合排放 - COD
permit("PWXK-2024-001", "GWXK-2024-HN-001001", "废水", "DW001-综合废水排放口",
"COD", 50d, "mg/L", "月度在线+季度手工",
"岳阳市生态环境局", "2024-01-01", "2029-12-31", "有效");
// 废水综合排放 - 氨氮
permit("PWXK-2024-002", "GWXK-2024-HN-001001", "废水", "DW001-综合废水排放口",
"氨氮", 5d, "mg/L", "月度在线+季度手工",
"岳阳市生态环境局", "2024-01-01", "2029-12-31", "有效");
// 废水综合排放 - 总磷
permit("PWXK-2024-003", "GWXK-2024-HN-001001", "废水", "DW001-综合废水排放口",
"总磷", 0.5d, "mg/L", "季度手工",
"岳阳市生态环境局", "2024-01-01", "2029-12-31", "有效");
// 废气 - 臭气浓度(即将到期,测试提醒)
permit("PWXK-2024-004", "GWXK-2024-HN-001002", "废气", "DA001-污水处理区无组织废气",
"臭气浓度", 20d, "无量纲", "半年度手工",
"岳阳市生态环境局", "2022-01-01", "2024-12-31", "已过期");
// 噪声
permit("PWXK-2024-005", "GWXK-2024-HN-001003", "噪声", "厂界噪声",
"噪声dB(A)", 65d, "dB(A)", "半年度手工",
"岳阳市生态环境局", "2024-01-01", "2026-06-30", "即将到期");
}
private void permit(String code, String permitNo, String envType, String outlet,
String pollutant, Double limitValue, String unit,
String monitorRequire, String issuer,
String validFrom, String validTo, String status) {
if (permitRepo.existsByPermitNo(permitNo + "-" + pollutant)) return;
EnvDischargePermit p = new EnvDischargePermit();
p.setCode(code);
p.setPermitNo(permitNo);
p.setEnvType(envType);
p.setOutlet(outlet);
p.setPollutant(pollutant);
p.setLimitValue(limitValue);
p.setUnit(unit);
p.setMonitorRequire(monitorRequire);
p.setIssuer(issuer);
p.setValidFrom(validFrom);
p.setValidTo(validTo);
p.setStatus(status);
p.setCreatedAt(Instant.now());
permitRepo.save(p);
}
// ------------------------------------------------------------------ 废水采样任务(Gap-11 桥接演示)
private void seedSamplingTasks() {
if (samplingRepo.count() > 0) return;
// 任务1:达标 - 岳阳恒昌印染来水
sampling("WW-2026-001", "岳阳恒昌印染来水口", "企业来水", 1L,
"岳阳恒昌印染有限公司", "2026-05-08", "2026-05-08 09:30",
"陈静", 86d, 12.5d, null, 0.4d, 7.2d, null,
null, null, null, null, null, null, null,
"GB8978", "达标", null, "已检测", "陈静", "2026-05-08");
// 任务2:超标 - 岳阳恒昌印染来水(COD超标)
sampling("WW-2026-002", "岳阳恒昌印染来水口", "企业来水", 1L,
"岳阳恒昌印染有限公司", "2026-06-08", "2026-06-08 08:45",
"陈静", 132d, 18.6d, null, 0.7d, 6.8d, null,
null, null, null, null, null, null, null,
"GB8978", "超标", "COD", "已检测", "陈静", "2026-06-08");
// 任务3:达标 - 湖南华盛化工来水
sampling("WW-2026-003", "湖南华盛化工来水口", "企业来水", 2L,
"湖南华盛化工股份有限公司", "2026-05-12", "2026-05-12 10:15",
"王芳", 72d, 9.8d, null, 0.3d, 7.5d, null,
0.05d, 0.2d, null, null, null, null, null,
"纳管标准", "达标", null, "已检测", "王芳", "2026-05-12");
// 任务4:预警 - 化工来水(六价铬接近限值)
sampling("WW-2026-004", "湖南华盛化工来水口", "企业来水", 2L,
"湖南华盛化工股份有限公司", "2026-06-12", "2026-06-12 09:00",
"王芳", 95d, 14.2d, null, 0.55d, 7.0d, null,
0.08d, 0.45d, null, null, null, null, null,
"纳管标准", "预警", "六价铬", "已检测", "王芳", "2026-06-12");
// 任务5:超标 - 制药来水(氨氮超标)
sampling("WW-2026-005", "靖州康宁制药来水口", "企业来水", 4L,
"靖州康宁制药有限公司", "2026-05-18", "2026-05-18 11:00",
"刘洋", 148d, 22.4d, null, 0.82d, 6.5d, null,
null, null, null, null, null, null, null,
"GB8978", "超标", "COD;氨氮", "已检测", "刘洋", "2026-05-18");
// 任务6:工艺段 - 好氧池出水
sampling("WW-2026-006", "好氧池出水", "工艺段", null,
null, "2026-06-01", "2026-06-01 14:00",
"陈静", 38d, 3.2d, 8.5d, 0.15d, 7.3d, 4.5d,
null, null, null, null, null, null, null,
"工艺控制值", "达标", null, "已检测", "陈静", "2026-06-01");
// 任务7:最终出水 - 达标排放
sampling("WW-2026-007", "最终出水口", "出水", null,
null, "2026-06-08", "2026-06-08 15:30",
"王芳", 32d, 2.8d, 7.1d, 0.12d, 7.5d, 3.2d,
null, null, null, null, null, null, null,
"GB8978", "达标", null, "已检测", "王芳", "2026-06-08");
// 任务8:待采样(未执行)
sampling("WW-2026-008", "长沙金属表面电镀来水口", "企业来水", 5L,
"长沙金属表面电镀厂", "2026-06-15", null,
"刘洋", null, null, null, null, null, null,
null, null, null, null, null, null, null,
"GB8978", null, null, "待采样", null, null);
}
private void sampling(String sampleCode, String samplePoint, String sampleType,
Long clientId, String clientName, String planDate, String actualTime,
String sampler, Double cod, Double ammonia, Double tn, Double tp,
Double ph, Double ss,
Double cr6, Double totalCr, Double cyanide,
Double phenol, Double nickel, Double copper, Double zinc,
String standard, String judgeResult, String exceedItems,
String status, String analyst, String analyzeDate) {
WwSamplingTask t = new WwSamplingTask();
t.setSampleCode(sampleCode);
t.setSamplePoint(samplePoint);
t.setSampleType(sampleType);
t.setClientId(clientId);
t.setClientName(clientName);
t.setPlanDate(planDate);
t.setActualTime(actualTime);
t.setSampler(sampler);
t.setCodResult(cod);
t.setAmmoniaResult(ammonia);
t.setTnResult(tn);
t.setTpResult(tp);
t.setPhResult(ph);
t.setSsResult(ss);
t.setCr6Result(cr6);
t.setTotalCrResult(totalCr);
t.setCyanideResult(cyanide);
t.setPhenolResult(phenol);
t.setNickelResult(nickel);
t.setCopperResult(copper);
t.setZincResult(zinc);
t.setStandard(standard);
t.setJudgeResult(judgeResult);
t.setExceedItems(exceedItems);
t.setStatus(status);
t.setAnalyst(analyst);
t.setAnalyzeDate(analyzeDate);
t.setCreatedAt(Instant.now());
samplingRepo.save(t);
}
}