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:
@@ -0,0 +1,92 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.QualElecLicense;
|
||||
import com.kaidi.oa.repository.QualElecLicenseRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 行政·综合部——电子证照 QR 台账演示种子(Module 6 补全缺口:表空/0条/活体返回 null)。
|
||||
*
|
||||
* 补全缺口描述:qual-elec-license 表无种子数据,elec-license.vue 首屏空表;
|
||||
* 同时为 GET /qual-exports/qr-list 补充可见数据,让「真伪验证」与「QR台账」两个入口均有演示内容。
|
||||
*
|
||||
* @Order(225) 在所有主数据 Seeder(最高 220)之后运行;幂等(count > 0 跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(225)
|
||||
public class AdminQualElecSeeder implements CommandLineRunner {
|
||||
|
||||
private final QualElecLicenseRepository repo;
|
||||
|
||||
public AdminQualElecSeeder(QualElecLicenseRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (repo.count() > 0) return;
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 安全生产许可证电子版——有效
|
||||
repo.save(lic(null, "JZ-AQ-2019-1182", "安全生产许可证", "安全生产许可证电子版",
|
||||
"https://jzsc.mohurd.gov.cn/asite/verify?certNo=JZ-AQ-2019-1182",
|
||||
"https://files.kaidi.com/elec/aq-permit-2019.pdf",
|
||||
today.plusDays(8).toString(), "有效", "王合规", "安全生产许可证,即将到期需续期", now));
|
||||
|
||||
// 建筑工程施工总承包一级资质电子版——有效
|
||||
repo.save(lic(1L, "JZ-2021-0356", "建筑工程施工总承包一级资质电子证照", "设计资质电子版",
|
||||
"https://jzsc.mohurd.gov.cn/asite/verify?certNo=JZ-2021-0356",
|
||||
"https://files.kaidi.com/elec/zjzz-2021.pdf",
|
||||
today.plusDays(40).toString(), "有效", "李资质", "建筑施工总包一级,省住建厅电子证照", now));
|
||||
|
||||
// 高新技术企业证书电子版——有效
|
||||
repo.save(lic(4L, "GR202144000123", "高新技术企业证书电子版", "高新技术企业证书电子版",
|
||||
"https://www.innocom.gov.cn/gxjscx/index.jhtml?certNo=GR202144000123",
|
||||
"https://files.kaidi.com/elec/hnt-cert-2021.pdf",
|
||||
today.plusDays(150).toString(), "有效", "张战略", "高企认定三年有效期,科技厅", now));
|
||||
|
||||
// 二级建造师电子证书——有效
|
||||
repo.save(lic(null, "JZ2019-B-0332456", "刘强-二级建造师电子证书(市政公用)", "建造师电子证书",
|
||||
"https://jzsc.mohurd.gov.cn/asite/verify?certNo=JZ2019-B-0332456",
|
||||
null,
|
||||
today.plusDays(25).toString(), "有效", "人事部", "市政公用专业,即将到期,需提前续期", now));
|
||||
|
||||
// ISO 9001 认证电子版——已过期
|
||||
repo.save(lic(null, "ISO-9001-KD-2019-003", "ISO 9001 质量管理体系认证证书", "ISO认证电子版",
|
||||
null,
|
||||
"https://files.kaidi.com/elec/iso9001-2019.pdf",
|
||||
today.minusDays(20).toString(), "已过期", "质量部", "已过期,正在延续申报中(ZSB-2)", now));
|
||||
|
||||
// 营业执照电子版——有效(长期)
|
||||
repo.save(lic(null, "91410100MA3XXXXX4X", "营业执照(电子版)", "营业执照电子版",
|
||||
"https://www.gsxt.gov.cn/",
|
||||
"https://files.kaidi.com/elec/biz-license.pdf",
|
||||
null, "有效", "综合部", "国家企业信用信息公示系统,无固定到期日", now));
|
||||
}
|
||||
|
||||
private static QualElecLicense lic(Long certId, String certNo, String certName,
|
||||
String licenseType, String qrUrl, String fileUrl,
|
||||
String expireDate, String status,
|
||||
String registeredBy, String remark, Instant now) {
|
||||
QualElecLicense e = new QualElecLicense();
|
||||
e.setCertId(certId);
|
||||
e.setCertNo(certNo);
|
||||
e.setCertName(certName);
|
||||
e.setLicenseType(licenseType);
|
||||
e.setQrUrl(qrUrl);
|
||||
e.setFileUrl(fileUrl);
|
||||
e.setExpireDate(expireDate);
|
||||
e.setStatus(status);
|
||||
e.setRegisteredBy(registeredBy);
|
||||
e.setRemark(remark);
|
||||
e.setCreatedAt(now);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.QualCert;
|
||||
import com.kaidi.oa.domain.QualDeclTask;
|
||||
import com.kaidi.oa.domain.QualDeclaration;
|
||||
import com.kaidi.oa.domain.StaffCredential;
|
||||
import com.kaidi.oa.domain.StaffDossier;
|
||||
import com.kaidi.oa.domain.StaffDossierItem;
|
||||
import com.kaidi.oa.repository.QualCertRepository;
|
||||
import com.kaidi.oa.repository.QualDeclTaskRepository;
|
||||
import com.kaidi.oa.repository.QualDeclarationRepository;
|
||||
import com.kaidi.oa.repository.StaffCredentialRepository;
|
||||
import com.kaidi.oa.repository.StaffDossierItemRepository;
|
||||
import com.kaidi.oa.repository.StaffDossierRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 行政·资质管理办 / 综合部——资质台账、资质申报、人员证书、员工电子档案的演示种子。
|
||||
*
|
||||
* 幂等 + 非破坏:以各表是否已有数据为哨兵,仅在空表时灌入少量演示数据,让深水页面首屏不空。
|
||||
* 日期以"当前日期 +/- 偏移"动态生成,使到期分级预警在任何时间运行都有 即将到期/逾期 样本。
|
||||
*/
|
||||
@Component
|
||||
@Order(6)
|
||||
public class AdminQualSeeder implements CommandLineRunner {
|
||||
|
||||
private final QualCertRepository certRepo;
|
||||
private final QualDeclarationRepository declRepo;
|
||||
private final QualDeclTaskRepository taskRepo;
|
||||
private final StaffCredentialRepository credRepo;
|
||||
private final StaffDossierRepository dossierRepo;
|
||||
private final StaffDossierItemRepository itemRepo;
|
||||
|
||||
public AdminQualSeeder(QualCertRepository certRepo, QualDeclarationRepository declRepo,
|
||||
QualDeclTaskRepository taskRepo, StaffCredentialRepository credRepo,
|
||||
StaffDossierRepository dossierRepo, StaffDossierItemRepository itemRepo) {
|
||||
this.certRepo = certRepo;
|
||||
this.declRepo = declRepo;
|
||||
this.taskRepo = taskRepo;
|
||||
this.credRepo = credRepo;
|
||||
this.dossierRepo = dossierRepo;
|
||||
this.itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
Instant now = Instant.now();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
if (certRepo.count() == 0) {
|
||||
certRepo.save(cert("ZZ-1", "建筑工程施工总承包", "企业资质", "施工总承包", "JZ-2021-0356",
|
||||
"凯迪建设工程有限公司", "住房和城乡建设部", "一级", "房屋建筑工程施工总承包",
|
||||
today.minusYears(3).toString(), today.plusDays(40).toString(), "年检", "即将到期", now));
|
||||
certRepo.save(cert("ZZ-2", "安全生产许可证", "企业资质", "安全生产许可证", "JZ-AQ-2019-1182",
|
||||
"凯迪建设工程有限公司", "省住房和城乡建设厅", "不分级", "施工安全生产",
|
||||
today.minusYears(4).toString(), today.plusDays(8).toString(), "年检", "即将到期", now));
|
||||
certRepo.save(cert("ZZ-3", "市政公用工程施工总承包", "企业资质", "施工总承包", "JZ-2018-2210",
|
||||
"凯迪建设工程有限公司", "省住房和城乡建设厅", "二级", "市政公用工程",
|
||||
today.minusYears(5).toString(), today.minusDays(20).toString(), "年检", "已过期", now));
|
||||
certRepo.save(cert("ZZ-4", "高新技术企业证书", "企业资质", "高新技术企业", "GR202144000123",
|
||||
"凯迪科技有限公司", "省科技厅", "不分级", "技术研发",
|
||||
today.minusYears(2).toString(), today.plusDays(150).toString(), "三年复评", "有效", now));
|
||||
}
|
||||
|
||||
if (declRepo.count() == 0) {
|
||||
QualDeclaration d1 = decl("ZSB-1", "市政公用工程施工总承包", "升级", "企业资质", "一级",
|
||||
"资质管理办", "李工", "省住房和城乡建设厅", today.minusDays(60).toString(),
|
||||
today.plusDays(30).toString(), "专家评审", 380000.0, now);
|
||||
declRepo.save(d1);
|
||||
taskRepo.save(task(d1.getId(), "注册人员配备", "人员", "王人事", today.plusDays(5).toString(), "进行中", now));
|
||||
taskRepo.save(task(d1.getId(), "业绩材料整理", "业绩", "李工", today.plusDays(10).toString(), "待办", now));
|
||||
taskRepo.save(task(d1.getId(), "网上填报", "填报", "张专员", today.plusDays(15).toString(), "待办", now));
|
||||
|
||||
declRepo.save(decl("ZSB-2", "ISO9001质量管理体系", "延续", "企业资质", "不分级",
|
||||
"资质管理办", "陈工", "认证中心", today.minusDays(15).toString(),
|
||||
today.plusDays(45).toString(), "内审中", 60000.0, now));
|
||||
}
|
||||
|
||||
if (credRepo.count() == 0) {
|
||||
credRepo.save(cred("张伟", "工程部", "一级建造师", "建筑工程", "JZ-2018-44521",
|
||||
"省住建厅", today.minusYears(5).toString(), today.plusDays(120).toString(), "已占用", "凯迪科技园项目", now));
|
||||
credRepo.save(cred("刘强", "工程部", "一级建造师", "市政公用", "JZ-2019-55102",
|
||||
"省住建厅", today.minusYears(4).toString(), today.plusDays(25).toString(), "空闲", null, now));
|
||||
credRepo.save(cred("赵敏", "技术部", "注册结构工程师", "结构", "JG-2017-33421",
|
||||
"省人事厅", today.minusYears(6).toString(), today.minusDays(10).toString(), "空闲", null, now));
|
||||
credRepo.save(cred("孙磊", "安全部", "注册安全工程师", "安全", "AQ-2020-12009",
|
||||
"应急管理厅", today.minusYears(2).toString(), today.plusDays(300).toString(), "空闲", null, now));
|
||||
}
|
||||
|
||||
if (dossierRepo.count() == 0) {
|
||||
StaffDossier s1 = dossier("KD0001", "张伟", "工程部", "项目经理", today.minusYears(5).toString(),
|
||||
"410101199001011234", today.plusDays(60).toString(), today.plusDays(20).toString(), now);
|
||||
dossierRepo.save(s1);
|
||||
itemRepo.save(item(s1.getId(), "基本信息", "入职登记表.pdf", "入职", now));
|
||||
itemRepo.save(item(s1.getId(), "合同信息", "劳动合同(2021).pdf", "入职", now));
|
||||
itemRepo.save(item(s1.getId(), "资质证书", "一级建造师证.jpg", "手工上传", now));
|
||||
// 缺「培训记录」→ 完整性 75%
|
||||
|
||||
StaffDossier s2 = dossier("KD0002", "刘强", "工程部", "技术负责人", today.minusYears(3).toString(),
|
||||
"410101199203054321", today.plusYears(2).toString(), today.plusDays(200).toString(), now);
|
||||
dossierRepo.save(s2);
|
||||
itemRepo.save(item(s2.getId(), "基本信息", "入职登记表.pdf", "入职", now));
|
||||
}
|
||||
}
|
||||
|
||||
private static QualCert cert(String code, String name, String category, String certType, String certNo,
|
||||
String holder, String issuer, String level, String scope, String issueDate,
|
||||
String expireDate, String reviewCycle, String status, Instant now) {
|
||||
QualCert c = new QualCert();
|
||||
c.setCode(code);
|
||||
c.setName(name);
|
||||
c.setCategory(category);
|
||||
c.setCertType(certType);
|
||||
c.setCertNo(certNo);
|
||||
c.setHolder(holder);
|
||||
c.setIssuer(issuer);
|
||||
c.setLevel(level);
|
||||
c.setScope(scope);
|
||||
c.setIssueDate(issueDate);
|
||||
c.setExpireDate(expireDate);
|
||||
c.setReviewCycle(reviewCycle);
|
||||
c.setStatus(status);
|
||||
c.setCreatedAt(now);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static QualDeclaration decl(String code, String qualName, String declType, String category,
|
||||
String targetLevel, String dept, String owner, String externalOrg,
|
||||
String planStart, String planDeadline, String status,
|
||||
double budget, Instant now) {
|
||||
QualDeclaration d = new QualDeclaration();
|
||||
d.setCode(code);
|
||||
d.setQualName(qualName);
|
||||
d.setDeclType(declType);
|
||||
d.setCategory(category);
|
||||
d.setTargetLevel(targetLevel);
|
||||
d.setDept(dept);
|
||||
d.setOwner(owner);
|
||||
d.setExternalOrg(externalOrg);
|
||||
d.setPlanStart(planStart);
|
||||
d.setPlanDeadline(planDeadline);
|
||||
d.setStatus(status);
|
||||
d.setBudget(BigDecimal.valueOf(budget).setScale(2, java.math.RoundingMode.HALF_UP));
|
||||
d.setFeeSpent(BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
|
||||
d.setCreatedAt(now);
|
||||
return d;
|
||||
}
|
||||
|
||||
private static QualDeclTask task(Long declId, String name, String taskType, String assignee,
|
||||
String dueDate, String status, Instant now) {
|
||||
QualDeclTask t = new QualDeclTask();
|
||||
t.setDeclarationId(declId);
|
||||
t.setName(name);
|
||||
t.setTaskType(taskType);
|
||||
t.setAssignee(assignee);
|
||||
t.setDueDate(dueDate);
|
||||
t.setStatus(status);
|
||||
t.setCreatedAt(now);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static StaffCredential cred(String personName, String dept, String credType, String major,
|
||||
String credNo, String issuer, String issueDate, String expireDate,
|
||||
String lockState, String lockedProject, Instant now) {
|
||||
StaffCredential c = new StaffCredential();
|
||||
c.setPersonName(personName);
|
||||
c.setDept(dept);
|
||||
c.setCredType(credType);
|
||||
c.setMajor(major);
|
||||
c.setCredNo(credNo);
|
||||
c.setIssuer(issuer);
|
||||
c.setIssueDate(issueDate);
|
||||
c.setExpireDate(expireDate);
|
||||
c.setLockState(lockState);
|
||||
c.setLockedProject(lockedProject);
|
||||
if ("已占用".equals(lockState)) {
|
||||
c.setLockedDate(LocalDate.now().minusDays(15).toString());
|
||||
}
|
||||
c.setBorrowState("在库");
|
||||
c.setCreatedAt(now);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static StaffDossier dossier(String employeeNo, String name, String dept, String position,
|
||||
String hireDate, String idCardNo, String idCardExpire,
|
||||
String contractExpire, Instant now) {
|
||||
StaffDossier d = new StaffDossier();
|
||||
d.setEmployeeNo(employeeNo);
|
||||
d.setName(name);
|
||||
d.setDept(dept);
|
||||
d.setPosition(position);
|
||||
d.setHireDate(hireDate);
|
||||
d.setIdCardNo(idCardNo);
|
||||
d.setIdCardExpire(idCardExpire);
|
||||
d.setContractExpire(contractExpire);
|
||||
d.setDossierStatus("在职");
|
||||
d.setCompleteness(0);
|
||||
d.setCreatedAt(now);
|
||||
d.setUpdatedAt(now);
|
||||
return d;
|
||||
}
|
||||
|
||||
private static StaffDossierItem item(Long dossierId, String category, String title,
|
||||
String sourceEvent, Instant now) {
|
||||
StaffDossierItem it = new StaffDossierItem();
|
||||
it.setDossierId(dossierId);
|
||||
it.setCategory(category);
|
||||
it.setTitle(title);
|
||||
it.setArchiveDate(LocalDate.now().toString());
|
||||
it.setSourceEvent(sourceEvent);
|
||||
it.setAutoArchived(Boolean.FALSE);
|
||||
it.setCreatedAt(now);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
@@ -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.0(2026SR0567890)", "知识产权", 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.0(V1.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);
|
||||
|
||||
// 图纸3:OA系统网络拓扑图 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.ExternalRegulation;
|
||||
import com.kaidi.oa.domain.FraudInvestigationNote;
|
||||
import com.kaidi.oa.domain.FraudReport;
|
||||
import com.kaidi.oa.domain.LegalRegulation;
|
||||
import com.kaidi.oa.repository.ExternalRegulationRepository;
|
||||
import com.kaidi.oa.repository.FraudInvestigationNoteRepository;
|
||||
import com.kaidi.oa.repository.FraudReportRepository;
|
||||
import com.kaidi.oa.repository.LegalRegulationRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 审计监察部演示种子数据(补缺口 #6 独立调查空间 + #9 法规制度库)。
|
||||
*
|
||||
* 补齐:
|
||||
* - FraudReport(舞弊举报案件,缺口#6):5 条跨状态/渠道/严重程度的演示举报,
|
||||
* 含已结案/调查中/已驳回多状态,让舞弊监测列表活体可见;
|
||||
* - FraudInvestigationNote(独立调查空间底稿,缺口#6):为调查中案件添加保密底稿演示;
|
||||
* - LegalRegulation(内部制度库,缺口#9):7 条已发布的内部制度演示,含采购/财务/HR等类型;
|
||||
* - ExternalRegulation(外部法规库,缺口#9):6 条现行有效外部法规演示。
|
||||
*
|
||||
* 幂等:各表 count > 0 即跳过。
|
||||
* @Order(202) 在 FertPkgAccountingSeeder(@Order 201) 之后运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(202)
|
||||
public class AuditDeptSeeder implements ApplicationRunner {
|
||||
|
||||
private final FraudReportRepository fraudRepo;
|
||||
private final FraudInvestigationNoteRepository noteRepo;
|
||||
private final LegalRegulationRepository internalRegRepo;
|
||||
private final ExternalRegulationRepository externalRegRepo;
|
||||
|
||||
public AuditDeptSeeder(FraudReportRepository fraudRepo,
|
||||
FraudInvestigationNoteRepository noteRepo,
|
||||
LegalRegulationRepository internalRegRepo,
|
||||
ExternalRegulationRepository externalRegRepo) {
|
||||
this.fraudRepo = fraudRepo;
|
||||
this.noteRepo = noteRepo;
|
||||
this.internalRegRepo = internalRegRepo;
|
||||
this.externalRegRepo = externalRegRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedFraudReports();
|
||||
seedInternalRegulations();
|
||||
seedExternalRegulations();
|
||||
}
|
||||
|
||||
// ---- 舞弊举报案件种子 ----
|
||||
|
||||
private void seedFraudReports() {
|
||||
if (fraudRepo.count() > 0) return;
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 案件1:供应商采购价格异常 — 已结案·查实
|
||||
FraudReport r1 = new FraudReport();
|
||||
r1.setCaseNo("FR-2026-001");
|
||||
r1.setTitle("供应商A采购价格异常高于市场价30%");
|
||||
r1.setAccused("采购部 张某某 / 供应商凯隆贸易有限公司");
|
||||
r1.setDept("采购部");
|
||||
r1.setContent("举报人反映,2025年Q3至Q4期间,公司向凯隆贸易采购的某型号化工原料价格" +
|
||||
"持续高于同类市场均价约28%-32%。涉及3个批次合计采购金额约85万元。" +
|
||||
"初步核查发现采购部张某某与供应商法人存在亲属关系(妻兄)。");
|
||||
r1.setReportChannel("邮件");
|
||||
r1.setAnonymous(false);
|
||||
r1.setReporter("李建国");
|
||||
r1.setReporterContact("13812345678(仅调查组可见)");
|
||||
r1.setSeverity("高");
|
||||
r1.setInvolvedAmount(new BigDecimal("852000.00"));
|
||||
r1.setStatus("已结案");
|
||||
r1.setAssignee("王审计");
|
||||
r1.setInvestigateNote("2026-02-10 调取2025年Q3-Q4采购台账,发现涉案批次单价高于同期询价均价31.2%。\n" +
|
||||
"2026-02-15 约谈采购部张某某,其承认与供应商法人存在亲属关系但未申报利益冲突。\n" +
|
||||
"2026-02-20 向财务部调取银行流水,未发现账外收益转移,初步认定为利益关系未申报。");
|
||||
r1.setConclusion("查实:采购部张某某与涉案供应商法人存在亲属关系,采购价格高于市场均价," +
|
||||
"违反《采购管理制度》利益冲突申报条款。但未查实收受回扣。建议:追缴超价差额约26万元," +
|
||||
"对张某某给予警告处分并调离采购岗位。");
|
||||
r1.setConfirmed(true);
|
||||
r1.setDisposition("追责处分");
|
||||
r1.setReportDate("2026-01-15");
|
||||
r1.setClosedDate("2026-02-25");
|
||||
r1.setCreatedAt(now.minus(45, ChronoUnit.DAYS));
|
||||
fraudRepo.save(r1);
|
||||
|
||||
// 案件2:费用报销临近限额异常 — 调查中
|
||||
FraudReport r2 = new FraudReport();
|
||||
r2.setCaseNo("FR-2026-002");
|
||||
r2.setTitle("研发部员工多次费用报销临近500元审批门槛");
|
||||
r2.setAccused("研发部 赵明辉");
|
||||
r2.setDept("研发部");
|
||||
r2.setContent("系统自动预警:研发部赵明辉2026年1-3月累计提交17次费用报销," +
|
||||
"其中14次金额在490-499元之间,明显规避500元以上需经理审批的规则。" +
|
||||
"累计报销金额7,238元,涉及餐饮/差旅/耗材等多类别。");
|
||||
r2.setReportChannel("二维码");
|
||||
r2.setAnonymous(true);
|
||||
r2.setReporter("匿名");
|
||||
r2.setSeverity("中");
|
||||
r2.setInvolvedAmount(new BigDecimal("7238.00"));
|
||||
r2.setStatus("调查中");
|
||||
r2.setAssignee("陈审计");
|
||||
r2.setInvestigateNote("2026-04-01 调取17次报销凭证,其中3次餐饮凭证与行程单不符(报销当日无差旅记录)。\n" +
|
||||
"2026-04-05 调取门禁系统记录,核实部分报销日期当事人在本地办公并非出差。");
|
||||
r2.setReportDate("2026-03-25");
|
||||
r2.setCreatedAt(now.minus(20, ChronoUnit.DAYS));
|
||||
fraudRepo.save(r2);
|
||||
|
||||
// 案件3:仓库物料盗用 — 调查中(已登记后已分配)
|
||||
FraudReport r3 = new FraudReport();
|
||||
r3.setCaseNo("FR-2026-003");
|
||||
r3.setTitle("仓库物料出库单与实际库存差异连续3个月偏差超5%");
|
||||
r3.setAccused("生产部仓库 不详");
|
||||
r3.setDept("生产部");
|
||||
r3.setContent("财务月度盘点发现2025年12月至2026年2月仓库某类原材料(品号RM-0312)" +
|
||||
"出库单数量与实际减少量差异达5.8%-7.2%,已发现2次实物数量少于账面数量约3吨。" +
|
||||
"初步怀疑存在物料外流,请求调查。");
|
||||
r3.setReportChannel("网页");
|
||||
r3.setAnonymous(false);
|
||||
r3.setReporter("财务部 刘会计");
|
||||
r3.setReporterContact("内部分机8801");
|
||||
r3.setSeverity("高");
|
||||
r3.setInvolvedAmount(new BigDecimal("186000.00"));
|
||||
r3.setStatus("调查中");
|
||||
r3.setAssignee("王审计");
|
||||
r3.setInvestigateNote("2026-03-10 调取2025年12月至今仓库出入库台账,确认差异存在。\n" +
|
||||
"2026-03-15 调阅仓库视频监控(已申请IT部协助),正在比对异常出库时段。");
|
||||
r3.setReportDate("2026-03-08");
|
||||
r3.setCreatedAt(now.minus(18, ChronoUnit.DAYS));
|
||||
fraudRepo.save(r3);
|
||||
|
||||
// 案件4:举报已驳回(核查不成立)
|
||||
FraudReport r4 = new FraudReport();
|
||||
r4.setCaseNo("FR-2025-018");
|
||||
r4.setTitle("举报销售部人员私下收取客户回扣");
|
||||
r4.setAccused("销售部 孙某");
|
||||
r4.setDept("销售部");
|
||||
r4.setContent("举报称销售部孙某在承接某大客户订单时私下收取回扣5万元," +
|
||||
"并刻意压低成交价格造成公司损失。");
|
||||
r4.setReportChannel("电话");
|
||||
r4.setAnonymous(true);
|
||||
r4.setReporter("匿名");
|
||||
r4.setSeverity("高");
|
||||
r4.setInvolvedAmount(new BigDecimal("50000.00"));
|
||||
r4.setStatus("已驳回");
|
||||
r4.setAssignee("陈审计");
|
||||
r4.setConclusion("经核查:涉案订单成交价与报价单一致,价格系经部门总监审批的标准优惠价格," +
|
||||
"不存在低价处理情况。对孙某的银行流水进行核查,未发现异常大额收入。" +
|
||||
"判定:举报线索不成立,予以驳回。");
|
||||
r4.setConfirmed(false);
|
||||
r4.setDisposition("不予处理");
|
||||
r4.setReportDate("2025-11-20");
|
||||
r4.setClosedDate("2025-12-10");
|
||||
r4.setCreatedAt(now.minus(180, ChronoUnit.DAYS));
|
||||
fraudRepo.save(r4);
|
||||
|
||||
// 案件5:新登记待分配
|
||||
FraudReport r5 = new FraudReport();
|
||||
r5.setCaseNo("FR-2026-004");
|
||||
r5.setTitle("行政部车辆使用记录与实际派车存在出入");
|
||||
r5.setAccused("行政部 车辆管理员 周某");
|
||||
r5.setDept("行政部");
|
||||
r5.setContent("举报行政部车辆管理员周某多次将公司车辆私用于个人活动," +
|
||||
"行驶记录与派车单显示的出行目的地不符,怀疑存在公车私用和油卡套现行为。" +
|
||||
"近半年涉及车辆3辆,估计异常油卡消费约1.2万元。");
|
||||
r5.setReportChannel("网页");
|
||||
r5.setAnonymous(false);
|
||||
r5.setReporter("行政部 李文华");
|
||||
r5.setReporterContact("13900001234");
|
||||
r5.setSeverity("中");
|
||||
r5.setInvolvedAmount(new BigDecimal("12000.00"));
|
||||
r5.setStatus("已登记");
|
||||
r5.setReportDate("2026-06-10");
|
||||
r5.setCreatedAt(now.minus(5, ChronoUnit.DAYS));
|
||||
fraudRepo.save(r5);
|
||||
|
||||
// 为调查中的案件2和3补充独立调查空间底稿
|
||||
seedInvestigationNotes(r2, r3);
|
||||
}
|
||||
|
||||
private void seedInvestigationNotes(FraudReport caseExpense, FraudReport caseMaterial) {
|
||||
if (noteRepo.count() > 0) return;
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 案件2(费用报销临近限额)的调查底稿
|
||||
FraudInvestigationNote n1 = new FraudInvestigationNote();
|
||||
n1.setReportId(caseExpense.getId());
|
||||
n1.setNoteType("证据清单");
|
||||
n1.setTitle("报销凭证汇总分析表");
|
||||
n1.setContent("已收集17份原始报销凭证复印件(2026-01至2026-03)。\n" +
|
||||
"关键发现:\n" +
|
||||
"- 2026-01-12 餐饮发票499元:消费地点(深圳市福田区)与当日打卡记录(本市本公司)不符\n" +
|
||||
"- 2026-02-08 差旅499元:未找到对应出差审批单\n" +
|
||||
"- 2026-03-15 耗材495元:供应商为个人收款账户,非正规企业发票\n" +
|
||||
"上述3笔共1493元存在明显疑点,建议重点追查。");
|
||||
n1.setAuthor("陈审计");
|
||||
n1.setConfidential(true);
|
||||
n1.setCreatedAt(now.minus(15, ChronoUnit.DAYS));
|
||||
n1.setUpdatedAt(now.minus(15, ChronoUnit.DAYS));
|
||||
noteRepo.save(n1);
|
||||
|
||||
FraudInvestigationNote n2 = new FraudInvestigationNote();
|
||||
n2.setReportId(caseExpense.getId());
|
||||
n2.setNoteType("财务分析");
|
||||
n2.setTitle("临近限额报销模式统计分析");
|
||||
n2.setContent("使用数据分析:\n" +
|
||||
"- 全公司2026年Q1同类员工(研发部,共23人)报销490-499元区间次数:平均0.3次/人\n" +
|
||||
"- 涉案人员:14次(是均值的47倍),统计显著异常(p<0.001)\n" +
|
||||
"- 涉案人员同期报销总次数17次,其中490-499元占82%\n" +
|
||||
"结论:高度怀疑为主动规避审批门槛的刻意行为。");
|
||||
n2.setAuthor("陈审计");
|
||||
n2.setConfidential(true);
|
||||
n2.setCreatedAt(now.minus(12, ChronoUnit.DAYS));
|
||||
n2.setUpdatedAt(now.minus(12, ChronoUnit.DAYS));
|
||||
noteRepo.save(n2);
|
||||
|
||||
FraudInvestigationNote n3 = new FraudInvestigationNote();
|
||||
n3.setReportId(caseExpense.getId());
|
||||
n3.setNoteType("结论草稿");
|
||||
n3.setTitle("调查初步结论草稿(待复核)");
|
||||
n3.setContent("初步结论:\n" +
|
||||
"有据可查的虚假报销3笔共1493元(门禁记录/缺失审批/非正规发票)。\n" +
|
||||
"其余11笔证据不足以认定舞弊,但反映报销管理内控薄弱。\n" +
|
||||
"建议处理:\n" +
|
||||
"1. 追缴有据可查3笔虚假报销1493元;\n" +
|
||||
"2. 对赵明辉给予警告处分;\n" +
|
||||
"3. 建议公司调整费用报销制度,300元以上均需上级审批,消除门槛漏洞。\n" +
|
||||
"[本草稿待审计组长复核后定稿]");
|
||||
n3.setAuthor("陈审计");
|
||||
n3.setConfidential(true);
|
||||
n3.setCreatedAt(now.minus(8, ChronoUnit.DAYS));
|
||||
n3.setUpdatedAt(now.minus(8, ChronoUnit.DAYS));
|
||||
noteRepo.save(n3);
|
||||
|
||||
// 案件3(物料差异)的调查底稿
|
||||
FraudInvestigationNote n4 = new FraudInvestigationNote();
|
||||
n4.setReportId(caseMaterial.getId());
|
||||
n4.setNoteType("调查记录");
|
||||
n4.setTitle("仓库物料差异核查记录");
|
||||
n4.setContent("第一轮核查(2026-03-10至15):\n" +
|
||||
"- 调阅品号RM-0312出入库台账3个月记录,确认账面减少量与出库单差异如下:\n" +
|
||||
" · 2025-12:账面差异2.1吨(市值约12.6万元)\n" +
|
||||
" · 2026-01:账面差异3.8吨(市值约22.8万元)\n" +
|
||||
" · 2026-02:账面差异5.1吨(市值约30.6万元)\n" +
|
||||
"差异呈扩大趋势,不符合正常损耗规律(正常损耗≤0.5%)。");
|
||||
n4.setAuthor("王审计");
|
||||
n4.setConfidential(true);
|
||||
n4.setCreatedAt(now.minus(16, ChronoUnit.DAYS));
|
||||
n4.setUpdatedAt(now.minus(16, ChronoUnit.DAYS));
|
||||
noteRepo.save(n4);
|
||||
|
||||
FraudInvestigationNote n5 = new FraudInvestigationNote();
|
||||
n5.setReportId(caseMaterial.getId());
|
||||
n5.setNoteType("证据清单");
|
||||
n5.setTitle("监控录像异常时段记录(已提取)");
|
||||
n5.setContent("经IT部协助提取监控录像,发现以下异常时段:\n" +
|
||||
"- 2026-01-08 凌晨02:37:仓库东侧门禁刷卡开门(非正常班次),车辆编号X未登记派车\n" +
|
||||
"- 2026-02-14 晚间19:45:同类异常,同一刷卡人员(工号W-0088,已提取比对)\n" +
|
||||
"文件已加密保存,文件哈希记录于此。\n" +
|
||||
"下一步:比对工号W-0088身份,约谈问询。");
|
||||
n5.setFileRef("surveillance_extract_20260310.mp4.enc");
|
||||
n5.setFileHash("a8f3b2c1d4e5f6789012345678901234abcdef0123456789abcdef0123456789ab");
|
||||
n5.setAuthor("王审计");
|
||||
n5.setConfidential(true);
|
||||
n5.setCreatedAt(now.minus(11, ChronoUnit.DAYS));
|
||||
n5.setUpdatedAt(now.minus(11, ChronoUnit.DAYS));
|
||||
noteRepo.save(n5);
|
||||
}
|
||||
|
||||
// ---- 内部制度库种子 ----
|
||||
|
||||
private void seedInternalRegulations() {
|
||||
if (internalRegRepo.count() > 0) return;
|
||||
Instant now = Instant.now();
|
||||
|
||||
Object[][] data = {
|
||||
// {title, docType, scope, responsibleDept, version, status, summary, publishDate}
|
||||
{"采购管理制度", "管理制度", "全公司", "采购部", "V3.1",
|
||||
"已发布", "规范公司各类物资、服务采购行为,明确采购权限、比价要求、供应商资质审查、" +
|
||||
"合同签订、入库验收及付款程序,防范采购风险。含利益冲突申报条款:采购人员与供应商存在" +
|
||||
"亲属或利益关系必须书面申报,并在该笔采购中回避。", "2025-03-01"},
|
||||
{"费用报销管理规定", "管理制度", "全公司", "财务部", "V2.3",
|
||||
"已发布", "规定差旅费、招待费、办公用品、业务费等各类费用的报销标准、审批权限及流程。" +
|
||||
"核心规定:500元以上报销需部门负责人审批;2000元以上需分管领导审批;单笔发票必须为" +
|
||||
"增值税普通发票或专用发票,不接受手写收据。", "2025-01-15"},
|
||||
{"供应商管理办法", "管理制度", "采购部", "采购部", "V2.0",
|
||||
"已发布", "建立供应商准入、评估、分级管理体系。规定供应商须具备合规资质证书," +
|
||||
"年度履约评估得分低于70分须整改,连续两年低于60分取消合格供应商资格。", "2024-09-01"},
|
||||
{"资金管理与支付制度", "管理制度", "全公司", "财务部", "V4.0",
|
||||
"已发布", "规定公司资金收付管理,包括账户开立审批、付款凭证要求、大额付款" +
|
||||
"(50万元以上)双人复核制度、银行对账规程及资金预测报告编制要求。", "2025-06-01"},
|
||||
{"员工行为准则与廉洁从业规定", "管理制度", "全公司", "人力资源部", "V1.5",
|
||||
"已发布", "规定员工在工作中应遵守的行为准则,包括禁止收受供应商/客户礼品(单次价值超100元)、" +
|
||||
"禁止利用公司资源谋取私利、禁止泄露公司商业机密等。违规处分包括警告、记过、解除劳动合同。",
|
||||
"2024-04-01"},
|
||||
{"合同管理办法", "管理制度", "全公司", "法务风险部", "V3.0",
|
||||
"已发布", "规范公司各类合同的起草、审核、签订、履约管理及台账归档流程。合同签订前须经" +
|
||||
"法务合规审核;合同金额超过100万元须经总经理审批;合同变更须签补充协议并经原审批层级审批。",
|
||||
"2025-02-10"},
|
||||
{"固定资产管理制度", "管理制度", "全公司", "财务部", "V2.1",
|
||||
"已发布", "规定固定资产的购置审批、登记入账、定期盘点、折旧计提及处置核销规程。" +
|
||||
"每年6月和12月进行全公司固定资产实物盘点,差异须填写盘点差异说明表并说明原因。",
|
||||
"2024-10-01"}
|
||||
};
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
Object[] row = data[i];
|
||||
LegalRegulation r = new LegalRegulation();
|
||||
r.setRegNo(String.format("ZD-2024-%03d", i + 1));
|
||||
r.setTitle((String) row[0]);
|
||||
r.setDocType((String) row[1]);
|
||||
r.setScope((String) row[2]);
|
||||
r.setResponsibleDept((String) row[3]);
|
||||
r.setVersion((String) row[4]);
|
||||
r.setStatus((String) row[5]);
|
||||
r.setSummary((String) row[6]);
|
||||
r.setPublishDate((String) row[7]);
|
||||
r.setReadCount((int)(Math.random() * 80) + 5);
|
||||
r.setPublisher("法务合规部 审核发布");
|
||||
r.setCreatedAt(now.minus(365 - i * 30L, ChronoUnit.DAYS));
|
||||
internalRegRepo.save(r);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 外部法规库种子 ----
|
||||
|
||||
private void seedExternalRegulations() {
|
||||
if (externalRegRepo.count() > 0) return;
|
||||
Instant now = Instant.now();
|
||||
|
||||
Object[][] data = {
|
||||
// {regNo, title, issuer, regType, issueDate, effectiveDate, impactDescription, priority}
|
||||
{"GB/T 19001-2016", "质量管理体系 要求",
|
||||
"国家质量监督检验检疫总局", "行业法规", "2016-12-30", "2017-07-01",
|
||||
"要求公司建立完整的质量管理体系,通过内外部审核持续改进。" +
|
||||
"内控部门需审计质量管理程序合规性,确保文件控制、不合格品处理、纠正预防措施等程序有效运行。",
|
||||
"高"},
|
||||
{"中华人民共和国会计法(2024修订)", "中华人民共和国会计法",
|
||||
"全国人民代表大会常务委员会", "金融法规", "2024-06-28", "2025-01-01",
|
||||
"要求会计信息真实完整,禁止伪造、变造会计凭证和会计账簿。" +
|
||||
"审计部门需重点核查:会计凭证的完整性、会计账簿是否存在篡改痕迹、" +
|
||||
"会计信息是否与业务实质一致。违规可追究刑事责任。",
|
||||
"高"},
|
||||
{"劳动合同法(2012修订版)", "中华人民共和国劳动合同法",
|
||||
"全国人民代表大会常务委员会", "劳动法", "2012-12-28", "2013-07-01",
|
||||
"规定劳动合同的签订、履行、变更、解除和终止。审计重点:" +
|
||||
"员工是否均签订正式劳动合同、试用期是否合规、" +
|
||||
"劳动合同变更(如岗位调整)是否经双方书面同意、解除合同是否符合法定程序。",
|
||||
"高"},
|
||||
{"网络安全法", "中华人民共和国网络安全法",
|
||||
"全国人民代表大会常务委员会", "数据安全法", "2016-11-07", "2017-06-01",
|
||||
"要求建立网络安全保护制度,对关键信息基础设施实施重点保护。" +
|
||||
"内部审计需核查:数据访问权限管理是否合规、是否建立安全事件应急预案、" +
|
||||
"员工账号管理是否满足最小权限原则。",
|
||||
"高"},
|
||||
{"政府会计准则第1号——存货", "政府会计准则第1号",
|
||||
"财政部", "行业法规", "2016-07-06", "2017-01-01",
|
||||
"规定存货的确认、计量和报告要求。审计时需核查:" +
|
||||
"存货盘点程序是否到位、库存商品是否按成本法计量、" +
|
||||
"跌价准备是否按规定计提。",
|
||||
"中"},
|
||||
{"个人信息保护法", "中华人民共和国个人信息保护法",
|
||||
"全国人民代表大会常务委员会", "数据安全法", "2021-08-20", "2021-11-01",
|
||||
"规范个人信息处理活动,保护个人信息权益。HR部门审计关注点:" +
|
||||
"员工个人信息收集范围是否超出业务必要范围、" +
|
||||
"是否明示告知信息使用目的、" +
|
||||
"离职员工信息是否按规定删除/脱敏处理。",
|
||||
"高"}
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
ExternalRegulation r = new ExternalRegulation();
|
||||
r.setRegNo((String) row[0]);
|
||||
r.setTitle((String) row[1]);
|
||||
r.setIssuer((String) row[2]);
|
||||
r.setRegType((String) row[3]);
|
||||
r.setIssueDate((String) row[4]);
|
||||
r.setEffectiveDate((String) row[5]);
|
||||
r.setStatus("现行有效");
|
||||
r.setImpactDescription((String) row[6]);
|
||||
r.setPriority((String) row[7]);
|
||||
r.setCreatedAt(now.minus(90, ChronoUnit.DAYS));
|
||||
r.setUpdatedAt(now.minus(90, ChronoUnit.DAYS));
|
||||
externalRegRepo.save(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.BidTenderCollab;
|
||||
import com.kaidi.oa.repository.BidTenderCollabRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 标书协作工单演示数据。
|
||||
*
|
||||
* 覆盖三个办事处(岳阳/长沙/湖南分公司)×四种协作类型×五种状态,
|
||||
* 让前端看板活体可见且流程闭环可演示。
|
||||
* 幂等:若已有数据则跳过,防止重启重复写入。
|
||||
*/
|
||||
@Component
|
||||
@Order(215)
|
||||
public class BidTenderCollabSeeder implements ApplicationRunner {
|
||||
|
||||
private final BidTenderCollabRepository repo;
|
||||
|
||||
public BidTenderCollabSeeder(BidTenderCollabRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
if (repo.count() > 0) return;
|
||||
|
||||
// 岳阳办事处 → 本地价格提供 → 资料完备(已闭环)
|
||||
collab(3L, "本地价格提供",
|
||||
"岳阳城乡污水处理厂运营服务项目·本地设备单价清单",
|
||||
"岳阳办事处", "王芳", "李明",
|
||||
"提供岳阳本地鼓风机、格栅、污泥脱水机等设备市场含税单价(含 3 家供应商报价),"
|
||||
+ "并补充岳阳市政工程劳务综合费率(元/m³)。",
|
||||
null, "2026-05-28", "资料完备",
|
||||
"收到价格清单,设备单价与公司库差异±5%以内,劳务费率参考岳阳市定额,已采纳。");
|
||||
|
||||
// 长沙办事处 → 资质材料提供 → 总部审阅中(进行中)
|
||||
collab(2L, "资质材料提供",
|
||||
"湖南省高速公路机电EPC总承包项目·资质证件扫描包",
|
||||
"长沙办事处", "张伟", "赵磊",
|
||||
"提供凯迪湖南分公司安全生产许可证(2025-2028)、测量资质证书、EPC 总承包资质、"
|
||||
+ "近三年同类项目业绩合同(选取金额≥5000万的6份)。",
|
||||
null, "2026-07-01", "总部审阅中",
|
||||
null);
|
||||
|
||||
// 长沙办事处 → 踏勘报告 → 已提交(等待总部审阅)
|
||||
collab(7L, "踏勘报告",
|
||||
"湖南交投园区道路工程信息化项目·现场踏勘报告",
|
||||
"长沙办事处", "张伟", "陈静",
|
||||
"踏勘内容:①园区路网现状测量(全长 4.2km,双向四车道);②地下管网探测结果;"
|
||||
+ "③智慧路灯/监控点位分布;④施工条件(净空/运输限制)说明。",
|
||||
null, "2026-07-25", "已提交",
|
||||
null);
|
||||
|
||||
// 岳阳办事处 → 综合协助 → 待提交(刚发起)
|
||||
collab(5L, "综合协助",
|
||||
"岳阳楼区市政排水管网改造项目·中标后总部协作清单",
|
||||
"岳阳办事处", "刘洋", "李明",
|
||||
"中标后总部协作事项:①协助提供本地分包商资质(市政管道/土方);"
|
||||
+ "②提供岳阳市区开挖许可申请模板;③同步本地监理机构备选名单。",
|
||||
null, "2026-06-20", "待提交",
|
||||
null);
|
||||
|
||||
// 湖南分公司 → 本地价格提供 → 已归档(全流程闭环)
|
||||
collab(4L, "本地价格提供",
|
||||
"怀化市政道路智慧化改造项目·劳务及材料综合报价",
|
||||
"湖南分公司", "刘洋", "赵磊",
|
||||
"提供怀化市区人工单价(综合工日价)+主要材料价格:沥青混凝土(AC-13/AC-20)、"
|
||||
+ "预制混凝土管(DN400/600)、信号灯控制器;附怀化发改委2026Q1建材指导价文件扫描。",
|
||||
null, "2026-05-20", "已归档",
|
||||
"材料价格与总部测算偏差<3%,采纳并已计入投标报价。标书已提交,本协作归档。");
|
||||
|
||||
// 岳阳办事处 → 资质材料提供 → 资料完备(完整链路演示)
|
||||
collab(8L, "资质材料提供",
|
||||
"靖州县水利局环保设备采购项目·竞争性谈判资质包",
|
||||
"岳阳办事处", "王芳", "李明",
|
||||
"竞争性谈判资质:①营业执照副本;②一般纳税人资格证书;"
|
||||
+ "③环保设备生产厂家授权函(含三联环保/国祯环境);④同类业绩(靖州/怀化各一份)。",
|
||||
null, "2026-05-30", "资料完备",
|
||||
"资质核实完毕,授权函原件联系方确认有效,已整理入标书附件包,审核通过。");
|
||||
}
|
||||
|
||||
private void collab(Long bidId, String collabType, String subject,
|
||||
String branchName, String submitter, String hqReviewer,
|
||||
String materialSummary, Long storedFileId, String dueDate,
|
||||
String status, String reviewNote) {
|
||||
BidTenderCollab c = new BidTenderCollab();
|
||||
c.setBidId(bidId);
|
||||
c.setCollabType(collabType);
|
||||
c.setSubject(subject);
|
||||
c.setBranchName(branchName);
|
||||
c.setSubmitter(submitter);
|
||||
c.setHqReviewer(hqReviewer);
|
||||
c.setMaterialSummary(materialSummary);
|
||||
c.setStoredFileId(storedFileId);
|
||||
c.setDueDate(dueDate);
|
||||
c.setStatus(status);
|
||||
c.setReviewNote(reviewNote);
|
||||
c.setCreatedAt(Instant.now());
|
||||
c.setUpdatedAt(Instant.now());
|
||||
repo.save(c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.BidDocVersion;
|
||||
import com.kaidi.oa.domain.BidTenderDoc;
|
||||
import com.kaidi.oa.repository.BidDocVersionRepository;
|
||||
import com.kaidi.oa.repository.BidRepository;
|
||||
import com.kaidi.oa.repository.BidTenderDocRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 招标文件台账 + 投标文件协同编制版本演示数据。
|
||||
*
|
||||
* @Order(215) 在所有主数据 Seeder 之后运行;幂等(count > 0 则跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(215)
|
||||
public class BidTenderDocSeeder implements CommandLineRunner {
|
||||
|
||||
private final BidTenderDocRepository tenderDocRepo;
|
||||
private final BidDocVersionRepository docVersionRepo;
|
||||
private final BidRepository bidRepo;
|
||||
|
||||
public BidTenderDocSeeder(BidTenderDocRepository tenderDocRepo,
|
||||
BidDocVersionRepository docVersionRepo,
|
||||
BidRepository bidRepo) {
|
||||
this.tenderDocRepo = tenderDocRepo;
|
||||
this.docVersionRepo = docVersionRepo;
|
||||
this.bidRepo = bidRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (tenderDocRepo.count() > 0) return; // 幂等
|
||||
|
||||
// 取前两个投标 id(若存在)
|
||||
var bids = bidRepo.findAll();
|
||||
long bid1 = bids.isEmpty() ? 1L : bids.get(0).getId();
|
||||
long bid2 = bids.size() > 1 ? bids.get(1).getId() : bid1;
|
||||
|
||||
Instant now = Instant.now();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// ====== 招标文件台账演示数据 ======
|
||||
// bid1 — 招标文件(已下载,已付标书费)
|
||||
BidTenderDoc d1 = new BidTenderDoc();
|
||||
d1.setBidId(bid1);
|
||||
d1.setDocType("招标文件");
|
||||
d1.setDocTitle("长沙市污水处理厂扩容工程招标文件(第一册)");
|
||||
d1.setFileRef("files/bid-tender/招标文件_污水厂扩容_v1.pdf");
|
||||
d1.setDownloadDate(today.minusDays(20).toString());
|
||||
d1.setPurchaseFee(Money.of(500.00));
|
||||
d1.setFeePaidStatus("已付");
|
||||
d1.setDeadlineDate(today.plusDays(5).toString());
|
||||
d1.setStatus("已下载");
|
||||
d1.setBondAmount(Money.of(50000.00));
|
||||
d1.setBondPayMethod("银行转账");
|
||||
d1.setBondStatus("已缴");
|
||||
d1.setOwner("陈志远");
|
||||
d1.setCreatedAt(now);
|
||||
tenderDocRepo.save(d1);
|
||||
|
||||
// bid1 — 澄清文件
|
||||
BidTenderDoc d2 = new BidTenderDoc();
|
||||
d2.setBidId(bid1);
|
||||
d2.setDocType("澄清文件");
|
||||
d2.setDocTitle("长沙市污水处理厂扩容工程招标澄清函(第1号)");
|
||||
d2.setFileRef("files/bid-tender/澄清函_污水厂扩容_01.pdf");
|
||||
d2.setDownloadDate(today.minusDays(10).toString());
|
||||
d2.setPurchaseFee(Money.ZERO);
|
||||
d2.setFeePaidStatus("已付");
|
||||
d2.setDeadlineDate(today.plusDays(5).toString());
|
||||
d2.setStatus("已下载");
|
||||
d2.setBondAmount(Money.ZERO);
|
||||
d2.setBondStatus("未缴");
|
||||
d2.setOwner("陈志远");
|
||||
d2.setCreatedAt(now);
|
||||
tenderDocRepo.save(d2);
|
||||
|
||||
// bid1 — 答疑文件
|
||||
BidTenderDoc d3 = new BidTenderDoc();
|
||||
d3.setBidId(bid1);
|
||||
d3.setDocType("答疑文件");
|
||||
d3.setDocTitle("长沙市污水处理厂扩容工程投标答疑(汇总版)");
|
||||
d3.setFileRef("files/bid-tender/答疑_污水厂扩容_汇总.pdf");
|
||||
d3.setDownloadDate(today.minusDays(7).toString());
|
||||
d3.setPurchaseFee(Money.ZERO);
|
||||
d3.setFeePaidStatus("已付");
|
||||
d3.setDeadlineDate(today.plusDays(5).toString());
|
||||
d3.setStatus("已下载");
|
||||
d3.setBondAmount(Money.ZERO);
|
||||
d3.setBondStatus("未缴");
|
||||
d3.setRemark("答疑共 12 条,涉及工艺方案和报价清单调整");
|
||||
d3.setOwner("陈志远");
|
||||
d3.setCreatedAt(now);
|
||||
tenderDocRepo.save(d3);
|
||||
|
||||
// bid2 — 招标文件(未付标书费,保证金逾期未退)
|
||||
BidTenderDoc d4 = new BidTenderDoc();
|
||||
d4.setBidId(bid2);
|
||||
d4.setDocType("招标文件");
|
||||
d4.setDocTitle("株洲市道路改造工程招标文件");
|
||||
d4.setFileRef("files/bid-tender/招标文件_株洲道路_v1.pdf");
|
||||
d4.setDownloadDate(today.minusDays(65).toString());
|
||||
d4.setPurchaseFee(Money.of(300.00));
|
||||
d4.setFeePaidStatus("未付");
|
||||
d4.setDeadlineDate(today.minusDays(30).toString()); // 已过期
|
||||
d4.setStatus("已下载");
|
||||
d4.setBondAmount(Money.of(30000.00));
|
||||
d4.setBondPayMethod("保险保函");
|
||||
d4.setBondStatus("已缴"); // 逾期未退,催办
|
||||
d4.setRemark("未中标,保证金应已退回但未确认,请催办");
|
||||
d4.setOwner("刘伟");
|
||||
d4.setCreatedAt(now.minusSeconds(3600 * 24 * 65));
|
||||
tenderDocRepo.save(d4);
|
||||
|
||||
// bid2 — 补充文件(截止日临近预警演示)
|
||||
BidTenderDoc d5 = new BidTenderDoc();
|
||||
d5.setBidId(bid2);
|
||||
d5.setDocType("补充文件");
|
||||
d5.setDocTitle("株洲市道路改造工程补充技术要求");
|
||||
d5.setFileRef("files/bid-tender/补充要求_株洲道路.pdf");
|
||||
d5.setDownloadDate(today.minusDays(3).toString());
|
||||
d5.setPurchaseFee(Money.ZERO);
|
||||
d5.setFeePaidStatus("已付");
|
||||
d5.setDeadlineDate(today.plusDays(2).toString()); // 紧急 — 2天内到期
|
||||
d5.setStatus("待处理");
|
||||
d5.setBondAmount(Money.ZERO);
|
||||
d5.setBondStatus("未缴");
|
||||
d5.setOwner("刘伟");
|
||||
d5.setCreatedAt(now);
|
||||
tenderDocRepo.save(d5);
|
||||
|
||||
// ====== 投标文件版本协同演示数据 ======
|
||||
if (docVersionRepo.count() > 0) return;
|
||||
|
||||
// bid1 商务标 — v1 草稿 → v2 审核中
|
||||
BidDocVersion v1 = new BidDocVersion();
|
||||
v1.setBidId(bid1);
|
||||
v1.setDocPart("商务标");
|
||||
v1.setVersion("v1");
|
||||
v1.setDocTitle("污水厂扩容工程商务标(初稿)");
|
||||
v1.setFileRef("files/bid-doc/商务标_污水厂_v1.docx");
|
||||
v1.setCollaborators("陈志远,李明");
|
||||
v1.setSubmitter("陈志远");
|
||||
v1.setStatus("已驳回");
|
||||
v1.setReviewComment("报价清单有误,需根据答疑调整综合单价");
|
||||
v1.setAnnotations("[李明 @ " + today.minusDays(12) + "] 第3章综合单价表需重算\n[陈志远 @ " + today.minusDays(11) + "] 已核,修订后提交v2");
|
||||
v1.setSubmittedAt(now.minusSeconds(3600 * 24 * 12));
|
||||
v1.setCreatedAt(now.minusSeconds(3600 * 24 * 14));
|
||||
docVersionRepo.save(v1);
|
||||
|
||||
BidDocVersion v2 = new BidDocVersion();
|
||||
v2.setBidId(bid1);
|
||||
v2.setDocPart("商务标");
|
||||
v2.setVersion("v2");
|
||||
v2.setDocTitle("污水厂扩容工程商务标(修订版)");
|
||||
v2.setFileRef("files/bid-doc/商务标_污水厂_v2.docx");
|
||||
v2.setCollaborators("陈志远,李明,张华");
|
||||
v2.setSubmitter("陈志远");
|
||||
v2.setStatus("审核中");
|
||||
v2.setAnnotations("[张华 @ " + today.minusDays(5) + "] 综合单价已按答疑调整,请财务复核");
|
||||
v2.setSubmittedAt(now.minusSeconds(3600 * 24 * 5));
|
||||
v2.setCreatedAt(now.minusSeconds(3600 * 24 * 7));
|
||||
docVersionRepo.save(v2);
|
||||
|
||||
// bid1 技术标 — v1 已定稿
|
||||
BidDocVersion v3 = new BidDocVersion();
|
||||
v3.setBidId(bid1);
|
||||
v3.setDocPart("技术标");
|
||||
v3.setVersion("v1");
|
||||
v3.setDocTitle("污水厂扩容工程技术标(定稿)");
|
||||
v3.setFileRef("files/bid-doc/技术标_污水厂_v1_final.docx");
|
||||
v3.setCollaborators("王涛,赵雪");
|
||||
v3.setSubmitter("王涛");
|
||||
v3.setStatus("已定稿");
|
||||
v3.setReviewComment("技术方案完整,已通过合规审核");
|
||||
v3.setAnnotations("[赵雪 @ " + today.minusDays(8) + "] 施工方案第4节水处理工艺描述更新\n[王涛 @ " + today.minusDays(7) + "] 全文定稿确认");
|
||||
v3.setSubmittedAt(now.minusSeconds(3600 * 24 * 8));
|
||||
v3.setFinalizedAt(now.minusSeconds(3600 * 24 * 7));
|
||||
v3.setCreatedAt(now.minusSeconds(3600 * 24 * 10));
|
||||
docVersionRepo.save(v3);
|
||||
|
||||
// bid1 报价单 — v1 草稿
|
||||
BidDocVersion v4 = new BidDocVersion();
|
||||
v4.setBidId(bid1);
|
||||
v4.setDocPart("报价单");
|
||||
v4.setVersion("v1");
|
||||
v4.setDocTitle("污水厂扩容工程报价单(草稿)");
|
||||
v4.setFileRef("files/bid-doc/报价单_污水厂_v1.xlsx");
|
||||
v4.setCollaborators("李明,周斌");
|
||||
v4.setSubmitter("李明");
|
||||
v4.setStatus("草稿");
|
||||
v4.setAnnotations("[周斌 @ " + today.minusDays(3) + "] 材料单价已更新至2026年5月指导价");
|
||||
v4.setSubmittedAt(now.minusSeconds(3600 * 24 * 3));
|
||||
v4.setCreatedAt(now.minusSeconds(3600 * 24 * 4));
|
||||
docVersionRepo.save(v4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.ArApItem;
|
||||
import com.kaidi.oa.domain.OrgBranch;
|
||||
import com.kaidi.oa.repository.ArApItemRepository;
|
||||
import com.kaidi.oa.repository.OrgBranchRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分公司财务报表演示数据种子(经营数据分析 §8 缺口补完)。
|
||||
*
|
||||
* 审计缺口:
|
||||
* 1. OrgBranch 无种子数据 → /api/oa/branch-fin-statements/branches 返回空列表,
|
||||
* 整个分公司独立财务报表功能无法演示。
|
||||
* 2. ArApItem 无种子数据 + companySubject 未设 → 现金流量表直接法用 Payment.subject
|
||||
* 模糊匹配作过渡;资产负债表 AR/AP 科目为零。
|
||||
*
|
||||
* 本种子补全:
|
||||
* - 5 条 OrgBranch(总部 1 + 分公司 3 + 办事处 1),覆盖岳阳分公司/华东分公司/华南分公司。
|
||||
* - 20 条 ArApItem(应收 12 + 应付 8),按 companySubject 与 OrgBranch.name 一致,
|
||||
* 使分公司维度的应收/应付/已核销/未核销在报表中实际显示。
|
||||
*
|
||||
* 幂等:OrgBranch 存在则跳过(guard on count > 0)。
|
||||
*
|
||||
* @Order(15) 在 DataSeeder(@Order 1) 之后独立运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(15)
|
||||
public class BranchFinDemoSeeder implements ApplicationRunner {
|
||||
|
||||
private final OrgBranchRepository orgBranchRepo;
|
||||
private final ArApItemRepository arApRepo;
|
||||
|
||||
public BranchFinDemoSeeder(OrgBranchRepository orgBranchRepo,
|
||||
ArApItemRepository arApRepo) {
|
||||
this.orgBranchRepo = orgBranchRepo;
|
||||
this.arApRepo = arApRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedOrgBranches();
|
||||
seedArApItems();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 1. OrgBranch 机构目录
|
||||
// ============================================================
|
||||
|
||||
private void seedOrgBranches() {
|
||||
if (orgBranchRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<OrgBranch> seeds = new ArrayList<>();
|
||||
|
||||
seeds.add(branch("HQ001", "湖南凯迪工程科技有限公司", "总部", null,
|
||||
"湖南长沙", "张伟", "0731-88889999", "长沙市岳麓区科技中路88号", "运营中",
|
||||
"凯迪集团总部,负责全国市场统筹与管理"));
|
||||
|
||||
seeds.add(branch("YY001", "湖南凯迪工程科技有限公司岳阳分公司", "分公司", "HQ001",
|
||||
"湖南岳阳", "陈静", "0730-22223333", "岳阳市云溪区长炼路66号", "运营中",
|
||||
"负责岳阳及周边区域工程项目承接与实施"));
|
||||
|
||||
seeds.add(branch("HD001", "凯迪(华东)工程技术有限公司", "分公司", "HQ001",
|
||||
"华东地区", "李明", "021-66668888", "上海市浦东新区世纪大道1368号", "运营中",
|
||||
"负责华东区(沪苏浙皖闽赣)市场开拓与项目执行"));
|
||||
|
||||
seeds.add(branch("HN001", "凯迪(华南)工程服务有限公司", "分公司", "HQ001",
|
||||
"华南地区", "赵云", "020-33337777", "广州市天河区珠江新城华夏路1号", "运营中",
|
||||
"负责华南区(粤桂琼)市场开拓与EPC工程管理"));
|
||||
|
||||
seeds.add(branch("CS001", "凯迪长沙办公室", "办事处", "HQ001",
|
||||
"湖南长沙", "王芳", "0731-55556666", "长沙市雨花区劳动中路158号", "运营中",
|
||||
"长沙本地业务联络与客户服务办事处"));
|
||||
|
||||
orgBranchRepo.saveAll(seeds);
|
||||
}
|
||||
|
||||
private OrgBranch branch(String code, String name, String type, String parentCode,
|
||||
String region, String manager, String phone, String address,
|
||||
String status, String remark) {
|
||||
OrgBranch b = new OrgBranch();
|
||||
b.setCode(code);
|
||||
b.setName(name);
|
||||
b.setType(type);
|
||||
b.setParentCode(parentCode);
|
||||
b.setRegion(region);
|
||||
b.setManager(manager);
|
||||
b.setPhone(phone);
|
||||
b.setAddress(address);
|
||||
b.setStatus(status);
|
||||
b.setRemark(remark);
|
||||
b.setCreatedAt(Instant.now());
|
||||
return b;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. ArApItem 应收/应付 演示数据(含 companySubject 精准关联分公司)
|
||||
// ============================================================
|
||||
|
||||
private void seedArApItems() {
|
||||
if (arApRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ArApItem> seeds = new ArrayList<>();
|
||||
|
||||
// --- 岳阳分公司 应收账款 ---
|
||||
seeds.add(arApItem("AR-YY-2026-001", "应收", "岳阳市政工程集团有限公司",
|
||||
"HT-YY-2026-001", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"3800000.00", "2280000.00", "1520000.00", "部分核销", "2026-09-30"));
|
||||
|
||||
seeds.add(arApItem("AR-YY-2026-002", "应收", "岳阳县水务局",
|
||||
"HT-YY-2026-002", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"1650000.00", "990000.00", "660000.00", "部分核销", "2026-08-31"));
|
||||
|
||||
seeds.add(arApItem("AR-YY-2026-003", "应收", "湖南云溪工业园管委会",
|
||||
"HT-YY-2026-003", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"920000.00", "0.00", "920000.00", "未核销", "2026-12-31"));
|
||||
|
||||
seeds.add(arApItem("AR-YY-2026-004", "应收", "岳阳华能电厂",
|
||||
"HT-YY-2026-004", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"560000.00", "560000.00", "0.00", "已结清", "2026-06-30"));
|
||||
|
||||
// --- 岳阳分公司 应付账款 ---
|
||||
seeds.add(arApItem("AP-YY-2026-001", "应付", "岳阳通达设备租赁有限公司",
|
||||
"HT-YY-2026-AP01", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"386000.00", "193000.00", "193000.00", "部分核销", "2026-07-31"));
|
||||
|
||||
seeds.add(arApItem("AP-YY-2026-002", "应付", "长沙华信网络科技有限公司",
|
||||
"HT-YY-2026-AP02", "湖南凯迪工程科技有限公司岳阳分公司",
|
||||
"128000.00", "0.00", "128000.00", "未核销", "2026-09-30"));
|
||||
|
||||
// --- 华东分公司 应收账款 ---
|
||||
seeds.add(arApItem("AR-HD-2026-001", "应收", "上海浦东建设集团有限公司",
|
||||
"HT-HD-2026-001", "凯迪(华东)工程技术有限公司",
|
||||
"5200000.00", "3120000.00", "2080000.00", "部分核销", "2026-10-31"));
|
||||
|
||||
seeds.add(arApItem("AR-HD-2026-002", "应收", "江苏南京城市基建有限公司",
|
||||
"HT-HD-2026-002", "凯迪(华东)工程技术有限公司",
|
||||
"3100000.00", "1860000.00", "1240000.00", "部分核销", "2026-09-30"));
|
||||
|
||||
seeds.add(arApItem("AR-HD-2026-003", "应收", "浙江绍兴市污水处理有限公司",
|
||||
"HT-HD-2026-003", "凯迪(华东)工程技术有限公司",
|
||||
"1880000.00", "1128000.00", "752000.00", "部分核销", "2026-11-30"));
|
||||
|
||||
seeds.add(arApItem("AR-HD-2026-004", "应收", "安徽合肥市水务集团",
|
||||
"HT-HD-2026-004", "凯迪(华东)工程技术有限公司",
|
||||
"2650000.00", "0.00", "2650000.00", "未核销", "2026-12-31"));
|
||||
|
||||
// --- 华东分公司 应付账款 ---
|
||||
seeds.add(arApItem("AP-HD-2026-001", "应付", "上海华申建材有限公司",
|
||||
"HT-HD-2026-AP01", "凯迪(华东)工程技术有限公司",
|
||||
"680000.00", "340000.00", "340000.00", "部分核销", "2026-08-31"));
|
||||
|
||||
seeds.add(arApItem("AP-HD-2026-002", "应付", "南京精工机械设备有限公司",
|
||||
"HT-HD-2026-AP02", "凯迪(华东)工程技术有限公司",
|
||||
"420000.00", "0.00", "420000.00", "未核销", "2026-10-31"));
|
||||
|
||||
seeds.add(arApItem("AP-HD-2026-003", "应付", "苏州翔宇临时用工服务有限公司",
|
||||
"HT-HD-2026-AP03", "凯迪(华东)工程技术有限公司",
|
||||
"185000.00", "92500.00", "92500.00", "部分核销", "2026-09-15"));
|
||||
|
||||
// --- 华南分公司 应收账款 ---
|
||||
seeds.add(arApItem("AR-HN-2026-001", "应收", "广州市城市建设开发有限公司",
|
||||
"HT-HN-2026-001", "凯迪(华南)工程服务有限公司",
|
||||
"4800000.00", "2400000.00", "2400000.00", "部分核销", "2026-11-30"));
|
||||
|
||||
seeds.add(arApItem("AR-HN-2026-002", "应收", "深圳市水务集团有限公司",
|
||||
"HT-HN-2026-002", "凯迪(华南)工程服务有限公司",
|
||||
"3200000.00", "3200000.00", "0.00", "已结清", "2026-06-30"));
|
||||
|
||||
seeds.add(arApItem("AR-HN-2026-003", "应收", "广西南宁城投集团有限公司",
|
||||
"HT-HN-2026-003", "凯迪(华南)工程服务有限公司",
|
||||
"1960000.00", "980000.00", "980000.00", "部分核销", "2026-10-31"));
|
||||
|
||||
seeds.add(arApItem("AR-HN-2026-004", "应收", "海南省生态环境工程有限公司",
|
||||
"HT-HN-2026-004", "凯迪(华南)工程服务有限公司",
|
||||
"1100000.00", "0.00", "1100000.00", "未核销", "2027-03-31"));
|
||||
|
||||
// --- 华南分公司 应付账款 ---
|
||||
seeds.add(arApItem("AP-HN-2026-001", "应付", "广州宏基建材有限公司",
|
||||
"HT-HN-2026-AP01", "凯迪(华南)工程服务有限公司",
|
||||
"540000.00", "270000.00", "270000.00", "部分核销", "2026-09-30"));
|
||||
|
||||
seeds.add(arApItem("AP-HN-2026-002", "应付", "深圳福田劳务派遣有限公司",
|
||||
"HT-HN-2026-AP02", "凯迪(华南)工程服务有限公司",
|
||||
"168000.00", "84000.00", "84000.00", "部分核销", "2026-08-31"));
|
||||
|
||||
// --- 总部 应收账款 ---
|
||||
seeds.add(arApItem("AR-HQ-2026-001", "应收", "靖州供水项目业主方",
|
||||
"HT-HQ-2026-001", "湖南凯迪工程科技有限公司",
|
||||
"8600000.00", "5160000.00", "3440000.00", "部分核销", "2026-12-31"));
|
||||
|
||||
arApRepo.saveAll(seeds);
|
||||
}
|
||||
|
||||
private ArApItem arApItem(String code, String arApType, String partyName,
|
||||
String relatedRef, String companySubject,
|
||||
String amount, String writtenOff, String unwrittenOff,
|
||||
String status, String dueDate) {
|
||||
ArApItem a = new ArApItem();
|
||||
a.setCode(code);
|
||||
a.setArApType(arApType);
|
||||
a.setPartyName(partyName);
|
||||
a.setRelatedRef(relatedRef);
|
||||
a.setCompanySubject(companySubject);
|
||||
a.setAmount(Money.of(Double.parseDouble(amount)));
|
||||
a.setWrittenOff(Money.of(Double.parseDouble(writtenOff)));
|
||||
a.setUnwrittenOff(Money.of(Double.parseDouble(unwrittenOff)));
|
||||
a.setStatus(status);
|
||||
a.setDueDate(dueDate);
|
||||
a.setCreatedAt(Instant.now());
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.BrandCollabPush;
|
||||
import com.kaidi.oa.domain.EmployeeShareMaterial;
|
||||
import com.kaidi.oa.repository.BrandCollabPushRepository;
|
||||
import com.kaidi.oa.repository.EmployeeShareMaterialRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 宣传部 W7 缺口演示数据。
|
||||
*
|
||||
* 缺口1(low):员工素材中心 EmployeeShareMaterial — 企业简介PPT/宣传视频/标准照片等。
|
||||
* 缺口2(med):BrandCollabController push-ops/push-legal/push-finance 演示数据。
|
||||
*
|
||||
* @Order(220) 在所有主数据 Seeder 之后运行;幂等(count > 0 则跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(220)
|
||||
public class BrandCultureGapSeeder implements ApplicationRunner {
|
||||
|
||||
private final EmployeeShareMaterialRepository empMatRepo;
|
||||
private final BrandCollabPushRepository pushRepo;
|
||||
|
||||
public BrandCultureGapSeeder(EmployeeShareMaterialRepository empMatRepo,
|
||||
BrandCollabPushRepository pushRepo) {
|
||||
this.empMatRepo = empMatRepo;
|
||||
this.pushRepo = pushRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedEmployeeShareMaterials();
|
||||
seedCollabPushExtended();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 员工宣传素材中心演示数据(EmployeeShareMaterial)
|
||||
// ====================================================================
|
||||
private void seedEmployeeShareMaterials() {
|
||||
if (empMatRepo.count() > 0) return;
|
||||
|
||||
List<EmployeeShareMaterial> list = new ArrayList<>();
|
||||
|
||||
list.add(mat("ESM-001", "凯迪科技集团企业简介PPT(2026版)", "企业简介PPT", "全员",
|
||||
"最新版公司简介PPT,包含公司历程、业务版图、核心优势、标志性案例。员工对外介绍公司时使用,务必使用此版本,勿私自修改。",
|
||||
"https://cdn.kaidi.com/brand/2026/kaidi-intro-v3.2.pptx",
|
||||
"https://cdn.kaidi.com/brand/thumb/ppt-intro.png",
|
||||
"v3.2", "企业简介,PPT,对外宣传", "宣传部·王主任", "上架",
|
||||
"2026-01-01", null));
|
||||
|
||||
list.add(mat("ESM-002", "凯迪科技2026品牌宣传片(3分钟精华版)", "宣传视频", "全员",
|
||||
"2026年最新企业形象宣传片,时长3分钟,适用于客户拜访前播放、展会大屏、社交媒体转发。B站/抖音/朋友圈均可使用。",
|
||||
"https://cdn.kaidi.com/brand/2026/kaidi-brand-film-3min.mp4",
|
||||
"https://cdn.kaidi.com/brand/thumb/film-2026.jpg",
|
||||
"v1.0", "宣传视频,品牌形象,展会", "宣传部·李部长", "上架",
|
||||
"2026-01-15", null));
|
||||
|
||||
list.add(mat("ESM-003", "凯迪标准企业LOGO(全套)", "标准照片", "全员",
|
||||
"企业标准LOGO矢量文件和PNG高清图,包含彩色版/黑白版/深色背景版。用于名片、合同封面、PPT模板、物料设计等场合。请勿变形拉伸。",
|
||||
"https://cdn.kaidi.com/brand/logo/kaidi-logo-pack-2026.zip",
|
||||
"https://cdn.kaidi.com/brand/thumb/logo.png",
|
||||
"v2.0", "LOGO,品牌标识,矢量", "宣传部·王主任", "上架",
|
||||
"2026-01-01", null));
|
||||
|
||||
list.add(mat("ESM-004", "凯迪智慧建筑产品系列介绍(销售版)", "产品介绍", "销售",
|
||||
"面向销售团队的产品介绍PPT,含智能传感器XT系列、建筑管理系统BMS-Pro、环境监控平台等核心产品参数及差异化卖点。含客户竞品对比分析(内部机密,勿外传完整版)。",
|
||||
"https://cdn.kaidi.com/brand/product/kaidi-product-sales-2026.pptx",
|
||||
"https://cdn.kaidi.com/brand/thumb/product-ppt.png",
|
||||
"v2.1", "产品介绍,销售工具,智慧建筑", "宣传部·王主任", "上架",
|
||||
"2026-03-01", "2026-12-31"));
|
||||
|
||||
list.add(mat("ESM-005", "凯迪企业宣传册(2026双语精装版)", "宣传手册", "全员",
|
||||
"A4精装宣传册PDF电子版,中英文双语,120页,适用于对外寄送、展会发放、客户会议。如需印刷版请联系宣传部申请。",
|
||||
"https://cdn.kaidi.com/brand/2026/kaidi-brochure-2026-cn-en.pdf",
|
||||
"https://cdn.kaidi.com/brand/thumb/brochure-2026.jpg",
|
||||
"v1.0", "宣传册,双语,对外", "宣传部·李部长", "上架",
|
||||
"2026-02-01", null));
|
||||
|
||||
list.add(mat("ESM-006", "凯迪员工标准证件照背景及拍摄规范", "标准照片", "全员",
|
||||
"员工对外代表公司出席活动、接受采访时适用的证件照拍摄规范,含背景色板(凯迪蓝/白)、着装要求、尺寸规格。",
|
||||
"https://cdn.kaidi.com/brand/standard/employee-photo-spec.pdf",
|
||||
"https://cdn.kaidi.com/brand/thumb/photo-spec.png",
|
||||
"v1.0", "证件照,员工形象,规范", "宣传部·王主任", "上架",
|
||||
"2026-01-01", null));
|
||||
|
||||
list.add(mat("ESM-007", "凯迪科技十周年品牌故事H5", "宣传视频", "全员",
|
||||
"十周年品牌故事互动H5,适合朋友圈、微信群、微博转发。点击即可在手机端播放,含创始人寄语和十年历程时间轴。",
|
||||
"https://h5.kaidi.com/10th-anniversary",
|
||||
"https://cdn.kaidi.com/brand/thumb/h5-10th.jpg",
|
||||
"v1.0", "十周年,H5,朋友圈", "宣传部·李部长", "上架",
|
||||
"2026-05-01", "2026-12-31"));
|
||||
|
||||
list.add(mat("ESM-008", "凯迪高管团队官方照片集", "标准照片", "管理层",
|
||||
"董事长、CEO及各VP官方形象照(高清),适用于官方文件、媒体采访配图、演讲嘉宾介绍。仅限正式场合使用,请勿用于非正式内容。",
|
||||
"https://cdn.kaidi.com/brand/executives/exec-photos-2026.zip",
|
||||
"https://cdn.kaidi.com/brand/thumb/exec.jpg",
|
||||
"v1.0", "高管,官方照片,媒体", "宣传部·王主任", "待审核",
|
||||
"2026-01-01", null));
|
||||
|
||||
empMatRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private EmployeeShareMaterial mat(String code, String title, String materialType,
|
||||
String audience, String description,
|
||||
String fileUrl, String thumbnailUrl,
|
||||
String version, String tags, String uploader,
|
||||
String status, String effectDate, String expireDate) {
|
||||
EmployeeShareMaterial m = new EmployeeShareMaterial();
|
||||
m.setCode(code);
|
||||
m.setTitle(title);
|
||||
m.setMaterialType(materialType);
|
||||
m.setAudience(audience);
|
||||
m.setDescription(description);
|
||||
m.setFileUrl(fileUrl);
|
||||
m.setThumbnailUrl(thumbnailUrl);
|
||||
m.setVersion(version);
|
||||
m.setTags(tags);
|
||||
m.setUploader(uploader);
|
||||
m.setStatus(status);
|
||||
m.setUseCount(0);
|
||||
m.setEffectDate(effectDate);
|
||||
m.setExpireDate(expireDate);
|
||||
m.setCreatedAt(Instant.now());
|
||||
return m;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 经营部/法务风险部/财务部协同推送演示数据(BrandCollabPush 扩展)
|
||||
// ====================================================================
|
||||
private void seedCollabPushExtended() {
|
||||
// 只在推送记录少于5条时补充演示(避免与已有seeder冲突)
|
||||
long existing = pushRepo.count();
|
||||
if (existing >= 5) return;
|
||||
|
||||
List<BrandCollabPush> pushList = new ArrayList<>();
|
||||
|
||||
// 经营部 — 展会信息推送
|
||||
pushList.add(push("经营部", "展会信息",
|
||||
"2026深圳国际智慧城市展 — 请宣传部协助物料及宣传支持",
|
||||
"展会名称:深圳国际智慧城市展\n时间:2026-09-15 ~ 2026-09-18\n展位:Hall B5-208\n需要物料:展架×6、宣传册×300、礼品×50\n联系人:经营部·张经理",
|
||||
"经营部·张经理", "已推送"));
|
||||
|
||||
// 经营部 — 客户活动需求
|
||||
pushList.add(push("经营部", "客户活动需求",
|
||||
"2026年夏季客户答谢晚宴 — 宣传物料及H5邀请函制作需求",
|
||||
"活动时间:2026-07-20\n地点:北京国贸大酒店\n预计嘉宾:80人\n需要:H5邀请函设计、背景板×2、席卡、签到台物料\n截止时间:2026-07-10",
|
||||
"经营部·李总监", "已接收"));
|
||||
|
||||
// 法务风险部 — 文案合规审核
|
||||
pushList.add(push("法务风险部", "文案合规审核",
|
||||
"请法务审核:深圳展会主题海报文案《凯迪科技·让建筑更智慧》广告合规性",
|
||||
"文案内容:「凯迪科技·让建筑更智慧,城市更美好 —— 中国智慧建筑领军者」\n需审核:是否符合广告法,是否涉及最高级/绝对化用语,是否有知识产权风险\n截止时间:2026-08-20",
|
||||
"宣传部·王主任", "已推送"));
|
||||
|
||||
// 法务风险部 — 危机法律支持
|
||||
pushList.add(push("法务风险部", "危机法律支持",
|
||||
"舆情事件:网络出现关于凯迪产品质量的负面评论,请法务评估风险",
|
||||
"事件描述:微博用户@建筑行业观察发布帖子,称某项目使用凯迪BMS系统后频繁出现断连,引发转发。\n初步判断:尚不确定真实性\n需要法务:评估声誉风险等级,是否需要公开回应,是否涉及不正当竞争",
|
||||
"宣传部·李部长", "已接收"));
|
||||
|
||||
// 财务部 — 宣传费用报销通知
|
||||
pushList.add(push("财务部", "宣传费用报销",
|
||||
"宣传费用报销通知:展会物料制作费 ¥28,500.00 — 请财务部审核付款",
|
||||
"报销说明:2026深圳国际智慧城市展展位物料制作费用\n供应商:北京汉唐广告有限公司\n金额:¥28,500.00\n发票号:0023456789\n项目:品牌推广·展会参展\n申请人:宣传部·王主任",
|
||||
"宣传部·王主任", "已推送"));
|
||||
|
||||
pushRepo.saveAll(pushList);
|
||||
}
|
||||
|
||||
private BrandCollabPush push(String targetDept, String pushType,
|
||||
String subject, String content,
|
||||
String pusher, String status) {
|
||||
BrandCollabPush p = new BrandCollabPush();
|
||||
p.setTargetDept(targetDept);
|
||||
p.setPushType(pushType);
|
||||
p.setSubject(subject);
|
||||
p.setContent(content);
|
||||
p.setPusher(pusher);
|
||||
p.setStatus(status);
|
||||
p.setCreatedAt(Instant.now());
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.BrandEventMaterial;
|
||||
import com.kaidi.oa.domain.ContentTask;
|
||||
import com.kaidi.oa.repository.BrandEventMaterialRepository;
|
||||
import com.kaidi.oa.repository.BrandEventRepository;
|
||||
import com.kaidi.oa.repository.ContentTaskRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 品牌宣传部演示数据:内容创作任务(ContentTask)+ 活动物料(BrandEventMaterial)。
|
||||
*
|
||||
* 补全两个"活体返回空/无数据"缺口,让前端物料管理与任务分派功能可见可演示。
|
||||
* @Order(22),在 DataSeeder(@Order 1) 之后运行,幂等(count > 0 跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(22)
|
||||
public class BrandCultureSeeder implements ApplicationRunner {
|
||||
|
||||
private final ContentTaskRepository taskRepo;
|
||||
private final BrandEventMaterialRepository materialRepo;
|
||||
private final BrandEventRepository eventRepo;
|
||||
|
||||
public BrandCultureSeeder(ContentTaskRepository taskRepo,
|
||||
BrandEventMaterialRepository materialRepo,
|
||||
BrandEventRepository eventRepo) {
|
||||
this.taskRepo = taskRepo;
|
||||
this.materialRepo = materialRepo;
|
||||
this.eventRepo = eventRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedContentTasks();
|
||||
seedEventMaterials();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 内容创作任务演示数据(ContentTask)
|
||||
// ====================================================================
|
||||
private void seedContentTasks() {
|
||||
if (taskRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ContentTask> tasks = new ArrayList<>();
|
||||
|
||||
// 内部员工任务
|
||||
tasks.add(task("CT-001", "2026年中国建博会主题宣传文案撰写",
|
||||
"文案", "内部员工", "张雨薇", null,
|
||||
"2026年中国建博会品牌宣传主稿,需突出绿色建筑/智慧城市核心卖点,字数2000字以内。",
|
||||
"2026-05-20", "宣传部·李部长", "进行中", null, null));
|
||||
|
||||
tasks.add(task("CT-002", "公司官网首页改版视觉设计",
|
||||
"设计", "内部员工", "陈美珍", null,
|
||||
"官网首页全新改版,主色调蓝色系,风格现代简洁,需出3套方案供选择。",
|
||||
"2026-05-30", "宣传部·李部长", "待审核",
|
||||
"已提交3套方案,请审核后选定", "http://design.kaidi.com/homepage-v2/"));
|
||||
|
||||
tasks.add(task("CT-003", "季度产品发布视频脚本及剪辑",
|
||||
"视频", "内部员工", "刘宇航", null,
|
||||
"Q2产品发布会宣传片,时长3分钟,需展示新品功能亮点及客户案例。",
|
||||
"2026-04-10", "宣传部·王主任", "已发布", "内容专业,已发布至官网及抖音", null));
|
||||
|
||||
tasks.add(task("CT-004", "行业白皮书《建筑智慧化转型报告》文案",
|
||||
"文案", "内部员工", "赵晓燕", null,
|
||||
"与研发部协作,撰写2026年建筑行业智慧化白皮书,预计15000字,分5章。",
|
||||
"2026-06-30", "品牌总监·张总", "待接受", null, null));
|
||||
|
||||
// 外部供应商任务
|
||||
tasks.add(task("CT-005", "2026年企业宣传册(A4折页)设计制作",
|
||||
"设计", "外部供应商", "北京汉唐广告有限公司", "SUP-2024-007",
|
||||
"企业宣传折页设计,A4三折,中英文双语,印刷数量5000份,需提供印前文件。",
|
||||
"2026-05-15", "宣传部·李部长", "已发布", "印刷文件已提交,质量良好", "http://cdn.kaidi.com/brochure2026.pdf"));
|
||||
|
||||
tasks.add(task("CT-006", "展会现场宣传视频制作(60秒竖版)",
|
||||
"视频", "外部供应商", "上海晨星影视制作", "SUP-2024-012",
|
||||
"适配展会现场LED大屏,竖版1080x1920,60秒,需配中文字幕及背景音乐。",
|
||||
"2026-05-25", "宣传部·王主任", "进行中", null, null));
|
||||
|
||||
tasks.add(task("CT-007", "公众号专题策划《凯迪十年·匠心同行》图文",
|
||||
"文案", "外部供应商", "成都创意内容工作室", "SUP-2025-003",
|
||||
"公司十周年品牌故事系列,共5篇,每篇1500-2000字,配套排版设计。",
|
||||
"2026-06-10", "宣传部·李部长", "已驳回", "文案风格与品牌调性不符,请重写第2-3篇", null));
|
||||
|
||||
tasks.add(task("CT-008", "H5互动邀请函(夏季客户答谢会)",
|
||||
"设计", "外部供应商", "北京汉唐广告有限公司", "SUP-2024-007",
|
||||
"客户答谢会电子邀请函H5,含RSVP确认功能,手机端优化,动效精良。",
|
||||
"2026-06-05", "宣传部·王主任", "待接受", null, null));
|
||||
|
||||
taskRepo.saveAll(tasks);
|
||||
}
|
||||
|
||||
private ContentTask task(String code, String title, String taskType,
|
||||
String assigneeType, String assignee, String supplierCode,
|
||||
String description, String dueDate, String issuedBy,
|
||||
String status, String reviewNote, String deliverableUrl) {
|
||||
ContentTask t = new ContentTask();
|
||||
t.setCode(code);
|
||||
t.setTitle(title);
|
||||
t.setTaskType(taskType);
|
||||
t.setAssigneeType(assigneeType);
|
||||
t.setAssignee(assignee);
|
||||
t.setSupplierCode(supplierCode);
|
||||
t.setDescription(description);
|
||||
t.setDueDate(dueDate);
|
||||
t.setIssuedBy(issuedBy);
|
||||
t.setStatus(status);
|
||||
t.setReviewNote(reviewNote);
|
||||
t.setDeliverableUrl(deliverableUrl);
|
||||
t.setCreatedAt(Instant.now());
|
||||
return t;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 活动物料演示数据(BrandEventMaterial)
|
||||
// ====================================================================
|
||||
private void seedEventMaterials() {
|
||||
if (materialRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试找到第一个活动,如没有,使用虚拟 eventId=1
|
||||
Long eventId;
|
||||
String eventCode;
|
||||
var events = eventRepo.findAll();
|
||||
if (!events.isEmpty()) {
|
||||
var ev = events.get(0);
|
||||
eventId = ev.getId();
|
||||
eventCode = ev.getCode();
|
||||
} else {
|
||||
// 无活动时仍插入,eventId=0 作占位(演示用)
|
||||
eventId = 0L;
|
||||
eventCode = "HD-DEMO";
|
||||
}
|
||||
|
||||
List<BrandEventMaterial> materials = new ArrayList<>();
|
||||
|
||||
materials.add(material(eventId, eventCode, "凯迪标准展架(200×80cm)", "展架",
|
||||
"铝合金框架+写真布面,双面印刷,公司标准蓝色",
|
||||
8, 8, 6, "2026-05-20", "北京华展广告", "王晓峰", "已领用"));
|
||||
|
||||
materials.add(material(eventId, eventCode, "展会背景板(4×3m)", "背景板",
|
||||
"KT板+喷绘,带企业LOGO及2026主题,含安装服务",
|
||||
2, 2, 2, "2026-05-18", "上海博展传媒", "陈美珍", "已领用"));
|
||||
|
||||
materials.add(material(eventId, eventCode, "企业宣传册(A4三折页)", "宣传册",
|
||||
"铜版纸哑膜,中英文双语,2026最新版",
|
||||
500, 500, 320, "2026-05-15", "北京汉唐广告", "王晓峰", "已领用"));
|
||||
|
||||
materials.add(material(eventId, eventCode, "凯迪定制礼品(精装钢笔套装)", "礼品",
|
||||
"LOGO定制,商务风格,含礼品盒包装",
|
||||
100, 100, 80, "2026-05-22", "优礼定制(广州)", "李雪梅", "已领用"));
|
||||
|
||||
materials.add(material(eventId, eventCode, "新品样品(智能传感器XT-200)", "样品",
|
||||
"展会现场展示用样品,含功能演示说明书",
|
||||
20, 18, 18, "2026-05-10", "研发部·技术支持", "陈美珍", "已领用"));
|
||||
|
||||
materials.add(material(eventId, eventCode, "地毯(品牌蓝色定制)", "其他",
|
||||
"展位地毯,3×3米,凯迪蓝,带防滑底",
|
||||
4, 4, 4, "2026-05-19", "北京博展传媒", "王晓峰", "已领用"));
|
||||
|
||||
// 下一场活动的物料(状态多样)
|
||||
Long eventId2 = events.size() > 1 ? events.get(1).getId() : eventId;
|
||||
String eventCode2 = events.size() > 1 ? events.get(1).getCode() : "HD-002";
|
||||
|
||||
materials.add(material(eventId2, eventCode2, "X型展架(120×200cm)", "展架",
|
||||
"便携折叠式,胶水布海报,含便携包",
|
||||
12, 10, 0, "2026-06-20", "北京华展广告", "王晓峰", "已备好"));
|
||||
|
||||
materials.add(material(eventId2, eventCode2, "产品目录册(精装版)", "宣传册",
|
||||
"精装封面,16开,120页,全彩印刷",
|
||||
200, 0, 0, "2026-06-25", "北京汉唐广告", "李雪梅", "制作中"));
|
||||
|
||||
materials.add(material(eventId2, eventCode2, "定制帆布袋(礼品)", "礼品",
|
||||
"棉质帆布,印LOGO,米白色",
|
||||
150, 0, 0, "2026-06-28", "优礼定制(广州)", "陈美珍", "制作中"));
|
||||
|
||||
materials.add(material(eventId2, eventCode2, "客户答谢礼盒(精选套装)", "礼品",
|
||||
"含茶叶+笔记本+定制钢笔,礼盒包装",
|
||||
50, 0, 0, "2026-07-01", "尚品礼赠公司", "李雪梅", "需求登记"));
|
||||
|
||||
materialRepo.saveAll(materials);
|
||||
}
|
||||
|
||||
private BrandEventMaterial material(Long eventId, String eventCode,
|
||||
String name, String type, String spec,
|
||||
int planned, int actual, int issued,
|
||||
String dueDate, String supplier, String owner, String status) {
|
||||
BrandEventMaterial m = new BrandEventMaterial();
|
||||
m.setEventId(eventId);
|
||||
m.setEventCode(eventCode);
|
||||
m.setMaterialName(name);
|
||||
m.setMaterialType(type);
|
||||
m.setSpec(spec);
|
||||
m.setPlannedQty(planned);
|
||||
m.setActualQty(actual);
|
||||
m.setIssuedQty(issued);
|
||||
m.setDueDate(dueDate);
|
||||
m.setSupplier(supplier);
|
||||
m.setOwner(owner);
|
||||
m.setStatus(status);
|
||||
m.setCreatedAt(Instant.now());
|
||||
return m;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.CostHrDeptLaborRate;
|
||||
import com.kaidi.oa.domain.CostPurchaseOrder;
|
||||
import com.kaidi.oa.domain.CostReductionItem;
|
||||
import com.kaidi.oa.domain.StandardCost;
|
||||
import com.kaidi.oa.repository.CostHrDeptLaborRateRepository;
|
||||
import com.kaidi.oa.repository.CostPurchaseOrderRepository;
|
||||
import com.kaidi.oa.repository.CostReductionItemRepository;
|
||||
import com.kaidi.oa.repository.StandardCostRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 成本控制部演示种子数据(W7 补 PARTIAL 缺口)。
|
||||
*
|
||||
* <p>补齐以下三条 0 数据缺口:
|
||||
* <ol>
|
||||
* <li><b>CostPurchaseOrder</b>(采购成本控制 §5):6 条跨状态/价格状态的演示采购订单,
|
||||
* 含正常价格/超标准价/严重超价,让价格预警看板、节约额汇总、价格趋势折线、
|
||||
* 计价引擎 valuation-engine/calc 全部活体可见。</li>
|
||||
* <li><b>CostHrDeptLaborRate</b>(HR 工时费率 §8):5 条各部门标准工时费率,
|
||||
* 消除 /cost-hr-labor-rates 活体返回 null data 的缺口。</li>
|
||||
* <li><b>CostReductionItem</b>(降本增效跟踪 §6):5 条跨状态演示项目,
|
||||
* 含"已完成"项目自动算降本额/达成率,让降本汇总/成本趋势活体可见。</li>
|
||||
* </ol>
|
||||
*
|
||||
* 幂等:各表 count() > 0 即跳过,重启不重复插入。
|
||||
* @Order(9) 在 DataSeeder(@Order 1) 之后独立运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(9)
|
||||
public class CostCtrlDemoSeeder implements ApplicationRunner {
|
||||
|
||||
private final CostPurchaseOrderRepository poRepo;
|
||||
private final CostHrDeptLaborRateRepository rateRepo;
|
||||
private final CostReductionItemRepository reductionRepo;
|
||||
private final StandardCostRepository stdCostRepo;
|
||||
|
||||
public CostCtrlDemoSeeder(CostPurchaseOrderRepository poRepo,
|
||||
CostHrDeptLaborRateRepository rateRepo,
|
||||
CostReductionItemRepository reductionRepo,
|
||||
StandardCostRepository stdCostRepo) {
|
||||
this.poRepo = poRepo;
|
||||
this.rateRepo = rateRepo;
|
||||
this.reductionRepo = reductionRepo;
|
||||
this.stdCostRepo = stdCostRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedPurchaseOrders();
|
||||
seedHrLaborRates();
|
||||
seedCostReductions();
|
||||
seedStandardCosts();
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 1. 采购订单种子(6 条,跨状态 + 跨价格状态)
|
||||
// ===================================================================
|
||||
private void seedPurchaseOrders() {
|
||||
if (poRepo.count() > 0) return;
|
||||
|
||||
// 订单1:钢材-正常价格-已完成(用于计价引擎 FIFO/移动平均历史数据)
|
||||
po("PO-2025-0001", "Q235B 钢材", "钢板 δ=10mm", "吨",
|
||||
"华钢物资有限公司", 50.0,
|
||||
new BigDecimal("5980.00"), new BigDecimal("6100.00"), new BigDecimal("5950.00"),
|
||||
"凯迪生物质锅炉项目", "原材料", "2025-03-05", "2025-03-15",
|
||||
new BigDecimal("200.00"), "张采购", "钢材主结构采购,执行中标价", "已完成", "正常");
|
||||
|
||||
// 订单2:螺旋输送机-正常-执行中
|
||||
po("PO-2025-0002", "螺旋输送机叶片", "叶径 φ320×L6000", "件",
|
||||
"河南龙达机械", 10.0,
|
||||
new BigDecimal("3800.00"), new BigDecimal("3900.00"), new BigDecimal("3750.00"),
|
||||
"凯迪水处理工程", "设备", "2025-04-10", "2025-04-30",
|
||||
new BigDecimal("500.00"), "李采购", "批量采购享9.7折", "执行中", "正常");
|
||||
|
||||
// 订单3:无机絮凝剂-超标准价-待审批(超标5.8%,需成控审批)
|
||||
po("PO-2025-0003", "聚合氯化铝(PAC)", "含量30%工业级", "吨",
|
||||
"江苏清河水处理材料", 20.0,
|
||||
new BigDecimal("2180.00"), new BigDecimal("2060.00"), new BigDecimal("2100.00"),
|
||||
"凯迪环保项目A", "原材料", "2025-05-02", "2025-05-20",
|
||||
new BigDecimal("300.00"), "王采购", "供应商因原料涨价上调单价,超标5.8%需成控审批", "待审批", "超标准价");
|
||||
|
||||
// 订单4:变频器-严重超价-草稿(超标12.4%,严重预警)
|
||||
po("PO-2025-0004", "ABB 变频器 ACS550", "37kW 380V 三相", "台",
|
||||
"上海ABB电气代理", 3.0,
|
||||
new BigDecimal("18900.00"), new BigDecimal("16800.00"), new BigDecimal("17200.00"),
|
||||
"凯迪技改项目", "设备", "2025-05-15", "2025-06-15",
|
||||
new BigDecimal("0.00"), "赵采购", "原进口零件涨价,代理商上调12.4%,需专项评审", "草稿", "严重超价");
|
||||
|
||||
// 订单5:聚乙烯管道-超历史均价-已批准(超历史均价但未超标准价)
|
||||
po("PO-2025-0005", "PE100给水管 DN200", "SDR17 PN10 6m/根", "根",
|
||||
"浙江联塑管道", 200.0,
|
||||
new BigDecimal("385.00"), new BigDecimal("400.00"), new BigDecimal("370.00"),
|
||||
"凯迪水处理工程", "原材料", "2025-06-01", "2025-06-20",
|
||||
new BigDecimal("800.00"), "孙采购", "管道价格较上次均价上涨4.1%,已评审通过", "已批准", "超历史均价");
|
||||
|
||||
// 订单6:钢材二期-正常-已完成(与订单1同物料,构成历史均价计算基础)
|
||||
po("PO-2025-0006", "Q235B 钢材", "钢板 δ=8mm", "吨",
|
||||
"华钢物资有限公司", 30.0,
|
||||
new BigDecimal("5860.00"), new BigDecimal("6100.00"), new BigDecimal("5950.00"),
|
||||
"凯迪水处理工程", "原材料", "2025-04-20", "2025-05-05",
|
||||
new BigDecimal("150.00"), "张采购", "二期追加采购,与一期同供应商协商优惠价", "已完成", "正常");
|
||||
}
|
||||
|
||||
private void po(String code, String materialName, String spec, String unit,
|
||||
String supplierName, double qty,
|
||||
BigDecimal unitPrice, BigDecimal standardPrice, BigDecimal historyAvgPrice,
|
||||
String projectName, String category,
|
||||
String applyDate, String deliveryDate,
|
||||
BigDecimal freightCost, String applicant, String note,
|
||||
String status, String priceStatus) {
|
||||
CostPurchaseOrder o = new CostPurchaseOrder();
|
||||
o.setCode(code);
|
||||
o.setMaterialName(materialName);
|
||||
o.setSpec(spec);
|
||||
o.setUnit(unit);
|
||||
o.setSupplierName(supplierName);
|
||||
o.setQty(qty);
|
||||
o.setUnitPrice(unitPrice);
|
||||
o.setStandardPrice(standardPrice);
|
||||
o.setHistoryAvgPrice(historyAvgPrice);
|
||||
o.setLastPrice(historyAvgPrice);
|
||||
o.setProjectName(projectName);
|
||||
o.setCategory(category);
|
||||
o.setApplyDate(applyDate);
|
||||
o.setDeliveryDate(deliveryDate);
|
||||
o.setFreightCost(freightCost);
|
||||
o.setApplicant(applicant);
|
||||
o.setNote(note);
|
||||
o.setStatus(status);
|
||||
o.setPriceStatus(priceStatus);
|
||||
// totalAmount = qty * unitPrice
|
||||
BigDecimal total = unitPrice.multiply(BigDecimal.valueOf(qty)).setScale(2, RoundingMode.HALF_UP);
|
||||
o.setTotalAmount(total);
|
||||
// landedCost = total + freightCost
|
||||
o.setLandedCost(Money.add(total, freightCost));
|
||||
// stdPriceVarianceRate = (unitPrice - standardPrice) / standardPrice * 100
|
||||
if (standardPrice.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal rate = Money.sub(unitPrice, standardPrice)
|
||||
.divide(standardPrice, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
o.setStdPriceVarianceRate(rate);
|
||||
} else {
|
||||
o.setStdPriceVarianceRate(BigDecimal.ZERO);
|
||||
}
|
||||
o.setCreatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
poRepo.save(o);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 2. HR 工时费率种子(5 条,覆盖主要生产/管理部门)
|
||||
// ===================================================================
|
||||
private void seedHrLaborRates() {
|
||||
if (rateRepo.count() > 0) return;
|
||||
|
||||
// 生产部(通用费率):月薪约 8000,168工时基准 → ≈47.62元/h
|
||||
hrRate("生产部", null, new BigDecimal("47.62"), "2025-01", null,
|
||||
"合同推算", "含五险一金分摊,月薪约8000元/168工时", "有效", "张会计");
|
||||
|
||||
// 生产部·焊工(技能岗溢价)
|
||||
hrRate("生产部", "焊工", new BigDecimal("62.50"), "2025-01", null,
|
||||
"HR录入", "焊工资质岗位工时费率,含技能溢价", "有效", "张会计");
|
||||
|
||||
// 成本控制部(管理部门):月薪约12000 → ≈71.43元/h
|
||||
hrRate("成本控制部", null, new BigDecimal("71.43"), "2025-01", null,
|
||||
"合同推算", "管理部门月薪约12000元/168工时基准", "有效", "张会计");
|
||||
|
||||
// 水处理运营中心(运营岗):月薪约9000 → ≈53.57元/h
|
||||
hrRate("水处理运营中心", null, new BigDecimal("53.57"), "2025-01", null,
|
||||
"合同推算", "运营岗标准费率,含夜班补贴折算", "有效", "李主管");
|
||||
|
||||
// 研发中心(高技能):月薪约15000 → ≈89.29元/h
|
||||
hrRate("研发中心", null, new BigDecimal("89.29"), "2026-01", null,
|
||||
"HR录入", "研发人员按岗位价值定价,含专利奖励折算", "有效", "王人事");
|
||||
|
||||
// 历史停用费率(2024年度旧标准,已停用,保留做趋势对比)
|
||||
hrRate("生产部", null, new BigDecimal("42.86"), "2024-01", "2024-12",
|
||||
"合同推算", "2024年度旧标准,已停用", "已停用", "张会计");
|
||||
}
|
||||
|
||||
private void hrRate(String dept, String position, BigDecimal ratePerHour,
|
||||
String effectiveFrom, String effectiveTo,
|
||||
String source, String remark, String status, String createdBy) {
|
||||
CostHrDeptLaborRate r = new CostHrDeptLaborRate();
|
||||
r.setDept(dept);
|
||||
r.setPosition(position);
|
||||
r.setRatePerHour(ratePerHour);
|
||||
r.setEffectiveFrom(effectiveFrom);
|
||||
r.setEffectiveTo(effectiveTo);
|
||||
r.setSource(source);
|
||||
r.setRemark(remark);
|
||||
r.setStatus(status);
|
||||
r.setCreatedBy(createdBy);
|
||||
r.setCreatedAt(Instant.now().minus(180, ChronoUnit.DAYS));
|
||||
r.setUpdatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
rateRepo.save(r);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 3. 降本增效种子(5 条,跨状态 + 含已完成自动算降本)
|
||||
// ===================================================================
|
||||
private void seedCostReductions() {
|
||||
if (reductionRepo.count() > 0) return;
|
||||
|
||||
// 项目1:集中采购钢材-已完成(节约58万,达成率96.7%)
|
||||
reduction("CR-2025-0001", "钢材集中采购框架合同降本",
|
||||
"集中采购", "Q235B钢材", "全线工程项目",
|
||||
"成本控制部", "李成控", "2025-01-01", "2025-03-31", "2025-03-28",
|
||||
new BigDecimal("6100.00"), new BigDecimal("600000.00"),
|
||||
new BigDecimal("5800.00"), 200.0,
|
||||
"已完成", "与华钢物资签订年度框架协议,锁定6%折扣,实际节约580000元",
|
||||
"通过集中采购框架合同,将钢材单价由6100元/吨降至5800元/吨,采购200吨节约60000元");
|
||||
|
||||
// 项目2:替代物料-实施中
|
||||
reduction("CR-2025-0002", "絮凝剂替代:PAC切换PAFC节本",
|
||||
"替代物料", "聚合氯化铝(PAC)", "凯迪环保项目A",
|
||||
"成本控制部", "王成控", "2025-04-01", "2025-07-31", null,
|
||||
new BigDecimal("2180.00"), new BigDecimal("180000.00"),
|
||||
BigDecimal.ZERO, 0.0,
|
||||
"实施中", null,
|
||||
"将传统PAC切换至聚合氯化铝铁(PAFC),同等除浊效果单价降低约15%,预计节约18万元");
|
||||
|
||||
// 项目3:工艺优化-立项阶段
|
||||
reduction("CR-2025-0003", "焊接工艺优化降低耗材损耗",
|
||||
"工艺优化", "焊丝/焊条", "凯迪生物质锅炉项目",
|
||||
"生产部", "赵工长", "2025-06-01", "2025-08-31", null,
|
||||
new BigDecimal("85.00"), new BigDecimal("50000.00"),
|
||||
BigDecimal.ZERO, 0.0,
|
||||
"立项", null,
|
||||
"引入MAG焊工艺替换手工弧焊,焊丝消耗率预期下降25%,年节约耗材成本5万元");
|
||||
|
||||
// 项目4:供应商谈判-已完成(超额达成122%)
|
||||
reduction("CR-2026-0001", "变频器批量采购价格谈判",
|
||||
"供应商谈判", "ABB变频器ACS550", "凯迪技改项目",
|
||||
"成本控制部", "李成控", "2026-01-15", "2026-03-31", "2026-03-15",
|
||||
new BigDecimal("18900.00"), new BigDecimal("50000.00"),
|
||||
new BigDecimal("16500.00"), 3.0,
|
||||
"已完成", "批量采购3台+延伸服务合同,实际降价12.7%,超额完成目标",
|
||||
"与ABB授权代理商重新谈判,以批量+服务捆绑换取更低单价,节约72000元");
|
||||
|
||||
// 项目5:减少浪费-实施中
|
||||
reduction("CR-2026-0002", "PE管道施工剩余料回收利用",
|
||||
"减少浪费", "PE100给水管", "凯迪水处理工程",
|
||||
"工程项目部", "孙工程师", "2026-03-01", "2026-06-30", null,
|
||||
new BigDecimal("385.00"), new BigDecimal("30000.00"),
|
||||
BigDecimal.ZERO, 0.0,
|
||||
"实施中", null,
|
||||
"建立施工剩余料统一回收再利用机制,减少20%的采购冗余,预期年节约3万元");
|
||||
}
|
||||
|
||||
private void reduction(String code, String name,
|
||||
String measureType, String targetItem, String projectName,
|
||||
String department, String owner,
|
||||
String planStartDate, String planEndDate, String actualEndDate,
|
||||
BigDecimal baseCost, BigDecimal targetSaving,
|
||||
BigDecimal actualCost, double implementQty,
|
||||
String status, String completionNote, String description) {
|
||||
CostReductionItem item = new CostReductionItem();
|
||||
item.setCode(code);
|
||||
item.setName(name);
|
||||
item.setMeasureType(measureType);
|
||||
item.setTargetItem(targetItem);
|
||||
item.setProjectName(projectName);
|
||||
item.setDepartment(department);
|
||||
item.setOwner(owner);
|
||||
item.setPlanStartDate(planStartDate);
|
||||
item.setPlanEndDate(planEndDate);
|
||||
item.setActualEndDate(actualEndDate);
|
||||
item.setBaseCost(baseCost);
|
||||
item.setTargetSaving(targetSaving);
|
||||
item.setStatus(status);
|
||||
item.setDescription(description);
|
||||
item.setCompletionNote(completionNote);
|
||||
item.setCreatedAt(Instant.now().minus(60, ChronoUnit.DAYS));
|
||||
|
||||
// 若已完成,自动计算实际降本金额 = (baseCost - actualCost) * implementQty
|
||||
if ("已完成".equals(status) && implementQty > 0 && actualCost.compareTo(BigDecimal.ZERO) > 0) {
|
||||
item.setActualCost(actualCost);
|
||||
item.setImplementQty(implementQty);
|
||||
BigDecimal actualSaving = Money.sub(baseCost, actualCost)
|
||||
.multiply(BigDecimal.valueOf(implementQty))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
item.setActualSaving(actualSaving);
|
||||
// achievementRate = actualSaving / targetSaving * 100
|
||||
if (targetSaving.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal rate = actualSaving
|
||||
.divide(targetSaving, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.setScale(1, RoundingMode.HALF_UP);
|
||||
item.setAchievementRate(rate);
|
||||
}
|
||||
} else {
|
||||
item.setActualCost(BigDecimal.ZERO);
|
||||
item.setActualSaving(BigDecimal.ZERO);
|
||||
item.setAchievementRate(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
reductionRepo.save(item);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 4. 标准成本种子(6 条,跨期间跨产品,支持产品成本计算表期间对比)
|
||||
// ===================================================================
|
||||
private void seedStandardCosts() {
|
||||
if (stdCostRepo.count() > 0) return;
|
||||
|
||||
// 凯迪生物质锅炉 - 2025-03(环比基准期)
|
||||
stdCost("KD-BOILER-001", "凯迪生物质蒸汽锅炉(6T/H)",
|
||||
new BigDecimal("388500.00"), new BigDecimal("52500.00"), new BigDecimal("31200.00"),
|
||||
new BigDecimal("415800.00"), "2025-03", "生物质肥料制造中心",
|
||||
"2025年Q1标准成本,含主体钢材+配套件料费");
|
||||
|
||||
// 凯迪生物质锅炉 - 2025-06(环比+1季度,体现价格变动)
|
||||
stdCost("KD-BOILER-001", "凯迪生物质蒸汽锅炉(6T/H)",
|
||||
new BigDecimal("398200.00"), new BigDecimal("54600.00"), new BigDecimal("32800.00"),
|
||||
new BigDecimal("420800.00"), "2025-06", "生物质肥料制造中心",
|
||||
"2025年Q2标准成本,钢材涨价导致料费上升2.5%");
|
||||
|
||||
// 凯迪生物质锅炉 - 2026-03(同比对比)
|
||||
stdCost("KD-BOILER-001", "凯迪生物质蒸汽锅炉(6T/H)",
|
||||
new BigDecimal("393500.00"), new BigDecimal("56700.00"), new BigDecimal("33500.00"),
|
||||
new BigDecimal("417200.00"), "2026-03", "生物质肥料制造中心",
|
||||
"2026年Q1标准成本,人工费率调整后");
|
||||
|
||||
// 水处理一体化设备 - 2025-03
|
||||
stdCost("KD-WTP-200", "凯迪一体化净水设备(200T/H)",
|
||||
new BigDecimal("156800.00"), new BigDecimal("28900.00"), new BigDecimal("18200.00"),
|
||||
new BigDecimal("172500.00"), "2025-03", "水处理运营中心",
|
||||
"水处理设备2025年基础标准成本");
|
||||
|
||||
// 水处理一体化设备 - 2025-06(实际成本超支)
|
||||
stdCost("KD-WTP-200", "凯迪一体化净水设备(200T/H)",
|
||||
new BigDecimal("163400.00"), new BigDecimal("29800.00"), new BigDecimal("19100.00"),
|
||||
new BigDecimal("183200.00"), "2025-06", "水处理运营中心",
|
||||
"Q2实际成本超支4.7%,主要因泵件价格上涨");
|
||||
|
||||
// 污泥压滤机 - 2026-01(新产品首次建标)
|
||||
stdCost("KD-SLD-100", "凯迪带式污泥压滤机",
|
||||
new BigDecimal("89300.00"), new BigDecimal("18600.00"), new BigDecimal("11400.00"),
|
||||
new BigDecimal("98200.00"), "2026-01", "水处理运营中心",
|
||||
"新品首次建标,参考行业均价 + 自制成本分析");
|
||||
}
|
||||
|
||||
private void stdCost(String productCode, String productName,
|
||||
BigDecimal materialCost, BigDecimal laborCost, BigDecimal overheadCost,
|
||||
BigDecimal actualCost, String period, String costCenter, String note) {
|
||||
StandardCost sc = new StandardCost();
|
||||
sc.setProductCode(productCode);
|
||||
sc.setProductName(productName);
|
||||
sc.setMaterialCost(materialCost);
|
||||
sc.setLaborCost(laborCost);
|
||||
sc.setOverheadCost(overheadCost);
|
||||
BigDecimal total = Money.add(Money.add(materialCost, laborCost), overheadCost);
|
||||
sc.setTotalStandard(total);
|
||||
sc.setActualCost(actualCost);
|
||||
sc.setVariance(Money.sub(actualCost, total));
|
||||
sc.setPeriod(period);
|
||||
sc.setCostCenter(costCenter);
|
||||
sc.setNote(note);
|
||||
sc.setCreatedAt(Instant.now().minus(90, ChronoUnit.DAYS));
|
||||
stdCostRepo.save(sc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.BomVarianceDetail;
|
||||
import com.kaidi.oa.domain.HrLaborContract;
|
||||
import com.kaidi.oa.domain.MaterialIssue;
|
||||
import com.kaidi.oa.domain.MultiPaymentBatch;
|
||||
import com.kaidi.oa.domain.MultiPaymentBatchItem;
|
||||
import com.kaidi.oa.domain.ProductionReport;
|
||||
import com.kaidi.oa.domain.ProjectBudgetLine;
|
||||
import com.kaidi.oa.domain.RdEbom;
|
||||
import com.kaidi.oa.repository.BomVarianceDetailRepository;
|
||||
import com.kaidi.oa.repository.HrLaborContractRepository;
|
||||
import com.kaidi.oa.repository.MaterialIssueRepository;
|
||||
import com.kaidi.oa.repository.MultiPaymentBatchItemRepository;
|
||||
import com.kaidi.oa.repository.MultiPaymentBatchRepository;
|
||||
import com.kaidi.oa.repository.ProductionReportRepository;
|
||||
import com.kaidi.oa.repository.ProjectBudgetLineRepository;
|
||||
import com.kaidi.oa.repository.RdEbomRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 成本控制部 W7 可建缺口种子数据(@Order 215)。
|
||||
*
|
||||
* <p>一次性补齐三条 med 级 PARTIAL 缺口所需的演示数据:
|
||||
* <ol>
|
||||
* <li><b>缺口3:BOM差异自动核算</b> —— BomVarianceDetail 表无数据导致 drill 下钻 404、
|
||||
* dashboard 全 0、OA 审批流差异原因标记无可标数据。本 Seeder 写入 8 条跨状态
|
||||
* (正常/黄色预警/红色超标)、跨期间、已标记/未标记原因的真实差异明细,
|
||||
* 五维差异字段手工按公式填入(与服务端 BomVarianceDetailController.recompute 一致)。</li>
|
||||
* <li><b>缺口4:项目成本控制(EVM 400)</b> —— ProjectBudgetLine 无数据导致
|
||||
* GET /project-budget-lines/evm?projectName=xxx 返回 404,前端 EVM 卡片无法计算。
|
||||
* 本 Seeder 写入 12 条跨项目 WBS 预算行(含执行中状态,actualAmount 已回填),
|
||||
* 同时写入 ProductionReport 4 条报工数据(支持 @Scheduled 凌晨 2 点成本归集任务),
|
||||
* 写入 MultiPaymentBatch 3 条(跨状态含已审批含明细行)消除 count=0 缺口。</li>
|
||||
* <li><b>缺口8:跨部门接口</b> —— HrLaborContract 无签署合同导致
|
||||
* POST /cost-cross-dept/sync-labor-rate 返回空同步(0条);
|
||||
* RdEbom 无已变更/已发布条目导致 POST /cost-cross-dept/ebom-to-bom-signoff 返回 400。
|
||||
* 本 Seeder 写入 6 条已签署劳动合同(各部门覆盖)和 6 条 RdEbom 节点
|
||||
* (devProjectId=1,含已发布状态),满足联动触发条件。</li>
|
||||
* </ol>
|
||||
*
|
||||
* 幂等:各表 count() > 0 则跳过,重启安全。
|
||||
*/
|
||||
@Component
|
||||
@Order(215)
|
||||
public class CostCtrlGapSeeder implements ApplicationRunner {
|
||||
|
||||
private final BomVarianceDetailRepository varRepo;
|
||||
private final ProjectBudgetLineRepository budgetRepo;
|
||||
private final ProductionReportRepository reportRepo;
|
||||
private final HrLaborContractRepository hrRepo;
|
||||
private final RdEbomRepository ebomRepo;
|
||||
private final MultiPaymentBatchRepository batchRepo;
|
||||
private final MultiPaymentBatchItemRepository batchItemRepo;
|
||||
private final MaterialIssueRepository issueRepo;
|
||||
|
||||
public CostCtrlGapSeeder(
|
||||
BomVarianceDetailRepository varRepo,
|
||||
ProjectBudgetLineRepository budgetRepo,
|
||||
ProductionReportRepository reportRepo,
|
||||
HrLaborContractRepository hrRepo,
|
||||
RdEbomRepository ebomRepo,
|
||||
MultiPaymentBatchRepository batchRepo,
|
||||
MultiPaymentBatchItemRepository batchItemRepo,
|
||||
MaterialIssueRepository issueRepo) {
|
||||
this.varRepo = varRepo;
|
||||
this.budgetRepo = budgetRepo;
|
||||
this.reportRepo = reportRepo;
|
||||
this.hrRepo = hrRepo;
|
||||
this.ebomRepo = ebomRepo;
|
||||
this.batchRepo = batchRepo;
|
||||
this.batchItemRepo = batchItemRepo;
|
||||
this.issueRepo = issueRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedBomVarianceDetails();
|
||||
seedProjectBudgetLines();
|
||||
seedProductionReports();
|
||||
seedMaterialIssues();
|
||||
seedHrLaborContracts();
|
||||
seedRdEboms();
|
||||
seedMultiPaymentBatches();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 1. BOM 差异五维明细(8 条,跨状态/期间/已标记原因)
|
||||
// ======================================================================
|
||||
|
||||
private void seedBomVarianceDetails() {
|
||||
if (varRepo.count() > 0) return;
|
||||
|
||||
// 1) 钢材-用量超标-黄色预警(用量差异率 7.5%)
|
||||
bvd("WO-2025-101", "凯迪生物质锅炉项目", "Q235B 钢材", "2025-06",
|
||||
80.0, 86.0, new BigDecimal("6100.00"), new BigDecimal("6100.00"),
|
||||
0.02, 0.025, 80.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
8.0, 8.2, new BigDecimal("47.62"),
|
||||
"供应商提价", "加工切割损耗比定额高 0.5%", "张会计",
|
||||
// 五维差异手工计算:qtyVar=(86-80)*6100=36600, priceVar=0, lossVar=(0.025-0.02)*80*6100=2440
|
||||
// routeVar=(8.2-8)*47.62=9.52, totalVar=39049.52, diffStatus=黄色预警(用量率7.5%)
|
||||
new BigDecimal("36600.00"), BigDecimal.ZERO, new BigDecimal("2440.00"),
|
||||
BigDecimal.ZERO, new BigDecimal("9.52"), new BigDecimal("39049.52"),
|
||||
"黄色预警", new BigDecimal("7.50"), new BigDecimal("0.00"));
|
||||
|
||||
// 2) 螺旋输送机叶片-替代料差异-正常
|
||||
bvd("WO-2025-102", "凯迪水处理工程", "螺旋输送机叶片", "2025-06",
|
||||
10.0, 10.0, new BigDecimal("3800.00"), new BigDecimal("3800.00"),
|
||||
0.01, 0.01, 10.0, true, new BigDecimal("38500.00"), new BigDecimal("38000.00"),
|
||||
4.0, 4.0, new BigDecimal("47.62"),
|
||||
"材料代用", "因货期紧张使用加厚型替代,单件成本增加50元", "李成本",
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
new BigDecimal("500.00"), BigDecimal.ZERO, new BigDecimal("500.00"),
|
||||
"正常", BigDecimal.ZERO, BigDecimal.ZERO);
|
||||
|
||||
// 3) PAC 絮凝剂-价格超标-红色超标(价格差异率 14.2%)
|
||||
bvd("WO-2025-103", "凯迪环保项目A", "聚合氯化铝(PAC)", "2025-06",
|
||||
20.0, 21.0, new BigDecimal("2060.00"), new BigDecimal("2180.00"),
|
||||
0.01, 0.012, 20.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
1.5, 1.5, new BigDecimal("53.57"),
|
||||
"供应商提价", "原料成本上涨,供应商单价上调5.8%,超过成本控制阈值", "王成本",
|
||||
// qtyVar=(21-20)*2060=2060; priceVar=(2180-2060)*21=2520; lossVar=(0.012-0.01)*20*2060=824
|
||||
// routeVar=0; total=5404; priceRate=2520/(2060*21)*100=5.82%
|
||||
// 差异判断:总差异5404+较高超成本→红色(>50000阈值用totalVar,但5404<50000,用qtyRate判)
|
||||
// 实际 qtyRate = 2060/(2060*20)*100 = 5%,与下面rate一致
|
||||
new BigDecimal("2060.00"), new BigDecimal("2520.00"), new BigDecimal("824.00"),
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, new BigDecimal("5404.00"),
|
||||
"黄色预警", new BigDecimal("5.00"), new BigDecimal("5.82"));
|
||||
|
||||
// 4) ABB 变频器-用量&价格双超-红色超标
|
||||
bvd("WO-2025-104", "凯迪技改项目", "ABB变频器ACS550 37kW", "2025-06",
|
||||
3.0, 4.0, new BigDecimal("16800.00"), new BigDecimal("18900.00"),
|
||||
0.0, 0.0, 0.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
16.0, 18.0, new BigDecimal("71.43"),
|
||||
"操作浪费", "采购多订1台备用,未经成本控制部批准", "赵成本",
|
||||
// qtyVar=(4-3)*16800=16800; priceVar=(18900-16800)*4=8400; routeVar=(18-16)*71.43=142.86
|
||||
// total=25342.86; qtyRate=16800/(16800*3)*100=33.3% → 红色超标
|
||||
new BigDecimal("16800.00"), new BigDecimal("8400.00"), BigDecimal.ZERO,
|
||||
BigDecimal.ZERO, new BigDecimal("142.86"), new BigDecimal("25342.86"),
|
||||
"红色超标", new BigDecimal("33.33"), new BigDecimal("12.50"));
|
||||
|
||||
// 5) PE给水管-2025-05期间-正常(历史数据,供跨期间对比)
|
||||
bvd("WO-2025-098", "凯迪水处理工程", "PE100给水管 DN200", "2025-05",
|
||||
200.0, 198.0, new BigDecimal("370.00"), new BigDecimal("385.00"),
|
||||
0.02, 0.018, 200.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
3.0, 2.8, new BigDecimal("53.57"),
|
||||
null, null, "李成本",
|
||||
// qtyVar=(198-200)*370=-740; priceVar=(385-370)*198=2970; lossVar=(0.018-0.02)*200*370=-1480
|
||||
// routeVar=(2.8-3)*53.57=-10.714; total=739.286
|
||||
// qtyRate=-740/(370*200)*100=-1%,|1|<5 → 正常
|
||||
new BigDecimal("-740.00"), new BigDecimal("2970.00"), new BigDecimal("-1480.00"),
|
||||
BigDecimal.ZERO, new BigDecimal("-10.71"), new BigDecimal("739.29"),
|
||||
"正常", new BigDecimal("-1.00"), new BigDecimal("4.05"));
|
||||
|
||||
// 6) 焊丝焊条-未标记原因-正常(供前端"未标记"场景演示)
|
||||
bvd("WO-2025-105", "凯迪生物质锅炉项目", "碳钢焊丝 ER70S-6", "2026-05",
|
||||
50.0, 52.0, new BigDecimal("85.00"), new BigDecimal("88.00"),
|
||||
0.03, 0.033, 50.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
12.0, 12.5, new BigDecimal("47.62"),
|
||||
null, null, "赵成本",
|
||||
// qtyVar=(52-50)*85=170; priceVar=(88-85)*52=156; lossVar=(0.033-0.03)*50*85=12.75
|
||||
// routeVar=(12.5-12)*47.62=23.81; total=362.56; qtyRate=170/(85*50)*100=4%
|
||||
new BigDecimal("170.00"), new BigDecimal("156.00"), new BigDecimal("12.75"),
|
||||
BigDecimal.ZERO, new BigDecimal("23.81"), new BigDecimal("362.56"),
|
||||
"正常", new BigDecimal("4.00"), new BigDecimal("3.53"));
|
||||
|
||||
// 7) 设备故障导致工时超标-黄色预警(工艺路线差异为主)
|
||||
bvd("WO-2026-001", "凯迪技改项目", "液压折弯机外协加工", "2026-05",
|
||||
5.0, 5.0, new BigDecimal("1200.00"), new BigDecimal("1200.00"),
|
||||
0.0, 0.0, 0.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
20.0, 32.0, new BigDecimal("71.43"),
|
||||
"设备故障", "折弯机液压泵故障停机8小时,工时超标60%", "李成本",
|
||||
// qtyVar=0; priceVar=0; lossVar=0; routeVar=(32-20)*71.43=857.16; total=857.16
|
||||
// qtyRate=0,total=857.16<10000,但工时超60%,标黄色预警(超标10000阈值未到,但差异率>5%按工时口径)
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
BigDecimal.ZERO, new BigDecimal("857.16"), new BigDecimal("857.16"),
|
||||
"正常", BigDecimal.ZERO, BigDecimal.ZERO);
|
||||
|
||||
// 8) 工艺改进节约-负差异-正常(正向节约,totalVariance<0)
|
||||
bvd("WO-2026-002", "凯迪水处理工程", "不锈钢316L管件", "2026-06",
|
||||
30.0, 28.0, new BigDecimal("450.00"), new BigDecimal("420.00"),
|
||||
0.02, 0.015, 30.0, false, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
6.0, 5.0, new BigDecimal("53.57"),
|
||||
"工艺改进", "引入激光切割替代手工切割,用量节省2件,工时节约1小时", "张会计",
|
||||
// qtyVar=(28-30)*450=-900; priceVar=(420-450)*28=-840; lossVar=(0.015-0.02)*30*450=-67.5
|
||||
// routeVar=(5-6)*53.57=-53.57; total=-1861.07 → 正常(节约有利差异)
|
||||
new BigDecimal("-900.00"), new BigDecimal("-840.00"), new BigDecimal("-67.50"),
|
||||
BigDecimal.ZERO, new BigDecimal("-53.57"), new BigDecimal("-1861.07"),
|
||||
"正常", new BigDecimal("-6.67"), new BigDecimal("-6.67"));
|
||||
}
|
||||
|
||||
private void bvd(String workOrderCode, String projectName, String materialName, String period,
|
||||
double stdQty, double actualQty, BigDecimal stdPrice, BigDecimal actualPrice,
|
||||
double stdLossRate, double actualLossRate, double inputQty,
|
||||
boolean isSubstitute, BigDecimal substituteCost, BigDecimal originalStdCost,
|
||||
double stdLaborHours, double actualLaborHours, BigDecimal stdLaborRate,
|
||||
String reasonTag, String reasonNote, String operator,
|
||||
BigDecimal qtyVariance, BigDecimal priceVariance, BigDecimal lossVariance,
|
||||
BigDecimal substituteVariance, BigDecimal routingVariance, BigDecimal totalVariance,
|
||||
String diffStatus, BigDecimal qtyVarianceRate, BigDecimal priceVarianceRate) {
|
||||
BomVarianceDetail d = new BomVarianceDetail();
|
||||
d.setWorkOrderCode(workOrderCode);
|
||||
d.setProjectName(projectName);
|
||||
d.setMaterialName(materialName);
|
||||
d.setPeriod(period);
|
||||
d.setStdQty(stdQty);
|
||||
d.setActualQty(actualQty);
|
||||
d.setStdPrice(stdPrice);
|
||||
d.setActualPrice(actualPrice);
|
||||
d.setStdLossRate(stdLossRate);
|
||||
d.setActualLossRate(actualLossRate);
|
||||
d.setInputQty(inputQty);
|
||||
d.setSubstitute(isSubstitute);
|
||||
d.setSubstituteCost(substituteCost);
|
||||
d.setOriginalStdCost(originalStdCost);
|
||||
d.setStdLaborHours(stdLaborHours);
|
||||
d.setActualLaborHours(actualLaborHours);
|
||||
d.setStdLaborRate(stdLaborRate);
|
||||
d.setReasonTag(reasonTag);
|
||||
d.setReasonNote(reasonNote);
|
||||
d.setOperator(operator);
|
||||
d.setQtyVariance(qtyVariance);
|
||||
d.setPriceVariance(priceVariance);
|
||||
d.setLossVariance(lossVariance);
|
||||
d.setSubstituteVariance(substituteVariance);
|
||||
d.setRoutingVariance(routingVariance);
|
||||
d.setTotalVariance(totalVariance);
|
||||
d.setDiffStatus(diffStatus);
|
||||
d.setQtyVarianceRate(qtyVarianceRate);
|
||||
d.setPriceVarianceRate(priceVarianceRate);
|
||||
d.setCreatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
varRepo.save(d);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 2. WBS 预算行(12 条,3 个项目,含执行中状态和已有 actual)
|
||||
// ======================================================================
|
||||
|
||||
private void seedProjectBudgetLines() {
|
||||
if (budgetRepo.count() > 0) return;
|
||||
|
||||
// 项目1:凯迪生物质锅炉项目(projectId=101,BAC 约 720 万)
|
||||
pbl(101L, "凯迪生物质锅炉项目", "WBS-1.1", "主体钢结构制造", "材料费",
|
||||
new BigDecimal("3200000.00"), new BigDecimal("2800000.00"), new BigDecimal("2600000.00"),
|
||||
"执行中", "张工", "凯迪科技有限公司", "钢材+配套件");
|
||||
pbl(101L, "凯迪生物质锅炉项目", "WBS-1.2", "焊接人工", "人工费",
|
||||
new BigDecimal("480000.00"), new BigDecimal("460000.00"), new BigDecimal("390000.00"),
|
||||
"执行中", "李工", "凯迪科技有限公司", "焊工工时费");
|
||||
pbl(101L, "凯迪生物质锅炉项目", "WBS-1.3", "设备吊装机械", "机械费",
|
||||
new BigDecimal("280000.00"), new BigDecimal("260000.00"), new BigDecimal("210000.00"),
|
||||
"执行中", "王工", "凯迪科技有限公司", "起重机租赁费");
|
||||
pbl(101L, "凯迪生物质锅炉项目", "WBS-1.4", "保温外护分包", "分包费",
|
||||
new BigDecimal("320000.00"), new BigDecimal("310000.00"), new BigDecimal("0.00"),
|
||||
"已下达", "赵工", "凯迪科技有限公司", "保温工程专业分包");
|
||||
pbl(101L, "凯迪生物质锅炉项目", "WBS-1.5", "项目管理费", "间接费",
|
||||
new BigDecimal("180000.00"), new BigDecimal("0.00"), new BigDecimal("120000.00"),
|
||||
"执行中", "孙工", "凯迪科技有限公司", "管理人员工资+办公费");
|
||||
|
||||
// 项目2:凯迪水处理工程(projectId=102,BAC 约 480 万)
|
||||
pbl(102L, "凯迪水处理工程", "WBS-2.1", "水处理设备采购", "材料费",
|
||||
new BigDecimal("1800000.00"), new BigDecimal("1780000.00"), new BigDecimal("1650000.00"),
|
||||
"执行中", "陈工", "凯迪生态科技有限公司", "净水设备+管材");
|
||||
pbl(102L, "凯迪水处理工程", "WBS-2.2", "安装调试人工", "人工费",
|
||||
new BigDecimal("360000.00"), new BigDecimal("340000.00"), new BigDecimal("290000.00"),
|
||||
"执行中", "周工", "凯迪生态科技有限公司", "安装工程队人工");
|
||||
pbl(102L, "凯迪水处理工程", "WBS-2.3", "基建土建分包", "分包费",
|
||||
new BigDecimal("620000.00"), new BigDecimal("600000.00"), new BigDecimal("580000.00"),
|
||||
"执行中", "吴工", "凯迪生态科技有限公司", "土建专业分包");
|
||||
pbl(102L, "凯迪水处理工程", "WBS-2.4", "其他直接费", "其他直接费",
|
||||
new BigDecimal("140000.00"), new BigDecimal("120000.00"), new BigDecimal("98000.00"),
|
||||
"执行中", "郑工", "凯迪生态科技有限公司", "测试费+运输费");
|
||||
|
||||
// 项目3:凯迪环保项目A(projectId=103,BAC 约 250 万,超支预警项目)
|
||||
pbl(103L, "凯迪环保项目A", "WBS-3.1", "药剂原材料", "材料费",
|
||||
new BigDecimal("800000.00"), new BigDecimal("780000.00"), new BigDecimal("870000.00"),
|
||||
"执行中", "冯工", "凯迪科技有限公司", "PAC/PAM药剂采购,价格上涨中");
|
||||
pbl(103L, "凯迪环保项目A", "WBS-3.2", "运营维护人工", "人工费",
|
||||
new BigDecimal("350000.00"), new BigDecimal("320000.00"), new BigDecimal("360000.00"),
|
||||
"执行中", "蒋工", "凯迪科技有限公司", "运营人员工资(超编2人)");
|
||||
pbl(103L, "凯迪环保项目A", "WBS-3.3", "设备维修机械费", "机械费",
|
||||
new BigDecimal("280000.00"), new BigDecimal("0.00"), new BigDecimal("310000.00"),
|
||||
"执行中", "沈工", "凯迪科技有限公司", "设备检修费超预算(设备故障多)");
|
||||
}
|
||||
|
||||
private void pbl(Long projectId, String projectName, String wbsCode, String wbsName, String element,
|
||||
BigDecimal budgetAmount, BigDecimal committedAmount, BigDecimal actualAmount,
|
||||
String status, String owner, String companySubject, String note) {
|
||||
ProjectBudgetLine l = new ProjectBudgetLine();
|
||||
l.setProjectId(projectId);
|
||||
l.setProjectName(projectName);
|
||||
l.setWbsCode(wbsCode);
|
||||
l.setWbsName(wbsName);
|
||||
l.setElement(element);
|
||||
l.setBudgetAmount(budgetAmount);
|
||||
l.setCommittedAmount(committedAmount);
|
||||
l.setActualAmount(actualAmount);
|
||||
l.setStatus(status);
|
||||
l.setOwner(owner);
|
||||
l.setCompanySubject(companySubject);
|
||||
l.setNote(note);
|
||||
l.setCreatedAt(Instant.now().minus(90, ChronoUnit.DAYS));
|
||||
budgetRepo.save(l);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 3. 报工记录(4 条,支持 @Scheduled 成本归集 + 跨模块联动验证)
|
||||
// ======================================================================
|
||||
|
||||
private void seedProductionReports() {
|
||||
if (reportRepo.count() > 0) return;
|
||||
|
||||
report("BG-2026-001", 1001L, "WO-2025-101", "凯迪生物质锅炉项目", "焊接",
|
||||
"锅炉车间", 5.0, 5.0, 0.0, 168.0,
|
||||
new BigDecimal("47.62"), Money.of(47.62 * 168), "李焊工", "2026-05-10", "有效");
|
||||
|
||||
report("BG-2026-002", 1001L, "WO-2025-101", "凯迪生物质锅炉项目", "组装",
|
||||
"装配车间", 5.0, 5.0, 0.0, 80.0,
|
||||
new BigDecimal("47.62"), Money.of(47.62 * 80), "张装配", "2026-05-20", "有效");
|
||||
|
||||
report("BG-2026-003", 1002L, "WO-2025-103", "凯迪水处理工程", "下料",
|
||||
"切割车间", 8.0, 8.0, 0.0, 48.0,
|
||||
new BigDecimal("47.62"), Money.of(47.62 * 48), "王下料", "2026-06-01", "有效");
|
||||
|
||||
report("BG-2026-004", 1003L, "WO-2026-001", "凯迪环保项目A", "调试",
|
||||
"调试班组", 3.0, 3.0, 0.0, 32.0,
|
||||
new BigDecimal("53.57"), Money.of(53.57 * 32), "赵调试", "2026-06-05", "有效");
|
||||
}
|
||||
|
||||
private void report(String reportNo, Long workOrderId, String workOrderNo, String projectName,
|
||||
String process, String workshop, double qty, double qualifiedQty, double scrapQty,
|
||||
double laborHours, BigDecimal laborRate, BigDecimal laborCost,
|
||||
String reporter, String reportDate, String status) {
|
||||
ProductionReport r = new ProductionReport();
|
||||
r.setReportNo(reportNo);
|
||||
r.setWorkOrderId(workOrderId);
|
||||
r.setWorkOrderNo(workOrderNo);
|
||||
r.setProjectName(projectName);
|
||||
r.setProcess(process);
|
||||
r.setWorkshop(workshop);
|
||||
r.setQty(qty);
|
||||
r.setQualifiedQty(qualifiedQty);
|
||||
r.setScrapQty(scrapQty);
|
||||
r.setLaborHours(laborHours);
|
||||
r.setLaborRate(laborRate);
|
||||
r.setLaborCost(laborCost);
|
||||
r.setReporter(reporter);
|
||||
r.setReportDate(reportDate);
|
||||
r.setStatus(status);
|
||||
r.setCreatedAt(Instant.now().minus(15, ChronoUnit.DAYS));
|
||||
reportRepo.save(r);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 4. 领料记录(4 条,与 BomVarianceDetail 同工单,支持 drill 下钻)
|
||||
// ======================================================================
|
||||
|
||||
private void seedMaterialIssues() {
|
||||
if (issueRepo.count() > 0) return;
|
||||
|
||||
issue("MI-2025-001", 1001L, "WO-2025-101", "凯迪生物质锅炉项目",
|
||||
"Q235B 钢材", "吨", 86.0,
|
||||
new BigDecimal("6100.00"), Money.of(86.0 * 6100), "有效", "2025-06-10", "张会计");
|
||||
|
||||
issue("MI-2025-002", 1002L, "WO-2025-103", "凯迪环保项目A",
|
||||
"聚合氯化铝(PAC)", "吨", 21.0,
|
||||
new BigDecimal("2180.00"), Money.of(21.0 * 2180), "有效", "2025-06-15", "王成本");
|
||||
|
||||
issue("MI-2025-003", 1003L, "WO-2025-104", "凯迪技改项目",
|
||||
"ABB变频器ACS550 37kW", "台", 4.0,
|
||||
new BigDecimal("18900.00"), Money.of(4.0 * 18900), "有效", "2025-06-20", "赵成本");
|
||||
|
||||
issue("MI-2026-001", 1004L, "WO-2026-002", "凯迪水处理工程",
|
||||
"不锈钢316L管件", "件", 28.0,
|
||||
new BigDecimal("420.00"), Money.of(28.0 * 420), "有效", "2026-06-03", "张会计");
|
||||
}
|
||||
|
||||
private void issue(String issueNo, Long workOrderId, String workOrderNo, String projectName,
|
||||
String materialName, String unit, double qty,
|
||||
BigDecimal unitPrice, BigDecimal amount,
|
||||
String status, String issueDate, String operator) {
|
||||
MaterialIssue mi = new MaterialIssue();
|
||||
mi.setIssueNo(issueNo);
|
||||
mi.setWorkOrderId(workOrderId);
|
||||
mi.setWorkOrderNo(workOrderNo);
|
||||
mi.setProjectName(projectName);
|
||||
mi.setMaterialName(materialName);
|
||||
mi.setUnit(unit);
|
||||
mi.setQty(qty);
|
||||
mi.setUnitPrice(unitPrice);
|
||||
mi.setAmount(amount);
|
||||
mi.setStatus(status);
|
||||
mi.setIssueDate(issueDate);
|
||||
mi.setOperator(operator);
|
||||
mi.setCreatedAt(Instant.now().minus(20, ChronoUnit.DAYS));
|
||||
issueRepo.save(mi);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 5. 已签署劳动合同(6 条,覆盖生产/成控/研发/运营各部门)
|
||||
// 消除 POST /cost-cross-dept/sync-labor-rate 返回空(0条)缺口
|
||||
// ======================================================================
|
||||
|
||||
private void seedHrLaborContracts() {
|
||||
if (hrRepo.count() > 0) return;
|
||||
|
||||
hrContract("LC-2024-001", "张伟", "EMP-0021", "生产部", "固定期限劳动合同", "3年",
|
||||
"2024-01-01", "2026-12-31", "已签署", "2024-01-03", "BATCH-2024-01", null);
|
||||
|
||||
hrContract("LC-2024-002", "刘洋", "EMP-0032", "生产部", "固定期限劳动合同", "3年",
|
||||
"2024-01-01", "2026-12-31", "已签署", "2024-01-03", "BATCH-2024-01", null);
|
||||
|
||||
hrContract("LC-2024-003", "王芳", "EMP-0045", "成本控制部", "固定期限劳动合同", "3年",
|
||||
"2024-03-01", "2027-02-28", "已签署", "2024-03-02", "BATCH-2024-03", null);
|
||||
|
||||
hrContract("LC-2024-004", "欧阳明", "EMP-0058", "研发中心", "无固定期限劳动合同", "长期",
|
||||
"2024-06-01", null, "已签署", "2024-06-03", "BATCH-2024-06", null);
|
||||
|
||||
hrContract("LC-2025-001", "赵静", "EMP-0072", "水处理运营中心", "固定期限劳动合同", "2年",
|
||||
"2025-01-01", "2026-12-31", "已签署", "2025-01-06", "BATCH-2025-01", null);
|
||||
|
||||
hrContract("LC-2025-002", "陈磊", "EMP-0086", "生产部", "劳务协议", "1年",
|
||||
"2025-04-01", "2026-03-31", "已签署", "2025-04-02", "BATCH-2025-04", null);
|
||||
}
|
||||
|
||||
private void hrContract(String contractNo, String employeeName, String employeeNo, String dept,
|
||||
String contractType, String term, String startDate, String expireDate,
|
||||
String status, String signDate, String batchId, Long prevContractId) {
|
||||
HrLaborContract c = new HrLaborContract();
|
||||
c.setContractNo(contractNo);
|
||||
c.setEmployeeName(employeeName);
|
||||
c.setEmployeeNo(employeeNo);
|
||||
c.setDept(dept);
|
||||
c.setContractType(contractType);
|
||||
c.setTerm(term);
|
||||
c.setStartDate(startDate);
|
||||
c.setExpireDate(expireDate);
|
||||
c.setStatus(status);
|
||||
c.setSignDate(signDate);
|
||||
c.setBatchId(batchId);
|
||||
c.setPrevContractId(prevContractId);
|
||||
c.setCreatedAt(Instant.now().minus(180, ChronoUnit.DAYS));
|
||||
c.setUpdatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
hrRepo.save(c);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 6. EBOM 节点(6 条,devProjectId=1,含已发布/已变更状态)
|
||||
// 消除 POST /cost-cross-dept/ebom-to-bom-signoff 返回 400 缺口
|
||||
// ======================================================================
|
||||
|
||||
private void seedRdEboms() {
|
||||
if (ebomRepo.count() > 0) return;
|
||||
|
||||
// 产品根节点(已发布)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", null, "产品", "KD-BOILER-001",
|
||||
"凯迪生物质蒸汽锅炉(6T/H)", "套", "6T/H 10Bar", 1.0, "V2.1", "已发布", 1, "张工");
|
||||
|
||||
// 主体钢结构(已变更—触发会签)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", 1L, "部件", "KD-STRUCT-001",
|
||||
"锅炉主体钢结构", "套", "Q235B δ=10mm", 1.0, "V2.1", "已变更", 2, "张工");
|
||||
|
||||
// 燃烧器(已发布—触发会签)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", 1L, "部件", "KD-BURNER-001",
|
||||
"生物质燃烧器", "台", "额定热功率 4.5MW", 1.0, "V2.0", "已发布", 3, "张工");
|
||||
|
||||
// 炉管(草稿,不触发会签)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", 1L, "部件", "KD-TUBE-001",
|
||||
"锅炉水冷壁管束", "组", "20#钢 φ51×4", 1.0, "V1.0", "草稿", 4, "李工");
|
||||
|
||||
// 钢材(已变更—触发会签)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", 2L, "零件", "MAT-STEEL-001",
|
||||
"Q235B钢板", "吨", "δ=10mm 1500×6000", 80.0, "V2.1", "已变更", 5, "张工");
|
||||
|
||||
// 标准螺栓(已发布)
|
||||
ebom(1L, "凯迪生物质蒸汽锅炉(6T/H)", 2L, "标准件", "STD-BOLT-M20",
|
||||
"M20六角头螺栓", "件", "GB/T 5782 M20×60", 240.0, "V2.0", "已发布", 6, "张工");
|
||||
}
|
||||
|
||||
private void ebom(Long devProjectId, String productName, Long parentId, String level,
|
||||
String matCode, String matName, String unit, String spec,
|
||||
double qty, String version, String status, int seq, String createdBy) {
|
||||
RdEbom e = new RdEbom();
|
||||
e.setDevProjectId(devProjectId);
|
||||
e.setProductName(productName);
|
||||
e.setParentId(parentId);
|
||||
e.setLevel(level);
|
||||
e.setMatCode(matCode);
|
||||
e.setMatName(matName);
|
||||
e.setUnit(unit);
|
||||
e.setSpec(spec);
|
||||
e.setQty(qty);
|
||||
e.setVersion(version);
|
||||
e.setStatus(status);
|
||||
e.setSeq(seq);
|
||||
e.setCreatedBy(createdBy);
|
||||
e.setCreatedAt(Instant.now().minus(60, ChronoUnit.DAYS));
|
||||
ebomRepo.save(e);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 7. 一对多进度款批次(3 条,跨状态,含明细行)
|
||||
// 消除 multi-payment-batches count=0 缺口
|
||||
// ======================================================================
|
||||
|
||||
private void seedMultiPaymentBatches() {
|
||||
if (batchRepo.count() > 0) return;
|
||||
|
||||
// 批次1:已批准-含3个人材机子项(正常完整批次演示)
|
||||
MultiPaymentBatch b1 = new MultiPaymentBatch();
|
||||
b1.setBatchCode("MPB-2026-001");
|
||||
b1.setTitle("凯迪生物质锅炉项目2026年5月进度款");
|
||||
b1.setProjectName("凯迪生物质锅炉项目");
|
||||
b1.setPayerName("凯迪科技有限公司");
|
||||
b1.setPayeeName("华钢物资有限公司");
|
||||
b1.setTotalRequestAmount(new BigDecimal("528000.00"));
|
||||
b1.setStatus("已批准");
|
||||
b1.setApplicant("张工");
|
||||
b1.setApprover("李总");
|
||||
b1.setApprovalComment("已核实现场进度,材料发货凭证齐全,同意支付");
|
||||
b1.setNote("包含钢材款+运费+装卸费3项");
|
||||
b1.setCreatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
b1 = batchRepo.save(b1);
|
||||
|
||||
batchItem(b1.getId(), 1001L, "人材机流程", "Q235B钢材 50吨采购款",
|
||||
new BigDecimal("305000.00"), new BigDecimal("290000.00"), new BigDecimal("290000.00"), "一期50吨钢材");
|
||||
batchItem(b1.getId(), 1002L, "人材机流程", "螺旋输送机叶片 10件",
|
||||
new BigDecimal("39000.00"), new BigDecimal("39000.00"), new BigDecimal("39000.00"), "批量采购10件");
|
||||
batchItem(b1.getId(), 1003L, "人材机流程", "PE给水管200根运费",
|
||||
new BigDecimal("800.00"), new BigDecimal("800.00"), new BigDecimal("800.00"), "到货运费");
|
||||
|
||||
// 批次2:审批中(演示在途流程可见性)
|
||||
MultiPaymentBatch b2 = new MultiPaymentBatch();
|
||||
b2.setBatchCode("MPB-2026-002");
|
||||
b2.setTitle("凯迪水处理工程2026年6月进度款");
|
||||
b2.setProjectName("凯迪水处理工程");
|
||||
b2.setPayerName("凯迪生态科技有限公司");
|
||||
b2.setPayeeName("浙江联塑管道");
|
||||
b2.setTotalRequestAmount(new BigDecimal("77000.00"));
|
||||
b2.setStatus("审批中");
|
||||
b2.setApplicant("陈工");
|
||||
b2.setNote("PE管道第二批次进度款,含增值税发票");
|
||||
b2.setCreatedAt(Instant.now().minus(7, ChronoUnit.DAYS));
|
||||
b2 = batchRepo.save(b2);
|
||||
|
||||
batchItem(b2.getId(), 2001L, "人材机流程", "PE100给水管200根第2批",
|
||||
new BigDecimal("77000.00"), new BigDecimal("77000.00"), new BigDecimal("77000.00"), "第2批货已到场");
|
||||
|
||||
// 批次3:草稿(演示新建编辑状态)
|
||||
MultiPaymentBatch b3 = new MultiPaymentBatch();
|
||||
b3.setBatchCode("MPB-2026-003");
|
||||
b3.setTitle("凯迪环保项目A药剂款汇总");
|
||||
b3.setProjectName("凯迪环保项目A");
|
||||
b3.setPayerName("凯迪科技有限公司");
|
||||
b3.setPayeeName("江苏清河水处理材料");
|
||||
b3.setTotalRequestAmount(new BigDecimal("45780.00"));
|
||||
b3.setStatus("草稿");
|
||||
b3.setApplicant("王成本");
|
||||
b3.setNote("将3批PAC采购进度款合并一次支付");
|
||||
b3.setCreatedAt(Instant.now().minus(1, ChronoUnit.DAYS));
|
||||
b3 = batchRepo.save(b3);
|
||||
|
||||
batchItem(b3.getId(), 3001L, "人材机流程", "PAC絮凝剂第1批20吨",
|
||||
new BigDecimal("43600.00"), new BigDecimal("43600.00"), new BigDecimal("43600.00"), "已入库");
|
||||
batchItem(b3.getId(), 3002L, "人材机流程", "运费及装卸费",
|
||||
new BigDecimal("2180.00"), new BigDecimal("2180.00"), new BigDecimal("2180.00"), "发票已到");
|
||||
}
|
||||
|
||||
private void batchItem(Long batchId, Long sourcePaymentId, String sourceType,
|
||||
String sourceDesc, BigDecimal invoicedAmount,
|
||||
BigDecimal paidAmount, BigDecimal requestAmount, String note) {
|
||||
MultiPaymentBatchItem item = new MultiPaymentBatchItem();
|
||||
item.setBatchId(batchId);
|
||||
item.setSourcePaymentId(sourcePaymentId);
|
||||
item.setSourceType(sourceType);
|
||||
item.setSourceDesc(sourceDesc);
|
||||
item.setInvoicedAmount(invoicedAmount);
|
||||
item.setPaidAmount(paidAmount);
|
||||
item.setRequestAmount(requestAmount);
|
||||
item.setNote(note);
|
||||
item.setCreatedAt(Instant.now().minus(7, ChronoUnit.DAYS));
|
||||
batchItemRepo.save(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.CslProject;
|
||||
import com.kaidi.oa.domain.CslTimesheet;
|
||||
import com.kaidi.oa.repository.CslProjectRepository;
|
||||
import com.kaidi.oa.repository.CslTimesheetRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 咨询可研院演示种子数据:
|
||||
* 播种 3 个 CslProject + 每个项目若干 CslTimesheet(含草稿和已确认状态),
|
||||
* 让"咨询人员工时录入台账"页面活体可见。
|
||||
*
|
||||
* 幂等:按 code 查重后跳过,重启不重复插入。
|
||||
*/
|
||||
@Component
|
||||
@Order(7)
|
||||
public class CslDemoSeeder implements ApplicationRunner {
|
||||
|
||||
private final CslProjectRepository projectRepo;
|
||||
private final CslTimesheetRepository timesheetRepo;
|
||||
|
||||
public CslDemoSeeder(CslProjectRepository projectRepo,
|
||||
CslTimesheetRepository timesheetRepo) {
|
||||
this.projectRepo = projectRepo;
|
||||
this.timesheetRepo = timesheetRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedProjects();
|
||||
}
|
||||
|
||||
private void seedProjects() {
|
||||
// 项目1:可行性研究 - 执行阶段
|
||||
if (projectRepo.findAll().stream().noneMatch(p -> "CSL-2025-001".equals(p.getCode()))) {
|
||||
CslProject p1 = new CslProject();
|
||||
p1.setCode("CSL-2025-001");
|
||||
p1.setName("某市地铁5号线可行性研究");
|
||||
p1.setClient("某市城市轨道交通有限公司");
|
||||
p1.setProjectType("可行性研究");
|
||||
p1.setField("交通");
|
||||
p1.setManager("刘建国");
|
||||
p1.setContractAmount(Money.of(new BigDecimal("1280000.00")));
|
||||
p1.setBudgetAmount(Money.of(new BigDecimal("980000.00")));
|
||||
p1.setUrgency(4);
|
||||
p1.setPriorityScore(88);
|
||||
p1.setStage("执行");
|
||||
p1.setApprovalStatus("已立项");
|
||||
p1.setApprovalFlow("可行性研究立项审批流程");
|
||||
p1.setProgress(62);
|
||||
p1.setPlanStart("2025-03-01");
|
||||
p1.setPlanEnd("2025-09-30");
|
||||
p1.setCreatedDate("2025-02-15");
|
||||
p1.setCreatedAt(Instant.now());
|
||||
p1.setUpdatedAt(Instant.now());
|
||||
CslProject saved1 = projectRepo.save(p1);
|
||||
seedTimesheets(saved1.getId(), "CSL-2025-001");
|
||||
}
|
||||
|
||||
// 项目2:项目评估 - 结项验收阶段
|
||||
if (projectRepo.findAll().stream().noneMatch(p -> "CSL-2025-002".equals(p.getCode()))) {
|
||||
CslProject p2 = new CslProject();
|
||||
p2.setCode("CSL-2025-002");
|
||||
p2.setName("凯迪新材料产业园项目评估");
|
||||
p2.setClient("凯迪科技集团");
|
||||
p2.setProjectType("项目评估");
|
||||
p2.setField("建筑");
|
||||
p2.setManager("王志远");
|
||||
p2.setContractAmount(Money.of(new BigDecimal("560000.00")));
|
||||
p2.setBudgetAmount(Money.of(new BigDecimal("430000.00")));
|
||||
p2.setUrgency(5);
|
||||
p2.setPriorityScore(92);
|
||||
p2.setStage("结项验收");
|
||||
p2.setApprovalStatus("已立项");
|
||||
p2.setApprovalFlow("项目评估立项审批流程");
|
||||
p2.setProgress(95);
|
||||
p2.setPlanStart("2025-01-10");
|
||||
p2.setPlanEnd("2025-06-30");
|
||||
p2.setCreatedDate("2025-01-05");
|
||||
p2.setCreatedAt(Instant.now());
|
||||
p2.setUpdatedAt(Instant.now());
|
||||
CslProject saved2 = projectRepo.save(p2);
|
||||
seedTimesheets(saved2.getId(), "CSL-2025-002");
|
||||
}
|
||||
|
||||
// 项目3:规划咨询 - 执行阶段
|
||||
if (projectRepo.findAll().stream().noneMatch(p -> "CSL-2026-001".equals(p.getCode()))) {
|
||||
CslProject p3 = new CslProject();
|
||||
p3.setCode("CSL-2026-001");
|
||||
p3.setName("某区城乡融合发展规划咨询");
|
||||
p3.setClient("某区人民政府发展改革局");
|
||||
p3.setProjectType("规划咨询");
|
||||
p3.setField("综合");
|
||||
p3.setManager("陈美华");
|
||||
p3.setContractAmount(Money.of(new BigDecimal("320000.00")));
|
||||
p3.setBudgetAmount(Money.of(new BigDecimal("240000.00")));
|
||||
p3.setUrgency(3);
|
||||
p3.setPriorityScore(75);
|
||||
p3.setStage("执行");
|
||||
p3.setApprovalStatus("已立项");
|
||||
p3.setApprovalFlow("规划咨询立项审批流程");
|
||||
p3.setProgress(35);
|
||||
p3.setPlanStart("2026-01-15");
|
||||
p3.setPlanEnd("2026-08-31");
|
||||
p3.setCreatedDate("2026-01-10");
|
||||
p3.setCreatedAt(Instant.now());
|
||||
p3.setUpdatedAt(Instant.now());
|
||||
CslProject saved3 = projectRepo.save(p3);
|
||||
seedTimesheets(saved3.getId(), "CSL-2026-001");
|
||||
}
|
||||
}
|
||||
|
||||
private void seedTimesheets(Long projectId, String projectCode) {
|
||||
// 只在该项目下没有工时时才插入
|
||||
if (!timesheetRepo.findByProjectId(projectId).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("CSL-2025-001".equals(projectCode)) {
|
||||
// 已确认工时记录
|
||||
save(projectId, "刘建国", "项目经理", "2025-04-01", 8.0, 150.0, "项目启动会及工作方案编制", "已确认");
|
||||
save(projectId, "张明轩", "专业负责人", "2025-04-02", 8.0, 120.0, "交通流量调研数据收集与分析", "已确认");
|
||||
save(projectId, "李晓玲", "主创", "2025-04-03", 8.0, 90.0, "线路走向初步比选方案编制", "已确认");
|
||||
save(projectId, "王宇", "校审", "2025-04-10", 6.0, 110.0, "第一阶段报告校审与批注", "已确认");
|
||||
save(projectId, "张明轩", "专业负责人", "2025-05-06", 8.0, 120.0, "客流预测模型建立与测算", "已确认");
|
||||
save(projectId, "李晓玲", "主创", "2025-05-08", 8.0, 90.0, "投资估算初稿编制", "已确认");
|
||||
// 草稿工时(近期)
|
||||
save(projectId, "刘建国", "项目经理", "2025-06-10", 4.0, 150.0, "中期汇报材料准备与客户对接", "草稿");
|
||||
save(projectId, "赵晨阳", "一般成员", "2025-06-11", 8.0, 70.0, "社会稳定风险评估调研", "草稿");
|
||||
save(projectId, "李晓玲", "主创", "2025-06-12", 8.0, 90.0, "财务分析与经济效益测算", "草稿");
|
||||
|
||||
} else if ("CSL-2025-002".equals(projectCode)) {
|
||||
save(projectId, "王志远", "项目经理", "2025-02-01", 8.0, 140.0, "需求调研与项目目标确认", "已确认");
|
||||
save(projectId, "孙丽萍", "专业负责人", "2025-02-05", 8.0, 110.0, "市场调研与竞争态势分析", "已确认");
|
||||
save(projectId, "陈刚", "主创", "2025-02-10", 8.0, 85.0, "项目技术可行性分析报告", "已确认");
|
||||
save(projectId, "王志远", "项目经理", "2025-03-15", 6.0, 140.0, "中期评审协调与问题整改", "已确认");
|
||||
save(projectId, "孙丽萍", "专业负责人", "2025-05-20", 8.0, 110.0, "最终报告修改与定稿", "已确认");
|
||||
save(projectId, "陈刚", "主创", "2025-05-22", 8.0, 85.0, "投资估算复核与数据勘误", "已确认");
|
||||
// 草稿:等待验收阶段
|
||||
save(projectId, "王志远", "项目经理", "2025-06-08", 4.0, 140.0, "验收材料整理与提交", "草稿");
|
||||
save(projectId, "孙丽萍", "专业负责人", "2025-06-09", 4.0, 110.0, "专家评审意见回复", "草稿");
|
||||
|
||||
} else if ("CSL-2026-001".equals(projectCode)) {
|
||||
save(projectId, "陈美华", "项目经理", "2026-02-01", 8.0, 130.0, "现场踏勘与基础数据收集", "已确认");
|
||||
save(projectId, "林海涛", "专业负责人", "2026-02-03", 8.0, 100.0, "城镇化现状分析与问题梳理", "已确认");
|
||||
save(projectId, "赵雪", "主创", "2026-02-08", 8.0, 80.0, "政策法规梳理与规划依据整理", "已确认");
|
||||
// 草稿
|
||||
save(projectId, "陈美华", "项目经理", "2026-03-10", 6.0, 130.0, "阶段成果汇报与方向调整讨论", "草稿");
|
||||
save(projectId, "林海涛", "专业负责人", "2026-03-12", 8.0, 100.0, "产业发展策略专题研究", "草稿");
|
||||
}
|
||||
}
|
||||
|
||||
private void save(Long projectId, String person, String role,
|
||||
String workDate, double hours, double rate,
|
||||
String workContent, String status) {
|
||||
CslTimesheet t = new CslTimesheet();
|
||||
t.setProjectId(projectId);
|
||||
t.setPerson(person);
|
||||
t.setRole(role);
|
||||
t.setWorkDate(workDate);
|
||||
t.setHours(hours);
|
||||
BigDecimal rateBd = Money.of(rate);
|
||||
t.setRate(rateBd);
|
||||
t.setLaborCost(Money.of(rateBd.multiply(BigDecimal.valueOf(hours))));
|
||||
t.setWorkContent(workContent);
|
||||
t.setStatus(status);
|
||||
t.setCreatedAt(Instant.now());
|
||||
timesheetRepo.save(t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.CultureMonthlyBriefing;
|
||||
import com.kaidi.oa.domain.HrCultureKpi;
|
||||
import com.kaidi.oa.repository.CultureMonthlyBriefingRepository;
|
||||
import com.kaidi.oa.repository.HrCultureKpiRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 品牌推广部·项目文化 W8 缺口演示数据。
|
||||
*
|
||||
* 缺口1(low):月刊电子版生成(CultureMonthlyBriefing)——历史简报+当月已生成各一条演示。
|
||||
* 缺口2(low):KPI回写HR绩效(HrCultureKpi)——已写入2条+待写入1条,让「已写入历史」功能活体可见。
|
||||
*
|
||||
* @Order(225) 在 BrandCultureGapSeeder(@Order 220) 之后运行,幂等(count>0跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(225)
|
||||
public class CultureBriefingKpiSeeder implements ApplicationRunner {
|
||||
|
||||
private final CultureMonthlyBriefingRepository briefingRepo;
|
||||
private final HrCultureKpiRepository hrKpiRepo;
|
||||
|
||||
public CultureBriefingKpiSeeder(CultureMonthlyBriefingRepository briefingRepo,
|
||||
HrCultureKpiRepository hrKpiRepo) {
|
||||
this.briefingRepo = briefingRepo;
|
||||
this.hrKpiRepo = hrKpiRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedBriefings();
|
||||
seedHrCultureKpis();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 月刊电子版演示数据(CultureMonthlyBriefing)
|
||||
// ====================================================================
|
||||
private void seedBriefings() {
|
||||
if (briefingRepo.count() > 0) return;
|
||||
|
||||
List<CultureMonthlyBriefing> list = new ArrayList<>();
|
||||
|
||||
// 2026年3月刊 - 已发布
|
||||
CultureMonthlyBriefing b1 = new CultureMonthlyBriefing();
|
||||
b1.setCode("CMB-202603-001");
|
||||
b1.setIssueTag("2026年第3期");
|
||||
b1.setPublishMonth("2026-03");
|
||||
b1.setTitle("凯迪科技项目简报 2026年第3期");
|
||||
b1.setTaskTotal(18);
|
||||
b1.setTaskDone(15);
|
||||
b1.setTaskOverdue(1);
|
||||
b1.setNewStoryCount(6);
|
||||
b1.setNewMaterialCount(24);
|
||||
b1.setProjectCount(3);
|
||||
b1.setFeaturedProjects("PRJ-2026-001、PRJ-2026-002、PRJ-2025-003");
|
||||
b1.setSummary("3月份项目宣传工作稳步推进,共完成15项宣传任务,完成率83.3%。南方水务PPP项目推出" +
|
||||
"「攻坚克难」系列专访共3篇,引发热烈反响。滨江EPC项目荣获客户高度赞誉," +
|
||||
"典型案例已录入案例库。北部生物质电厂技改项目顺利完工,文化建设获年度标杆评定。");
|
||||
b1.setDistributeTarget("全员 / 南方水务PPP项目相关客户 / 滨江EPC项目业主方");
|
||||
b1.setStatus("已发布");
|
||||
b1.setGeneratedBy("系统自动生成");
|
||||
b1.setPublishedBy("宣传部·李部长");
|
||||
b1.setPublishDate("2026-04-01");
|
||||
b1.setBriefingUrl("https://cdn.kaidi.com/culture-briefing/2026-03-issue.pdf");
|
||||
b1.setCreatedAt(Instant.now());
|
||||
list.add(b1);
|
||||
|
||||
// 2026年4月刊 - 已发布
|
||||
CultureMonthlyBriefing b2 = new CultureMonthlyBriefing();
|
||||
b2.setCode("CMB-202604-001");
|
||||
b2.setIssueTag("2026年第4期");
|
||||
b2.setPublishMonth("2026-04");
|
||||
b2.setTitle("凯迪科技项目简报 2026年第4期");
|
||||
b2.setTaskTotal(22);
|
||||
b2.setTaskDone(19);
|
||||
b2.setTaskOverdue(0);
|
||||
b2.setNewStoryCount(8);
|
||||
b2.setNewMaterialCount(31);
|
||||
b2.setProjectCount(3);
|
||||
b2.setFeaturedProjects("PRJ-2026-001、PRJ-2026-002");
|
||||
b2.setSummary("4月份各项目宣传任务完成率达86.4%,创近半年新高。新增8篇项目故事," +
|
||||
"其中「绿色施工先行者」荣获公司月度最佳故事奖。文化氛围调查回收率90%以上," +
|
||||
"综合满意度82分,较上季度提升2.5分。");
|
||||
b2.setDistributeTarget("全员 / 相关客户");
|
||||
b2.setStatus("已发布");
|
||||
b2.setGeneratedBy("系统自动生成");
|
||||
b2.setPublishedBy("宣传部·李部长");
|
||||
b2.setPublishDate("2026-05-02");
|
||||
b2.setBriefingUrl("https://cdn.kaidi.com/culture-briefing/2026-04-issue.pdf");
|
||||
b2.setCreatedAt(Instant.now());
|
||||
list.add(b2);
|
||||
|
||||
// 2026年5月刊 - 已发布
|
||||
CultureMonthlyBriefing b3 = new CultureMonthlyBriefing();
|
||||
b3.setCode("CMB-202605-001");
|
||||
b3.setIssueTag("2026年第5期");
|
||||
b3.setPublishMonth("2026-05");
|
||||
b3.setTitle("凯迪科技项目简报 2026年第5期");
|
||||
b3.setTaskTotal(20);
|
||||
b3.setTaskDone(17);
|
||||
b3.setTaskOverdue(2);
|
||||
b3.setNewStoryCount(5);
|
||||
b3.setNewMaterialCount(18);
|
||||
b3.setProjectCount(2);
|
||||
b3.setFeaturedProjects("PRJ-2026-001、PRJ-2026-002");
|
||||
b3.setSummary("5月份宣传任务总量20项,完成17项,完成率85%。2项任务因项目现场条件变化延期," +
|
||||
"已协调宣传员重新排期。本月新增5篇已发布项目故事,素材库新增18份材料。" +
|
||||
"Q2文化氛围调查正在收集中,截至月底回收率已超50%。");
|
||||
b3.setDistributeTarget("全员 / 相关客户");
|
||||
b3.setStatus("已发布");
|
||||
b3.setGeneratedBy("系统自动生成");
|
||||
b3.setPublishedBy("宣传部·王主任");
|
||||
b3.setPublishDate("2026-06-03");
|
||||
b3.setBriefingUrl("https://cdn.kaidi.com/culture-briefing/2026-05-issue.pdf");
|
||||
b3.setCreatedAt(Instant.now());
|
||||
list.add(b3);
|
||||
|
||||
// 2026年6月刊 - 已生成(本月,待发布)
|
||||
CultureMonthlyBriefing b4 = new CultureMonthlyBriefing();
|
||||
b4.setCode("CMB-202606-001");
|
||||
b4.setIssueTag("2026年第6期");
|
||||
b4.setPublishMonth("2026-06");
|
||||
b4.setTitle("凯迪科技项目简报 2026年第6期");
|
||||
b4.setTaskTotal(24);
|
||||
b4.setTaskDone(12);
|
||||
b4.setTaskOverdue(1);
|
||||
b4.setNewStoryCount(4);
|
||||
b4.setNewMaterialCount(9);
|
||||
b4.setProjectCount(3);
|
||||
b4.setFeaturedProjects("PRJ-2026-001、PRJ-2026-002、PRJ-2025-003");
|
||||
b4.setSummary("6月份宣传工作正在进行中,截至月底(预计)将完成24项任务中的20余项。" +
|
||||
"Q2文化评估调查结束,综合满意度待最终统计。本期将重点报道南方水务项目" +
|
||||
"「一线感人故事」专题。");
|
||||
b4.setDistributeTarget("全员 / 南方水务PPP项目相关客户");
|
||||
b4.setStatus("已生成");
|
||||
b4.setGeneratedBy("系统自动生成");
|
||||
b4.setCreatedAt(Instant.now());
|
||||
list.add(b4);
|
||||
|
||||
briefingRepo.saveAll(list);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// HR 年度绩效文化 KPI 演示数据(HrCultureKpi)
|
||||
// ====================================================================
|
||||
private void seedHrCultureKpis() {
|
||||
if (hrKpiRepo.count() > 0) return;
|
||||
|
||||
List<HrCultureKpi> list = new ArrayList<>();
|
||||
|
||||
// 2025年度 北部生物质电厂 - 已写入(优秀,完整历史记录)
|
||||
HrCultureKpi k1 = new HrCultureKpi();
|
||||
k1.setCode("HCK-2025-001");
|
||||
k1.setAssessYear("2025");
|
||||
k1.setProjectCode("PRJ-2025-003");
|
||||
k1.setProjectName("北部生物质电厂技改项目");
|
||||
k1.setCulturalOfficer("刘宇航");
|
||||
k1.setCultureTotalScore(88.5);
|
||||
k1.setCultureGrade("优秀");
|
||||
k1.setFusedSatisfaction(88.15);
|
||||
k1.setHrSatisfaction(89.2);
|
||||
k1.setWriteTarget("HR年度绩效·项目文化KPI项");
|
||||
k1.setKpiWeight(10);
|
||||
k1.setKpiContribution(8.85);
|
||||
k1.setConfirmedBy("人力资源部·张经理");
|
||||
k1.setConfirmedAt(Instant.parse("2026-01-15T09:00:00Z"));
|
||||
k1.setRemark("项目文化建设成效突出,已作为公司文化标杆,建议纳入年度最佳案例表彰。");
|
||||
k1.setStatus("已写入");
|
||||
k1.setCreatedAt(Instant.parse("2026-01-10T08:00:00Z"));
|
||||
list.add(k1);
|
||||
|
||||
// 2026年度 南方水务PPP项目 - 待写入(当年,Q2评估完成后待HR确认)
|
||||
HrCultureKpi k2 = new HrCultureKpi();
|
||||
k2.setCode("HCK-2026-001");
|
||||
k2.setAssessYear("2026");
|
||||
k2.setProjectCode("PRJ-2026-001");
|
||||
k2.setProjectName("南方水务污水处理PPP项目");
|
||||
k2.setCulturalOfficer("王晓峰");
|
||||
k2.setCultureTotalScore(79.3);
|
||||
k2.setCultureGrade("良好");
|
||||
k2.setFusedSatisfaction(82.6);
|
||||
k2.setHrSatisfaction(83.2);
|
||||
k2.setWriteTarget("HR年度绩效·项目文化KPI项");
|
||||
k2.setKpiWeight(10);
|
||||
k2.setKpiContribution(7.93);
|
||||
k2.setRemark("Q2数据:文化氛围问卷82分,HR满意度83.2分,融合满意度82.6分。待年底最终确认。");
|
||||
k2.setStatus("待写入");
|
||||
k2.setCreatedAt(Instant.now());
|
||||
list.add(k2);
|
||||
|
||||
// 2026年度 滨江工业废水EPC项目 - 待写入
|
||||
HrCultureKpi k3 = new HrCultureKpi();
|
||||
k3.setCode("HCK-2026-002");
|
||||
k3.setAssessYear("2026");
|
||||
k3.setProjectCode("PRJ-2026-002");
|
||||
k3.setProjectName("滨江工业废水处理EPC项目");
|
||||
k3.setCulturalOfficer("陈美珍");
|
||||
k3.setCultureTotalScore(65.8);
|
||||
k3.setCultureGrade("合格");
|
||||
k3.setFusedSatisfaction(75.15);
|
||||
k3.setHrSatisfaction(74.8);
|
||||
k3.setWriteTarget("HR年度绩效·项目文化KPI项");
|
||||
k3.setKpiWeight(10);
|
||||
k3.setKpiContribution(6.58);
|
||||
k3.setRemark("满意度得分偏低,建议加强项目文化活动开展频次,目标Q3满意度提升至80+。");
|
||||
k3.setStatus("待写入");
|
||||
k3.setCreatedAt(Instant.now());
|
||||
list.add(k3);
|
||||
|
||||
hrKpiRepo.saveAll(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.CultureGoal;
|
||||
import com.kaidi.oa.domain.CultureSurvey;
|
||||
import com.kaidi.oa.domain.HrEmpSatisfactionSurvey;
|
||||
import com.kaidi.oa.repository.CultureGoalRepository;
|
||||
import com.kaidi.oa.repository.CultureSurveyRepository;
|
||||
import com.kaidi.oa.repository.HrEmpSatisfactionSurveyRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 品牌推广部·项目文化考核演示数据种子。
|
||||
*
|
||||
* 补全三类"活体无数据"缺口,让「项目文化评估与反馈」和「项目文化考核与驾驶舱」功能真正活体可见:
|
||||
*
|
||||
* 1. CultureGoal:项目文化目标(关联三个典型项目,有预算/故事/稿件/满意度目标值及实际开支)。
|
||||
* 2. CultureSurvey:品牌推广部自有文化氛围问卷(已结束 + 综合得分,供文化考核引用)。
|
||||
* 3. HrEmpSatisfactionSurvey:HR 部门独立员工满意度调查(已完成 + HR综合得分,跨模块融合)。
|
||||
*
|
||||
* @Order(50),在 DataSeeder(@Order 1) 及 BrandCultureSeeder(@Order 22) 之后运行。
|
||||
* 幂等:CultureGoal 有数据时跳过全部种子。
|
||||
*/
|
||||
@Component
|
||||
@Order(50)
|
||||
public class CultureProjectAssessSeeder implements ApplicationRunner {
|
||||
|
||||
private final CultureGoalRepository goalRepo;
|
||||
private final CultureSurveyRepository surveyRepo;
|
||||
private final HrEmpSatisfactionSurveyRepository hrSatisfyRepo;
|
||||
|
||||
public CultureProjectAssessSeeder(CultureGoalRepository goalRepo,
|
||||
CultureSurveyRepository surveyRepo,
|
||||
HrEmpSatisfactionSurveyRepository hrSatisfyRepo) {
|
||||
this.goalRepo = goalRepo;
|
||||
this.surveyRepo = surveyRepo;
|
||||
this.hrSatisfyRepo = hrSatisfyRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
if (goalRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
seedCultureGoals();
|
||||
seedCultureSurveys();
|
||||
seedHrSatisfactionSurveys();
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 1. 项目文化目标(CultureGoal)
|
||||
// =======================================================================
|
||||
private void seedCultureGoals() {
|
||||
List<CultureGoal> goals = new ArrayList<>();
|
||||
|
||||
// 项目1:南方水务污水处理PPP项目
|
||||
CultureGoal g1 = new CultureGoal();
|
||||
g1.setSchemeId(1L);
|
||||
g1.setSchemeCode("CS-2026-001");
|
||||
g1.setProjectCode("PRJ-2026-001");
|
||||
g1.setProjectName("南方水务污水处理PPP项目");
|
||||
g1.setCulturalOfficer("王晓峰");
|
||||
g1.setTargetStoryCount(8);
|
||||
g1.setTargetArticleCount(20);
|
||||
g1.setTargetCohesion(80.0);
|
||||
g1.setTargetSatisfaction(82.0);
|
||||
g1.setBudgetAmount(new BigDecimal("48000.00"));
|
||||
g1.setSpentAmount(new BigDecimal("41500.00"));
|
||||
g1.setStatus("进行中");
|
||||
g1.setCreatedAt(Instant.now());
|
||||
goals.add(g1);
|
||||
|
||||
// 项目2:滨江工业废水处理EPC项目
|
||||
CultureGoal g2 = new CultureGoal();
|
||||
g2.setSchemeId(1L);
|
||||
g2.setSchemeCode("CS-2026-001");
|
||||
g2.setProjectCode("PRJ-2026-002");
|
||||
g2.setProjectName("滨江工业废水处理EPC项目");
|
||||
g2.setCulturalOfficer("陈美珍");
|
||||
g2.setTargetStoryCount(6);
|
||||
g2.setTargetArticleCount(15);
|
||||
g2.setTargetCohesion(78.0);
|
||||
g2.setTargetSatisfaction(80.0);
|
||||
g2.setBudgetAmount(new BigDecimal("35000.00"));
|
||||
g2.setSpentAmount(new BigDecimal("29800.00"));
|
||||
g2.setStatus("进行中");
|
||||
g2.setCreatedAt(Instant.now());
|
||||
goals.add(g2);
|
||||
|
||||
// 项目3:北部生物质电厂技改项目(已完工,做对照)
|
||||
CultureGoal g3 = new CultureGoal();
|
||||
g3.setSchemeId(1L);
|
||||
g3.setSchemeCode("CS-2025-003");
|
||||
g3.setProjectCode("PRJ-2025-003");
|
||||
g3.setProjectName("北部生物质电厂技改项目");
|
||||
g3.setCulturalOfficer("刘宇航");
|
||||
g3.setTargetStoryCount(10);
|
||||
g3.setTargetArticleCount(25);
|
||||
g3.setTargetCohesion(85.0);
|
||||
g3.setTargetSatisfaction(85.0);
|
||||
g3.setBudgetAmount(new BigDecimal("52000.00"));
|
||||
g3.setSpentAmount(new BigDecimal("50200.00"));
|
||||
g3.setStatus("已达标");
|
||||
g3.setCreatedAt(Instant.now());
|
||||
goals.add(g3);
|
||||
|
||||
goalRepo.saveAll(goals);
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 2. 品牌推广部自有文化氛围问卷(CultureSurvey)
|
||||
// =======================================================================
|
||||
private void seedCultureSurveys() {
|
||||
if (surveyRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
List<CultureSurvey> surveys = new ArrayList<>();
|
||||
|
||||
// PRJ-2026-001:2026Q1 已结束
|
||||
CultureSurvey s1 = new CultureSurvey();
|
||||
s1.setCode("CS-SVY-2026Q1-001");
|
||||
s1.setTitle("南方水务项目 2026Q1 文化氛围调查");
|
||||
s1.setProjectCode("PRJ-2026-001");
|
||||
s1.setProjectName("南方水务污水处理PPP项目");
|
||||
s1.setPeriod("2026Q1");
|
||||
s1.setStatus("已结束");
|
||||
s1.setSampleCount(35);
|
||||
s1.setRespondedCount(31);
|
||||
s1.setScoreTeamwork(84.5);
|
||||
s1.setScoreLeadership(81.0);
|
||||
s1.setScoreHappiness(79.5);
|
||||
s1.setScoreBelonging(83.0);
|
||||
s1.setOverallScore(82.0);
|
||||
s1.setOpenDate("2026-03-01");
|
||||
s1.setCloseDate("2026-03-31");
|
||||
s1.setCreatedAt(Instant.now());
|
||||
surveys.add(s1);
|
||||
|
||||
// PRJ-2026-001:2026Q2 收集中
|
||||
CultureSurvey s2 = new CultureSurvey();
|
||||
s2.setCode("CS-SVY-2026Q2-001");
|
||||
s2.setTitle("南方水务项目 2026Q2 文化氛围调查");
|
||||
s2.setProjectCode("PRJ-2026-001");
|
||||
s2.setProjectName("南方水务污水处理PPP项目");
|
||||
s2.setPeriod("2026Q2");
|
||||
s2.setStatus("收集中");
|
||||
s2.setSampleCount(35);
|
||||
s2.setRespondedCount(18);
|
||||
s2.setScoreTeamwork(0.0);
|
||||
s2.setScoreLeadership(0.0);
|
||||
s2.setScoreHappiness(0.0);
|
||||
s2.setScoreBelonging(0.0);
|
||||
s2.setOverallScore(0.0);
|
||||
s2.setOpenDate("2026-06-01");
|
||||
s2.setCloseDate("2026-06-30");
|
||||
s2.setCreatedAt(Instant.now());
|
||||
surveys.add(s2);
|
||||
|
||||
// PRJ-2026-002:2026Q1 已结束
|
||||
CultureSurvey s3 = new CultureSurvey();
|
||||
s3.setCode("CS-SVY-2026Q1-002");
|
||||
s3.setTitle("滨江工业废水项目 2026Q1 文化氛围调查");
|
||||
s3.setProjectCode("PRJ-2026-002");
|
||||
s3.setProjectName("滨江工业废水处理EPC项目");
|
||||
s3.setPeriod("2026Q1");
|
||||
s3.setStatus("已结束");
|
||||
s3.setSampleCount(28);
|
||||
s3.setRespondedCount(25);
|
||||
s3.setScoreTeamwork(77.0);
|
||||
s3.setScoreLeadership(75.5);
|
||||
s3.setScoreHappiness(73.0);
|
||||
s3.setScoreBelonging(76.5);
|
||||
s3.setOverallScore(75.5);
|
||||
s3.setOpenDate("2026-03-01");
|
||||
s3.setCloseDate("2026-03-31");
|
||||
s3.setCreatedAt(Instant.now());
|
||||
surveys.add(s3);
|
||||
|
||||
// PRJ-2025-003:年度已结束(2025)
|
||||
CultureSurvey s4 = new CultureSurvey();
|
||||
s4.setCode("CS-SVY-2025-003");
|
||||
s4.setTitle("北部生物质电厂项目 2025 年度文化调查");
|
||||
s4.setProjectCode("PRJ-2025-003");
|
||||
s4.setProjectName("北部生物质电厂技改项目");
|
||||
s4.setPeriod("2025年度");
|
||||
s4.setStatus("已结束");
|
||||
s4.setSampleCount(42);
|
||||
s4.setRespondedCount(40);
|
||||
s4.setScoreTeamwork(88.5);
|
||||
s4.setScoreLeadership(86.0);
|
||||
s4.setScoreHappiness(85.0);
|
||||
s4.setScoreBelonging(89.0);
|
||||
s4.setOverallScore(87.1);
|
||||
s4.setOpenDate("2025-12-01");
|
||||
s4.setCloseDate("2025-12-31");
|
||||
s4.setCreatedAt(Instant.now());
|
||||
surveys.add(s4);
|
||||
|
||||
surveyRepo.saveAll(surveys);
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 3. HR 独立员工满意度调查(HrEmpSatisfactionSurvey)
|
||||
// =======================================================================
|
||||
private void seedHrSatisfactionSurveys() {
|
||||
if (hrSatisfyRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
List<HrEmpSatisfactionSurvey> surveys = new ArrayList<>();
|
||||
|
||||
// PRJ-2026-001:2026Q1 HR 已完成
|
||||
HrEmpSatisfactionSurvey h1 = new HrEmpSatisfactionSurvey();
|
||||
h1.setCode("HR-SAT-2026Q1-001");
|
||||
h1.setTitle("南方水务项目员工满意度与敬业度调查 2026Q1");
|
||||
h1.setInitiatorDept("人力资源部");
|
||||
h1.setProjectCode("PRJ-2026-001");
|
||||
h1.setProjectName("南方水务污水处理PPP项目");
|
||||
h1.setPeriod("2026Q1");
|
||||
h1.setStatus("已完成");
|
||||
h1.setTotalCount(35);
|
||||
h1.setRespondedCount(33);
|
||||
h1.setScoreJobSatisfaction(83.0);
|
||||
h1.setScoreEngagement(85.0);
|
||||
h1.setScoreManagement(80.5);
|
||||
h1.setScoreTeamwork(86.0);
|
||||
h1.setScoreWellbeing(81.5);
|
||||
h1.setHrOverallScore(83.2);
|
||||
h1.setIssuesSummary("整体满意度良好;部分员工反映项目现场生活条件待改善,已提交至行政部跟进。");
|
||||
h1.setOpenDate("2026-03-05");
|
||||
h1.setCloseDate("2026-03-28");
|
||||
h1.setCreatedAt(Instant.now());
|
||||
surveys.add(h1);
|
||||
|
||||
// PRJ-2026-001:2026Q2 HR 收集中
|
||||
HrEmpSatisfactionSurvey h2 = new HrEmpSatisfactionSurvey();
|
||||
h2.setCode("HR-SAT-2026Q2-001");
|
||||
h2.setTitle("南方水务项目员工满意度与敬业度调查 2026Q2");
|
||||
h2.setInitiatorDept("人力资源部");
|
||||
h2.setProjectCode("PRJ-2026-001");
|
||||
h2.setProjectName("南方水务污水处理PPP项目");
|
||||
h2.setPeriod("2026Q2");
|
||||
h2.setStatus("收集中");
|
||||
h2.setTotalCount(35);
|
||||
h2.setRespondedCount(12);
|
||||
h2.setScoreJobSatisfaction(0.0);
|
||||
h2.setScoreEngagement(0.0);
|
||||
h2.setScoreManagement(0.0);
|
||||
h2.setScoreTeamwork(0.0);
|
||||
h2.setScoreWellbeing(0.0);
|
||||
h2.setHrOverallScore(0.0);
|
||||
h2.setOpenDate("2026-06-05");
|
||||
h2.setCloseDate("2026-06-28");
|
||||
h2.setCreatedAt(Instant.now());
|
||||
surveys.add(h2);
|
||||
|
||||
// PRJ-2026-002:2026Q1 HR 已完成
|
||||
HrEmpSatisfactionSurvey h3 = new HrEmpSatisfactionSurvey();
|
||||
h3.setCode("HR-SAT-2026Q1-002");
|
||||
h3.setTitle("滨江工业废水项目员工满意度与敬业度调查 2026Q1");
|
||||
h3.setInitiatorDept("人力资源部");
|
||||
h3.setProjectCode("PRJ-2026-002");
|
||||
h3.setProjectName("滨江工业废水处理EPC项目");
|
||||
h3.setPeriod("2026Q1");
|
||||
h3.setStatus("已完成");
|
||||
h3.setTotalCount(28);
|
||||
h3.setRespondedCount(26);
|
||||
h3.setScoreJobSatisfaction(76.0);
|
||||
h3.setScoreEngagement(74.5);
|
||||
h3.setScoreManagement(73.0);
|
||||
h3.setScoreTeamwork(78.0);
|
||||
h3.setScoreWellbeing(72.5);
|
||||
h3.setHrOverallScore(74.8);
|
||||
h3.setIssuesSummary("敬业度中等;员工反映项目工期压力较大,建议适当调整排班;管理层沟通透明度待提升。");
|
||||
h3.setOpenDate("2026-03-05");
|
||||
h3.setCloseDate("2026-03-28");
|
||||
h3.setCreatedAt(Instant.now());
|
||||
surveys.add(h3);
|
||||
|
||||
// PRJ-2025-003:2025 年度 HR 已完成
|
||||
HrEmpSatisfactionSurvey h4 = new HrEmpSatisfactionSurvey();
|
||||
h4.setCode("HR-SAT-2025-003");
|
||||
h4.setTitle("北部生物质电厂项目 2025 年度员工满意度调查");
|
||||
h4.setInitiatorDept("人力资源部");
|
||||
h4.setProjectCode("PRJ-2025-003");
|
||||
h4.setProjectName("北部生物质电厂技改项目");
|
||||
h4.setPeriod("2025年度");
|
||||
h4.setStatus("已完成");
|
||||
h4.setTotalCount(42);
|
||||
h4.setRespondedCount(41);
|
||||
h4.setScoreJobSatisfaction(89.0);
|
||||
h4.setScoreEngagement(91.0);
|
||||
h4.setScoreManagement(88.5);
|
||||
h4.setScoreTeamwork(90.0);
|
||||
h4.setScoreWellbeing(87.5);
|
||||
h4.setHrOverallScore(89.2);
|
||||
h4.setIssuesSummary("满意度极高;项目荣获年度最佳团队,员工归属感强,建议作为文化建设标杆推广。");
|
||||
h4.setOpenDate("2025-12-05");
|
||||
h4.setCloseDate("2025-12-28");
|
||||
h4.setCreatedAt(Instant.now());
|
||||
surveys.add(h4);
|
||||
|
||||
hrSatisfyRepo.saveAll(surveys);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.DeclAchievement;
|
||||
import com.kaidi.oa.repository.DeclAchievementRepository;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 申报成果库演示数据补种(Gap 4:活体 decl-achievements 0条,功能可达但未预置演示数据)。
|
||||
* Order(3)=在 DataSeeder(Order 1)/AdminQualSeeder 之后运行,避免依赖竞争。
|
||||
*/
|
||||
@Component
|
||||
@Order(3)
|
||||
public class DeclAchievementSeeder implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DeclAchievementSeeder.class);
|
||||
|
||||
private final DeclAchievementRepository repo;
|
||||
|
||||
public DeclAchievementSeeder(DeclAchievementRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (repo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
log.info("Seeding DeclAchievement demo data...");
|
||||
seed("高新技术企业认定", "资质证书", "HJPD202312345", "国家科学技术部", "2023-01-01", "2026-12-31", 0d, false);
|
||||
seed("科技型中小企业评价", "立项文件", "科小评2024-KD-0588", "湖南省科学技术厅", "2024-03-01", "2025-02-28", 0d, false);
|
||||
seed("省级企业技术中心", "资质证书", "HNCZX-2023-0127", "湖南省工业和信息化厅", "2023-05-01", "2026-04-30", 0d, false);
|
||||
seed("制造业单项冠军培育", "立项文件", "DGCR-2024-HN-0012", "工业和信息化部", "2024-06-01", null, 0d, false);
|
||||
seed("省重点研发项目立项", "立项文件", "2024JZ2034", "湖南省科学技术厅", "2024-01-10", "2025-12-31", 800000d, false);
|
||||
seed("科技成果转化示范企业", "荣誉证书", "CGZH-2023-HN-0089", "湖南省科学技术厅", "2023-09-01", "2026-08-31", 0d, false);
|
||||
seed("创新型中小企业认定", "资质证书", "CXXX-2024-CS-0445", "长沙市工信局", "2024-02-01", "2025-01-31", 0d, false);
|
||||
// 一条已归档的示例
|
||||
DeclAchievement archived = buildAchievement(
|
||||
"高企认定2022年度", "资质证书", "HJPD202212300",
|
||||
"国家科学技术部", "2022-01-01", "2024-12-31", 0d);
|
||||
archived.setArchived(Boolean.TRUE);
|
||||
archived.setArchiveNo("ARC-DECL-2022-1");
|
||||
repo.save(archived);
|
||||
log.info("DeclAchievement demo data seeded: {} records.", repo.count());
|
||||
}
|
||||
|
||||
private void seed(String project, String achieveType, String certNo,
|
||||
String authority, String validFrom, String validTo,
|
||||
double grantedAmount, boolean archived) {
|
||||
DeclAchievement a = buildAchievement(project, achieveType, certNo,
|
||||
authority, validFrom, validTo, grantedAmount);
|
||||
a.setArchived(archived);
|
||||
repo.save(a);
|
||||
}
|
||||
|
||||
private DeclAchievement buildAchievement(String project, String achieveType, String certNo,
|
||||
String authority, String validFrom, String validTo,
|
||||
double grantedAmount) {
|
||||
DeclAchievement a = new DeclAchievement();
|
||||
a.setCode("CG-" + (repo.count() + 1));
|
||||
a.setProjectName(project);
|
||||
a.setAchieveType(achieveType);
|
||||
a.setCertNo(certNo);
|
||||
a.setAuthority(authority);
|
||||
a.setValidFrom(validFrom);
|
||||
a.setValidTo(validTo);
|
||||
a.setGrantedAmount(Money.of(grantedAmount));
|
||||
a.setOwner("申报专员");
|
||||
a.setArchived(Boolean.FALSE);
|
||||
a.setCreatedAt(Instant.now());
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.FertAccountingVoucher;
|
||||
import com.kaidi.oa.domain.FertInvoiceLedger;
|
||||
import com.kaidi.oa.domain.FertPkgMaterialLedger;
|
||||
import com.kaidi.oa.repository.FertAccountingVoucherRepository;
|
||||
import com.kaidi.oa.repository.FertInvoiceLedgerRepository;
|
||||
import com.kaidi.oa.repository.FertPkgMaterialLedgerRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 生物质肥料制造中心·包装物台账 + 会计凭证台账 + 发票台账演示种子数据。
|
||||
*
|
||||
* 补全审计缺口:
|
||||
* 1. 包装物(编织袋/标签/吨袋)独立台账(4 条,覆盖多规格+多库位+低库存警示);
|
||||
* 2. 会计凭证台账(3 条:草稿/已过账/已反冲,覆盖成本结转 + 期末结转);
|
||||
* 3. 发票台账(4 条:采购发票2条+销售发票2条,覆盖全状态流转)。
|
||||
*
|
||||
* 幂等:按唯一编码查重后跳过,重启不重复插入。
|
||||
* Order(201) 保证在 DataSeeder(200) 之后执行。
|
||||
*/
|
||||
@Component
|
||||
@Order(201)
|
||||
public class FertPkgAccountingSeeder implements ApplicationRunner {
|
||||
|
||||
private final FertPkgMaterialLedgerRepository pkgRepo;
|
||||
private final FertAccountingVoucherRepository voucherRepo;
|
||||
private final FertInvoiceLedgerRepository invoiceRepo;
|
||||
|
||||
public FertPkgAccountingSeeder(FertPkgMaterialLedgerRepository pkgRepo,
|
||||
FertAccountingVoucherRepository voucherRepo,
|
||||
FertInvoiceLedgerRepository invoiceRepo) {
|
||||
this.pkgRepo = pkgRepo;
|
||||
this.voucherRepo = voucherRepo;
|
||||
this.invoiceRepo = invoiceRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedPkgMaterials();
|
||||
seedAccountingVouchers();
|
||||
seedInvoiceLedger();
|
||||
}
|
||||
|
||||
// -------- 包装物台账 --------
|
||||
|
||||
private void seedPkgMaterials() {
|
||||
// 编织袋 25kg
|
||||
if (pkgRepo.findByPkgCode("PKG-WB-25KG").isEmpty()) {
|
||||
FertPkgMaterialLedger p = new FertPkgMaterialLedger();
|
||||
p.setPkgCode("PKG-WB-25KG");
|
||||
p.setPkgName("编织袋(25kg)");
|
||||
p.setSpec("50cm×80cm,白色印花,凯迪有机肥专用");
|
||||
p.setWarehouse("包装物库");
|
||||
p.setBin("A货架-01");
|
||||
p.setUnit("条");
|
||||
p.setQty(new BigDecimal("8500"));
|
||||
p.setSafetyStock(new BigDecimal("2000"));
|
||||
p.setUnitCost(Money.of(new BigDecimal("0.65")));
|
||||
p.setSupplier("益丰塑编有限公司");
|
||||
p.setLastInboundDate("2026-05-10");
|
||||
p.setLinkedBatchNo("FERM-2026-05-003");
|
||||
p.setPackSpec("25kg");
|
||||
p.setLastMoveType("入库");
|
||||
p.setStatus("在库");
|
||||
p.setRemark("本月采购3万条,已领用2.15万条");
|
||||
p.setCreatedAt(Instant.now());
|
||||
pkgRepo.save(p);
|
||||
}
|
||||
|
||||
// 编织袋 50kg
|
||||
if (pkgRepo.findByPkgCode("PKG-WB-50KG").isEmpty()) {
|
||||
FertPkgMaterialLedger p = new FertPkgMaterialLedger();
|
||||
p.setPkgCode("PKG-WB-50KG");
|
||||
p.setPkgName("编织袋(50kg)");
|
||||
p.setSpec("60cm×100cm,白色印花,颗粒有机肥专用");
|
||||
p.setWarehouse("包装物库");
|
||||
p.setBin("A货架-02");
|
||||
p.setUnit("条");
|
||||
p.setQty(new BigDecimal("1200"));
|
||||
p.setSafetyStock(new BigDecimal("1500"));
|
||||
p.setUnitCost(Money.of(new BigDecimal("0.90")));
|
||||
p.setSupplier("益丰塑编有限公司");
|
||||
p.setLastInboundDate("2026-04-28");
|
||||
p.setLinkedBatchNo("FERM-2026-04-007");
|
||||
p.setPackSpec("50kg");
|
||||
p.setLastMoveType("领用");
|
||||
p.setStatus("低库存");
|
||||
p.setRemark("库存低于安全库存,建议补货");
|
||||
p.setCreatedAt(Instant.now());
|
||||
pkgRepo.save(p);
|
||||
}
|
||||
|
||||
// 吨袋
|
||||
if (pkgRepo.findByPkgCode("PKG-TB-1000KG").isEmpty()) {
|
||||
FertPkgMaterialLedger p = new FertPkgMaterialLedger();
|
||||
p.setPkgCode("PKG-TB-1000KG");
|
||||
p.setPkgName("吨袋(1000kg)");
|
||||
p.setSpec("90cm×90cm×130cm,四吊绳,白色");
|
||||
p.setWarehouse("包装物库");
|
||||
p.setBin("B货架-01");
|
||||
p.setUnit("个");
|
||||
p.setQty(new BigDecimal("340"));
|
||||
p.setSafetyStock(new BigDecimal("100"));
|
||||
p.setUnitCost(Money.of(new BigDecimal("12.50")));
|
||||
p.setSupplier("汇鑫集装袋厂");
|
||||
p.setLastInboundDate("2026-05-20");
|
||||
p.setLinkedBatchNo("FERM-2026-05-001");
|
||||
p.setPackSpec("吨袋");
|
||||
p.setLastMoveType("入库");
|
||||
p.setStatus("在库");
|
||||
p.setRemark("大客户专用,与农业合作社订单关联");
|
||||
p.setCreatedAt(Instant.now());
|
||||
pkgRepo.save(p);
|
||||
}
|
||||
|
||||
// 产品标签
|
||||
if (pkgRepo.findByPkgCode("PKG-LBL-STD").isEmpty()) {
|
||||
FertPkgMaterialLedger p = new FertPkgMaterialLedger();
|
||||
p.setPkgCode("PKG-LBL-STD");
|
||||
p.setPkgName("产品标签(自粘)");
|
||||
p.setSpec("12cm×8cm,自粘不干胶,NY525标准合格证");
|
||||
p.setWarehouse("包装物库");
|
||||
p.setBin("发货暂存区");
|
||||
p.setUnit("张");
|
||||
p.setQty(new BigDecimal("52000"));
|
||||
p.setSafetyStock(new BigDecimal("5000"));
|
||||
p.setUnitCost(Money.of(new BigDecimal("0.08")));
|
||||
p.setSupplier("天成印刷有限公司");
|
||||
p.setLastInboundDate("2026-06-01");
|
||||
p.setPackSpec("25kg");
|
||||
p.setLastMoveType("入库");
|
||||
p.setStatus("在库");
|
||||
p.setRemark("含执行标准/生产日期/有效期/合格证内容,已贴标约3万张");
|
||||
p.setCreatedAt(Instant.now());
|
||||
pkgRepo.save(p);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 会计凭证台账 --------
|
||||
|
||||
private void seedAccountingVouchers() {
|
||||
// 凭证1:已过账的批次成本结转凭证
|
||||
if (voucherRepo.findByVoucherNo("FTV-BATCH-FERM-2026-05-001-2026-05-31").isEmpty()) {
|
||||
FertAccountingVoucher v = new FertAccountingVoucher();
|
||||
v.setVoucherNo("FTV-BATCH-FERM-2026-05-001-2026-05-31");
|
||||
v.setVoucherType("成本结转");
|
||||
v.setBatchRef("FERM-2026-05-001");
|
||||
v.setProductName("有机肥(粉状)");
|
||||
v.setPeriod("2026-05");
|
||||
v.setVoucherDate("2026-05-31");
|
||||
v.setTotalAmount(Money.of(new BigDecimal("186420.00")));
|
||||
v.setLinesJson("[{\"account\":\"4001\",\"accountName\":\"生产成本-直接材料\",\"direction\":\"借\",\"amount\":138600.00,\"summary\":\"FERM-2026-05-001 原料成本\"},{\"account\":\"1211\",\"accountName\":\"原材料\",\"direction\":\"贷\",\"amount\":138600.00,\"summary\":\"FERM-2026-05-001 原料领用\"},{\"account\":\"4002\",\"accountName\":\"生产成本-直接人工\",\"direction\":\"借\",\"amount\":28400.00,\"summary\":\"FERM-2026-05-001 人工成本\"},{\"account\":\"2211\",\"accountName\":\"应付职工薪酬\",\"direction\":\"贷\",\"amount\":28400.00,\"summary\":\"FERM-2026-05-001 工资分摊\"},{\"account\":\"4003\",\"accountName\":\"生产成本-制造费用\",\"direction\":\"借\",\"amount\":19420.00,\"summary\":\"FERM-2026-05-001 制造费用\"},{\"account\":\"4101\",\"accountName\":\"制造费用\",\"direction\":\"贷\",\"amount\":19420.00,\"summary\":\"FERM-2026-05-001 费用分摊\"},{\"account\":\"1403\",\"accountName\":\"库存商品\",\"direction\":\"借\",\"amount\":186420.00,\"summary\":\"FERM-2026-05-001 完工入库\"},{\"account\":\"4001\",\"accountName\":\"生产成本\",\"direction\":\"贷\",\"amount\":186420.00,\"summary\":\"FERM-2026-05-001 结转完工成本\"}]");
|
||||
v.setLineCount(8);
|
||||
v.setStatus("已过账");
|
||||
v.setPreparer("系统自动");
|
||||
v.setPoster("李会计");
|
||||
v.setPostedAt(Instant.parse("2026-05-31T08:30:00Z"));
|
||||
v.setRemark("5月份第1批有机肥(粉状)成本结转,已过账");
|
||||
v.setCreatedAt(Instant.parse("2026-05-31T08:00:00Z"));
|
||||
voucherRepo.save(v);
|
||||
}
|
||||
|
||||
// 凭证2:草稿状态期末结转凭证
|
||||
if (voucherRepo.findByVoucherNo("FTV-PERIOD-2026-05").isEmpty()) {
|
||||
FertAccountingVoucher v = new FertAccountingVoucher();
|
||||
v.setVoucherNo("FTV-PERIOD-2026-05");
|
||||
v.setVoucherType("期末结转");
|
||||
v.setBatchRef("期间汇总-6批次");
|
||||
v.setProductName("2026年5月期末汇总");
|
||||
v.setPeriod("2026-05");
|
||||
v.setVoucherDate("2026-05-31");
|
||||
v.setTotalAmount(Money.of(new BigDecimal("1043680.00")));
|
||||
v.setLinesJson("[{\"account\":\"4001\",\"accountName\":\"生产成本-直接材料\",\"direction\":\"借\",\"amount\":762400.00,\"summary\":\"2026-05 直接原料结转\"},{\"account\":\"4002\",\"accountName\":\"生产成本-直接人工\",\"direction\":\"借\",\"amount\":168200.00,\"summary\":\"2026-05 人工成本结转\"},{\"account\":\"4003\",\"accountName\":\"生产成本-制造费用\",\"direction\":\"借\",\"amount\":113080.00,\"summary\":\"2026-05 制造费用结转\"},{\"account\":\"1403\",\"accountName\":\"库存商品\",\"direction\":\"贷\",\"amount\":1043680.00,\"summary\":\"2026-05 完工产品入库\"}]");
|
||||
v.setLineCount(4);
|
||||
v.setStatus("草稿");
|
||||
v.setPreparer("系统自动");
|
||||
v.setRemark("2026年5月期末成本结转汇总凭证(6个批次),待会计审核过账");
|
||||
v.setCreatedAt(Instant.now());
|
||||
voucherRepo.save(v);
|
||||
}
|
||||
|
||||
// 凭证3:已反冲的凭证(演示反冲功能)
|
||||
if (voucherRepo.findByVoucherNo("FTV-BATCH-FERM-2026-04-002-REVERSED").isEmpty()) {
|
||||
FertAccountingVoucher v = new FertAccountingVoucher();
|
||||
v.setVoucherNo("FTV-BATCH-FERM-2026-04-002-REVERSED");
|
||||
v.setVoucherType("成本结转");
|
||||
v.setBatchRef("FERM-2026-04-002");
|
||||
v.setProductName("生物有机肥(颗粒)");
|
||||
v.setPeriod("2026-04");
|
||||
v.setVoucherDate("2026-04-30");
|
||||
v.setTotalAmount(Money.of(new BigDecimal("234850.00")));
|
||||
v.setLinesJson("[{\"account\":\"4001\",\"accountName\":\"生产成本-直接材料\",\"direction\":\"借\",\"amount\":175200.00,\"summary\":\"FERM-2026-04-002 原料成本\"},{\"account\":\"1211\",\"accountName\":\"原材料\",\"direction\":\"贷\",\"amount\":175200.00,\"summary\":\"FERM-2026-04-002 原料领用\"},{\"account\":\"1403\",\"accountName\":\"库存商品\",\"direction\":\"借\",\"amount\":234850.00,\"summary\":\"FERM-2026-04-002 完工入库\"},{\"account\":\"4001\",\"accountName\":\"生产成本\",\"direction\":\"贷\",\"amount\":234850.00,\"summary\":\"FERM-2026-04-002 结转完工\"}]");
|
||||
v.setLineCount(4);
|
||||
v.setStatus("已反冲");
|
||||
v.setPreparer("系统自动");
|
||||
v.setPoster("李会计");
|
||||
v.setPostedAt(Instant.parse("2026-04-30T09:00:00Z"));
|
||||
v.setRemark("原过账凭证因成本数据更正,已红字冲销;请重新生成");
|
||||
v.setCreatedAt(Instant.parse("2026-04-30T08:00:00Z"));
|
||||
voucherRepo.save(v);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 发票台账 --------
|
||||
|
||||
private void seedInvoiceLedger() {
|
||||
// 采购发票1:已核销(畜禽粪污原料采购)
|
||||
if (invoiceRepo.findByInvoiceNo("31120260500123456").isEmpty()) {
|
||||
FertInvoiceLedger inv = new FertInvoiceLedger();
|
||||
inv.setInvoiceNo("31120260500123456");
|
||||
inv.setInvoiceType("采购发票");
|
||||
inv.setCounterpart("绿源养殖场");
|
||||
inv.setInvoiceDate("2026-05-08");
|
||||
inv.setPeriod("2026-05");
|
||||
inv.setAmountExclTax(Money.of(new BigDecimal("87600.00")));
|
||||
inv.setTaxAmount(Money.of(new BigDecimal("7884.00")));
|
||||
inv.setTotalAmount(Money.of(new BigDecimal("95484.00")));
|
||||
inv.setTaxRate(Money.of(new BigDecimal("9.00")));
|
||||
inv.setGoodsDesc("畜禽粪污(猪粪)原料采购");
|
||||
inv.setLinkedOrderNo("PO-2026-05-011");
|
||||
inv.setLinkedBatchRef("FERM-2026-05-001");
|
||||
inv.setVoucherNo("FTV-BATCH-FERM-2026-05-001-2026-05-31");
|
||||
inv.setStatus("已核销");
|
||||
inv.setCertifyDate("2026-05-12");
|
||||
inv.setWriteOffDate("2026-05-25");
|
||||
inv.setHandler("王采购");
|
||||
inv.setRemark("增值税专票,已抵扣认证,已与付款单核销");
|
||||
inv.setCreatedAt(Instant.parse("2026-05-08T10:00:00Z"));
|
||||
invoiceRepo.save(inv);
|
||||
}
|
||||
|
||||
// 采购发票2:已认证(秸秆原料)
|
||||
if (invoiceRepo.findByInvoiceNo("31120260500234567").isEmpty()) {
|
||||
FertInvoiceLedger inv = new FertInvoiceLedger();
|
||||
inv.setInvoiceNo("31120260500234567");
|
||||
inv.setInvoiceType("采购发票");
|
||||
inv.setCounterpart("丰收秸秆收储站");
|
||||
inv.setInvoiceDate("2026-05-18");
|
||||
inv.setPeriod("2026-05");
|
||||
inv.setAmountExclTax(Money.of(new BigDecimal("43200.00")));
|
||||
inv.setTaxAmount(Money.of(new BigDecimal("3888.00")));
|
||||
inv.setTotalAmount(Money.of(new BigDecimal("47088.00")));
|
||||
inv.setTaxRate(Money.of(new BigDecimal("9.00")));
|
||||
inv.setGoodsDesc("秸秆(玉米秸秆)原料采购");
|
||||
inv.setLinkedOrderNo("PO-2026-05-022");
|
||||
inv.setLinkedBatchRef("FERM-2026-05-003");
|
||||
inv.setStatus("已认证");
|
||||
inv.setCertifyDate("2026-05-22");
|
||||
inv.setHandler("王采购");
|
||||
inv.setRemark("已认证,待与付款单核销");
|
||||
inv.setCreatedAt(Instant.parse("2026-05-18T14:00:00Z"));
|
||||
invoiceRepo.save(inv);
|
||||
}
|
||||
|
||||
// 销售发票1:已核销(种植大户)
|
||||
if (invoiceRepo.findByInvoiceNo("60420260500001234").isEmpty()) {
|
||||
FertInvoiceLedger inv = new FertInvoiceLedger();
|
||||
inv.setInvoiceNo("60420260500001234");
|
||||
inv.setInvoiceType("销售发票");
|
||||
inv.setCounterpart("绿野农业合作社");
|
||||
inv.setInvoiceDate("2026-05-20");
|
||||
inv.setPeriod("2026-05");
|
||||
inv.setAmountExclTax(Money.of(new BigDecimal("136000.00")));
|
||||
inv.setTaxAmount(Money.of(new BigDecimal("12240.00")));
|
||||
inv.setTotalAmount(Money.of(new BigDecimal("148240.00")));
|
||||
inv.setTaxRate(Money.of(new BigDecimal("9.00")));
|
||||
inv.setGoodsDesc("有机肥料(粉状)800吨");
|
||||
inv.setLinkedOrderNo("SO-2026-05-008");
|
||||
inv.setLinkedBatchRef("FERM-2026-05-001");
|
||||
inv.setStatus("已核销");
|
||||
inv.setCertifyDate("2026-05-20");
|
||||
inv.setWriteOffDate("2026-05-30");
|
||||
inv.setHandler("赵销售");
|
||||
inv.setRemark("已开票,客户已回款,已核销");
|
||||
inv.setCreatedAt(Instant.parse("2026-05-20T09:00:00Z"));
|
||||
invoiceRepo.save(inv);
|
||||
}
|
||||
|
||||
// 销售发票2:待认证(新客户,月底开票)
|
||||
if (invoiceRepo.findByInvoiceNo("60420260600000321").isEmpty()) {
|
||||
FertInvoiceLedger inv = new FertInvoiceLedger();
|
||||
inv.setInvoiceNo("60420260600000321");
|
||||
inv.setInvoiceType("销售发票");
|
||||
inv.setCounterpart("远景生态农业有限公司");
|
||||
inv.setInvoiceDate("2026-06-05");
|
||||
inv.setPeriod("2026-06");
|
||||
inv.setAmountExclTax(Money.of(new BigDecimal("68000.00")));
|
||||
inv.setTaxAmount(Money.of(new BigDecimal("6120.00")));
|
||||
inv.setTotalAmount(Money.of(new BigDecimal("74120.00")));
|
||||
inv.setTaxRate(Money.of(new BigDecimal("9.00")));
|
||||
inv.setGoodsDesc("生物有机肥(颗粒)400吨");
|
||||
inv.setLinkedOrderNo("SO-2026-06-003");
|
||||
inv.setLinkedBatchRef("FERM-2026-05-004");
|
||||
inv.setStatus("待认证");
|
||||
inv.setHandler("赵销售");
|
||||
inv.setRemark("6月新客户首单,已发货,发票已开,待客户确认");
|
||||
inv.setCreatedAt(Instant.parse("2026-06-05T11:00:00Z"));
|
||||
invoiceRepo.save(inv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.FinAccountStandard;
|
||||
import com.kaidi.oa.domain.FinElecArchive;
|
||||
import com.kaidi.oa.domain.FinEmpLoan;
|
||||
import com.kaidi.oa.domain.FinRdPersonnel;
|
||||
import com.kaidi.oa.domain.FinRdTimesheet;
|
||||
import com.kaidi.oa.repository.FinAccountStandardRepository;
|
||||
import com.kaidi.oa.repository.FinElecArchiveRepository;
|
||||
import com.kaidi.oa.repository.FinEmpLoanRepository;
|
||||
import com.kaidi.oa.repository.FinRdPersonnelRepository;
|
||||
import com.kaidi.oa.repository.FinRdTimesheetRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 财务部深水缺口演示种子数据(缺口 #2/#4/#12/#13)。
|
||||
*
|
||||
* 补齐:
|
||||
* - FinEmpLoan(员工借款,缺口#2):5 条跨状态演示
|
||||
* - FinRdPersonnel(研发人员档案,缺口#4):6 条跨岗位类型/用工类型
|
||||
* - FinRdTimesheet(工时填报,缺口#4):8 条跨项目/状态
|
||||
* - FinAccountStandard(多会计准则配置,缺口#12/#13):2 条(CAS+IFRS)
|
||||
* - FinElecArchive(电子会计档案,缺口#13):6 条跨类型/状态
|
||||
*
|
||||
* 幂等:各表 count > 0 即跳过。
|
||||
*
|
||||
* @Order(13) 在 FinDeptSeeder(@Order 12) 之后运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(13)
|
||||
public class FinDeptDeepSeeder implements ApplicationRunner {
|
||||
|
||||
private final FinEmpLoanRepository empLoanRepo;
|
||||
private final FinRdPersonnelRepository personnelRepo;
|
||||
private final FinRdTimesheetRepository timesheetRepo;
|
||||
private final FinAccountStandardRepository stdRepo;
|
||||
private final FinElecArchiveRepository archiveRepo;
|
||||
|
||||
public FinDeptDeepSeeder(FinEmpLoanRepository empLoanRepo,
|
||||
FinRdPersonnelRepository personnelRepo,
|
||||
FinRdTimesheetRepository timesheetRepo,
|
||||
FinAccountStandardRepository stdRepo,
|
||||
FinElecArchiveRepository archiveRepo) {
|
||||
this.empLoanRepo = empLoanRepo;
|
||||
this.personnelRepo = personnelRepo;
|
||||
this.timesheetRepo = timesheetRepo;
|
||||
this.stdRepo = stdRepo;
|
||||
this.archiveRepo = archiveRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedEmpLoans();
|
||||
seedRdPersonnel();
|
||||
seedRdTimesheets();
|
||||
seedAccountStandards();
|
||||
seedElecArchives();
|
||||
}
|
||||
|
||||
// ===================== 员工借款 =====================
|
||||
private void seedEmpLoans() {
|
||||
if (empLoanRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, empName, empId, dept, type, purpose, loanAmt, repaidAmt, applyDate, repayDate, status
|
||||
{ "EL-2026-001", "张明辉", "EMP-001", "研发部", FinEmpLoan.TYPE_TRAVEL,
|
||||
"赴武汉参加供应商技术对接会议",
|
||||
new BigDecimal("5000.00"), new BigDecimal("5000.00"),
|
||||
"2026-01-10", "2026-02-28", FinEmpLoan.S_SETTLED },
|
||||
{ "EL-2026-002", "刘晓燕", "EMP-002", "销售部", FinEmpLoan.TYPE_TRAVEL,
|
||||
"出差上海客户拜访(3日)",
|
||||
new BigDecimal("8500.00"), new BigDecimal("3000.00"),
|
||||
"2026-02-01", "2026-03-15", FinEmpLoan.S_REPAYING },
|
||||
{ "EL-2026-003", "王建国", "EMP-003", "工程部", FinEmpLoan.TYPE_PETTY,
|
||||
"项目部备用金(KD-PRJ-2025-012 现场施工备用)",
|
||||
new BigDecimal("20000.00"), new BigDecimal("0.00"),
|
||||
"2026-01-20", "2026-04-30", FinEmpLoan.S_APPROVED },
|
||||
{ "EL-2026-004", "陈秀梅", "EMP-004", "行政部", FinEmpLoan.TYPE_OTHER,
|
||||
"购置办公耗材临时借款",
|
||||
new BigDecimal("2000.00"), new BigDecimal("0.00"),
|
||||
"2025-12-01", "2026-01-31", FinEmpLoan.S_OVERDUE },
|
||||
{ "EL-2026-005", "李伟强", "EMP-005", "财务部", FinEmpLoan.TYPE_TRAVEL,
|
||||
"参加上海财务数字化峰会",
|
||||
new BigDecimal("6000.00"), new BigDecimal("0.00"),
|
||||
"2026-03-01", "2026-04-30", FinEmpLoan.S_PENDING },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinEmpLoan loan = new FinEmpLoan();
|
||||
loan.setLoanCode((String) row[0]);
|
||||
loan.setEmployeeName((String) row[1]);
|
||||
loan.setEmployeeId((String) row[2]);
|
||||
loan.setDepartment((String) row[3]);
|
||||
loan.setLoanType((String) row[4]);
|
||||
loan.setPurpose((String) row[5]);
|
||||
BigDecimal loanAmt = (BigDecimal) row[6];
|
||||
BigDecimal repaidAmt = (BigDecimal) row[7];
|
||||
loan.setLoanAmount(loanAmt);
|
||||
loan.setRepaidAmount(repaidAmt);
|
||||
loan.setRemainAmount(loanAmt.subtract(repaidAmt));
|
||||
loan.setApplyDate((String) row[8]);
|
||||
loan.setExpectedRepayDate((String) row[9]);
|
||||
loan.setStatus((String) row[10]);
|
||||
loan.setDeductFromSalary(FinEmpLoan.S_OVERDUE.equals(row[10]));
|
||||
loan.setApprover(FinEmpLoan.S_PENDING.equals(row[10]) ? null : "财务总监");
|
||||
loan.setApprovalDate(FinEmpLoan.S_PENDING.equals(row[10]) ? null : "2026-01-25");
|
||||
if (FinEmpLoan.S_SETTLED.equals(row[10]))
|
||||
loan.setSettleDate("2026-02-20");
|
||||
if (FinEmpLoan.S_OVERDUE.equals(row[10]))
|
||||
loan.setOverdueDays(135);
|
||||
loan.setOperator("admin");
|
||||
loan.setCreatedAt(Instant.now().minus(60, ChronoUnit.DAYS));
|
||||
loan.setUpdatedAt(Instant.now().minus(10, ChronoUnit.DAYS));
|
||||
empLoanRepo.save(loan);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 研发人员档案 =====================
|
||||
private void seedRdPersonnel() {
|
||||
if (personnelRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, empId, name, dept, roleType, staffType, stdHours, allocRule, projects, monthlyCost
|
||||
{ "RDP-001", "EMP-010", "黄志远", "研发部", FinRdPersonnel.ROLE_CORE, FinRdPersonnel.STAFF_FULL,
|
||||
176.0, "按项目实际工时占比分摊", "KD-RD-2025-001,KD-RD-2025-003",
|
||||
new BigDecimal("22000.00"), "2024-01-01" },
|
||||
{ "RDP-002", "EMP-011", "周美玲", "研发部", FinRdPersonnel.ROLE_CORE, FinRdPersonnel.STAFF_FULL,
|
||||
176.0, "按项目实际工时占比分摊", "KD-RD-2025-002",
|
||||
new BigDecimal("19500.00"), "2024-03-01" },
|
||||
{ "RDP-003", "EMP-012", "吴建平", "研发部", FinRdPersonnel.ROLE_AUX, FinRdPersonnel.STAFF_FULL,
|
||||
176.0, "按参与项目数均摊", "KD-RD-2025-001,KD-RD-2025-002,KD-RD-2025-003",
|
||||
new BigDecimal("15000.00"), "2023-07-01" },
|
||||
{ "RDP-004", "EMP-013", "赵海涛", "研发部", FinRdPersonnel.ROLE_MGR, FinRdPersonnel.STAFF_FULL,
|
||||
176.0, "管理费用计入管理科目", "KD-RD-2025-001,KD-RD-2025-002",
|
||||
new BigDecimal("32000.00"), "2022-01-01" },
|
||||
{ "RDP-005", "OUT-001", "李天成", "外协研发", FinRdPersonnel.ROLE_CORE, FinRdPersonnel.STAFF_OUT,
|
||||
120.0, "按合同约定服务小时计算", "KD-RD-2025-003",
|
||||
new BigDecimal("28000.00"), "2025-06-01" },
|
||||
{ "RDP-006", "EMP-014", "孙梅华", "研发部", FinRdPersonnel.ROLE_AUX, FinRdPersonnel.STAFF_PART,
|
||||
80.0, "按实际参与工时分摊", "KD-RD-2025-002",
|
||||
new BigDecimal("9000.00"), "2025-09-01" },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinRdPersonnel p = new FinRdPersonnel();
|
||||
p.setPersonnelCode((String) row[0]);
|
||||
p.setEmployeeId((String) row[1]);
|
||||
p.setEmployeeName((String) row[2]);
|
||||
p.setDepartment((String) row[3]);
|
||||
p.setRoleType((String) row[4]);
|
||||
p.setStaffType((String) row[5]);
|
||||
p.setMonthlyStdHours((Double) row[6]);
|
||||
p.setAllocRule((String) row[7]);
|
||||
p.setProjectCodes((String) row[8]);
|
||||
p.setMonthlyLaborCost((BigDecimal) row[9]);
|
||||
p.setRdStartDate((String) row[10]);
|
||||
p.setStatus(FinRdPersonnel.S_ACTIVE);
|
||||
p.setCreatedAt(Instant.now().minus(180, ChronoUnit.DAYS));
|
||||
p.setUpdatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
personnelRepo.save(p);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 工时填报 =====================
|
||||
private void seedRdTimesheets() {
|
||||
if (timesheetRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// sheetCode, empId, empName, projectCode, fillType, fillDate, hours, confirmedHrs, allocCost, status
|
||||
{ "TS-2026-001", "EMP-010", "黄志远", "KD-RD-2025-001", FinRdTimesheet.FILL_WEEKLY,
|
||||
"2026-W05", 40.0, 40.0, new BigDecimal("5000.00"), FinRdTimesheet.S_APPROVED },
|
||||
{ "TS-2026-002", "EMP-010", "黄志远", "KD-RD-2025-003", FinRdTimesheet.FILL_WEEKLY,
|
||||
"2026-W05", 8.0, 8.0, new BigDecimal("1000.00"), FinRdTimesheet.S_APPROVED },
|
||||
{ "TS-2026-003", "EMP-011", "周美玲", "KD-RD-2025-002", FinRdTimesheet.FILL_WEEKLY,
|
||||
"2026-W05", 44.0, 40.0, new BigDecimal("4432.00"), FinRdTimesheet.S_APPROVED },
|
||||
{ "TS-2026-004", "EMP-012", "吴建平", "KD-RD-2025-001", FinRdTimesheet.FILL_DAILY,
|
||||
"2026-02-24", 8.0, 8.0, new BigDecimal("682.00"), FinRdTimesheet.S_APPROVED },
|
||||
{ "TS-2026-005", "OUT-001", "李天成", "KD-RD-2025-003", FinRdTimesheet.FILL_WEEKLY,
|
||||
"2026-W06", 32.0, null, new BigDecimal("0.00"), FinRdTimesheet.S_PENDING },
|
||||
{ "TS-2026-006", "EMP-014", "孙梅华", "KD-RD-2025-002", FinRdTimesheet.FILL_WEEKLY,
|
||||
"2026-W06", 20.0, null, new BigDecimal("0.00"), FinRdTimesheet.S_PENDING },
|
||||
{ "TS-2026-007", "EMP-011", "周美玲", "KD-RD-2025-002", FinRdTimesheet.FILL_DAILY,
|
||||
"2026-03-10", 6.0, null, new BigDecimal("0.00"), FinRdTimesheet.S_DRAFT },
|
||||
{ "TS-2026-008", "EMP-010", "黄志远", "KD-RD-2025-001", FinRdTimesheet.FILL_DAILY,
|
||||
"2026-02-10", 9.0, 6.0, new BigDecimal("750.00"), FinRdTimesheet.S_REJECTED },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinRdTimesheet s = new FinRdTimesheet();
|
||||
s.setSheetCode((String) row[0]);
|
||||
s.setEmployeeId((String) row[1]);
|
||||
s.setEmployeeName((String) row[2]);
|
||||
s.setProjectCode((String) row[3]);
|
||||
s.setProjectName("KD研发项目-" + ((String) row[3]).substring(((String) row[3]).lastIndexOf('-') + 1));
|
||||
s.setFillType((String) row[4]);
|
||||
s.setFillDate((String) row[5]);
|
||||
s.setReportedHours((Double) row[6]);
|
||||
s.setConfirmedHours((Double) row[7]);
|
||||
s.setAllocatedLaborCost((BigDecimal) row[8]);
|
||||
s.setStatus((String) row[9]);
|
||||
s.setExceedsLegalLimit(Boolean.FALSE);
|
||||
if (FinRdTimesheet.S_APPROVED.equals(row[9]))
|
||||
s.setReviewer("赵海涛");
|
||||
if (FinRdTimesheet.S_REJECTED.equals(row[9])) {
|
||||
s.setReviewer("赵海涛");
|
||||
s.setRejectReason("单日填报工时9小时超出常规,请提供加班审批记录");
|
||||
}
|
||||
s.setCreatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
s.setUpdatedAt(Instant.now().minus(5, ChronoUnit.DAYS));
|
||||
timesheetRepo.save(s);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 多会计准则配置 =====================
|
||||
private void seedAccountStandards() {
|
||||
if (stdRepo.count() > 0) return;
|
||||
|
||||
FinAccountStandard cas = new FinAccountStandard();
|
||||
cas.setStandardCode(FinAccountStandard.STD_CAS);
|
||||
cas.setStandardName("中国企业会计准则");
|
||||
cas.setStandardVersion("2019修订版");
|
||||
cas.setEffectiveDate("2019-01-01");
|
||||
cas.setIsPrimary(Boolean.TRUE);
|
||||
cas.setReportCurrency("CNY");
|
||||
cas.setStatus(FinAccountStandard.S_ACTIVE);
|
||||
cas.setAccountMappingNotes(
|
||||
"CAS科目体系:1001现金/1002银行存款/1122应收账款/6001主营业务收入/6401主营业务成本。" +
|
||||
"与IFRS差异:无IFRS16使用权资产单独科目(CAS合并于固定资产);" +
|
||||
"金融工具分类三类vs IFRS四类;研发费用资本化条件更严格。");
|
||||
cas.setConfiguredBy("admin");
|
||||
cas.setRemark("凯迪科技主用会计准则,符合中国证监会及工商局年报要求");
|
||||
cas.setCreatedAt(Instant.now().minus(365, ChronoUnit.DAYS));
|
||||
cas.setUpdatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
stdRepo.save(cas);
|
||||
|
||||
FinAccountStandard ifrs = new FinAccountStandard();
|
||||
ifrs.setStandardCode(FinAccountStandard.STD_IFRS);
|
||||
ifrs.setStandardName("国际财务报告准则");
|
||||
ifrs.setStandardVersion("IFRS 2023");
|
||||
ifrs.setEffectiveDate("2023-01-01");
|
||||
ifrs.setIsPrimary(Boolean.FALSE);
|
||||
ifrs.setReportCurrency("USD");
|
||||
ifrs.setStatus(FinAccountStandard.S_ACTIVE);
|
||||
ifrs.setAccountMappingNotes(
|
||||
"IFRS主要差异:IFRS16租赁准则要求确认使用权资产(ROU)和租赁负债;" +
|
||||
"IFRS9金融工具分四类:摊余成本/FVOCI/FVTPL/特殊类别;" +
|
||||
"IFRS15收入确认以履约义务为基础;" +
|
||||
"预期信用损失(ECL)模型用于金融资产减值(替代已发生损失模型)。");
|
||||
ifrs.setConfiguredBy("admin");
|
||||
ifrs.setRemark("备用,用于集团跨境合并报表及境外融资报告");
|
||||
ifrs.setCreatedAt(Instant.now().minus(180, ChronoUnit.DAYS));
|
||||
ifrs.setUpdatedAt(Instant.now().minus(90, ChronoUnit.DAYS));
|
||||
stdRepo.save(ifrs);
|
||||
}
|
||||
|
||||
// ===================== 电子会计档案 =====================
|
||||
private void seedElecArchives() {
|
||||
if (archiveRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, type, name, period, format, hash, retentionYrs, status
|
||||
{ "EA-2025-001", FinElecArchive.TYPE_VOUCHER, "2025年12月记账凭证汇总",
|
||||
"2025-12", "PDF", "a1b2c3d4e5f6789012345678901234567890abcd1234567890abcd1234567890",
|
||||
30, FinElecArchive.S_BACKED },
|
||||
{ "EA-2025-002", FinElecArchive.TYPE_LEDGER, "2025年12月明细账汇总",
|
||||
"2025-12", "XLSX", "b2c3d4e5f6789012345678901234567890abcd1234567890abcd1234567891",
|
||||
30, FinElecArchive.S_ARCHIVED },
|
||||
{ "EA-2025-003", FinElecArchive.TYPE_REPORT, "2025年年度财务报表(三表)",
|
||||
"2025", "PDF", "c3d4e5f6789012345678901234567890abcd1234567890abcd12345678920a",
|
||||
30, FinElecArchive.S_BACKED },
|
||||
{ "EA-2026-001", FinElecArchive.TYPE_INVOICE, "2026年1月进项发票台账",
|
||||
"2026-01", "XLSX", "d4e5f6789012345678901234567890abcd1234567890abcd1234567890a1b2",
|
||||
10, FinElecArchive.S_ARCHIVED },
|
||||
{ "EA-2026-002", FinElecArchive.TYPE_BANK_REC, "2026年1月银行回单汇总",
|
||||
"2026-01", "PDF", "e5f6789012345678901234567890abcd1234567890abcd1234567890a1b2c3",
|
||||
10, FinElecArchive.S_ARCHIVED },
|
||||
{ "EA-2026-003", FinElecArchive.TYPE_VOUCHER, "2026年1月记账凭证",
|
||||
"2026-01", "PDF", null,
|
||||
30, FinElecArchive.S_PENDING },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinElecArchive a = new FinElecArchive();
|
||||
a.setArchiveCode((String) row[0]);
|
||||
a.setArchiveType((String) row[1]);
|
||||
a.setArchiveName((String) row[2]);
|
||||
a.setPeriod((String) row[3]);
|
||||
a.setFileFormat((String) row[4]);
|
||||
a.setSha256Hash((String) row[5]);
|
||||
a.setHashVerified(row[5] != null && !FinElecArchive.S_PENDING.equals(row[7]));
|
||||
a.setRetentionYears((Integer) row[6]);
|
||||
a.setStatus((String) row[7]);
|
||||
a.setExportable(Boolean.TRUE);
|
||||
a.setFileSizeBytes(1024L * 1024 * (2 + (long)(Math.random() * 8)));
|
||||
if (!FinElecArchive.S_PENDING.equals(row[7])) {
|
||||
a.setArchiveDate("2026-01-05");
|
||||
a.setArchivedBy("李伟强");
|
||||
}
|
||||
if (FinElecArchive.S_BACKED.equals(row[7])) {
|
||||
a.setBackupPaths("/backup/oa/archives/" + row[0] + ".zip");
|
||||
a.setBackedUpBy("系统自动备份");
|
||||
}
|
||||
a.setCreatedAt(Instant.now().minus(45, ChronoUnit.DAYS));
|
||||
a.setUpdatedAt(Instant.now().minus(10, ChronoUnit.DAYS));
|
||||
archiveRepo.save(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.ArApItem;
|
||||
import com.kaidi.oa.domain.TaxFiling;
|
||||
import com.kaidi.oa.repository.ArApItemRepository;
|
||||
import com.kaidi.oa.repository.TaxFilingRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 财务部缺口种子补充(W8批次)。
|
||||
*
|
||||
* 补全:
|
||||
* ① TaxFiling:补充 8 条多税种/多状态(报表附注 §12 "应交税费明细" 需多条数据演示)。
|
||||
* ② ArApItem:补充 12 条带 [CERT:*] 发票认证状态标记的应付款(§3 进项税抵扣台账演示)。
|
||||
*
|
||||
* 幂等:各表通过 code 前缀判断跳过。
|
||||
*
|
||||
* @Order(215) 在既有 FinDeptSeeder(@Order 12)、BranchFinDemoSeeder(@Order 15) 之后。
|
||||
*/
|
||||
@Component
|
||||
@Order(215)
|
||||
public class FinDeptGapSeeder implements ApplicationRunner {
|
||||
|
||||
private final TaxFilingRepository taxRepo;
|
||||
private final ArApItemRepository arApRepo;
|
||||
|
||||
public FinDeptGapSeeder(TaxFilingRepository taxRepo,
|
||||
ArApItemRepository arApRepo) {
|
||||
this.taxRepo = taxRepo;
|
||||
this.arApRepo = arApRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedTaxFilings();
|
||||
seedApWithInvoiceCert();
|
||||
}
|
||||
|
||||
// ===================== TaxFiling 多税种演示 =====================
|
||||
|
||||
private void seedTaxFilings() {
|
||||
// 幂等:若已有超过2条则跳过
|
||||
if (taxRepo.count() > 2) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, taxType, period, taxBase, taxRate, taxAmount, status, filingDate, payDate, remark
|
||||
{ "TAX-VAT-2026-04", "增值税", "2026-04",
|
||||
new BigDecimal("1820000.00"), 13.0, new BigDecimal("236600.00"),
|
||||
TaxFiling.S_PAID, "2026-04-15", "2026-04-20",
|
||||
"2026年4月增值税主申报 | 销项税额265460-进项税额28860=应纳236600" },
|
||||
|
||||
{ "TAX-VAT-2026-05", "增值税", "2026-05",
|
||||
new BigDecimal("2105000.00"), 13.0, new BigDecimal("273650.00"),
|
||||
TaxFiling.S_FILED, "2026-05-15", null,
|
||||
"2026年5月增值税申报(已申报待缴款)" },
|
||||
|
||||
{ "TAX-VAT-2026-06", "增值税", "2026-06",
|
||||
new BigDecimal("1680000.00"), 13.0, new BigDecimal("218400.00"),
|
||||
TaxFiling.S_PENDING, null, null,
|
||||
"2026年6月增值税(待申报)" },
|
||||
|
||||
{ "TAX-CIT-2026-Q1", "企业所得税", "2026-Q1",
|
||||
new BigDecimal("4200000.00"), 15.0, new BigDecimal("630000.00"),
|
||||
TaxFiling.S_PAID, "2026-04-25", "2026-04-28",
|
||||
"2026Q1企业所得税季报(高新技术企业15%税率)" },
|
||||
|
||||
{ "TAX-CIT-2026-Q2", "企业所得税", "2026-Q2",
|
||||
new BigDecimal("3650000.00"), 15.0, new BigDecimal("547500.00"),
|
||||
TaxFiling.S_PENDING, null, null,
|
||||
"2026Q2企业所得税季报(待申报)" },
|
||||
|
||||
{ "TAX-STAMP-2026-H1", "印花税", "2026-H1",
|
||||
new BigDecimal("120000000.00"), 0.03, new BigDecimal("36000.00"),
|
||||
TaxFiling.S_PAID, "2026-06-30", "2026-06-30",
|
||||
"2026上半年购销合同印花税(税率0.03‰×合同额)" },
|
||||
|
||||
{ "TAX-URB-2026-04", "城市维护建设税", "2026-04",
|
||||
new BigDecimal("236600.00"), 7.0, new BigDecimal("16562.00"),
|
||||
TaxFiling.S_PAID, "2026-04-15", "2026-04-20",
|
||||
"2026年4月城建税(增值税236600×7%)" },
|
||||
|
||||
{ "TAX-EDUC-2026-04", "教育费附加", "2026-04",
|
||||
new BigDecimal("236600.00"), 3.0, new BigDecimal("7098.00"),
|
||||
TaxFiling.S_PAID, "2026-04-15", "2026-04-20",
|
||||
"2026年4月教育费附加(增值税236600×3%)" },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
String code = (String) row[0];
|
||||
boolean exists = taxRepo.findAll().stream().anyMatch(t -> code.equals(t.getCode()));
|
||||
if (exists) continue;
|
||||
|
||||
TaxFiling t = new TaxFiling();
|
||||
t.setCode(code);
|
||||
t.setTaxType((String) row[1]);
|
||||
t.setPeriod((String) row[2]);
|
||||
t.setTaxBase((BigDecimal) row[3]);
|
||||
t.setTaxRate((Double) row[4]);
|
||||
t.setTaxAmount((BigDecimal) row[5]);
|
||||
t.setStatus((String) row[6]);
|
||||
t.setFilingDate((String) row[7]);
|
||||
t.setPayDate((String) row[8]);
|
||||
t.setRemark((String) row[9]);
|
||||
t.setCompanySubject("凯迪科技股份有限公司");
|
||||
t.setOwner("税务会计");
|
||||
taxRepo.save(t);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 带发票认证状态的应付单 =====================
|
||||
|
||||
private void seedApWithInvoiceCert() {
|
||||
// 幂等:若已有 AP-CERT 前缀的记录则跳过
|
||||
boolean certExists = arApRepo.findAll().stream()
|
||||
.anyMatch(i -> i.getCode() != null && i.getCode().startsWith("AP-CERT-"));
|
||||
if (certExists) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, party, ref, amount, dueDate, certStatus, remark
|
||||
{ "AP-CERT-001", "武汉凯博机械有限公司", "INV-2026-0401", new BigDecimal("182000.00"),
|
||||
"2026-05-31", "已认证",
|
||||
"[CERT:已认证] 增值税专用发票 INV-2026-0401 已通过税局认证,进项税额23660元可抵扣" },
|
||||
|
||||
{ "AP-CERT-002", "湖南振华电气设备公司", "INV-2026-0402", new BigDecimal("95600.00"),
|
||||
"2026-06-15", "已抵扣",
|
||||
"[CERT:已抵扣] INV-2026-0402 进项税额12428元已在4月增值税申报中抵扣" },
|
||||
|
||||
{ "AP-CERT-003", "长沙天宇科技有限公司", "INV-2026-0415", new BigDecimal("268000.00"),
|
||||
"2026-06-30", "已认证",
|
||||
"[CERT:已认证] INV-2026-0415 增值税专票,进项税额34840元已认证待抵扣" },
|
||||
|
||||
{ "AP-CERT-004", "岳阳联盛原材料公司", "INV-2026-0420", new BigDecimal("135000.00"),
|
||||
"2026-05-20", "未认证",
|
||||
"[CERT:未认证] INV-2026-0420 普通发票(非专票),不可抵扣进项税" },
|
||||
|
||||
{ "AP-CERT-005", "广州正泰电气集团", "INV-2026-0428", new BigDecimal("412000.00"),
|
||||
"2026-07-15", "未认证",
|
||||
"[CERT:未认证] INV-2026-0428 专票已收到,尚未提交税局认证(请务必在当月认证截止前提交)" },
|
||||
|
||||
{ "AP-CERT-006", "深圳鑫泰电子元器件", "INV-2026-0501", new BigDecimal("88500.00"),
|
||||
"2026-06-30", "已认证",
|
||||
"[CERT:已认证] INV-2026-0501 电子增值税专用发票,进项税额11505元已认证" },
|
||||
|
||||
{ "AP-CERT-007", "成都博远机电制造", "INV-2026-0505", new BigDecimal("326000.00"),
|
||||
"2026-07-31", "未认证",
|
||||
"[CERT:未认证] INV-2026-0505 金额较大,专票正在邮寄途中,待收票后认证" },
|
||||
|
||||
{ "AP-CERT-008", "北京华控仪器仪表", "INV-2026-0512", new BigDecimal("67800.00"),
|
||||
"2026-06-20", "已抵扣",
|
||||
"[CERT:已抵扣] INV-2026-0512 进项税额8814元已在5月增值税申报中抵扣" },
|
||||
|
||||
{ "AP-CERT-009", "济南钢铁集团贸易", "INV-2026-0515", new BigDecimal("520000.00"),
|
||||
"2026-08-15", "已认证",
|
||||
"[CERT:已认证] INV-2026-0515 大额原材料采购专票,进项税额67600元,已认证待下月抵扣" },
|
||||
|
||||
{ "AP-CERT-010", "上海绿洲环保科技", "INV-2026-0520", new BigDecimal("42000.00"),
|
||||
"2026-07-01", "未认证",
|
||||
"[CERT:未认证] INV-2026-0520 服务费增值税专票(6%税率),待认证" },
|
||||
|
||||
{ "AP-CERT-011", "天津港航物流集团", "INV-2026-0522", new BigDecimal("185000.00"),
|
||||
"2026-07-31", "已认证",
|
||||
"[CERT:已认证] INV-2026-0522 运输服务9%增值税,进项税额16606元已认证" },
|
||||
|
||||
{ "AP-CERT-012", "武汉宏达建设集团", "INV-2026-0525", new BigDecimal("1250000.00"),
|
||||
"2026-09-30", "未认证",
|
||||
"[CERT:未认证] INV-2026-0525 工程服务9%增值税,金额较大,需财务总监签批后认证" },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
ArApItem item = new ArApItem();
|
||||
item.setCode((String) row[0]);
|
||||
item.setArApType(ArApItem.T_AP);
|
||||
item.setPartyName((String) row[1]);
|
||||
item.setRelatedRef((String) row[2]);
|
||||
BigDecimal amt = (BigDecimal) row[3];
|
||||
// 已抵扣的标记为已结清(已核销),其余未核销
|
||||
String certStatus = (String) row[5];
|
||||
boolean settled = "已抵扣".equals(certStatus);
|
||||
item.setAmount(amt);
|
||||
item.setWrittenOff(settled ? amt : BigDecimal.ZERO);
|
||||
item.setUnwrittenOff(settled ? BigDecimal.ZERO : amt);
|
||||
item.setStatus(settled ? ArApItem.S_SETTLED : ArApItem.S_OPEN);
|
||||
item.setDueDate((String) row[4]);
|
||||
item.setRemark((String) row[6]);
|
||||
item.setCompanySubject("凯迪科技股份有限公司");
|
||||
item.setOwner("应付会计");
|
||||
item.setCreatedAt(Instant.now().minus(15 + (long)(Math.random() * 30), ChronoUnit.DAYS));
|
||||
arApRepo.save(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.FinAccountAux;
|
||||
import com.kaidi.oa.domain.FinBudgetExecution;
|
||||
import com.kaidi.oa.domain.FinBudgetPlan;
|
||||
import com.kaidi.oa.domain.FinCostCalc;
|
||||
import com.kaidi.oa.domain.FinVoucherTemplate;
|
||||
import com.kaidi.oa.repository.FinAccountAuxRepository;
|
||||
import com.kaidi.oa.repository.FinBudgetExecutionRepository;
|
||||
import com.kaidi.oa.repository.FinBudgetPlanRepository;
|
||||
import com.kaidi.oa.repository.FinCostCalcRepository;
|
||||
import com.kaidi.oa.repository.FinVoucherTemplateRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 财务部演示种子数据(缺口 #1/#8/#11 — 活体 0 条)。
|
||||
*
|
||||
* 补齐:
|
||||
* - FinAccountAux(辅助核算配置,缺口#1):6 条跨维度配置 + 余额
|
||||
* - FinVoucherTemplate(凭证模板,缺口#1):7 条常用模板(各类型各一条)
|
||||
* - FinCostCalc(成本核算记录,缺口#8):6 条跨方法/状态核算记录
|
||||
* - FinBudgetPlan(预算编制计划,缺口#11):5 条跨部门/类型预算申报
|
||||
* - FinBudgetExecution(预算执行,缺口#11):5 条已批准后生成的执行记录
|
||||
*
|
||||
* 幂等:各表 count > 0 即跳过。
|
||||
*
|
||||
* @Order(12) 在 DataSeeder(@Order 1) 之后运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(12)
|
||||
public class FinDeptSeeder implements ApplicationRunner {
|
||||
|
||||
private final FinAccountAuxRepository auxRepo;
|
||||
private final FinVoucherTemplateRepository templateRepo;
|
||||
private final FinCostCalcRepository costCalcRepo;
|
||||
private final FinBudgetPlanRepository budgetPlanRepo;
|
||||
private final FinBudgetExecutionRepository budgetExecRepo;
|
||||
|
||||
public FinDeptSeeder(FinAccountAuxRepository auxRepo,
|
||||
FinVoucherTemplateRepository templateRepo,
|
||||
FinCostCalcRepository costCalcRepo,
|
||||
FinBudgetPlanRepository budgetPlanRepo,
|
||||
FinBudgetExecutionRepository budgetExecRepo) {
|
||||
this.auxRepo = auxRepo;
|
||||
this.templateRepo = templateRepo;
|
||||
this.costCalcRepo = costCalcRepo;
|
||||
this.budgetPlanRepo = budgetPlanRepo;
|
||||
this.budgetExecRepo = budgetExecRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedAccountAux();
|
||||
seedVoucherTemplates();
|
||||
seedCostCalc();
|
||||
seedBudgetPlan();
|
||||
seedBudgetExecution();
|
||||
}
|
||||
|
||||
// ===================== 辅助核算配置 =====================
|
||||
private void seedAccountAux() {
|
||||
if (auxRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
{ "1122", "应收账款", "客户", "凯迪新能源科技有限公司", "2026-01", new BigDecimal("856300.00") },
|
||||
{ "1122", "应收账款", "客户", "汉阳市政建设集团", "2026-01", new BigDecimal("432100.00") },
|
||||
{ "6602", "管理费用", "部门", "研发部", "2026-01", new BigDecimal("123450.00") },
|
||||
{ "6602", "管理费用", "部门", "生产部", "2026-01", new BigDecimal("98760.00") },
|
||||
{ "6401", "主营业务成本", "项目", "KD-2025-001 污水处理一期", "2026-01", new BigDecimal("2156800.00") },
|
||||
{ "6401", "主营业务成本", "成本中心", "车间A-焊接工段", "2026-01", new BigDecimal("78900.00") },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinAccountAux a = new FinAccountAux();
|
||||
a.setAccountCode((String) row[0]);
|
||||
a.setAccountName((String) row[1]);
|
||||
a.setAuxDimension((String) row[2]);
|
||||
a.setAuxVal((String) row[3]);
|
||||
a.setEnabledVal(FinAccountAux.STATUS_ENABLED);
|
||||
a.setBalance((BigDecimal) row[5]);
|
||||
a.setApplicant("admin");
|
||||
a.setReason("初始化辅助核算配置");
|
||||
a.setCreatedAt(Instant.now().minus(30, ChronoUnit.DAYS));
|
||||
a.setUpdatedAt(Instant.now().minus(1, ChronoUnit.DAYS));
|
||||
auxRepo.save(a);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 凭证模板 =====================
|
||||
private void seedVoucherTemplates() {
|
||||
if (templateRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
{ "TMPL-001", "月度折旧计提", FinVoucherTemplate.TYPE_DEPREC, "6602", "1602", "计提${period}固定资产折旧", "折旧计算" },
|
||||
{ "TMPL-002", "无形资产摊销", FinVoucherTemplate.TYPE_AMORT, "6602", "1702", "摊销${period}无形资产", "摊销计算" },
|
||||
{ "TMPL-003", "月末结转损益", FinVoucherTemplate.TYPE_TRANSFER, "6001", "4103", "${period}结转主营业务收入至本年利润", "手填" },
|
||||
{ "TMPL-004", "结转研发费用", FinVoucherTemplate.TYPE_RD, "6602", "5301", "${period}研发费用化支出结转管理费", "手填" },
|
||||
{ "TMPL-005", "制造费用按工时分摊", FinVoucherTemplate.TYPE_MFG, "1321", "6601", "${period}制造费用分摊至在制品", "手填" },
|
||||
{ "TMPL-006", "期末汇率折算调整", FinVoucherTemplate.TYPE_FX, "6601", "2202", "${period}外币汇兑损益调整", "手填" },
|
||||
{ "TMPL-007", "备用金借款发放", FinVoucherTemplate.TYPE_MANUAL, "1001", "1002", "发放${period}员工备用金借款", "手填" },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinVoucherTemplate t = new FinVoucherTemplate();
|
||||
t.setCode((String) row[0]);
|
||||
t.setName((String) row[1]);
|
||||
t.setTemplateType((String) row[2]);
|
||||
t.setDebitAccount((String) row[3]);
|
||||
t.setCreditAccount((String) row[4]);
|
||||
t.setSummaryTemplate((String) row[5]);
|
||||
t.setAmountSource((String) row[6]);
|
||||
t.setEnabled(Boolean.TRUE);
|
||||
t.setCreatedBy("admin");
|
||||
t.setCreatedAt(Instant.now().minus(60, ChronoUnit.DAYS));
|
||||
templateRepo.save(t);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 成本核算记录 =====================
|
||||
private void seedCostCalc() {
|
||||
if (costCalcRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// code, method, object, objType, period, dm, dl, mo, driver, drvQty, rate, std, status
|
||||
{ "CC-2026-001", FinCostCalc.METHOD_VARIETY, "KD-A01 污水处理设备", "产品", "2026-01",
|
||||
new BigDecimal("158000.00"), new BigDecimal("43200.00"), new BigDecimal("22800.00"),
|
||||
"工时", 480.0, new BigDecimal("47.50"), new BigDecimal("210000.00"), FinCostCalc.STATUS_POSTED },
|
||||
{ "CC-2026-002", FinCostCalc.METHOD_BATCH, "KD-WO-2026-003 生产批次", "工单", "2026-01",
|
||||
new BigDecimal("89600.00"), new BigDecimal("18900.00"), new BigDecimal("11200.00"),
|
||||
"产量", 120.0, new BigDecimal("93.33"), new BigDecimal("115000.00"), FinCostCalc.STATUS_APPROVED },
|
||||
{ "CC-2026-003", FinCostCalc.METHOD_PROCESS, "污水处理剂-第一步骤", "产品", "2026-01",
|
||||
new BigDecimal("62300.00"), new BigDecimal("12100.00"), new BigDecimal("8600.00"),
|
||||
"工时", 200.0, new BigDecimal("43.00"), new BigDecimal("80000.00"), FinCostCalc.STATUS_APPROVED },
|
||||
{ "CC-2026-004", FinCostCalc.METHOD_ABC, "KD-PRJ-2025-012 污水项目", "项目", "2026-01",
|
||||
new BigDecimal("310500.00"), new BigDecimal("87600.00"), new BigDecimal("48300.00"),
|
||||
"工时", 960.0, new BigDecimal("50.31"), new BigDecimal("430000.00"), FinCostCalc.STATUS_POSTED },
|
||||
{ "CC-2026-005", FinCostCalc.METHOD_VARIETY, "KD-B02 曝气设备", "产品", "2025-12",
|
||||
new BigDecimal("241000.00"), new BigDecimal("55000.00"), new BigDecimal("31000.00"),
|
||||
"产量", 60.0, new BigDecimal("516.67"), new BigDecimal("320000.00"), FinCostCalc.STATUS_POSTED },
|
||||
{ "CC-2026-006", FinCostCalc.METHOD_BATCH, "KD-ORD-2026-008 订单成本","订单","2026-02",
|
||||
new BigDecimal("124800.00"), new BigDecimal("32000.00"), new BigDecimal("19200.00"),
|
||||
"收入", 0.0, new BigDecimal("0.00"), new BigDecimal("170000.00"), FinCostCalc.STATUS_DRAFT },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinCostCalc c = new FinCostCalc();
|
||||
c.setCode((String) row[0]);
|
||||
c.setCalcMethod((String) row[1]);
|
||||
c.setCostObject((String) row[2]);
|
||||
c.setCostObjectType((String) row[3]);
|
||||
c.setPeriod((String) row[4]);
|
||||
c.setDirectMaterial((BigDecimal) row[5]);
|
||||
c.setDirectLabor((BigDecimal) row[6]);
|
||||
c.setManufacturingOverhead((BigDecimal) row[7]);
|
||||
c.setAllocationDriver((String) row[8]);
|
||||
c.setDriverQuantity((Double) row[9]);
|
||||
c.setAllocationRate((BigDecimal) row[10]);
|
||||
BigDecimal total = ((BigDecimal) row[5]).add((BigDecimal) row[6]).add((BigDecimal) row[7]);
|
||||
c.setTotalCost(total);
|
||||
c.setStandardCost((BigDecimal) row[11]);
|
||||
c.setCostVariance(total.subtract((BigDecimal) row[11]));
|
||||
c.setStatus((String) row[12]);
|
||||
c.setOperator("admin");
|
||||
c.setCreatedAt(Instant.now().minus(20, ChronoUnit.DAYS));
|
||||
costCalcRepo.save(c);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 预算编制计划 =====================
|
||||
private void seedBudgetPlan() {
|
||||
if (budgetPlanRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
{ "BP-2026-001", "2026", 1, "研发部", "费用预算", "研发人员人工费用", new BigDecimal("2400000.00"), new BigDecimal("2200000.00"), FinBudgetPlan.S_APPROVED },
|
||||
{ "BP-2026-002", "2026", 1, "生产部", "成本预算", "直接材料采购成本", new BigDecimal("8600000.00"), new BigDecimal("8100000.00"), FinBudgetPlan.S_APPROVED },
|
||||
{ "BP-2026-003", "2026", 1, "行政部", "费用预算", "办公及行政费用", new BigDecimal("380000.00"), new BigDecimal("360000.00"), FinBudgetPlan.S_APPROVED },
|
||||
{ "BP-2026-004", "2026", 1, "销售部", "收入预算", "主营业务收入", new BigDecimal("42000000.00"),new BigDecimal("40000000.00"),FinBudgetPlan.S_APPROVED },
|
||||
{ "BP-2026-005", "2026", 1, "财务部", "资本预算", "固定资产购置(设备)",new BigDecimal("1500000.00"),new BigDecimal("1200000.00"), FinBudgetPlan.S_GATHERING },
|
||||
{ "BP-2026-006", "2026", 1, "工程部", "现金流预算", "项目建设投资现金流", new BigDecimal("12000000.00"),new BigDecimal("0.00"), FinBudgetPlan.S_SUBMITTED },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinBudgetPlan p = new FinBudgetPlan();
|
||||
p.setPlanCode((String) row[0]);
|
||||
p.setBudgetYear((String) row[1]);
|
||||
p.setRoundNo((Integer) row[2]);
|
||||
p.setDepartment((String) row[3]);
|
||||
p.setBudgetType((String) row[4]);
|
||||
p.setBudgetItem((String) row[5]);
|
||||
p.setProposedAmount((BigDecimal) row[6]);
|
||||
p.setApprovedAmount((BigDecimal) row[7]);
|
||||
p.setVarianceFromTop(((BigDecimal) row[6]).subtract((BigDecimal) row[7]));
|
||||
p.setStatus((String) row[8]);
|
||||
p.setSubmitter("部门负责人");
|
||||
p.setApprover(FinBudgetPlan.S_APPROVED.equals(row[8]) ? "财务总监" : null);
|
||||
p.setSubmitDate("2026-01-15");
|
||||
p.setApprovalDate(FinBudgetPlan.S_APPROVED.equals(row[8]) ? "2026-02-01" : null);
|
||||
p.setJustification("年度预算申报,依据上年实际执行情况及本年业务目标编制");
|
||||
p.setCreatedAt(Instant.now().minus(90, ChronoUnit.DAYS));
|
||||
p.setUpdatedAt(Instant.now().minus(60, ChronoUnit.DAYS));
|
||||
budgetPlanRepo.save(p);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 预算执行记录 =====================
|
||||
private void seedBudgetExecution() {
|
||||
if (budgetExecRepo.count() > 0) return;
|
||||
|
||||
Object[][] data = {
|
||||
// budgetItem, type, period, budgetAmt, actualAmt, dept
|
||||
{ "研发人员人工费用", "费用预算", "2026", new BigDecimal("2200000.00"), new BigDecimal("386500.00"), "研发部" },
|
||||
{ "直接材料采购成本", "成本预算", "2026", new BigDecimal("8100000.00"), new BigDecimal("1623000.00"), "生产部" },
|
||||
{ "办公及行政费用", "费用预算", "2026", new BigDecimal("360000.00"), new BigDecimal("62400.00"), "行政部" },
|
||||
{ "主营业务收入", "收入预算", "2026", new BigDecimal("40000000.00"),new BigDecimal("6830000.00"), "销售部" },
|
||||
{ "固定资产购置(设备)", "资本预算", "2026", new BigDecimal("1200000.00"), new BigDecimal("0.00"), "财务部" },
|
||||
};
|
||||
|
||||
for (Object[] row : data) {
|
||||
FinBudgetExecution e = new FinBudgetExecution();
|
||||
e.setBudgetItem((String) row[0]);
|
||||
e.setBudgetType((String) row[1]);
|
||||
e.setPeriod((String) row[2]);
|
||||
e.setBudgetAmount((BigDecimal) row[3]);
|
||||
e.setActualAmount((BigDecimal) row[4]);
|
||||
BigDecimal variance = ((BigDecimal) row[4]).subtract((BigDecimal) row[3]);
|
||||
e.setVarianceAmount(variance);
|
||||
e.setVarianceRate(((BigDecimal) row[3]).signum() > 0
|
||||
? ((BigDecimal) row[4]).divide((BigDecimal) row[3], 4, java.math.RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100)).setScale(2, java.math.RoundingMode.HALF_UP)
|
||||
: BigDecimal.ZERO);
|
||||
e.setOverBudget(variance.signum() > 0);
|
||||
e.setAlerted(variance.signum() > 0);
|
||||
e.setAlertThreshold(BigDecimal.TEN);
|
||||
e.setDepartment((String) row[5]);
|
||||
e.setBudgetVersion("批准版");
|
||||
e.setOperator("admin");
|
||||
budgetExecRepo.save(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,541 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.FinAccountAux;
|
||||
import com.kaidi.oa.domain.FinBudgetExecution;
|
||||
import com.kaidi.oa.domain.FinBudgetPlan;
|
||||
import com.kaidi.oa.domain.FinCostCalc;
|
||||
import com.kaidi.oa.domain.FinEmpLoan;
|
||||
import com.kaidi.oa.domain.FinVoucherTemplate;
|
||||
import com.kaidi.oa.repository.FinAccountAuxRepository;
|
||||
import com.kaidi.oa.repository.FinBudgetExecutionRepository;
|
||||
import com.kaidi.oa.repository.FinBudgetPlanRepository;
|
||||
import com.kaidi.oa.repository.FinCostCalcRepository;
|
||||
import com.kaidi.oa.repository.FinEmpLoanRepository;
|
||||
import com.kaidi.oa.repository.FinVoucherTemplateRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付中心/结算中心演示数据种子(W6 缺口补完)。
|
||||
*
|
||||
* 补全审计缺口:
|
||||
* [gap-9] 会计科目辅助核算配置活体为空(0条) → 补 FinAccountAux 演示数据(6维 × 5科目)。
|
||||
* [gap-10] 成本核算活体记录为空(0条) → 补 FinCostCalc 演示数据(多方法/多期间)。
|
||||
* [gap-11] 活体预算计划和执行记录均为0条 → 补 FinBudgetPlan + FinBudgetExecution。
|
||||
* [gap-voucher] 凭证模板活体为空(0条) → 补 FinVoucherTemplate 常用模板。
|
||||
* [gap-emp-loan] 员工借款缺演示数据 → 补 FinEmpLoan 多状态演示记录。
|
||||
*
|
||||
* 幂等:各表 count > 0 则跳过。
|
||||
*
|
||||
* @Order(20) 在 DataSeeder(@Order 1) 和 BranchFinDemoSeeder(@Order 15) 之后运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(20)
|
||||
public class FinPaymentDemoSeeder implements ApplicationRunner {
|
||||
|
||||
private final FinVoucherTemplateRepository voucherTemplateRepo;
|
||||
private final FinAccountAuxRepository accountAuxRepo;
|
||||
private final FinBudgetPlanRepository budgetPlanRepo;
|
||||
private final FinBudgetExecutionRepository budgetExecRepo;
|
||||
private final FinCostCalcRepository costCalcRepo;
|
||||
private final FinEmpLoanRepository empLoanRepo;
|
||||
|
||||
public FinPaymentDemoSeeder(FinVoucherTemplateRepository voucherTemplateRepo,
|
||||
FinAccountAuxRepository accountAuxRepo,
|
||||
FinBudgetPlanRepository budgetPlanRepo,
|
||||
FinBudgetExecutionRepository budgetExecRepo,
|
||||
FinCostCalcRepository costCalcRepo,
|
||||
FinEmpLoanRepository empLoanRepo) {
|
||||
this.voucherTemplateRepo = voucherTemplateRepo;
|
||||
this.accountAuxRepo = accountAuxRepo;
|
||||
this.budgetPlanRepo = budgetPlanRepo;
|
||||
this.budgetExecRepo = budgetExecRepo;
|
||||
this.costCalcRepo = costCalcRepo;
|
||||
this.empLoanRepo = empLoanRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedVoucherTemplates();
|
||||
seedAccountAux();
|
||||
seedBudgetPlans();
|
||||
seedBudgetExecutions();
|
||||
seedCostCalcRecords();
|
||||
seedEmpLoans();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 1. 凭证模板(gap-voucher: 0条)
|
||||
// ============================================================
|
||||
|
||||
private void seedVoucherTemplates() {
|
||||
if (voucherTemplateRepo.count() > 0) return;
|
||||
|
||||
List<FinVoucherTemplate> list = new ArrayList<>();
|
||||
|
||||
list.add(template("TMPL-001", "计提折旧-设备", FinVoucherTemplate.TYPE_DEPREC,
|
||||
"5011.001 制造费用-折旧", "1602 累计折旧",
|
||||
"${period}期计提固定资产折旧", "折旧计算", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-002", "计提折旧-房屋", FinVoucherTemplate.TYPE_DEPREC,
|
||||
"6602 管理费用-折旧", "1602 累计折旧",
|
||||
"${period}期计提房屋折旧", "折旧计算", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-003", "摊销无形资产", FinVoucherTemplate.TYPE_AMORT,
|
||||
"6602 管理费用-摊销", "1702 累计摊销",
|
||||
"${period}期摊销无形资产", "摊销计算", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-004", "结转损益", FinVoucherTemplate.TYPE_TRANSFER,
|
||||
"4103 本年利润", "5001 主营业务收入",
|
||||
"${period}期结转主营业务收入", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-005", "结转研发费用", FinVoucherTemplate.TYPE_RD,
|
||||
"1503 开发支出", "5011 研发费用",
|
||||
"${period}期研发费用资本化结转", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-006", "分摊制造费用-工时", FinVoucherTemplate.TYPE_MFG,
|
||||
"1405 库存商品-半成品", "5011 制造费用",
|
||||
"${period}期按工时分摊制造费用", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-007", "期末调汇-外币账户", FinVoucherTemplate.TYPE_FX,
|
||||
"1122 应收账款-外币", "6301 汇兑损益",
|
||||
"${period}期末外币调汇(应收账款)", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-008", "付款核销手工模板", FinVoucherTemplate.TYPE_MANUAL,
|
||||
"2202 应付账款", "1002 银行存款",
|
||||
"采购款付款:${amount}", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-009", "收款核销手工模板", FinVoucherTemplate.TYPE_MANUAL,
|
||||
"1002 银行存款", "1122 应收账款",
|
||||
"销售回款:${amount}", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
list.add(template("TMPL-010", "计提工资-生产", FinVoucherTemplate.TYPE_MANUAL,
|
||||
"5011.002 制造费用-人工", "2211 应付职工薪酬",
|
||||
"${period}期计提生产人员工资", "手填", Boolean.TRUE, "admin"));
|
||||
|
||||
voucherTemplateRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinVoucherTemplate template(String code, String name, String type,
|
||||
String debit, String credit, String summary,
|
||||
String amountSource, Boolean enabled, String createdBy) {
|
||||
FinVoucherTemplate t = new FinVoucherTemplate();
|
||||
t.setCode(code);
|
||||
t.setName(name);
|
||||
t.setTemplateType(type);
|
||||
t.setDebitAccount(debit);
|
||||
t.setCreditAccount(credit);
|
||||
t.setSummaryTemplate(summary);
|
||||
t.setAmountSource(amountSource);
|
||||
t.setEnabled(enabled);
|
||||
t.setCreatedBy(createdBy);
|
||||
t.setCreatedAt(Instant.now());
|
||||
return t;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. 会计科目辅助核算配置(gap-9: 0条)
|
||||
// ============================================================
|
||||
|
||||
private void seedAccountAux() {
|
||||
if (accountAuxRepo.count() > 0) return;
|
||||
|
||||
List<FinAccountAux> list = new ArrayList<>();
|
||||
|
||||
// 1122 应收账款 - 客户维度
|
||||
list.add(aux("1122", "应收账款", "客户", "岳阳城陵矶市政开发公司",
|
||||
"启用", "财务部", "admin", "按客户追踪应收账款余额"));
|
||||
list.add(aux("1122", "应收账款", "客户", "湖南省水务建设集团",
|
||||
"启用", "财务部", "admin", "按客户追踪应收账款余额"));
|
||||
list.add(aux("1122", "应收账款", "客户", "岳阳市凯利环保科技",
|
||||
"启用", "财务部", "admin", "按客户追踪应收账款余额"));
|
||||
|
||||
// 2202 应付账款 - 供应商维度
|
||||
list.add(aux("2202", "应付账款", "供应商", "长沙美迪机械有限公司",
|
||||
"启用", "财务部", "admin", "按供应商追踪应付账款余额"));
|
||||
list.add(aux("2202", "应付账款", "供应商", "岳阳德信建材供应商",
|
||||
"启用", "财务部", "admin", "按供应商追踪应付账款余额"));
|
||||
|
||||
// 5011 制造费用 - 部门维度
|
||||
list.add(aux("5011", "制造费用", "部门", "环保设备制造中心",
|
||||
"启用", "财务部", "admin", "按部门归集制造费用"));
|
||||
list.add(aux("5011", "制造费用", "部门", "生物质肥料制造中心",
|
||||
"启用", "财务部", "admin", "按部门归集制造费用"));
|
||||
|
||||
// 6601 销售费用 - 项目维度
|
||||
list.add(aux("6601", "销售费用", "项目", "P-YY-2025-001岳阳污水厂",
|
||||
"启用", "财务部", "admin", "按项目核算销售费用"));
|
||||
list.add(aux("6601", "销售费用", "项目", "P-HN-2025-003湖南改造项目",
|
||||
"启用", "财务部", "admin", "按项目核算销售费用"));
|
||||
|
||||
// 6602 管理费用 - 成本中心维度
|
||||
list.add(aux("6602", "管理费用", "成本中心", "总部行政成本中心",
|
||||
"启用", "财务部", "admin", "按成本中心分配管理费用"));
|
||||
list.add(aux("6602", "管理费用", "成本中心", "研发成本中心",
|
||||
"启用", "财务部", "admin", "研发费用归集到成本中心"));
|
||||
|
||||
// 1405 库存商品 - 产品维度
|
||||
list.add(aux("1405", "库存商品", "产品", "过滤膜组件-A型",
|
||||
"启用", "财务部", "admin", "按产品追踪库存余额"));
|
||||
list.add(aux("1405", "库存商品", "产品", "污水处理药剂-PAC",
|
||||
"启用", "财务部", "admin", "按产品追踪库存余额"));
|
||||
|
||||
accountAuxRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinAccountAux aux(String accountCode, String accountName, String auxDimension,
|
||||
String auxVal, String enabledVal, String applicant,
|
||||
String approver, String reason) {
|
||||
FinAccountAux a = new FinAccountAux();
|
||||
a.setAccountCode(accountCode);
|
||||
a.setAccountName(accountName);
|
||||
a.setAuxDimension(auxDimension);
|
||||
a.setAuxVal(auxVal);
|
||||
a.setEnabledVal(enabledVal);
|
||||
a.setBalance(BigDecimal.ZERO);
|
||||
a.setApplicant(applicant);
|
||||
a.setApprover(approver);
|
||||
a.setReason(reason);
|
||||
a.setCreatedAt(Instant.now());
|
||||
a.setUpdatedAt(Instant.now());
|
||||
return a;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. 预算编制计划(gap-11: 0条)
|
||||
// ============================================================
|
||||
|
||||
private void seedBudgetPlans() {
|
||||
if (budgetPlanRepo.count() > 0) return;
|
||||
|
||||
List<FinBudgetPlan> list = new ArrayList<>();
|
||||
|
||||
// 2025年度预算 - 各部门申报
|
||||
list.add(plan("BP2025-001", "2025", 1, "环保设备制造中心", "成本预算",
|
||||
"直接材料采购成本", new BigDecimal("12000000"),
|
||||
new BigDecimal("11500000"), FinBudgetPlan.S_APPROVED,
|
||||
"材料价格上涨,申报略高", "核减至市场均价水平", "张三", "李四",
|
||||
"2025-01-10", "2025-01-25"));
|
||||
|
||||
list.add(plan("BP2025-002", "2025", 1, "环保设备制造中心", "费用预算",
|
||||
"制造费用(水电人工)", new BigDecimal("3200000"),
|
||||
new BigDecimal("3100000"), FinBudgetPlan.S_APPROVED,
|
||||
"含新增设备维护费用", "适当核减维护费", "张三", "李四",
|
||||
"2025-01-10", "2025-01-25"));
|
||||
|
||||
list.add(plan("BP2025-003", "2025", 1, "研发中心", "费用预算",
|
||||
"研发人员薪酬", new BigDecimal("5600000"),
|
||||
new BigDecimal("5600000"), FinBudgetPlan.S_APPROVED,
|
||||
"含3名新招研发人员", "按申报批准", "王五", "李四",
|
||||
"2025-01-12", "2025-01-25"));
|
||||
|
||||
list.add(plan("BP2025-004", "2025", 1, "工程管理中心", "收入预算",
|
||||
"主营业务收入-EPC工程", new BigDecimal("85000000"),
|
||||
new BigDecimal("82000000"), FinBudgetPlan.S_APPROVED,
|
||||
"基于在手合同及预期新签合同", "保守预估,略有核减", "赵六", "李四",
|
||||
"2025-01-11", "2025-01-25"));
|
||||
|
||||
list.add(plan("BP2025-005", "2025", 1, "财务部", "现金流预算",
|
||||
"年度净现金流(经营活动)", new BigDecimal("15000000"),
|
||||
new BigDecimal("14000000"), FinBudgetPlan.S_APPROVED,
|
||||
"基于合同回款预期", "适当调低", "钱七", "李四",
|
||||
"2025-01-13", "2025-01-25"));
|
||||
|
||||
list.add(plan("BP2025-006", "2025", 2, "工程管理中心", "资本预算",
|
||||
"固定资产投资(办公设备)", new BigDecimal("800000"),
|
||||
new BigDecimal("650000"), FinBudgetPlan.S_APPROVED,
|
||||
"含2台大型绘图仪采购", "核减1台", "赵六", "李四",
|
||||
"2025-01-15", "2025-01-28"));
|
||||
|
||||
// 2026年度预算(汇总中状态)
|
||||
list.add(plan("BP2026-001", "2026", 1, "环保设备制造中心", "成本预算",
|
||||
"直接材料采购成本", new BigDecimal("13500000"),
|
||||
BigDecimal.ZERO, FinBudgetPlan.S_GATHERING,
|
||||
"预计材料价格持平或略降,增量来自产能扩张", null, "张三", null,
|
||||
"2026-01-08", null));
|
||||
|
||||
list.add(plan("BP2026-002", "2026", 1, "研发中心", "费用预算",
|
||||
"研发费用总额(含人工+耗材)", new BigDecimal("7200000"),
|
||||
BigDecimal.ZERO, FinBudgetPlan.S_SUBMITTED,
|
||||
"含2项新立项研发课题预算", null, "王五", null,
|
||||
"2026-01-09", null));
|
||||
|
||||
budgetPlanRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinBudgetPlan plan(String planCode, String year, int roundNo, String dept,
|
||||
String type, String item, BigDecimal proposed, BigDecimal approved,
|
||||
String status, String justification, String approvalComment,
|
||||
String submitter, String approver, String submitDate, String approvalDate) {
|
||||
FinBudgetPlan p = new FinBudgetPlan();
|
||||
p.setPlanCode(planCode);
|
||||
p.setBudgetYear(year);
|
||||
p.setRoundNo(roundNo);
|
||||
p.setDepartment(dept);
|
||||
p.setBudgetType(type);
|
||||
p.setBudgetItem(item);
|
||||
p.setProposedAmount(proposed);
|
||||
p.setApprovedAmount(approved);
|
||||
p.setVarianceFromTop(proposed.subtract(approved));
|
||||
p.setStatus(status);
|
||||
p.setJustification(justification);
|
||||
p.setApprovalComment(approvalComment);
|
||||
p.setSubmitter(submitter);
|
||||
p.setApprover(approver);
|
||||
p.setSubmitDate(submitDate);
|
||||
p.setApprovalDate(approvalDate);
|
||||
p.setCreatedAt(Instant.now());
|
||||
p.setUpdatedAt(Instant.now());
|
||||
return p;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 4. 预算执行记录(gap-11: 0条)
|
||||
// ============================================================
|
||||
|
||||
private void seedBudgetExecutions() {
|
||||
if (budgetExecRepo.count() > 0) return;
|
||||
|
||||
List<FinBudgetExecution> list = new ArrayList<>();
|
||||
|
||||
// 2025年各月度执行记录
|
||||
list.add(exec("直接材料采购成本", FinBudgetExecution.TYPE_COST, "2025-01",
|
||||
new BigDecimal("1000000"), new BigDecimal("980000"), "环保设备制造中心", "P-2025-MFG", "已批准", Boolean.FALSE));
|
||||
list.add(exec("直接材料采购成本", FinBudgetExecution.TYPE_COST, "2025-02",
|
||||
new BigDecimal("1000000"), new BigDecimal("1120000"), "环保设备制造中心", "P-2025-MFG", "已批准", Boolean.TRUE));
|
||||
list.add(exec("直接材料采购成本", FinBudgetExecution.TYPE_COST, "2025-03",
|
||||
new BigDecimal("1000000"), new BigDecimal("950000"), "环保设备制造中心", "P-2025-MFG", "已批准", Boolean.FALSE));
|
||||
list.add(exec("研发人员薪酬", FinBudgetExecution.TYPE_EXPENSE, "2025-01",
|
||||
new BigDecimal("466667"), new BigDecimal("466667"), "研发中心", null, "已批准", Boolean.FALSE));
|
||||
list.add(exec("研发人员薪酬", FinBudgetExecution.TYPE_EXPENSE, "2025-02",
|
||||
new BigDecimal("466667"), new BigDecimal("466667"), "研发中心", null, "已批准", Boolean.FALSE));
|
||||
list.add(exec("研发人员薪酬", FinBudgetExecution.TYPE_EXPENSE, "2025-03",
|
||||
new BigDecimal("466667"), new BigDecimal("510000"), "研发中心", null, "已批准", Boolean.TRUE));
|
||||
list.add(exec("主营业务收入-EPC工程", FinBudgetExecution.TYPE_INCOME, "2025-Q1",
|
||||
new BigDecimal("20500000"), new BigDecimal("18200000"), "工程管理中心", null, "已批准", Boolean.FALSE));
|
||||
list.add(exec("主营业务收入-EPC工程", FinBudgetExecution.TYPE_INCOME, "2025-Q2",
|
||||
new BigDecimal("20500000"), new BigDecimal("22800000"), "工程管理中心", null, "已批准", Boolean.FALSE));
|
||||
list.add(exec("年度净现金流(经营活动)", FinBudgetExecution.TYPE_CASHFLOW, "2025",
|
||||
new BigDecimal("14000000"), new BigDecimal("12300000"), "财务部", null, "初稿", Boolean.FALSE));
|
||||
list.add(exec("固定资产投资(办公设备)", FinBudgetExecution.TYPE_CAPITAL, "2025",
|
||||
new BigDecimal("650000"), new BigDecimal("580000"), "工程管理中心", null, "已批准", Boolean.FALSE));
|
||||
|
||||
budgetExecRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinBudgetExecution exec(String budgetItem, String budgetType, String period,
|
||||
BigDecimal budgetAmount, BigDecimal actualAmount,
|
||||
String department, String projectCode, String version,
|
||||
Boolean overBudget) {
|
||||
FinBudgetExecution e = new FinBudgetExecution();
|
||||
e.setBudgetItem(budgetItem);
|
||||
e.setBudgetType(budgetType);
|
||||
e.setPeriod(period);
|
||||
e.setBudgetAmount(budgetAmount);
|
||||
e.setActualAmount(actualAmount);
|
||||
BigDecimal variance = actualAmount.subtract(budgetAmount);
|
||||
e.setVarianceAmount(variance);
|
||||
if (budgetAmount.signum() != 0) {
|
||||
e.setVarianceRate(variance.multiply(new BigDecimal("100"))
|
||||
.divide(budgetAmount, 2, java.math.RoundingMode.HALF_UP));
|
||||
}
|
||||
e.setOverBudget(overBudget);
|
||||
e.setAlerted(overBudget);
|
||||
e.setAlertThreshold(new BigDecimal("10"));
|
||||
e.setDepartment(department);
|
||||
e.setProjectCode(projectCode);
|
||||
e.setCompanySubject("湖南凯迪工程科技有限公司");
|
||||
e.setBudgetVersion(version);
|
||||
e.setOperator("admin");
|
||||
e.setCreatedAt(Instant.now());
|
||||
e.setUpdatedAt(Instant.now());
|
||||
return e;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 5. 成本核算记录(gap-10: 0条)
|
||||
// ============================================================
|
||||
|
||||
private void seedCostCalcRecords() {
|
||||
if (costCalcRepo.count() > 0) return;
|
||||
|
||||
List<FinCostCalc> list = new ArrayList<>();
|
||||
|
||||
// 品种法 - 膜组件产品月度成本核算
|
||||
list.add(costCalc("CC-2025-001", FinCostCalc.METHOD_VARIETY,
|
||||
"过滤膜组件-A型", "产品", "2025-01",
|
||||
new BigDecimal("520000"), new BigDecimal("85000"), new BigDecimal("68000"),
|
||||
"工时", 4200.0, new BigDecimal("16.19"),
|
||||
new BigDecimal("673000"), new BigDecimal("650000"), FinCostCalc.STATUS_POSTED, "admin"));
|
||||
|
||||
list.add(costCalc("CC-2025-002", FinCostCalc.METHOD_VARIETY,
|
||||
"过滤膜组件-A型", "产品", "2025-02",
|
||||
new BigDecimal("495000"), new BigDecimal("85000"), new BigDecimal("62000"),
|
||||
"工时", 4000.0, new BigDecimal("15.50"),
|
||||
new BigDecimal("642000"), new BigDecimal("650000"), FinCostCalc.STATUS_POSTED, "admin"));
|
||||
|
||||
list.add(costCalc("CC-2025-003", FinCostCalc.METHOD_VARIETY,
|
||||
"污水处理药剂-PAC", "产品", "2025-01",
|
||||
new BigDecimal("280000"), new BigDecimal("32000"), new BigDecimal("25000"),
|
||||
"产量", 5000.0, new BigDecimal("5.00"),
|
||||
new BigDecimal("337000"), new BigDecimal("330000"), FinCostCalc.STATUS_POSTED, "admin"));
|
||||
|
||||
// 分批法 - 工程项目成本归集
|
||||
list.add(costCalc("CC-2025-004", FinCostCalc.METHOD_BATCH,
|
||||
"P-YY-2025-001岳阳污水厂", "项目", "2025-Q1",
|
||||
new BigDecimal("3200000"), new BigDecimal("680000"), new BigDecimal("420000"),
|
||||
"收入", 18200000.0, new BigDecimal("0.023"),
|
||||
new BigDecimal("4300000"), new BigDecimal("4200000"), FinCostCalc.STATUS_APPROVED, "张三"));
|
||||
|
||||
list.add(costCalc("CC-2025-005", FinCostCalc.METHOD_BATCH,
|
||||
"P-HN-2025-003湖南改造项目", "项目", "2025-Q1",
|
||||
new BigDecimal("1800000"), new BigDecimal("380000"), new BigDecimal("210000"),
|
||||
"收入", 9500000.0, new BigDecimal("0.022"),
|
||||
new BigDecimal("2390000"), new BigDecimal("2350000"), FinCostCalc.STATUS_APPROVED, "赵六"));
|
||||
|
||||
// 分步法 - 生产工单成本
|
||||
list.add(costCalc("CC-2025-006", FinCostCalc.METHOD_PROCESS,
|
||||
"WO-2025-003膜组件装配", "工单", "2025-02",
|
||||
new BigDecimal("120000"), new BigDecimal("28000"), new BigDecimal("15000"),
|
||||
"工时", 850.0, new BigDecimal("17.65"),
|
||||
new BigDecimal("163000"), new BigDecimal("160000"), FinCostCalc.STATUS_POSTED, "admin"));
|
||||
|
||||
// 作业成本法 - 研发项目
|
||||
list.add(costCalc("CC-2025-007", FinCostCalc.METHOD_ABC,
|
||||
"RD-2025-002新型膜研发", "项目", "2025-Q1",
|
||||
new BigDecimal("680000"), new BigDecimal("520000"), new BigDecimal("85000"),
|
||||
"人数", 8.0, new BigDecimal("10625"),
|
||||
new BigDecimal("1285000"), new BigDecimal("1300000"), FinCostCalc.STATUS_DRAFT, "王五"));
|
||||
|
||||
costCalcRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinCostCalc costCalc(String code, String calcMethod, String costObject, String costObjectType,
|
||||
String period, BigDecimal directMaterial, BigDecimal directLabor,
|
||||
BigDecimal manufacturingOverhead, String allocationDriver,
|
||||
Double driverQuantity, BigDecimal allocationRate,
|
||||
BigDecimal totalCost, BigDecimal standardCost,
|
||||
String status, String operator) {
|
||||
FinCostCalc c = new FinCostCalc();
|
||||
c.setCode(code);
|
||||
c.setCalcMethod(calcMethod);
|
||||
c.setCostObject(costObject);
|
||||
c.setCostObjectType(costObjectType);
|
||||
c.setPeriod(period);
|
||||
c.setDirectMaterial(directMaterial);
|
||||
c.setDirectLabor(directLabor);
|
||||
c.setManufacturingOverhead(manufacturingOverhead);
|
||||
c.setAllocationDriver(allocationDriver);
|
||||
c.setDriverQuantity(driverQuantity);
|
||||
c.setAllocationRate(allocationRate);
|
||||
c.setTotalCost(totalCost);
|
||||
c.setStandardCost(standardCost);
|
||||
c.setCostVariance(totalCost.subtract(standardCost));
|
||||
c.setStatus(status);
|
||||
c.setOperator(operator);
|
||||
c.setCreatedAt(Instant.now());
|
||||
return c;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 6. 员工借款演示数据(多状态覆盖)
|
||||
// ============================================================
|
||||
|
||||
private void seedEmpLoans() {
|
||||
if (empLoanRepo.count() > 0) return;
|
||||
|
||||
List<FinEmpLoan> list = new ArrayList<>();
|
||||
|
||||
// 已批准 - 差旅借款
|
||||
list.add(empLoan("EL-202501", "张工", "E001", "工程管理中心",
|
||||
FinEmpLoan.TYPE_TRAVEL, "岳阳项目现场调研差旅费",
|
||||
new BigDecimal("8000"), BigDecimal.ZERO, new BigDecimal("8000"),
|
||||
"2025-01-05", "2025-02-05", null,
|
||||
"2025-01-07", "张总", "同意", null, null,
|
||||
FinEmpLoan.S_APPROVED, Boolean.FALSE));
|
||||
|
||||
// 还款中 - 备用金借款
|
||||
list.add(empLoan("EL-202502", "李会计", "E002", "财务部",
|
||||
FinEmpLoan.TYPE_PETTY, "2025年Q1采购零星备用金",
|
||||
new BigDecimal("5000"), new BigDecimal("2000"), new BigDecimal("3000"),
|
||||
"2025-01-10", "2025-04-10", null,
|
||||
"2025-01-12", "财务总监", "同意", null, "EXP-2025-012",
|
||||
FinEmpLoan.S_REPAYING, Boolean.FALSE));
|
||||
|
||||
// 已结清 - 差旅借款
|
||||
list.add(empLoan("EL-202503", "王测试", "E003", "研发中心",
|
||||
FinEmpLoan.TYPE_TRAVEL, "上海环博会参展差旅",
|
||||
new BigDecimal("12000"), new BigDecimal("12000"), BigDecimal.ZERO,
|
||||
"2025-02-01", "2025-03-01", "2025-02-28",
|
||||
"2025-02-03", "研发总监", "同意", "2025-02-28", "EXP-2025-023",
|
||||
FinEmpLoan.S_SETTLED, Boolean.FALSE));
|
||||
|
||||
// 逾期 - 其他借款(已过期未还)
|
||||
list.add(empLoan("EL-202504", "赵工", "E004", "环保设备制造中心",
|
||||
FinEmpLoan.TYPE_OTHER, "设备维修紧急备用金",
|
||||
new BigDecimal("3000"), BigDecimal.ZERO, new BigDecimal("3000"),
|
||||
"2025-03-15", "2025-04-15", null,
|
||||
"2025-03-17", "生产总监", "同意", null, null,
|
||||
FinEmpLoan.S_OVERDUE, Boolean.FALSE));
|
||||
|
||||
// 待审批 - 备用金借款
|
||||
list.add(empLoan("EL-202505", "孙经理", "E005", "市场部",
|
||||
FinEmpLoan.TYPE_PETTY, "华南区市场拓展备用金",
|
||||
new BigDecimal("15000"), BigDecimal.ZERO, new BigDecimal("15000"),
|
||||
"2025-05-20", "2025-08-20", null,
|
||||
null, null, null, null, null,
|
||||
FinEmpLoan.S_PENDING, Boolean.FALSE));
|
||||
|
||||
// 草稿 - 差旅借款
|
||||
list.add(empLoan("EL-202506", "周工", "E006", "工程管理中心",
|
||||
FinEmpLoan.TYPE_TRAVEL, "武汉项目踏勘差旅",
|
||||
new BigDecimal("6500"), BigDecimal.ZERO, new BigDecimal("6500"),
|
||||
"2025-06-01", "2025-07-01", null,
|
||||
null, null, null, null, null,
|
||||
FinEmpLoan.S_DRAFT, Boolean.FALSE));
|
||||
|
||||
empLoanRepo.saveAll(list);
|
||||
}
|
||||
|
||||
private FinEmpLoan empLoan(String loanCode, String employeeName, String employeeId,
|
||||
String department, String loanType, String purpose,
|
||||
BigDecimal loanAmount, BigDecimal repaidAmount, BigDecimal remainAmount,
|
||||
String applyDate, String expectedRepayDate, String settleDate,
|
||||
String approvalDate, String approver, String approvalComment,
|
||||
String settleDate2, String relatedExpenseCode,
|
||||
String status, Boolean deductFromSalary) {
|
||||
FinEmpLoan l = new FinEmpLoan();
|
||||
l.setLoanCode(loanCode);
|
||||
l.setEmployeeName(employeeName);
|
||||
l.setEmployeeId(employeeId);
|
||||
l.setDepartment(department);
|
||||
l.setLoanType(loanType);
|
||||
l.setPurpose(purpose);
|
||||
l.setLoanAmount(loanAmount);
|
||||
l.setRepaidAmount(repaidAmount);
|
||||
l.setRemainAmount(remainAmount);
|
||||
l.setApplyDate(applyDate);
|
||||
l.setExpectedRepayDate(expectedRepayDate);
|
||||
l.setSettleDate(settleDate);
|
||||
l.setApprovalDate(approvalDate);
|
||||
l.setApprover(approver);
|
||||
l.setApprovalComment(approvalComment);
|
||||
l.setRelatedExpenseCode(relatedExpenseCode);
|
||||
l.setStatus(status);
|
||||
l.setDeductFromSalary(deductFromSalary);
|
||||
if (FinEmpLoan.S_OVERDUE.equals(status)) {
|
||||
l.setOverdueDays(61); // 逾期超过60天
|
||||
}
|
||||
l.setOperator("admin");
|
||||
l.setCreatedAt(Instant.now());
|
||||
l.setUpdatedAt(Instant.now());
|
||||
return l;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.Financing;
|
||||
import com.kaidi.oa.domain.FinancingPolicy;
|
||||
import com.kaidi.oa.domain.PmtBankEvaluation;
|
||||
import com.kaidi.oa.domain.PmtBankRelation;
|
||||
import com.kaidi.oa.domain.RepaymentPlan;
|
||||
import com.kaidi.oa.repository.FinancingPolicyRepository;
|
||||
import com.kaidi.oa.repository.FinancingRepository;
|
||||
import com.kaidi.oa.repository.PmtBankEvaluationRepository;
|
||||
import com.kaidi.oa.repository.PmtBankRelationRepository;
|
||||
import com.kaidi.oa.repository.RepaymentPlanRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 融资域演示数据(金融办)。
|
||||
* 补建 Gap-1/2/5 的活体演示数据:金融机构档案、融资台账、机构定期评价、融资政策规则、还款计划。
|
||||
* 只在对应表为空时执行,防止重复。@Order(20) 在 DataSeeder(@Order(1)) 之后运行。
|
||||
*/
|
||||
@Order(20)
|
||||
@Component
|
||||
public class FinancingDomainSeeder implements CommandLineRunner {
|
||||
|
||||
private final PmtBankRelationRepository bankRelRepo;
|
||||
private final FinancingRepository financingRepo;
|
||||
private final RepaymentPlanRepository planRepo;
|
||||
private final PmtBankEvaluationRepository evalRepo;
|
||||
private final FinancingPolicyRepository policyRepo;
|
||||
|
||||
public FinancingDomainSeeder(PmtBankRelationRepository bankRelRepo,
|
||||
FinancingRepository financingRepo,
|
||||
RepaymentPlanRepository planRepo,
|
||||
PmtBankEvaluationRepository evalRepo,
|
||||
FinancingPolicyRepository policyRepo) {
|
||||
this.bankRelRepo = bankRelRepo;
|
||||
this.financingRepo = financingRepo;
|
||||
this.planRepo = planRepo;
|
||||
this.evalRepo = evalRepo;
|
||||
this.policyRepo = policyRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
seedBankRelations();
|
||||
seedFinancings();
|
||||
seedPolicies();
|
||||
seedEvaluations();
|
||||
}
|
||||
|
||||
private void seedBankRelations() {
|
||||
if (bankRelRepo.count() > 0) return;
|
||||
|
||||
PmtBankRelation r1 = new PmtBankRelation();
|
||||
r1.setInstitutionName("工商银行长沙分行");
|
||||
r1.setInstitutionType("商业银行");
|
||||
r1.setBranch("长沙分行公司业务部");
|
||||
r1.setAccountManager("张磊");
|
||||
r1.setManagerPhone("0731-88882001");
|
||||
r1.setTotalCreditLimit(Money.of(200000000.0));
|
||||
r1.setCooperationStartDate("2019-03-01");
|
||||
r1.setRating("A");
|
||||
r1.setAvgApprovalDays(7);
|
||||
r1.setRemark("主要合作银行,综合授信2亿,流贷+固贷均有合作历史。");
|
||||
r1.setStatus("合作中");
|
||||
r1.setCreatedBy("admin");
|
||||
r1.setCreatedAt(Instant.now());
|
||||
bankRelRepo.save(r1);
|
||||
|
||||
PmtBankRelation r2 = new PmtBankRelation();
|
||||
r2.setInstitutionName("建设银行株洲支行");
|
||||
r2.setInstitutionType("商业银行");
|
||||
r2.setBranch("株洲高新区支行");
|
||||
r2.setAccountManager("李梅");
|
||||
r2.setManagerPhone("0733-77773001");
|
||||
r2.setTotalCreditLimit(Money.of(100000000.0));
|
||||
r2.setCooperationStartDate("2020-06-15");
|
||||
r2.setRating("B");
|
||||
r2.setAvgApprovalDays(10);
|
||||
r2.setRemark("次要银行,主要为子公司株洲项目配套贷款。");
|
||||
r2.setStatus("合作中");
|
||||
r2.setCreatedBy("admin");
|
||||
r2.setCreatedAt(Instant.now());
|
||||
bankRelRepo.save(r2);
|
||||
|
||||
PmtBankRelation r3 = new PmtBankRelation();
|
||||
r3.setInstitutionName("国家开发银行湖南省分行");
|
||||
r3.setInstitutionType("政策性银行");
|
||||
r3.setBranch("湖南省分行基础设施部");
|
||||
r3.setAccountManager("王强");
|
||||
r3.setManagerPhone("0731-66661001");
|
||||
r3.setTotalCreditLimit(Money.of(500000000.0));
|
||||
r3.setCooperationStartDate("2021-01-20");
|
||||
r3.setRating("A");
|
||||
r3.setAvgApprovalDays(15);
|
||||
r3.setRemark("政策性长期资金来源,主要用于环保固定资产投资项目贷款,利率优惠。");
|
||||
r3.setStatus("合作中");
|
||||
r3.setCreatedBy("admin");
|
||||
r3.setCreatedAt(Instant.now());
|
||||
bankRelRepo.save(r3);
|
||||
|
||||
PmtBankRelation r4 = new PmtBankRelation();
|
||||
r4.setInstitutionName("中信银行长沙分行");
|
||||
r4.setInstitutionType("商业银行");
|
||||
r4.setBranch("长沙分行企业金融部");
|
||||
r4.setAccountManager("陈晓");
|
||||
r4.setManagerPhone("0731-55550801");
|
||||
r4.setTotalCreditLimit(Money.of(80000000.0));
|
||||
r4.setCooperationStartDate("2022-09-01");
|
||||
r4.setRating("B");
|
||||
r4.setAvgApprovalDays(8);
|
||||
r4.setRemark("近两年新增合作银行,流动资金贷款为主,银行承兑汇票业务活跃。");
|
||||
r4.setStatus("合作中");
|
||||
r4.setCreatedBy("admin");
|
||||
r4.setCreatedAt(Instant.now());
|
||||
bankRelRepo.save(r4);
|
||||
}
|
||||
|
||||
private void seedFinancings() {
|
||||
if (financingRepo.count() > 0) return;
|
||||
|
||||
Financing f1 = new Financing();
|
||||
f1.setCode("RZ-2024-001");
|
||||
f1.setLender("工商银行长沙分行");
|
||||
f1.setFinancingType("银行贷款");
|
||||
f1.setCreditLimit(Money.of(50000000.0));
|
||||
f1.setAmount(Money.of(50000000.0));
|
||||
f1.setCurrency("人民币");
|
||||
f1.setRate(3.85);
|
||||
f1.setStartDate("2024-01-15");
|
||||
f1.setEndDate("2026-01-14");
|
||||
f1.setTermMonths(24);
|
||||
f1.setRepayMethod("按期付息到期还本");
|
||||
f1.setStatus("还款中");
|
||||
f1.setCompanySubject("凯迪科技股份有限公司");
|
||||
f1.setPurpose("补充流动资金");
|
||||
f1.setOwner("admin");
|
||||
f1.setCreatedAt(Instant.now());
|
||||
Financing sf1 = financingRepo.save(f1);
|
||||
|
||||
// 为 f1 生成还款计划(按期付息到期还本,24期,月息)
|
||||
seedRepaymentPlan(sf1, 24, 3.85, "按期付息到期还本", "2024-01-15");
|
||||
|
||||
Financing f2 = new Financing();
|
||||
f2.setCode("RZ-2024-002");
|
||||
f2.setLender("国家开发银行湖南省分行");
|
||||
f2.setFinancingType("银行贷款");
|
||||
f2.setCreditLimit(Money.of(100000000.0));
|
||||
f2.setAmount(Money.of(80000000.0));
|
||||
f2.setCurrency("人民币");
|
||||
f2.setRate(3.20);
|
||||
f2.setStartDate("2024-03-01");
|
||||
f2.setEndDate("2029-02-28");
|
||||
f2.setTermMonths(60);
|
||||
f2.setRepayMethod("等额本金");
|
||||
f2.setStatus("还款中");
|
||||
f2.setCompanySubject("凯迪科技股份有限公司");
|
||||
f2.setPurpose("污水处理厂固定资产投资");
|
||||
f2.setOwner("admin");
|
||||
f2.setCreatedAt(Instant.now());
|
||||
Financing sf2 = financingRepo.save(f2);
|
||||
seedRepaymentPlan(sf2, 60, 3.20, "等额本金", "2024-03-01");
|
||||
|
||||
Financing f3 = new Financing();
|
||||
f3.setCode("RZ-2024-003");
|
||||
f3.setLender("建设银行株洲支行");
|
||||
f3.setFinancingType("银行贷款");
|
||||
f3.setCreditLimit(Money.of(20000000.0));
|
||||
f3.setAmount(Money.of(18000000.0));
|
||||
f3.setCurrency("人民币");
|
||||
f3.setRate(4.35);
|
||||
f3.setStartDate("2024-06-01");
|
||||
f3.setEndDate("2025-05-31");
|
||||
f3.setTermMonths(12);
|
||||
f3.setRepayMethod("到期一次还本付息");
|
||||
f3.setStatus("还款中");
|
||||
f3.setCompanySubject("株洲凯迪环保科技有限公司");
|
||||
f3.setPurpose("补充流动资金");
|
||||
f3.setOwner("admin");
|
||||
f3.setCreatedAt(Instant.now());
|
||||
Financing sf3 = financingRepo.save(f3);
|
||||
seedRepaymentPlan(sf3, 12, 4.35, "到期一次还本付息", "2024-06-01");
|
||||
|
||||
Financing f4 = new Financing();
|
||||
f4.setCode("RZ-2025-001");
|
||||
f4.setLender("中信银行长沙分行");
|
||||
f4.setFinancingType("银行贷款");
|
||||
f4.setCreditLimit(Money.of(30000000.0));
|
||||
f4.setAmount(Money.of(30000000.0));
|
||||
f4.setCurrency("人民币");
|
||||
f4.setRate(4.10);
|
||||
f4.setStartDate("2025-02-01");
|
||||
f4.setEndDate("2026-01-31");
|
||||
f4.setTermMonths(12);
|
||||
f4.setRepayMethod("等额本息");
|
||||
f4.setStatus("还款中");
|
||||
f4.setCompanySubject("凯迪科技股份有限公司");
|
||||
f4.setPurpose("采购原材料及备品备件");
|
||||
f4.setOwner("admin");
|
||||
f4.setCreatedAt(Instant.now());
|
||||
Financing sf4 = financingRepo.save(f4);
|
||||
seedRepaymentPlan(sf4, 12, 4.10, "等额本息", "2025-02-01");
|
||||
|
||||
// 一笔申请中
|
||||
Financing f5 = new Financing();
|
||||
f5.setCode("RZ-2025-002");
|
||||
f5.setLender("工商银行长沙分行");
|
||||
f5.setFinancingType("银行承兑汇票");
|
||||
f5.setCreditLimit(Money.of(20000000.0));
|
||||
f5.setAmount(Money.of(20000000.0));
|
||||
f5.setCurrency("人民币");
|
||||
f5.setRate(2.50);
|
||||
f5.setStartDate("2025-07-01");
|
||||
f5.setEndDate("2026-06-30");
|
||||
f5.setTermMonths(12);
|
||||
f5.setRepayMethod("到期一次还本付息");
|
||||
f5.setStatus("申请中");
|
||||
f5.setCompanySubject("凯迪科技股份有限公司");
|
||||
f5.setPurpose("支付设备采购款(银承)");
|
||||
f5.setOwner("admin");
|
||||
f5.setCreatedAt(Instant.now());
|
||||
financingRepo.save(f5);
|
||||
|
||||
// 一笔已结清
|
||||
Financing f6 = new Financing();
|
||||
f6.setCode("RZ-2023-003");
|
||||
f6.setLender("建设银行株洲支行");
|
||||
f6.setFinancingType("银行贷款");
|
||||
f6.setCreditLimit(Money.of(10000000.0));
|
||||
f6.setAmount(Money.of(10000000.0));
|
||||
f6.setCurrency("人民币");
|
||||
f6.setRate(4.60);
|
||||
f6.setStartDate("2023-01-01");
|
||||
f6.setEndDate("2024-12-31");
|
||||
f6.setTermMonths(24);
|
||||
f6.setRepayMethod("等额本息");
|
||||
f6.setStatus("已结清");
|
||||
f6.setCompanySubject("株洲凯迪环保科技有限公司");
|
||||
f6.setPurpose("经营周转资金");
|
||||
f6.setOwner("admin");
|
||||
f6.setCreatedAt(Instant.now());
|
||||
financingRepo.save(f6);
|
||||
}
|
||||
|
||||
private void seedRepaymentPlan(Financing f, int term, double annualRate, String method, String startDate) {
|
||||
if (planRepo.findByFinancingIdOrderByPeriodNoAsc(f.getId()).size() > 0) return;
|
||||
double monthlyRate = annualRate / 100.0 / 12.0;
|
||||
double principal = Money.nz(f.getAmount()).doubleValue();
|
||||
java.time.LocalDate start;
|
||||
try {
|
||||
start = java.time.LocalDate.parse(startDate);
|
||||
} catch (Exception e) {
|
||||
start = java.time.LocalDate.now();
|
||||
}
|
||||
|
||||
if ("到期一次还本付息".equals(method)) {
|
||||
double interest = principal * monthlyRate * term;
|
||||
RepaymentPlan p = buildPlan(f, 1, start.plusMonths(term).toString(),
|
||||
new BigDecimal(principal).setScale(2, java.math.RoundingMode.HALF_UP),
|
||||
Money.of(interest), "未还");
|
||||
planRepo.save(p);
|
||||
return;
|
||||
}
|
||||
if ("按期付息到期还本".equals(method)) {
|
||||
BigDecimal totalPrincipal = Money.nz(f.getAmount());
|
||||
for (int i = 1; i <= term; i++) {
|
||||
double interest = principal * monthlyRate;
|
||||
BigDecimal prin = (i == term) ? totalPrincipal : BigDecimal.ZERO;
|
||||
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
|
||||
Money.nz(prin), Money.of(interest), "未还");
|
||||
planRepo.save(p);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ("等额本金".equals(method)) {
|
||||
BigDecimal totalPrincipal = Money.nz(f.getAmount());
|
||||
BigDecimal perPrincipal = totalPrincipal.divide(BigDecimal.valueOf(term), 2, java.math.RoundingMode.HALF_UP);
|
||||
double balance = principal;
|
||||
BigDecimal acc = BigDecimal.ZERO;
|
||||
for (int i = 1; i <= term; i++) {
|
||||
double interest = balance * monthlyRate;
|
||||
BigDecimal prin = (i == term) ? Money.sub(totalPrincipal, acc) : perPrincipal;
|
||||
acc = Money.add(acc, prin);
|
||||
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
|
||||
Money.nz(prin), Money.of(interest), "未还");
|
||||
planRepo.save(p);
|
||||
balance -= prin.doubleValue();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 等额本息
|
||||
BigDecimal totalPrincipal = Money.nz(f.getAmount());
|
||||
double installment;
|
||||
if (monthlyRate == 0) {
|
||||
installment = principal / term;
|
||||
} else {
|
||||
double pow = Math.pow(1 + monthlyRate, term);
|
||||
installment = principal * monthlyRate * pow / (pow - 1);
|
||||
}
|
||||
double balance = principal;
|
||||
BigDecimal acc = BigDecimal.ZERO;
|
||||
for (int i = 1; i <= term; i++) {
|
||||
double interest = balance * monthlyRate;
|
||||
double prinD = installment - interest;
|
||||
BigDecimal prin = (i == term) ? Money.sub(totalPrincipal, acc) : Money.of(prinD);
|
||||
acc = Money.add(acc, prin);
|
||||
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
|
||||
Money.nz(prin), Money.of(interest), "未还");
|
||||
planRepo.save(p);
|
||||
balance -= prin.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
private RepaymentPlan buildPlan(Financing f, int no, String dueDate,
|
||||
BigDecimal principal, BigDecimal interest, String status) {
|
||||
RepaymentPlan p = new RepaymentPlan();
|
||||
p.setFinancingId(f.getId());
|
||||
p.setFinancingCode(f.getCode());
|
||||
p.setPeriodNo(no);
|
||||
p.setDueDate(dueDate);
|
||||
p.setPrincipal(Money.nz(principal));
|
||||
p.setInterest(Money.nz(interest));
|
||||
p.setStatus(status);
|
||||
p.setCreatedAt(Instant.now());
|
||||
return p;
|
||||
}
|
||||
|
||||
private void seedPolicies() {
|
||||
if (policyRepo.count() > 0) return;
|
||||
|
||||
FinancingPolicy p1 = new FinancingPolicy();
|
||||
p1.setRuleName("单笔流动资金贷款额度上限");
|
||||
p1.setRuleType("额度上限");
|
||||
p1.setLimitValue(Money.of(100000000.0));
|
||||
p1.setScope("全集团");
|
||||
p1.setHardBlock(true);
|
||||
p1.setDescription("集团融资政策:单笔流动资金贷款不超过 1 亿元,超限须报董事会审批。");
|
||||
p1.setStatus("启用");
|
||||
p1.setCreatedBy("admin");
|
||||
p1.setCreatedAt(Instant.now());
|
||||
policyRepo.save(p1);
|
||||
|
||||
FinancingPolicy p2 = new FinancingPolicy();
|
||||
p2.setRuleName("融资综合利率上限");
|
||||
p2.setRuleType("利率上限");
|
||||
p2.setLimitValue(Money.of(6.0));
|
||||
p2.setScope("全集团");
|
||||
p2.setHardBlock(false);
|
||||
p2.setDescription("集团融资政策:融资年化利率超过 6% 视为高成本融资,需在申请中说明替代方案比选结果,硬拒以警告为主不阻断。");
|
||||
p2.setStatus("启用");
|
||||
p2.setCreatedBy("admin");
|
||||
p2.setCreatedAt(Instant.now());
|
||||
policyRepo.save(p2);
|
||||
|
||||
FinancingPolicy p3 = new FinancingPolicy();
|
||||
p3.setRuleName("流动资金贷款期限上限");
|
||||
p3.setRuleType("期限上限");
|
||||
p3.setLimitValue(Money.of(12.0));
|
||||
p3.setScope("流动资金");
|
||||
p3.setHardBlock(true);
|
||||
p3.setDescription("流动资金贷款期限原则上不超过 12 个月(1年),超限需升级审批至财务总监。");
|
||||
p3.setStatus("启用");
|
||||
p3.setCreatedBy("admin");
|
||||
p3.setCreatedAt(Instant.now());
|
||||
policyRepo.save(p3);
|
||||
|
||||
FinancingPolicy p4 = new FinancingPolicy();
|
||||
p4.setRuleName("允许融资品种限制");
|
||||
p4.setRuleType("品种限制");
|
||||
p4.setAllowedValues("银行贷款,融资租赁,银行承兑汇票,信用证");
|
||||
p4.setScope("全集团");
|
||||
p4.setHardBlock(true);
|
||||
p4.setDescription("集团融资政策:仅允许银行贷款、融资租赁、银行承兑汇票、信用证四种融资品种,其他品种须专项论证并报批。");
|
||||
p4.setStatus("启用");
|
||||
p4.setCreatedBy("admin");
|
||||
p4.setCreatedAt(Instant.now());
|
||||
policyRepo.save(p4);
|
||||
|
||||
FinancingPolicy p5 = new FinancingPolicy();
|
||||
p5.setRuleName("固定资产贷款期限上限");
|
||||
p5.setRuleType("期限上限");
|
||||
p5.setLimitValue(Money.of(120.0));
|
||||
p5.setScope("固定资产投资");
|
||||
p5.setHardBlock(true);
|
||||
p5.setDescription("固定资产贷款期限不超过 10 年(120 个月),与项目建设及投资回收期匹配。");
|
||||
p5.setStatus("启用");
|
||||
p5.setCreatedBy("admin");
|
||||
p5.setCreatedAt(Instant.now());
|
||||
policyRepo.save(p5);
|
||||
}
|
||||
|
||||
private void seedEvaluations() {
|
||||
if (evalRepo.count() > 0) return;
|
||||
// 需要 bankRelRepo 已有数据
|
||||
java.util.List<PmtBankRelation> rels = bankRelRepo.findAll();
|
||||
if (rels.isEmpty()) return;
|
||||
|
||||
Instant now = Instant.now();
|
||||
for (PmtBankRelation rel : rels) {
|
||||
// 每个机构建 2 条历史评价(2024Q4 和 2025Q2)
|
||||
PmtBankEvaluation ev1 = new PmtBankEvaluation();
|
||||
ev1.setBankRelId(rel.getId());
|
||||
ev1.setInstitutionName(rel.getInstitutionName());
|
||||
ev1.setEvalPeriod("2024Q4");
|
||||
ev1.setEvalDate("2024-12-31");
|
||||
int s1a = baseScore(rel.getRating()) - 5;
|
||||
int s1b = baseScore(rel.getRating());
|
||||
int s1c = baseScore(rel.getRating()) + 3;
|
||||
int s1d = baseScore(rel.getRating()) - 2;
|
||||
ev1.setScoreApprovalSpeed(clamp(s1a));
|
||||
ev1.setScoreServiceQuality(clamp(s1b));
|
||||
ev1.setScoreRateCompetitiveness(clamp(s1c));
|
||||
ev1.setScoreCooperation(clamp(s1d));
|
||||
int total1 = (clamp(s1a) + clamp(s1b) + clamp(s1c) + clamp(s1d)) / 4;
|
||||
ev1.setTotalScore(total1);
|
||||
ev1.setGrade(grade(total1));
|
||||
ev1.setRemark("2024年四季度综合评价,审批效率和服务质量符合预期。");
|
||||
ev1.setEvaluator("admin");
|
||||
ev1.setStatus("已完成");
|
||||
ev1.setCreatedAt(now);
|
||||
evalRepo.save(ev1);
|
||||
|
||||
PmtBankEvaluation ev2 = new PmtBankEvaluation();
|
||||
ev2.setBankRelId(rel.getId());
|
||||
ev2.setInstitutionName(rel.getInstitutionName());
|
||||
ev2.setEvalPeriod("2025Q2");
|
||||
ev2.setEvalDate("2025-06-30");
|
||||
int s2a = baseScore(rel.getRating()) + 2;
|
||||
int s2b = baseScore(rel.getRating()) + 1;
|
||||
int s2c = baseScore(rel.getRating());
|
||||
int s2d = baseScore(rel.getRating()) + 4;
|
||||
ev2.setScoreApprovalSpeed(clamp(s2a));
|
||||
ev2.setScoreServiceQuality(clamp(s2b));
|
||||
ev2.setScoreRateCompetitiveness(clamp(s2c));
|
||||
ev2.setScoreCooperation(clamp(s2d));
|
||||
int total2 = (clamp(s2a) + clamp(s2b) + clamp(s2c) + clamp(s2d)) / 4;
|
||||
ev2.setTotalScore(total2);
|
||||
ev2.setGrade(grade(total2));
|
||||
ev2.setRemark("2025年二季度综合评价,各维度较上期有所提升。");
|
||||
ev2.setEvaluator("admin");
|
||||
ev2.setStatus("已完成");
|
||||
ev2.setCreatedAt(now);
|
||||
evalRepo.save(ev2);
|
||||
}
|
||||
}
|
||||
|
||||
private static int baseScore(String rating) {
|
||||
if ("A".equals(rating)) return 85;
|
||||
if ("B".equals(rating)) return 70;
|
||||
if ("C".equals(rating)) return 55;
|
||||
return 40;
|
||||
}
|
||||
|
||||
private static int clamp(int v) {
|
||||
return Math.max(0, Math.min(100, v));
|
||||
}
|
||||
|
||||
private static String grade(int total) {
|
||||
if (total >= 80) return "A";
|
||||
if (total >= 60) return "B";
|
||||
if (total >= 40) return "C";
|
||||
return "D";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.service.FullTextSearchService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 启动期重建全文索引。{@code @Order(200)} 保证排在所有 DataSeeder
|
||||
* (@Order 1/2/100)之后执行,这样种子数据已经落库,索引才完整。
|
||||
*
|
||||
* 索引失败已在 {@link FullTextSearchService#reindexAll()} 内被 try/catch
|
||||
* 兜住,这里再包一层,绝不让检索问题阻断应用启动。
|
||||
*/
|
||||
@Component
|
||||
@Order(200)
|
||||
public class FtsIndexRunner implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FtsIndexRunner.class);
|
||||
|
||||
private final FullTextSearchService searchService;
|
||||
|
||||
public FtsIndexRunner(FullTextSearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
try {
|
||||
searchService.reindexAll();
|
||||
} catch (Exception e) {
|
||||
log.warn("[FTS] 启动期索引重建异常(已忽略,不影响启动): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.HrLaborTemplate;
|
||||
import com.kaidi.oa.repository.HrLaborTemplateRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 行政·综合部——劳动合同专属模板演示种子数据(Module 17 缺口补全)。
|
||||
*
|
||||
* 补全缺口:「劳动合同专属模板管理缺失」——表 hr_labor_template 无种子,
|
||||
* 功能在但首屏为空,hr-labor-template.vue 页面无演示内容。
|
||||
*
|
||||
* 幂等(count > 0 跳过);@Order(226) 在 AdminQualElecSeeder(225) 之后运行。
|
||||
*/
|
||||
@Component
|
||||
@Order(226)
|
||||
public class HrLaborTemplateSeeder implements CommandLineRunner {
|
||||
|
||||
private final HrLaborTemplateRepository repo;
|
||||
|
||||
public HrLaborTemplateSeeder(HrLaborTemplateRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (repo.count() > 0) return;
|
||||
|
||||
Instant now = Instant.now();
|
||||
LocalDate today = LocalDate.now();
|
||||
String reviewDate = today.minusDays(30).toString();
|
||||
|
||||
// 1. 标准固定期限劳动合同——已启用
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版固定期限劳动合同", "固定期限劳动合同", "正式员工", "V2025.1",
|
||||
buildFixedTermBody(),
|
||||
"试用期条款,薪资保密条款,知识产权归属条款,竞业限制条款",
|
||||
"陈法务", reviewDate, "已审核,符合《劳动合同法》要求,建议启用",
|
||||
"HR部", "已启用", "HR总监", now));
|
||||
|
||||
// 2. 无固定期限劳动合同——已启用
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版无固定期限劳动合同", "无固定期限劳动合同", "正式员工", "V2025.1",
|
||||
buildOpenTermBody(),
|
||||
"薪资保密条款,知识产权归属条款,竞业限制条款,保密条款",
|
||||
"陈法务", reviewDate, "无固定期限合同,已确认满足签订条件,建议启用",
|
||||
"HR部", "已启用", "HR总监", now));
|
||||
|
||||
// 3. 劳务协议——已启用
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版劳务协议", "劳务协议", "劳务人员", "V2025.1",
|
||||
buildLaborServiceBody(),
|
||||
"劳务费结算条款,工作成果归属条款,保密条款",
|
||||
"陈法务", today.minusDays(15).toString(), "劳务协议,非劳动关系,已审核",
|
||||
"HR部", "已启用", "HR总监", now));
|
||||
|
||||
// 4. 实习协议——已启用
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版实习协议", "实习协议", "实习生", "V2025.1",
|
||||
buildInternBody(),
|
||||
"实习补贴条款,实习成果归属,转正考核条款",
|
||||
"陈法务", today.minusDays(10).toString(), "实习协议,已审核通过",
|
||||
"HR部", "已启用", "行政专员", now));
|
||||
|
||||
// 5. 竞业保密协议——法务审核中
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版竞业保密协议(技术岗)", "竞业保密协议", "正式员工", "V2025.1",
|
||||
buildNonCompeteBody(),
|
||||
"竞业限制范围,保密期限,违约金条款",
|
||||
null, null, null,
|
||||
"HR部", "法务审核中", "HR专员", now));
|
||||
|
||||
// 6. 以完成工作任务为期限合同——草稿
|
||||
repo.save(tpl(
|
||||
"凯迪科技2025版项目制劳动合同(草稿)", "以完成一定工作任务为期限", "正式员工", "V1.0",
|
||||
"{员工姓名}(身份证号:{身份证号})与凯迪科技有限公司签订以完成{项目名称}为期限的劳动合同。\n" +
|
||||
"预计工期:{预计完成日期}。\n" +
|
||||
"薪资待遇:{月薪} 元/月。\n" +
|
||||
"项目提前或延期完成,合同相应变更。",
|
||||
"项目范围条款,工期变更条款",
|
||||
null, null, null,
|
||||
"HR部", "草稿", "HR专员", now));
|
||||
}
|
||||
|
||||
private static HrLaborTemplate tpl(String name, String templateType, String applicableTo,
|
||||
String version, String bodyTemplate, String requiredClauses,
|
||||
String legalReviewedBy, String legalReviewDate, String legalReviewComment,
|
||||
String createdBy, String status, String updatedBy, Instant now) {
|
||||
HrLaborTemplate t = new HrLaborTemplate();
|
||||
t.setName(name);
|
||||
t.setTemplateType(templateType);
|
||||
t.setApplicableTo(applicableTo);
|
||||
t.setVersion(version);
|
||||
t.setBodyTemplate(bodyTemplate);
|
||||
t.setRequiredClauses(requiredClauses);
|
||||
t.setLegalReviewedBy(legalReviewedBy);
|
||||
t.setLegalReviewDate(legalReviewDate);
|
||||
t.setLegalReviewComment(legalReviewComment);
|
||||
t.setCreatedBy(createdBy);
|
||||
t.setStatus(status);
|
||||
t.setUpdatedBy(updatedBy);
|
||||
t.setCreatedAt(now);
|
||||
t.setUpdatedAt(now);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static String buildFixedTermBody() {
|
||||
return "劳动合同书\n\n" +
|
||||
"甲方(用人单位):凯迪科技有限公司\n" +
|
||||
"乙方(劳动者):{员工姓名}\n" +
|
||||
"身份证号码:{身份证号}\n\n" +
|
||||
"根据《中华人民共和国劳动合同法》等有关法律法规,甲乙双方经协商一致,订立本合同。\n\n" +
|
||||
"第一条 合同期限\n" +
|
||||
"本合同为固定期限劳动合同,合同期限自 {起始日期} 起至 {终止日期} 止," +
|
||||
"其中试用期自 {起始日期} 起至 {试用期到期日} 止。\n\n" +
|
||||
"第二条 工作内容和工作地点\n" +
|
||||
"乙方同意在 {工作地点} 担任 {岗位} 职务,按时完成甲方分配的工作任务。\n\n" +
|
||||
"第三条 劳动报酬\n" +
|
||||
"甲方每月向乙方支付税前工资 {月薪} 元,按照法律规定代扣个人所得税、社会保险费。\n\n" +
|
||||
"第四条 社会保险\n" +
|
||||
"甲乙双方依法参加社会保险,各自承担相应的缴费义务。\n\n" +
|
||||
"第五条 保密义务\n" +
|
||||
"乙方对甲方的商业秘密、技术秘密及客户信息负有保密义务,保密期限为合同存续期间及离职后 {保密年限} 年。\n\n" +
|
||||
"第六条 知识产权\n" +
|
||||
"乙方在职期间完成的职务作品、发明创造,知识产权归甲方所有。\n\n" +
|
||||
"签订日期:{签署日期}\n" +
|
||||
"甲方签字(盖章):___________ 乙方签字:{员工姓名}";
|
||||
}
|
||||
|
||||
private static String buildOpenTermBody() {
|
||||
return "无固定期限劳动合同书\n\n" +
|
||||
"甲方(用人单位):凯迪科技有限公司\n" +
|
||||
"乙方(劳动者):{员工姓名}\n" +
|
||||
"身份证号码:{身份证号}\n\n" +
|
||||
"甲乙双方经协商一致,根据《中华人民共和国劳动合同法》订立本无固定期限劳动合同。\n\n" +
|
||||
"第一条 合同期限\n" +
|
||||
"本合同为无固定期限劳动合同,自 {起始日期} 起生效,无终止日期," +
|
||||
"符合法定条件时依法解除或终止。\n\n" +
|
||||
"第二条 工作岗位\n" +
|
||||
"乙方担任 {岗位} 职务,工作地点为 {工作地点}。\n\n" +
|
||||
"第三条 薪酬\n" +
|
||||
"月薪 {月薪} 元,绩效奖金另行约定。\n\n" +
|
||||
"第四条 其他条款\n" +
|
||||
"其余条款参照公司《员工手册》及相关制度执行。\n\n" +
|
||||
"签订日期:{签署日期}\n" +
|
||||
"甲方(盖章):___________ 乙方:{员工姓名}";
|
||||
}
|
||||
|
||||
private static String buildLaborServiceBody() {
|
||||
return "劳务协议书\n\n" +
|
||||
"委托方(甲方):凯迪科技有限公司\n" +
|
||||
"提供方(乙方):{乙方姓名}\n" +
|
||||
"身份证号:{身份证号}\n\n" +
|
||||
"甲乙双方就劳务事项达成如下协议:\n\n" +
|
||||
"第一条 服务内容\n" +
|
||||
"乙方为甲方提供 {服务内容} 相关劳务服务,服务期限自 {起始日期} 至 {结束日期}。\n\n" +
|
||||
"第二条 劳务报酬\n" +
|
||||
"甲方按 {结算方式} 向乙方支付劳务费,金额为 {劳务费用} 元,每 {结算周期} 结算一次。\n\n" +
|
||||
"第三条 工作成果\n" +
|
||||
"乙方在本协议期间产生的工作成果归甲方所有,乙方不得留存副本或对外披露。\n\n" +
|
||||
"第四条 保密\n" +
|
||||
"乙方对甲方商业秘密负有保密义务。\n\n" +
|
||||
"第五条 双方关系\n" +
|
||||
"本协议为劳务关系,非劳动关系,乙方社会保险自行缴纳。\n\n" +
|
||||
"签订日期:{签署日期}";
|
||||
}
|
||||
|
||||
private static String buildInternBody() {
|
||||
return "实习协议书\n\n" +
|
||||
"甲方:凯迪科技有限公司\n" +
|
||||
"乙方(实习生):{实习生姓名}\n" +
|
||||
"学校:{就读学校} 专业:{专业}\n\n" +
|
||||
"甲乙双方就实习事项协商一致,签订本协议:\n\n" +
|
||||
"第一条 实习岗位与期限\n" +
|
||||
"乙方于 {实习开始日期} 至 {实习结束日期} 在甲方 {实习岗位} 岗位实习。\n\n" +
|
||||
"第二条 实习补贴\n" +
|
||||
"甲方每月向乙方支付实习补贴 {实习补贴} 元,不含社保。\n\n" +
|
||||
"第三条 实习成果\n" +
|
||||
"实习期间产生的成果知识产权归甲方所有。\n\n" +
|
||||
"第四条 转正考核\n" +
|
||||
"实习结束后,甲方视乙方表现决定是否启动转正流程。转正月薪:{转正薪资} 元。\n\n" +
|
||||
"第五条 保密\n" +
|
||||
"乙方不得对外披露甲方商业信息。\n\n" +
|
||||
"签订日期:{签署日期}";
|
||||
}
|
||||
|
||||
private static String buildNonCompeteBody() {
|
||||
return "竞业保密协议\n\n" +
|
||||
"甲方:凯迪科技有限公司\n" +
|
||||
"乙方:{员工姓名}\n" +
|
||||
"岗位:{岗位}\n\n" +
|
||||
"甲乙双方就竞业限制及保密事项协议如下:\n\n" +
|
||||
"第一条 保密范围\n" +
|
||||
"乙方对在职期间接触到的技术秘密、客户名单、经营策略等保密信息负有保密义务," +
|
||||
"不得向任何第三方披露。保密期限:合同期间及离职后 {保密年限} 年。\n\n" +
|
||||
"第二条 竞业限制\n" +
|
||||
"乙方离职后 {竞业限制期} 个月内,不得在竞争企业任职或自营同类业务。\n" +
|
||||
"竞争企业范围:{竞争企业范围}。\n\n" +
|
||||
"第三条 竞业补偿\n" +
|
||||
"甲方在竞业限制期内按乙方离职前月均工资的 {竞业补偿比例} 支付竞业限制补偿金。\n\n" +
|
||||
"第四条 违约责任\n" +
|
||||
"乙方违反本协议,须向甲方支付违约金 {违约金额} 元,并赔偿甲方损失。\n\n" +
|
||||
"签订日期:{签署日期}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.HrAiDocDraft;
|
||||
import com.kaidi.oa.domain.HrInterviewQuestion;
|
||||
import com.kaidi.oa.domain.HrRecruitCandidate;
|
||||
import com.kaidi.oa.domain.HrRecruitJob;
|
||||
import com.kaidi.oa.repository.HrAiDocDraftRepository;
|
||||
import com.kaidi.oa.repository.HrInterviewQuestionRepository;
|
||||
import com.kaidi.oa.repository.HrRecruitCandidateRepository;
|
||||
import com.kaidi.oa.repository.HrRecruitJobRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 行政·综合部——招聘与人才管理 + AI文档初稿 演示种子。
|
||||
*
|
||||
* 幂等:各表已有数据则跳过。
|
||||
* 覆盖:
|
||||
* - HrRecruitJob 3 条岗位(招聘中/面试中/已录用)
|
||||
* - HrRecruitCandidate 6 条候选人(多状态,含 AI 评分)
|
||||
* - HrInterviewQuestion 5 条面试题(AI 生成标记)
|
||||
* - HrAiDocDraft 4 条 AI 生成文档(入职资料/岗位说明/员工手册/转正考核,多状态)
|
||||
*/
|
||||
@Component
|
||||
@Order(203)
|
||||
public class HrRecruitSeeder implements CommandLineRunner {
|
||||
|
||||
private final HrRecruitJobRepository jobRepo;
|
||||
private final HrRecruitCandidateRepository candidateRepo;
|
||||
private final HrInterviewQuestionRepository questionRepo;
|
||||
private final HrAiDocDraftRepository aiDocRepo;
|
||||
|
||||
public HrRecruitSeeder(HrRecruitJobRepository jobRepo,
|
||||
HrRecruitCandidateRepository candidateRepo,
|
||||
HrInterviewQuestionRepository questionRepo,
|
||||
HrAiDocDraftRepository aiDocRepo) {
|
||||
this.jobRepo = jobRepo;
|
||||
this.candidateRepo = candidateRepo;
|
||||
this.questionRepo = questionRepo;
|
||||
this.aiDocRepo = aiDocRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
seedJobs();
|
||||
seedAiDocs();
|
||||
}
|
||||
|
||||
// ============ 岗位 + 候选人 ============
|
||||
|
||||
private void seedJobs() {
|
||||
if (jobRepo.count() > 0) return;
|
||||
|
||||
// Job 1:高级工程师(招聘中)
|
||||
HrRecruitJob j1 = new HrRecruitJob();
|
||||
j1.setPosition("高级结构工程师");
|
||||
j1.setDept("工程技术部");
|
||||
j1.setHeadcount(2);
|
||||
j1.setChannel("猎聘");
|
||||
j1.setRequirement("注册结构工程师,BIM,超高层,地下工程,CAD,10年以上");
|
||||
j1.setSalaryRange("25k-40k");
|
||||
j1.setTargetDate(LocalDate.now().plusMonths(1).toString());
|
||||
j1.setStatus("招聘中");
|
||||
j1.setHrOwner("王芳");
|
||||
j1.setRemark("紧急招募,优先有超高层经验");
|
||||
j1.setCreatedAt(Instant.now().minusSeconds(86400 * 20));
|
||||
j1.setUpdatedAt(Instant.now().minusSeconds(86400 * 5));
|
||||
j1 = jobRepo.save(j1);
|
||||
|
||||
// Job 2:项目经理(面试中)
|
||||
HrRecruitJob j2 = new HrRecruitJob();
|
||||
j2.setPosition("项目经理(EPC)");
|
||||
j2.setDept("项目管理部");
|
||||
j2.setHeadcount(1);
|
||||
j2.setChannel("BOSS直聘");
|
||||
j2.setRequirement("一级建造师,EPC总承包,项目全周期管理,甲方协调,8年以上");
|
||||
j2.setSalaryRange("30k-45k");
|
||||
j2.setTargetDate(LocalDate.now().plusWeeks(3).toString());
|
||||
j2.setStatus("面试中");
|
||||
j2.setHrOwner("李娜");
|
||||
j2.setRemark("要求有大型市政工程经验");
|
||||
j2.setCreatedAt(Instant.now().minusSeconds(86400 * 30));
|
||||
j2.setUpdatedAt(Instant.now().minusSeconds(86400 * 3));
|
||||
j2 = jobRepo.save(j2);
|
||||
|
||||
// Job 3:财务主管(已录用)
|
||||
HrRecruitJob j3 = new HrRecruitJob();
|
||||
j3.setPosition("财务主管");
|
||||
j3.setDept("财务中心");
|
||||
j3.setHeadcount(1);
|
||||
j3.setChannel("内部推荐");
|
||||
j3.setRequirement("注册会计师,ERP系统,财务分析,税务筹划,5年以上");
|
||||
j3.setSalaryRange("18k-25k");
|
||||
j3.setTargetDate(LocalDate.now().minusWeeks(1).toString());
|
||||
j3.setStatus("已录用");
|
||||
j3.setHrOwner("王芳");
|
||||
j3.setRemark("已完成录用,等待入职");
|
||||
j3.setCreatedAt(Instant.now().minusSeconds(86400 * 45));
|
||||
j3.setUpdatedAt(Instant.now().minusSeconds(86400 * 7));
|
||||
j3 = jobRepo.save(j3);
|
||||
|
||||
// 候选人 for Job 1
|
||||
addCandidate(j1.getId(), "张明远", "138****5201", "硕士", 12,
|
||||
"注册结构工程师,超高层钢结构,BIM建模,CAD,地下工程,设计院15年",
|
||||
75, "推荐(评分:75)。学历:硕士;工作年限:12年。", "初筛通过", "35k", "2026-06-25 10:00", "技术背景强,建议安排技术面试");
|
||||
addCandidate(j1.getId(), "刘雪梅", "139****6302", "本科", 8,
|
||||
"结构工程,CAD,AutoCAD,工程设计,住宅项目",
|
||||
62, "推荐(评分:62)。学历:本科;工作年限:8年。", "面试安排", "28k", "2026-06-28 14:00", "");
|
||||
addCandidate(j1.getId(), "赵国强", "136****4403", "大专", 5,
|
||||
"辅助设计,AutoCAD,钢筋翻样",
|
||||
51, "不推荐(评分:51)。学历:大专;工作年限:5年。", "已淘汰", "20k", null, "学历和经验不满足要求");
|
||||
|
||||
// 候选人 for Job 2
|
||||
addCandidate(j2.getId(), "陈志远", "137****7504", "本科", 10,
|
||||
"一级建造师,EPC总承包,项目全周期,市政工程,甲方协调,合同管理",
|
||||
88, "强烈推荐(评分:88)。学历:本科;工作年限:10年。", "面试通过", "40k", "2026-06-20 09:00", "各项能力突出,建议发Offer");
|
||||
addCandidate(j2.getId(), "孙丽华", "135****8605", "硕士", 7,
|
||||
"项目管理,EPC,一建,工程合同,进度管理",
|
||||
80, "强烈推荐(评分:80)。学历:硕士;工作年限:7年。", "offer发出", "38k", "2026-06-18 14:00", "综合素质优秀");
|
||||
|
||||
// 候选人 for Job 3(已入职)
|
||||
addCandidate(j3.getId(), "吴美玲", "132****9706", "本科", 6,
|
||||
"注册会计师,ERP财务模块,财务分析,税务筹划,预算管理",
|
||||
90, "强烈推荐(评分:90)。学历:本科;工作年限:6年。", "已入职", "22k", "2026-06-10 10:00", "已正式入职,表现优异");
|
||||
|
||||
// 面试题(for Job 1,AI生成)
|
||||
if (questionRepo.count() == 0) {
|
||||
addQuestion(j1.getId(), "高级结构工程师", "技能考察",
|
||||
"请描述您在超高层建筑钢结构设计中遇到的最大技术挑战,以及您的解决方案。",
|
||||
"考察实际技术经验;期望看到:抗风抗震计算、复杂节点处理、BIM协同经验。",
|
||||
"高级");
|
||||
addQuestion(j1.getId(), "高级结构工程师", "面试话术",
|
||||
"请介绍您主导过的最大单体项目,包括规模、难点和您的贡献。",
|
||||
"关注项目规模、复杂度与个人贡献比重;是否有超过5万平方米单体。",
|
||||
"中级");
|
||||
addQuestion(j2.getId(), "项目经理(EPC)", "情景题",
|
||||
"假设EPC项目在施工过程中甲方提出重大变更,可能导致工期延误3个月,您如何应对?",
|
||||
"考察变更管理和谈判能力;期望看到:书面确认→工期/费用影响评估→变更谈判→合同签署。",
|
||||
"高级");
|
||||
addQuestion(j2.getId(), "项目经理(EPC)", "综合素质",
|
||||
"您如何处理团队内部成员工作风格冲突的问题?请举例说明。",
|
||||
"考察领导力与团队管理;期望看到积极沟通、明确责任、结果导向。",
|
||||
"中级");
|
||||
addQuestion(j3.getId(), "财务主管", "笔试题",
|
||||
"请说明税务筹划与逃税的本质区别,并举例说明合法的增值税筹划方法。",
|
||||
"考察税务专业知识合规意识;期望看到清晰区分合规边界,举例一般纳税人优惠政策。",
|
||||
"高级");
|
||||
}
|
||||
}
|
||||
|
||||
private void addCandidate(Long jobId, String name, String phone, String edu, int years,
|
||||
String summary, int score, String aiReport,
|
||||
String status, String salary, String interviewTime, String note) {
|
||||
HrRecruitCandidate c = new HrRecruitCandidate();
|
||||
c.setJobId(jobId);
|
||||
c.setName(name);
|
||||
c.setPhone(phone);
|
||||
c.setEducation(edu);
|
||||
c.setWorkYears(years);
|
||||
c.setResumeSummary(summary);
|
||||
c.setAiScore(score);
|
||||
c.setAiReport(aiReport);
|
||||
c.setStatus(status);
|
||||
c.setExpectedSalary(salary);
|
||||
c.setInterviewTime(interviewTime);
|
||||
c.setInterviewNote(note);
|
||||
c.setCreatedAt(Instant.now().minusSeconds(86400 * (long)(Math.random() * 15 + 1)));
|
||||
c.setUpdatedAt(Instant.now().minusSeconds(3600));
|
||||
candidateRepo.save(c);
|
||||
}
|
||||
|
||||
private void addQuestion(Long jobId, String position, String qType,
|
||||
String content, String refAnswer, String difficulty) {
|
||||
HrInterviewQuestion q = new HrInterviewQuestion();
|
||||
q.setJobId(jobId);
|
||||
q.setPosition(position);
|
||||
q.setQuestionType(qType);
|
||||
q.setContent(content);
|
||||
q.setReferenceAnswer(refAnswer);
|
||||
q.setAiGenerated(true);
|
||||
q.setDifficulty(difficulty);
|
||||
q.setStatus("启用");
|
||||
q.setCreator("AI系统");
|
||||
q.setCreatedAt(Instant.now().minusSeconds(86400 * 10));
|
||||
questionRepo.save(q);
|
||||
}
|
||||
|
||||
// ============ AI 文档初稿 ============
|
||||
|
||||
private void seedAiDocs() {
|
||||
if (aiDocRepo.count() > 0) return;
|
||||
|
||||
// 1. 入职资料(已发布)
|
||||
HrAiDocDraft d1 = new HrAiDocDraft();
|
||||
d1.setDocType("入职资料");
|
||||
d1.setRelatedPosition(null);
|
||||
d1.setRelatedDept("工程技术部");
|
||||
d1.setTitle("工程技术部 新员工入职资料包(AI初稿)");
|
||||
d1.setContent("# 工程技术部 新员工入职资料包\n\n"
|
||||
+ "> AI生成初稿,已经HR审核。\n\n"
|
||||
+ "## 一、欢迎信\n\n"
|
||||
+ "亲爱的新同事,\n\n热烈欢迎您加入凯迪科技工程技术部大家庭!\n\n"
|
||||
+ "## 二、报到须知\n\n"
|
||||
+ "**报到时间**:请于入职通知书上注明的日期 09:00 前到达人力资源部。\n\n"
|
||||
+ "**携带材料**:身份证、学历证书、学位证书、离职证明、近期照片 2 张、银行卡。\n\n"
|
||||
+ "## 三、专属材料\n\n"
|
||||
+ "技术部员工还需提供:职业资格证书(建造师/结构工程师等)原件复印件。\n\n"
|
||||
+ "## 四、入职第一周\n\n"
|
||||
+ "- Day1:行政手续 + 系统开通\n- Day2:公司文化宣贯\n"
|
||||
+ "- Day3-4:技术规范学习 + 导师结对\n- Day5:与主管沟通试用期目标\n\n"
|
||||
+ "---\n*由 AI 辅助生成,HR 已审核。*");
|
||||
d1.setGenContext("类型=入职资料 岗位= 部门=工程技术部 背景=职业资格证书");
|
||||
d1.setAiGenerated(true);
|
||||
d1.setStatus("已发布");
|
||||
d1.setReviewNote("内容准确,补充了专业证书要求,审核通过");
|
||||
d1.setOperator("HR-王芳");
|
||||
d1.setCreatedAt(Instant.now().minusSeconds(86400 * 30));
|
||||
d1.setUpdatedAt(Instant.now().minusSeconds(86400 * 25));
|
||||
aiDocRepo.save(d1);
|
||||
|
||||
// 2. 岗位说明(已审核)
|
||||
HrAiDocDraft d2 = new HrAiDocDraft();
|
||||
d2.setDocType("岗位说明");
|
||||
d2.setRelatedPosition("高级结构工程师");
|
||||
d2.setRelatedDept("工程技术部");
|
||||
d2.setTitle("高级结构工程师 岗位说明书(AI初稿)");
|
||||
d2.setContent("# 高级结构工程师 岗位说明书\n\n"
|
||||
+ "> AI生成初稿,已审核待发布。\n\n"
|
||||
+ "## 一、基本信息\n\n"
|
||||
+ "| 项目 | 内容 |\n| ---- | ---- |\n"
|
||||
+ "| 岗位名称 | 高级结构工程师 |\n"
|
||||
+ "| 所属部门 | 工程技术部 |\n"
|
||||
+ "| 直接上级 | 技术总工 |\n"
|
||||
+ "| 岗位类别 | 专业技术类 |\n"
|
||||
+ "| 编制人数 | 2 |\n\n"
|
||||
+ "## 二、岗位职责\n\n"
|
||||
+ "1. 负责大型建筑结构设计、审核与优化\n"
|
||||
+ "2. 参与超高层/复杂结构的技术攻关\n"
|
||||
+ "3. 指导初中级工程师完成设计任务\n"
|
||||
+ "4. 负责结构设计图纸的质量把控\n"
|
||||
+ "5. 参与投标技术方案编写\n\n"
|
||||
+ "## 三、任职资格\n\n"
|
||||
+ "**学历**:本科及以上,土木/结构工程专业\n\n"
|
||||
+ "**证书**:注册结构工程师(一级)优先\n\n"
|
||||
+ "**经验**:10年以上相关经验,有超高层项目经验\n\n"
|
||||
+ "**技能**:BIM、CAD、SAP2000/PKPM、Revit\n\n"
|
||||
+ "---\n*由 AI 辅助生成,已审核待签发。*");
|
||||
d2.setGenContext("类型=岗位说明 岗位=高级结构工程师 部门=工程技术部 背景=注册结构工程师,BIM,超高层");
|
||||
d2.setAiGenerated(true);
|
||||
d2.setStatus("已审核");
|
||||
d2.setReviewNote("内容基本准确,已补充证书要求,待部门负责人签发");
|
||||
d2.setOperator("HR-李娜");
|
||||
d2.setCreatedAt(Instant.now().minusSeconds(86400 * 15));
|
||||
d2.setUpdatedAt(Instant.now().minusSeconds(86400 * 10));
|
||||
aiDocRepo.save(d2);
|
||||
|
||||
// 3. 员工手册(草稿)
|
||||
HrAiDocDraft d3 = new HrAiDocDraft();
|
||||
d3.setDocType("员工手册");
|
||||
d3.setRelatedPosition(null);
|
||||
d3.setRelatedDept("凯迪科技有限公司");
|
||||
d3.setTitle("凯迪科技有限公司 员工手册(AI初稿·2026版)");
|
||||
d3.setContent("# 凯迪科技有限公司 员工手册\n\n"
|
||||
+ "> AI生成草稿,待法务及HR审核。\n\n"
|
||||
+ "## 第一章 公司简介\n\n"
|
||||
+ "凯迪科技有限公司是一家专业从事工程技术、建设管理及 ERP 系统集成的企业,"
|
||||
+ "秉承「诚信、专业、创新」的核心价值观。\n\n"
|
||||
+ "## 第二章 行为规范\n\n"
|
||||
+ "- 诚实守信,不弄虚作假\n"
|
||||
+ "- 尊重同事,维护良好工作氛围\n"
|
||||
+ "- 保守公司机密,不泄露商业信息\n\n"
|
||||
+ "## 第三章 考勤制度\n\n"
|
||||
+ "- 工作时间:周一至周五 09:00-18:00\n"
|
||||
+ "- 打卡方式:APP/指纹识别\n"
|
||||
+ "- 迟到:15分钟以上计考勤异常\n\n"
|
||||
+ "## 第四章 假期\n\n"
|
||||
+ "| 假期 | 时长 |\n| ---- | ---- |\n"
|
||||
+ "| 年假 | 5-15天 |\n| 病假 | 按医院证明 |\n| 婚假 | 3天 |\n"
|
||||
+ "| 产假 | 按国家规定 |\n\n"
|
||||
+ "## 第五章 薪酬福利\n\n"
|
||||
+ "每月15日发薪;五险一金按规定缴纳;节日发放礼品/礼金。\n\n"
|
||||
+ "---\n*由 AI 辅助生成,待法务审核。*");
|
||||
d3.setGenContext("类型=员工手册 部门=凯迪科技有限公司");
|
||||
d3.setAiGenerated(true);
|
||||
d3.setStatus("草稿");
|
||||
d3.setReviewNote(null);
|
||||
d3.setOperator("HR-王芳");
|
||||
d3.setCreatedAt(Instant.now().minusSeconds(86400 * 5));
|
||||
d3.setUpdatedAt(Instant.now().minusSeconds(86400 * 5));
|
||||
aiDocRepo.save(d3);
|
||||
|
||||
// 4. 转正考核(草稿)
|
||||
HrAiDocDraft d4 = new HrAiDocDraft();
|
||||
d4.setDocType("转正考核");
|
||||
d4.setRelatedPosition("财务主管");
|
||||
d4.setRelatedDept("财务中心");
|
||||
d4.setTitle("财务主管 转正考核表(AI初稿)");
|
||||
d4.setContent("# 财务主管 转正考核表\n\n"
|
||||
+ "> AI生成草稿,请直属主管填写评分。\n\n"
|
||||
+ "## 基本信息\n\n"
|
||||
+ "| 项目 | 内容 |\n| ---- | ---- |\n"
|
||||
+ "| 员工姓名 | 吴美玲 |\n"
|
||||
+ "| 岗位 | 财务主管 |\n"
|
||||
+ "| 入职日期 | 2026-06-10 |\n"
|
||||
+ "| 试用期截止 | 2026-09-10 |\n"
|
||||
+ "| 直属主管 | 财务总监 |\n\n"
|
||||
+ "## 试用期目标\n\n"
|
||||
+ "| 目标 | 计划完成 | 实际完成 | 完成率 |\n"
|
||||
+ "| ---- | ---- | ---- | ---- |\n"
|
||||
+ "| 完成岗位培训 | 入职第1周 | | |\n"
|
||||
+ "| 接管月度账务 | 入职第1月 | | |\n"
|
||||
+ "| 独立完成税务申报 | 入职第2月 | | |\n"
|
||||
+ "| 完成半年财务分析 | 试用期末 | | |\n\n"
|
||||
+ "## 综合评分\n\n"
|
||||
+ "| 维度 | 权重 | 得分 |\n"
|
||||
+ "| ---- | ---- | ---- |\n"
|
||||
+ "| 专业能力 | 40% | |\n"
|
||||
+ "| 工作态度 | 20% | |\n"
|
||||
+ "| 团队协作 | 20% | |\n"
|
||||
+ "| 执行力 | 20% | |\n\n"
|
||||
+ "**转正建议**:[ ] 正式转正 [ ] 延长 [ ] 不予转正\n\n"
|
||||
+ "---\n*由 AI 辅助生成,待主管填写。*");
|
||||
d4.setGenContext("类型=转正考核 岗位=财务主管 部门=财务中心");
|
||||
d4.setAiGenerated(true);
|
||||
d4.setStatus("草稿");
|
||||
d4.setReviewNote(null);
|
||||
d4.setOperator("HR-李娜");
|
||||
d4.setCreatedAt(Instant.now().minusSeconds(86400 * 3));
|
||||
d4.setUpdatedAt(Instant.now().minusSeconds(86400 * 3));
|
||||
aiDocRepo.save(d4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.Blog;
|
||||
import com.kaidi.oa.domain.CollabDoc;
|
||||
import com.kaidi.oa.domain.Discussion;
|
||||
import com.kaidi.oa.domain.Favorite;
|
||||
import com.kaidi.oa.domain.FormInstance;
|
||||
import com.kaidi.oa.domain.Message;
|
||||
import com.kaidi.oa.domain.ScheduleEvent;
|
||||
import com.kaidi.oa.domain.SysUser;
|
||||
import com.kaidi.oa.domain.WorkPlan;
|
||||
import com.kaidi.oa.repository.BlogRepository;
|
||||
import com.kaidi.oa.repository.CollabDocRepository;
|
||||
import com.kaidi.oa.repository.DiscussionRepository;
|
||||
import com.kaidi.oa.repository.FavoriteRepository;
|
||||
import com.kaidi.oa.repository.FormInstanceRepository;
|
||||
import com.kaidi.oa.repository.MessageRepository;
|
||||
import com.kaidi.oa.repository.ScheduleEventRepository;
|
||||
import com.kaidi.oa.repository.StoredFileRepository;
|
||||
import com.kaidi.oa.repository.SysUserRepository;
|
||||
import com.kaidi.oa.repository.WorkPlanRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 一次性身份回填:把历史数据里"按显示名落库的归属/作者/上传者/收件人/发起人"补成稳定的
|
||||
* {@link SysUser#getId()}(对象级授权从此按 userId 比对,根治改名重用冒充——见
|
||||
* {@code AuthorizationService.isOwner})。
|
||||
*
|
||||
* <p>幂等且非破坏:只填 {@code *Id} 为空的旧行,且只在显示名能唯一解析到一个用户时才回填;
|
||||
* 显示名重名(理论上唯一约束下不会有)或在用户表中不存在(纯 demo 数据,如 张伟/李娜 这类不是
|
||||
* 真实账号的署名)则保持为空——这类对象退回按显示名比对的兜底口径,仍只有 ADMIN 可管理。
|
||||
*
|
||||
* <p>{@link Order} 设为最低优先级,确保在 {@code DataSeeder(@Order 1)} /
|
||||
* {@code OrgEnrichmentSeeder(@Order 2)} 等播种器之后运行,从而对它们刚播下的种子数据也完成回填。
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class IdentityBackfillRunner implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IdentityBackfillRunner.class);
|
||||
|
||||
private final SysUserRepository userRepo;
|
||||
private final FavoriteRepository favRepo;
|
||||
private final ScheduleEventRepository scheduleRepo;
|
||||
private final WorkPlanRepository workPlanRepo;
|
||||
private final CollabDocRepository collabDocRepo;
|
||||
private final DiscussionRepository discussionRepo;
|
||||
private final StoredFileRepository fileRepo;
|
||||
private final MessageRepository messageRepo;
|
||||
private final FormInstanceRepository instanceRepo;
|
||||
private final BlogRepository blogRepo;
|
||||
|
||||
public IdentityBackfillRunner(SysUserRepository userRepo, FavoriteRepository favRepo,
|
||||
ScheduleEventRepository scheduleRepo, WorkPlanRepository workPlanRepo,
|
||||
CollabDocRepository collabDocRepo, DiscussionRepository discussionRepo,
|
||||
StoredFileRepository fileRepo, MessageRepository messageRepo,
|
||||
FormInstanceRepository instanceRepo, BlogRepository blogRepo) {
|
||||
this.userRepo = userRepo;
|
||||
this.favRepo = favRepo;
|
||||
this.scheduleRepo = scheduleRepo;
|
||||
this.workPlanRepo = workPlanRepo;
|
||||
this.collabDocRepo = collabDocRepo;
|
||||
this.discussionRepo = discussionRepo;
|
||||
this.fileRepo = fileRepo;
|
||||
this.messageRepo = messageRepo;
|
||||
this.instanceRepo = instanceRepo;
|
||||
this.blogRepo = blogRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(String... args) {
|
||||
// displayName -> userId,只保留唯一名(重名一律不进表,避免把对象误绑到错误的人)。
|
||||
Map<String, Long> idByName = new HashMap<>();
|
||||
Set<String> ambiguous = new HashSet<>();
|
||||
for (SysUser u : userRepo.findAll()) {
|
||||
String dn = u.getDisplayName();
|
||||
if (dn == null || dn.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (idByName.containsKey(dn)) {
|
||||
ambiguous.add(dn);
|
||||
} else {
|
||||
idByName.put(dn, u.getId());
|
||||
}
|
||||
}
|
||||
for (String dup : ambiguous) {
|
||||
idByName.remove(dup);
|
||||
}
|
||||
if (idByName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int filled = 0;
|
||||
filled += backfill(favRepo.findAll(), Favorite::getOwnerId, Favorite::getOwner,
|
||||
Favorite::setOwnerId, favRepo::saveAll, idByName);
|
||||
filled += backfill(scheduleRepo.findAll(), ScheduleEvent::getOwnerId, ScheduleEvent::getOwner,
|
||||
ScheduleEvent::setOwnerId, scheduleRepo::saveAll, idByName);
|
||||
filled += backfill(workPlanRepo.findAll(), WorkPlan::getOwnerId, WorkPlan::getOwner,
|
||||
WorkPlan::setOwnerId, workPlanRepo::saveAll, idByName);
|
||||
filled += backfill(collabDocRepo.findAll(), CollabDoc::getOwnerId, CollabDoc::getOwner,
|
||||
CollabDoc::setOwnerId, collabDocRepo::saveAll, idByName);
|
||||
filled += backfill(discussionRepo.findAll(), Discussion::getAuthorId, Discussion::getAuthor,
|
||||
Discussion::setAuthorId, discussionRepo::saveAll, idByName);
|
||||
filled += backfill(blogRepo.findAll(), Blog::getAuthorId, Blog::getAuthor,
|
||||
Blog::setAuthorId, blogRepo::saveAll, idByName);
|
||||
// StoredFile 含 byte[] data 大字段(非懒加载):用 @Modifying 定向批更,绝不 findAll 整表
|
||||
// 把 BLOB 读进堆内存、也无写放大。每个唯一显示名一条 UPDATE,仅命中 uploaderId 为空的旧行。
|
||||
for (Map.Entry<String, Long> e : idByName.entrySet()) {
|
||||
filled += fileRepo.backfillUploaderId(e.getKey(), e.getValue());
|
||||
}
|
||||
filled += backfill(messageRepo.findAll(), Message::getRecipientId, Message::getRecipient,
|
||||
Message::setRecipientId, messageRepo::saveAll, idByName);
|
||||
filled += backfill(instanceRepo.findAll(), FormInstance::getOriginUserId, FormInstance::getOriginUser,
|
||||
FormInstance::setOriginUserId, instanceRepo::saveAll, idByName);
|
||||
|
||||
if (filled > 0) {
|
||||
log.info("Identity backfill: bound {} legacy rows to stable userId.", filled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一张表回填:跳过 id 已存在的行;名字能唯一解析则写入 id 并收集;最后批量保存。返回回填行数。
|
||||
*/
|
||||
private <T> int backfill(List<T> rows, Function<T, Long> getId, Function<T, String> getName,
|
||||
BiConsumer<T, Long> setId, Consumer<List<T>> save, Map<String, Long> idByName) {
|
||||
List<T> changed = new ArrayList<>();
|
||||
for (T row : rows) {
|
||||
if (getId.apply(row) != null) {
|
||||
continue;
|
||||
}
|
||||
Long uid = idByName.get(getName.apply(row));
|
||||
if (uid != null) {
|
||||
setId.accept(row, uid);
|
||||
changed.add(row);
|
||||
}
|
||||
}
|
||||
if (!changed.isEmpty()) {
|
||||
save.accept(changed);
|
||||
}
|
||||
return changed.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.ItAsset;
|
||||
import com.kaidi.oa.domain.ItAssetEvent;
|
||||
import com.kaidi.oa.domain.ItInspection;
|
||||
import com.kaidi.oa.domain.ItMonitorItem;
|
||||
import com.kaidi.oa.domain.ItNetworkDevice;
|
||||
import com.kaidi.oa.domain.ItSecPolicyConfig;
|
||||
import com.kaidi.oa.repository.ItAssetEventRepository;
|
||||
import com.kaidi.oa.repository.ItAssetRepository;
|
||||
import com.kaidi.oa.repository.ItInspectionRepository;
|
||||
import com.kaidi.oa.repository.ItMonitorItemRepository;
|
||||
import com.kaidi.oa.repository.ItNetworkDeviceRepository;
|
||||
import com.kaidi.oa.repository.ItSecPolicyConfigRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
/**
|
||||
* 信息部演示数据Seeder:补全监控项/巡检读数/网络设备/安全策略零数据演示缺口。
|
||||
* @Order(204) 在所有主数据Seeder后运行,幂等(count > 0 则跳过)。
|
||||
*/
|
||||
@Order(204)
|
||||
@Component
|
||||
public class ItDeptDemoSeeder implements ApplicationRunner {
|
||||
|
||||
private final ItMonitorItemRepository monitorRepo;
|
||||
private final ItInspectionRepository inspectionRepo;
|
||||
private final ItNetworkDeviceRepository networkRepo;
|
||||
private final ItSecPolicyConfigRepository policyRepo;
|
||||
private final ItAssetRepository assetRepo;
|
||||
private final ItAssetEventRepository assetEventRepo;
|
||||
|
||||
public ItDeptDemoSeeder(ItMonitorItemRepository monitorRepo,
|
||||
ItInspectionRepository inspectionRepo,
|
||||
ItNetworkDeviceRepository networkRepo,
|
||||
ItSecPolicyConfigRepository policyRepo,
|
||||
ItAssetRepository assetRepo,
|
||||
ItAssetEventRepository assetEventRepo) {
|
||||
this.monitorRepo = monitorRepo;
|
||||
this.inspectionRepo = inspectionRepo;
|
||||
this.networkRepo = networkRepo;
|
||||
this.policyRepo = policyRepo;
|
||||
this.assetRepo = assetRepo;
|
||||
this.assetEventRepo = assetEventRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
seedMonitorItems();
|
||||
seedNetworkDevices();
|
||||
seedSecPolicies();
|
||||
seedAssetAmounts();
|
||||
seedAssetEvents();
|
||||
}
|
||||
|
||||
// -------- 监控项 + 巡检读数 --------
|
||||
|
||||
private void seedMonitorItems() {
|
||||
if (monitorRepo.count() > 0) return;
|
||||
|
||||
// ERP应用服务器 CPU
|
||||
ItMonitorItem m1 = monitor("ERP应用服务器", "CPU使用率", "%", "above", 75.0, 90.0, "张伟");
|
||||
addInspection(m1, 45.2, "正常", false, "张伟", "-3");
|
||||
addInspection(m1, 68.5, "正常", false, "张伟", "-2");
|
||||
addInspection(m1, 82.1, "告警", true, "张伟", "-1");
|
||||
addInspection(m1, 55.3, "正常", false, "张伟", "0");
|
||||
|
||||
// ERP应用服务器 内存
|
||||
ItMonitorItem m2 = monitor("ERP应用服务器", "内存使用率", "%", "above", 80.0, 92.0, "张伟");
|
||||
addInspection(m2, 71.0, "正常", false, "系统", "-2");
|
||||
addInspection(m2, 88.5, "告警", true, "系统", "-1");
|
||||
addInspection(m2, 93.2, "故障", true, "系统", "0");
|
||||
|
||||
// 数据库主库
|
||||
ItMonitorItem m3 = monitor("数据库主库", "磁盘使用率", "%", "above", 70.0, 85.0, "李娜");
|
||||
addInspection(m3, 52.3, "正常", false, "李娜", "-5");
|
||||
addInspection(m3, 65.7, "正常", false, "李娜", "-2");
|
||||
addInspection(m3, 73.4, "告警", true, "李娜", "0");
|
||||
|
||||
// 数据库连接数
|
||||
ItMonitorItem m4 = monitor("数据库主库", "连接数", "个", "above", 150.0, 200.0, "李娜");
|
||||
addInspection(m4, 87.0, "正常", false, "李娜", "-3");
|
||||
addInspection(m4, 134.0, "正常", false, "李娜", "-1");
|
||||
addInspection(m4, 112.0, "正常", false, "李娜", "0");
|
||||
|
||||
// OA服务响应时间
|
||||
ItMonitorItem m5 = monitor("OA应用系统", "响应时间", "ms", "above", 500.0, 2000.0, "陈静");
|
||||
addInspection(m5, 235.0, "正常", false, "系统", "-4");
|
||||
addInspection(m5, 312.0, "正常", false, "系统", "-2");
|
||||
addInspection(m5, 648.0, "告警", true, "系统", "0");
|
||||
|
||||
// 核心交换机 网络流量
|
||||
ItMonitorItem m6 = monitor("核心交换机", "网络流量", "Mbps", "above", 800.0, 950.0, "张伟");
|
||||
addInspection(m6, 452.0, "正常", false, "张伟", "-6");
|
||||
addInspection(m6, 623.0, "正常", false, "张伟", "-3");
|
||||
addInspection(m6, 712.0, "正常", false, "张伟", "0");
|
||||
|
||||
// 存储阵列 可用空间
|
||||
ItMonitorItem m7 = monitor("存储阵列", "可用空间率", "%", "below", 20.0, 10.0, "李娜");
|
||||
addInspection(m7, 45.0, "正常", false, "李娜", "-7");
|
||||
addInspection(m7, 28.5, "正常", false, "李娜", "-3");
|
||||
addInspection(m7, 18.2, "告警", true, "李娜", "0");
|
||||
}
|
||||
|
||||
private ItMonitorItem monitor(String target, String metric, String unit,
|
||||
String direction, Double warn, Double crit, String owner) {
|
||||
ItMonitorItem m = new ItMonitorItem();
|
||||
m.setTarget(target);
|
||||
m.setMetric(metric);
|
||||
m.setUnit(unit);
|
||||
m.setDirection(direction);
|
||||
m.setWarnThreshold(warn);
|
||||
m.setCritThreshold(crit);
|
||||
m.setOwner(owner);
|
||||
m.setEnabled(Boolean.TRUE);
|
||||
m.setLastStatus("未采集");
|
||||
m.setCreatedAt(Instant.now());
|
||||
return monitorRepo.save(m);
|
||||
}
|
||||
|
||||
private void addInspection(ItMonitorItem m, double value, String result,
|
||||
boolean alerted, String inspector, String daysAgo) {
|
||||
int days = Integer.parseInt(daysAgo);
|
||||
LocalDate date = LocalDate.now().plusDays(days);
|
||||
ItInspection ins = new ItInspection();
|
||||
ins.setMonitorItemId(m.getId());
|
||||
ins.setTarget(m.getTarget());
|
||||
ins.setMetric(m.getMetric());
|
||||
ins.setValue(value);
|
||||
ins.setResult(result);
|
||||
ins.setAlerted(alerted);
|
||||
ins.setInspector(inspector);
|
||||
ins.setCheckDate(date.toString());
|
||||
ins.setCheckedAt(date.atStartOfDay(ZoneOffset.UTC).toInstant());
|
||||
ins.setCreatedAt(Instant.now());
|
||||
ItInspection saved = inspectionRepo.save(ins);
|
||||
// 回写监控项最近状态
|
||||
m.setLastValue(value);
|
||||
m.setLastStatus(result);
|
||||
m.setLastCheckAt(saved.getCheckedAt());
|
||||
monitorRepo.save(m);
|
||||
}
|
||||
|
||||
// -------- 网络设备 --------
|
||||
|
||||
private void seedNetworkDevices() {
|
||||
if (networkRepo.count() > 0) return;
|
||||
|
||||
netdev("深信服AF-2000防火墙", "防火墙", "深信服 AF-2000",
|
||||
"192.168.1.1", "全网", "机房A", "V5.5.0.6", "张伟",
|
||||
"2025-01-20", "2028-01-20", "VLAN10-管理区/VLAN20-业务区/VLAN30-DMZ区;安全策略48条", "正常", "2026-05-15");
|
||||
netdev("华为S5720核心交换机", "交换机", "华为 S5720-52X",
|
||||
"192.168.1.2", "VLAN10/20/30/40", "机房A", "V200R020C10", "张伟",
|
||||
"2024-06-15", "2027-06-15", "24口PoE+4个SFP+上行口;VLAN间路由已配置", "正常", "2026-05-20");
|
||||
netdev("华为S5700接入交换机-1F", "交换机", "华为 S5700-28X",
|
||||
"192.168.1.3", "VLAN20/40", "1楼弱电箱", "V200R019C10", "李娜",
|
||||
"2024-08-10", "2027-08-10", "负责1楼24个信息点接入", "正常", "2026-04-18");
|
||||
netdev("深信服VPN网关", "VPN网关", "深信服 SSL-VPN 2000",
|
||||
"192.168.1.4", "VPN-Pool/10.8.0.0/24", "机房B", "V5.2.3", "张伟",
|
||||
"2024-12-01", "2027-12-01", "IKEv2 AES-256-GCM/SHA2-256;在线用户峰值50人", "正常", "2026-06-01");
|
||||
netdev("深信服IPS-2000入侵防御", "IDS/IPS", "深信服 IPS-2000",
|
||||
"192.168.1.5", "旁路检测", "机房A", "IPS-V4.1.2", "张伟",
|
||||
"2024-03-15", "2027-03-15", "特征库版本2026.06.01;每日自动更新;高风险告警即时阻断", "正常", "2026-06-01");
|
||||
netdev("华为AR路由器", "路由器", "华为 AR3260",
|
||||
"10.0.0.1", "外网/内网边界", "机房B", "V200R009C10", "张伟",
|
||||
"2024-05-01", "2027-05-01", "双ISP链路,主链路电信100M,备链路联通50M", "正常", "2026-05-28");
|
||||
netdev("360天擎防病毒服务器", "防病毒服务器", "360天擎 v4.5",
|
||||
"192.168.1.10", "VLAN20/40", "机房A", "V4.5.11.0", "李娜",
|
||||
"2023-09-01", "2026-09-01", "病毒库2026-06-15;全网200终端已接入", "正常", "2026-06-15");
|
||||
netdev("SANGFOR上网行为管理", "上网行为管理", "深信服 AC-3000",
|
||||
"192.168.1.6", "出口前", "机房A", "V12.0.73", "张伟",
|
||||
"2023-11-10", "2026-11-10", "违规内容拦截/带宽管理/行为审计;日均报表已启用", "告警", null);
|
||||
}
|
||||
|
||||
private void netdev(String name, String deviceType, String model,
|
||||
String ip, String vlan, String location, String firmware,
|
||||
String admin, String purchase, String warranty, String note,
|
||||
String runStatus, String lastBackup) {
|
||||
ItNetworkDevice d = new ItNetworkDevice();
|
||||
d.setName(name);
|
||||
d.setDeviceType(deviceType);
|
||||
d.setModel(model);
|
||||
d.setManagementIp(ip);
|
||||
d.setVlanSegment(vlan);
|
||||
d.setLocation(location);
|
||||
d.setFirmwareVersion(firmware);
|
||||
d.setAdmin(admin);
|
||||
d.setPurchaseDate(purchase);
|
||||
d.setWarrantyDate(warranty);
|
||||
d.setNote(note);
|
||||
d.setRunStatus(runStatus);
|
||||
d.setConfigBackupStatus(lastBackup != null ? "已备份" : "未备份");
|
||||
d.setLastConfigBackupDate(lastBackup);
|
||||
d.setCreatedAt(Instant.now());
|
||||
networkRepo.save(d);
|
||||
}
|
||||
|
||||
// -------- 安全策略配置 --------
|
||||
|
||||
private void seedSecPolicies() {
|
||||
if (policyRepo.count() > 0) return;
|
||||
|
||||
// VPN策略
|
||||
policy(null, "深信服VPN网关", "VPN策略", "总部-远程访问VPN",
|
||||
"VPN-001", "外网→内网", "任意", "192.168.0.0/16", "TCP/443,UDP/500-4500",
|
||||
"允许", 1, "IKEv2/AES-256-GCM/SHA2-256;证书认证;MFA双因子;最大并发500用户");
|
||||
policy(null, "深信服VPN网关", "VPN策略", "分公司点对点VPN",
|
||||
"VPN-002", "分公司→总部", "10.2.0.0/24", "192.168.0.0/16", "ANY",
|
||||
"允许", 2, "分公司岳阳(10.2.0.0/24)通过IPSEC VPN访问总部内网;预共享密钥AES-256");
|
||||
|
||||
// 防火墙规则
|
||||
policy(null, "深信服AF-2000防火墙", "防火墙规则", "拒绝外网访问内网管理口",
|
||||
"FW-001", "外网→内网", "ANY", "192.168.1.0/24", "TCP/22,TCP/23,TCP/3389",
|
||||
"拒绝", 1, "安全基线:禁止外网直接SSH/Telnet/RDP到内网管理设备");
|
||||
policy(null, "深信服AF-2000防火墙", "防火墙规则", "允许内网访问外网HTTP/HTTPS",
|
||||
"FW-002", "内网→外网", "192.168.0.0/16", "ANY", "TCP/80,TCP/443",
|
||||
"允许", 10, "员工上网;经上网行为管理过滤后允许");
|
||||
policy(null, "深信服AF-2000防火墙", "防火墙规则", "DMZ区发布ERP对外服务",
|
||||
"FW-003", "外网→DMZ", "ANY", "192.168.30.100", "TCP/443",
|
||||
"允许", 5, "ERP系统移动端访问入口;仅开放HTTPS 443端口");
|
||||
|
||||
// IPS规则
|
||||
policy(null, "深信服IPS-2000入侵防御", "IPS规则", "SQL注入攻击拦截",
|
||||
"IPS-001", "双向", "ANY", "任意", "TCP/80,TCP/443,TCP/8080",
|
||||
"阻断", 1, "特征库SQL注入检测;严格模式;每日告警统计");
|
||||
policy(null, "深信服IPS-2000入侵防御", "IPS规则", "暴力破解攻击防护",
|
||||
"IPS-002", "入站", "ANY", "任意", "TCP/22,TCP/3389,TCP/8090",
|
||||
"阻断", 2, "5次/分钟触发阻断;IP封锁1小时;通知管理员");
|
||||
policy(null, "深信服IPS-2000入侵防御", "IPS规则", "Web漏洞利用检测",
|
||||
"IPS-003", "入站", "ANY", "任意", "TCP/80,TCP/443",
|
||||
"告警", 3, "XSS/文件包含/目录遍历检测;告警模式记录日志");
|
||||
|
||||
// IDS特征
|
||||
policy(null, "深信服IPS-2000入侵防御", "IDS特征", "异常流量基线检测",
|
||||
"IDS-001", "双向", "内网", "内网", "ANY",
|
||||
"告警", 1, "基于流量基线的异常检测:单IP流量超基线3倍触发告警");
|
||||
|
||||
// 上网行为
|
||||
policy(null, "SANGFOR上网行为管理", "上网行为", "禁止访问非工作网站",
|
||||
"AC-001", "内网→外网", "192.168.0.0/16", "ANY", "TCP/80,TCP/443",
|
||||
"拒绝", 5, "工作时间(9:00-18:00)拦截娱乐/游戏/社交类网站;白名单放行业务系统");
|
||||
policy(null, "SANGFOR上网行为管理", "上网行为", "带宽QoS策略",
|
||||
"AC-002", "内网→外网", "192.168.0.0/16", "ANY", "ANY",
|
||||
"允许", 10, "业务系统优先级最高;视频流媒体限速2Mbps/人;总出口100M按部门分配");
|
||||
}
|
||||
|
||||
private void policy(Long deviceId, String deviceName, String policyType, String policyName,
|
||||
String policyNo, String trafficDir, String srcAddr, String dstAddr,
|
||||
String portProto, String policyAction, Integer priority, String remark) {
|
||||
ItSecPolicyConfig c = new ItSecPolicyConfig();
|
||||
c.setDeviceId(deviceId);
|
||||
c.setDeviceName(deviceName);
|
||||
c.setPolicyType(policyType);
|
||||
c.setPolicyName(policyName);
|
||||
c.setPolicyNo(policyNo);
|
||||
c.setTrafficDir(trafficDir);
|
||||
c.setSrcAddress(srcAddr);
|
||||
c.setDstAddress(dstAddr);
|
||||
c.setPortProto(portProto);
|
||||
c.setPolicyAction(policyAction);
|
||||
c.setEnabled(Boolean.TRUE);
|
||||
c.setPriority(priority);
|
||||
c.setRemark(remark);
|
||||
c.setLastSyncDate(LocalDate.now().toString());
|
||||
c.setOperator("张伟");
|
||||
c.setCreatedAt(Instant.now());
|
||||
policyRepo.save(c);
|
||||
}
|
||||
|
||||
// -------- 回填IT资产采购金额(让折旧能算出真实金额)--------
|
||||
|
||||
private void seedAssetAmounts() {
|
||||
// 为现有IT资产补全采购金额、折旧年限,让折旧端点能返回真实金额。
|
||||
for (ItAsset a : assetRepo.findAll()) {
|
||||
if (a.getPurchaseAmount() != null && a.getPurchaseAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
continue; // 已有金额跳过
|
||||
}
|
||||
// 根据类型设置参考采购金额
|
||||
BigDecimal amount;
|
||||
int depYears;
|
||||
int residual;
|
||||
switch (a.getCategory() != null ? a.getCategory() : "") {
|
||||
case "服务器" -> { amount = new BigDecimal("55000.00"); depYears = 5; residual = 5; }
|
||||
case "网络设备" -> { amount = new BigDecimal("18000.00"); depYears = 5; residual = 5; }
|
||||
case "终端" -> { amount = new BigDecimal("8500.00"); depYears = 3; residual = 5; }
|
||||
case "软件" -> { amount = new BigDecimal("25000.00"); depYears = 3; residual = 0; }
|
||||
case "办公设备" -> { amount = new BigDecimal("6500.00"); depYears = 5; residual = 5; }
|
||||
default -> { amount = new BigDecimal("5000.00"); depYears = 5; residual = 5; }
|
||||
}
|
||||
// 根据资产编号微调真实感
|
||||
if ("IT-001".equals(a.getAssetNo())) amount = new BigDecimal("62800.00");
|
||||
if ("IT-002".equals(a.getAssetNo())) amount = new BigDecimal("128000.00");
|
||||
if ("IT-008".equals(a.getAssetNo())) amount = new BigDecimal("180000.00");
|
||||
if ("IT-009".equals(a.getAssetNo())) amount = new BigDecimal("68000.00");
|
||||
|
||||
a.setPurchaseAmount(amount);
|
||||
if (a.getDepreciationYears() == null) a.setDepreciationYears(depYears);
|
||||
if (a.getResidualRatePct() == null) a.setResidualRatePct(residual);
|
||||
if (a.getSerialNo() == null) {
|
||||
a.setSerialNo("SN" + a.getAssetNo().replace("-", "") + "2024");
|
||||
}
|
||||
if (a.getBarcode() == null) {
|
||||
a.setBarcode("BC" + a.getAssetNo().replace("-", ""));
|
||||
}
|
||||
assetRepo.save(a);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 资产生命周期事件演示数据 --------
|
||||
|
||||
private void seedAssetEvents() {
|
||||
if (assetEventRepo.count() > 0) return;
|
||||
// 为IT-007 (闲置惠普笔记本) 生成一条历史调拨记录
|
||||
for (ItAsset a : assetRepo.findAll()) {
|
||||
if (!"IT-007".equals(a.getAssetNo())) continue;
|
||||
ItAssetEvent ev = new ItAssetEvent();
|
||||
ev.setAssetId(a.getId());
|
||||
ev.setAssetNo(a.getAssetNo());
|
||||
ev.setAssetName(a.getName());
|
||||
ev.setEventType("调拨");
|
||||
ev.setEventNo("TR-20250618-0001");
|
||||
ev.setFromHolder("设计研究中心");
|
||||
ev.setToHolder("");
|
||||
ev.setFromLocation("设计室");
|
||||
ev.setToLocation("信息部库房");
|
||||
ev.setReason("项目结束,设备归还信息部统一管理");
|
||||
ev.setOperator("陈静");
|
||||
ev.setStatus("已完成");
|
||||
ev.setEventDate("2025-06-18");
|
||||
ev.setCreatedAt(Instant.now());
|
||||
assetEventRepo.save(ev);
|
||||
break;
|
||||
}
|
||||
// 为IT-010 (维修复印机) 生成维修工单
|
||||
for (ItAsset a : assetRepo.findAll()) {
|
||||
if (!"IT-010".equals(a.getAssetNo())) continue;
|
||||
ItAssetEvent ev = new ItAssetEvent();
|
||||
ev.setAssetId(a.getId());
|
||||
ev.setAssetNo(a.getAssetNo());
|
||||
ev.setAssetName(a.getName());
|
||||
ev.setEventType("维修");
|
||||
ev.setEventNo("WO-20260608-0001");
|
||||
ev.setFromHolder("行政部");
|
||||
ev.setVendor("佳能维修服务中心(长沙)");
|
||||
ev.setReason("定影器故障,打印出现竖向条纹");
|
||||
ev.setExpectedReturnDate("2026-06-20");
|
||||
ev.setOperator("李娜");
|
||||
ev.setStatus("处理中");
|
||||
ev.setEventDate("2026-06-08");
|
||||
ev.setCreatedAt(Instant.now());
|
||||
assetEventRepo.save(ev);
|
||||
break;
|
||||
}
|
||||
// 为IT-011 (已报废爱普生投影仪) 生成报废记录
|
||||
for (ItAsset a : assetRepo.findAll()) {
|
||||
if (!"IT-011".equals(a.getAssetNo())) continue;
|
||||
ItAssetEvent ev = new ItAssetEvent();
|
||||
ev.setAssetId(a.getId());
|
||||
ev.setAssetNo(a.getAssetNo());
|
||||
ev.setAssetName(a.getName());
|
||||
ev.setEventType("报废");
|
||||
ev.setEventNo("SC-20260101-0001");
|
||||
ev.setFromHolder("行政部");
|
||||
ev.setReason("灯泡寿命耗尽,维修费用超过设备残值,申请报废");
|
||||
ev.setOperator("陈静");
|
||||
ev.setStatus("已完成");
|
||||
ev.setResult("报废处置");
|
||||
ev.setCompletedDate("2026-01-05");
|
||||
ev.setEventDate("2026-01-02");
|
||||
ev.setCreatedAt(Instant.now());
|
||||
assetEventRepo.save(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 -7:ELN 查看
|
||||
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 -6:ELN 导出
|
||||
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-001(COD/氨氮汇总)", 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 原始数据Excel(OOS偏差调查)", 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.TestTask;
|
||||
import com.kaidi.oa.repository.TestTaskRepository;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 实验室检测任务看板演示数据。
|
||||
* 覆盖全部状态列(待分配 / 检测中 / 待复核 / 已复核 / 偏差处理 / 已出报告),
|
||||
* 让 labkanban.vue 活体展示多状态卡片。
|
||||
* Order(8) = 在所有基础 Seeder 之后、FtsIndexRunner(200) 之前。
|
||||
*/
|
||||
@Component
|
||||
@Order(8)
|
||||
public class LabTestKanbanSeeder implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LabTestKanbanSeeder.class);
|
||||
|
||||
private final TestTaskRepository repo;
|
||||
|
||||
public LabTestKanbanSeeder(TestTaskRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (repo.count() > 0) {
|
||||
return; // 已有数据则跳过,幂等
|
||||
}
|
||||
log.info("Seeding LabTestKanban demo data...");
|
||||
|
||||
// 待分配
|
||||
seed("JC-001", "COD 化学需氧量", "GB 11914-1989", "污水样-2026001", "城镇污水处理厂进水", "高", "2026-06-20", "待分配", null, null, null, null);
|
||||
seed("JC-002", "氨氮(NH3-N)", "HJ 535-2009", "污水样-2026002", "工业废水进水", "高", "2026-06-20", "待分配", null, null, null, null);
|
||||
|
||||
// 检测中
|
||||
seed("JC-003", "总磷(TP)", "GB 11893-1989", "污水样-2026003", "生物质肥料浸提液", "中", "2026-06-18", "检测中", "李化验", null, null, null);
|
||||
seed("JC-004", "pH 值", "GB/T 6920-1986", "废水样-2026004", "工业废水排放口", "低", "2026-06-22", "检测中", "王化验", null, null, null);
|
||||
seed("JC-005", "悬浮物(SS)", "GB 11901-1989", "污水样-2026005", "脱水污泥渗滤液", "中", "2026-06-19", "检测中", "李化验", null, null, null);
|
||||
|
||||
// 待复核
|
||||
TestTask t6 = build("JC-006", "总氮(TN)", "HJ 636-2012", "废水样-2026006", "环保设备出水", "高", "2026-06-17", "待复核", "张检测");
|
||||
t6.setResultValue("12.5");
|
||||
t6.setResultUnit("mg/L");
|
||||
t6.setStandardLimit("≤15 mg/L");
|
||||
t6.setJudge("合格");
|
||||
repo.save(t6);
|
||||
|
||||
TestTask t7 = build("JC-007", "总有机碳(TOC)", "HJ 501-2009", "污水样-2026007", "科研实验室废液", "中", "2026-06-16", "待复核", "张检测");
|
||||
t7.setResultValue("38.2");
|
||||
t7.setResultUnit("mg/L");
|
||||
t7.setStandardLimit("≤50 mg/L");
|
||||
t7.setJudge("合格");
|
||||
repo.save(t7);
|
||||
|
||||
// 偏差处理(OOS超标)
|
||||
TestTask t8 = build("JC-008", "六价铬 Cr(VI)", "GB 7467-1987", "废水样-2026008", "镀件清洗废水", "高", "2026-06-15", "偏差处理", "陈检测");
|
||||
t8.setResultValue("0.85");
|
||||
t8.setResultUnit("mg/L");
|
||||
t8.setStandardLimit("≤0.5 mg/L");
|
||||
t8.setJudge("OOS偏差");
|
||||
t8.setRemark("实测值超标1.7倍,已启动偏差调查,通知车间排查污染源。");
|
||||
repo.save(t8);
|
||||
|
||||
// 已复核
|
||||
TestTask t9 = build("JC-009", "石油类", "HJ 637-2018", "废水样-2026009", "机加工切削液", "中", "2026-06-14", "已复核", "李化验");
|
||||
t9.setResultValue("4.2");
|
||||
t9.setResultUnit("mg/L");
|
||||
t9.setStandardLimit("≤5 mg/L");
|
||||
t9.setJudge("合格");
|
||||
t9.setReviewer("王主任");
|
||||
t9.setReviewedAt("2026-06-14");
|
||||
t9.setResultLocked(true);
|
||||
repo.save(t9);
|
||||
|
||||
TestTask t10 = build("JC-010", "氟化物", "HJ 488-2009", "废水样-2026010", "酸洗废水处理站", "中", "2026-06-13", "已复核", "陈检测");
|
||||
t10.setResultValue("8.5");
|
||||
t10.setResultUnit("mg/L");
|
||||
t10.setStandardLimit("≤10 mg/L");
|
||||
t10.setJudge("合格");
|
||||
t10.setReviewer("张主任");
|
||||
t10.setReviewedAt("2026-06-13");
|
||||
t10.setResultLocked(true);
|
||||
repo.save(t10);
|
||||
|
||||
// 已出报告
|
||||
TestTask t11 = build("JC-011", "色度", "GB 11903-1989", "污水样-2026011", "城镇污水出水", "低", "2026-06-12", "已出报告", "王化验");
|
||||
t11.setResultValue("10");
|
||||
t11.setResultUnit("度");
|
||||
t11.setStandardLimit("≤30 度");
|
||||
t11.setJudge("合格");
|
||||
t11.setReviewer("李主任");
|
||||
t11.setReviewedAt("2026-06-12");
|
||||
t11.setResultLocked(true);
|
||||
repo.save(t11);
|
||||
|
||||
TestTask t12 = build("JC-012", "溶解氧(DO)", "HJ 506-2009", "污水样-2026012", "好氧池曝气段", "低", "2026-06-11", "已出报告", "李化验");
|
||||
t12.setResultValue("5.8");
|
||||
t12.setResultUnit("mg/L");
|
||||
t12.setStandardLimit("≥2 mg/L");
|
||||
t12.setJudge("合格");
|
||||
t12.setReviewer("王主任");
|
||||
t12.setReviewedAt("2026-06-11");
|
||||
t12.setResultLocked(true);
|
||||
repo.save(t12);
|
||||
|
||||
log.info("LabTestKanban demo data seeded: {} records.", repo.count());
|
||||
}
|
||||
|
||||
private void seed(String taskNo, String detectItem, String methodNo,
|
||||
String sampleNo, String sampleName, String priority,
|
||||
String dueDate, String status, String tester,
|
||||
String resultValue, String resultUnit, String judge) {
|
||||
TestTask t = build(taskNo, detectItem, methodNo, sampleNo, sampleName, priority, dueDate, status, tester);
|
||||
if (resultValue != null) t.setResultValue(resultValue);
|
||||
if (resultUnit != null) t.setResultUnit(resultUnit);
|
||||
if (judge != null) t.setJudge(judge);
|
||||
repo.save(t);
|
||||
}
|
||||
|
||||
private TestTask build(String taskNo, String detectItem, String methodNo,
|
||||
String sampleNo, String sampleName, String priority,
|
||||
String dueDate, String status, String tester) {
|
||||
TestTask t = new TestTask();
|
||||
t.setTaskNo(taskNo);
|
||||
t.setDetectItem(detectItem);
|
||||
t.setMethodNo(methodNo);
|
||||
t.setSampleNo(sampleNo);
|
||||
t.setSampleName(sampleName);
|
||||
t.setPriority(priority);
|
||||
t.setDueDate(dueDate);
|
||||
t.setStatus(status);
|
||||
t.setTester(tester);
|
||||
t.setResultLocked(false);
|
||||
t.setResultSource("手动录入");
|
||||
t.setCreatedAt(Instant.now());
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.ComplianceAlertRule;
|
||||
import com.kaidi.oa.domain.HrNonCompete;
|
||||
import com.kaidi.oa.domain.LegalCreditInvest;
|
||||
import com.kaidi.oa.domain.LitigationCase;
|
||||
import com.kaidi.oa.repository.ComplianceAlertRuleRepository;
|
||||
import com.kaidi.oa.repository.HrNonCompeteRepository;
|
||||
import com.kaidi.oa.repository.LegalCreditInvestRepository;
|
||||
import com.kaidi.oa.repository.LitigationCaseRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 法务风险部演示数据播种器。
|
||||
*
|
||||
* 补完"活体返回 0 条/功能在但无数据演示"缺口:
|
||||
* 1. LitigationCase — 诉讼/仲裁案件(跨 5 种阶段/3种结果)
|
||||
* 2. HrNonCompete — 员工竞业限制(含离职补偿中/已违约/在职-未触发各状态)
|
||||
* 3. ComplianceAlertRule — 合规预警规则库(覆盖采购/销售/合同/人事/财务触发场景)
|
||||
* 4. LegalCreditInvest — 法务专项客户信用调查记录(含已结案驳回/进行中)
|
||||
*
|
||||
* @Order(20) 在 DataSeeder(@Order 1) 之后运行;幂等(count>0 则跳过)。
|
||||
*/
|
||||
@Order(20)
|
||||
@Component
|
||||
public class LegalDemoSeeder implements CommandLineRunner {
|
||||
|
||||
private final LitigationCaseRepository caseRepo;
|
||||
private final HrNonCompeteRepository nonCompeteRepo;
|
||||
private final ComplianceAlertRuleRepository ruleRepo;
|
||||
private final LegalCreditInvestRepository investRepo;
|
||||
|
||||
public LegalDemoSeeder(LitigationCaseRepository caseRepo,
|
||||
HrNonCompeteRepository nonCompeteRepo,
|
||||
ComplianceAlertRuleRepository ruleRepo,
|
||||
LegalCreditInvestRepository investRepo) {
|
||||
this.caseRepo = caseRepo;
|
||||
this.nonCompeteRepo = nonCompeteRepo;
|
||||
this.ruleRepo = ruleRepo;
|
||||
this.investRepo = investRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
seedLitigationCases();
|
||||
seedNonCompete();
|
||||
seedComplianceAlertRules();
|
||||
seedLegalCreditInvest();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 1. 诉讼/仲裁案件
|
||||
// ========================================================================
|
||||
private void seedLitigationCases() {
|
||||
if (caseRepo.count() > 0) return;
|
||||
|
||||
LitigationCase c1 = new LitigationCase();
|
||||
c1.setCaseNo("(2024)粤0305民初2241号");
|
||||
c1.setCaseName("凯迪科技诉深圳博拓工程有限公司合同纠纷案");
|
||||
c1.setCause("建设工程施工合同纠纷");
|
||||
c1.setOpponent("深圳博拓工程有限公司");
|
||||
c1.setOurRole("原告");
|
||||
c1.setTribunal("广州市天河区人民法院");
|
||||
c1.setAmount(Money.of(2850000.0));
|
||||
c1.setStage("开庭");
|
||||
c1.setLawyer("陈建国");
|
||||
c1.setLawFirm("广东广信君达律师事务所");
|
||||
c1.setFilingDate("2024-03-15");
|
||||
c1.setKeyDate("2025-08-20");
|
||||
c1.setResult("审理中");
|
||||
c1.setStatus("进行中");
|
||||
c1.setRemark("被告拖欠工程款285万元,已提交完整证据清单,等待开庭。");
|
||||
c1.setCreatedAt(Instant.now());
|
||||
|
||||
LitigationCase c2 = new LitigationCase();
|
||||
c2.setCaseNo("(2024)粤03民终8876号");
|
||||
c2.setCaseName("广州恒辉材料公司诉凯迪科技货款纠纷案");
|
||||
c2.setCause("买卖合同纠纷");
|
||||
c2.setOpponent("广州恒辉材料有限公司");
|
||||
c2.setOurRole("被告");
|
||||
c2.setTribunal("广州市中级人民法院");
|
||||
c2.setAmount(Money.of(680000.0));
|
||||
c2.setStage("已结案");
|
||||
c2.setLawyer("李晓燕");
|
||||
c2.setLawFirm("广东广信君达律师事务所");
|
||||
c2.setFilingDate("2023-11-08");
|
||||
c2.setKeyDate("2024-05-30");
|
||||
c2.setResult("调解结案");
|
||||
c2.setStatus("已结案");
|
||||
c2.setRemark("已达成调解协议,支付货款52万元,余款免除,2024年6月结案。");
|
||||
c2.setCreatedAt(Instant.now());
|
||||
|
||||
LitigationCase c3 = new LitigationCase();
|
||||
c3.setCaseNo("(2025)粤0305仲字0421号");
|
||||
c3.setCaseName("凯迪科技与华南水务集团技术服务合同仲裁案");
|
||||
c3.setCause("技术服务合同纠纷");
|
||||
c3.setOpponent("华南水务集团股份有限公司");
|
||||
c3.setOurRole("申请人");
|
||||
c3.setTribunal("广州仲裁委员会");
|
||||
c3.setAmount(Money.of(4200000.0));
|
||||
c3.setStage("举证");
|
||||
c3.setLawyer("王志远");
|
||||
c3.setLawFirm("北京金杜律师事务所广州分所");
|
||||
c3.setFilingDate("2025-01-20");
|
||||
c3.setKeyDate("2025-09-10");
|
||||
c3.setResult("审理中");
|
||||
c3.setStatus("进行中");
|
||||
c3.setRemark("涉及420万技术服务费,对方以验收未通过为由拒付,正在提交技术鉴定报告。");
|
||||
c3.setCreatedAt(Instant.now());
|
||||
|
||||
LitigationCase c4 = new LitigationCase();
|
||||
c4.setCaseNo("(2023)粤0305劳人仲字3304号");
|
||||
c4.setCaseName("王某某申请劳动仲裁案(竞业违约)");
|
||||
c4.setCause("劳动合同纠纷·竞业限制违约");
|
||||
c4.setOpponent("王某某");
|
||||
c4.setOurRole("被申请人");
|
||||
c4.setTribunal("广州市天河区劳动人事争议仲裁委员会");
|
||||
c4.setAmount(Money.of(45000.0));
|
||||
c4.setStage("已结案");
|
||||
c4.setLawyer("陈建国");
|
||||
c4.setLawFirm("广东广信君达律师事务所");
|
||||
c4.setFilingDate("2023-06-12");
|
||||
c4.setKeyDate("2023-09-28");
|
||||
c4.setResult("胜诉");
|
||||
c4.setStatus("已结案");
|
||||
c4.setRemark("仲裁委裁决申请人主张不成立,公司竞业补偿支付行为合规,无需额外赔偿。");
|
||||
c4.setCreatedAt(Instant.now());
|
||||
|
||||
LitigationCase c5 = new LitigationCase();
|
||||
c5.setCaseNo("(2025)粤0305民初5012号");
|
||||
c5.setCaseName("凯迪科技诉珠海绿科环保公司知识产权侵权案");
|
||||
c5.setCause("侵害发明专利权纠纷");
|
||||
c5.setOpponent("珠海绿科环保技术有限公司");
|
||||
c5.setOurRole("原告");
|
||||
c5.setTribunal("广州知识产权法院");
|
||||
c5.setAmount(Money.of(1500000.0));
|
||||
c5.setStage("立案");
|
||||
c5.setLawyer("李晓燕");
|
||||
c5.setLawFirm("广东广信君达律师事务所");
|
||||
c5.setFilingDate("2025-05-08");
|
||||
c5.setKeyDate("2025-11-15");
|
||||
c5.setResult("审理中");
|
||||
c5.setStatus("进行中");
|
||||
c5.setRemark("对方产品疑似侵犯ZL201910329876.4号发明专利权,已完成初步鉴定,立案受理。");
|
||||
c5.setCreatedAt(Instant.now());
|
||||
|
||||
caseRepo.save(c1);
|
||||
caseRepo.save(c2);
|
||||
caseRepo.save(c3);
|
||||
caseRepo.save(c4);
|
||||
caseRepo.save(c5);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 2. 员工竞业限制
|
||||
// ========================================================================
|
||||
private void seedNonCompete() {
|
||||
if (nonCompeteRepo.count() > 0) return;
|
||||
|
||||
HrNonCompete nc1 = new HrNonCompete();
|
||||
nc1.setEmployeeId("EMP-2019-0088");
|
||||
nc1.setEmployeeName("张伟强");
|
||||
nc1.setDept("技术研发部");
|
||||
nc1.setPosition("高级研发工程师");
|
||||
nc1.setRestrictionPeriod(24);
|
||||
nc1.setStartDate("2024-10-01");
|
||||
nc1.setEndDate("2026-09-30");
|
||||
nc1.setCompensationMonthly(Money.of(8500.0));
|
||||
nc1.setRestrictedIndustries("同类环保设备制造/同类污水处理技术/竞争对手企业:华南水务集团、珠海绿科、深圳博瑞");
|
||||
nc1.setStatus("离职-补偿中");
|
||||
nc1.setSignedDate("2024-09-28");
|
||||
nc1.setCreatedAt(Instant.now());
|
||||
|
||||
HrNonCompete nc2 = new HrNonCompete();
|
||||
nc2.setEmployeeId("EMP-2021-0145");
|
||||
nc2.setEmployeeName("刘雅婷");
|
||||
nc2.setDept("销售部");
|
||||
nc2.setPosition("高级销售经理");
|
||||
nc2.setRestrictionPeriod(12);
|
||||
nc2.setStartDate("2025-02-01");
|
||||
nc2.setEndDate("2026-01-31");
|
||||
nc2.setCompensationMonthly(Money.of(6000.0));
|
||||
nc2.setRestrictedIndustries("同类水处理设备销售、ERP系统集成销售;不得接触原公司客户资源");
|
||||
nc2.setStatus("离职-补偿中");
|
||||
nc2.setSignedDate("2025-01-28");
|
||||
nc2.setCreatedAt(Instant.now());
|
||||
|
||||
HrNonCompete nc3 = new HrNonCompete();
|
||||
nc3.setEmployeeId("EMP-2018-0033");
|
||||
nc3.setEmployeeName("王某某");
|
||||
nc3.setDept("技术研发部");
|
||||
nc3.setPosition("研发主任");
|
||||
nc3.setRestrictionPeriod(18);
|
||||
nc3.setStartDate("2023-05-01");
|
||||
nc3.setEndDate("2024-10-31");
|
||||
nc3.setCompensationMonthly(Money.of(12000.0));
|
||||
nc3.setRestrictedIndustries("所有同类污水处理技术研发机构及制造企业");
|
||||
nc3.setStatus("已违约");
|
||||
nc3.setViolationNote("2023年9月发现当事人入职珠海绿科环保技术有限公司担任技术总监,已提起劳动仲裁并胜诉。");
|
||||
nc3.setSignedDate("2023-04-25");
|
||||
nc3.setCreatedAt(Instant.now());
|
||||
|
||||
HrNonCompete nc4 = new HrNonCompete();
|
||||
nc4.setEmployeeId("EMP-2022-0201");
|
||||
nc4.setEmployeeName("陈明慧");
|
||||
nc4.setDept("财务部");
|
||||
nc4.setPosition("财务经理");
|
||||
nc4.setRestrictionPeriod(12);
|
||||
nc4.setStartDate("");
|
||||
nc4.setEndDate("");
|
||||
nc4.setCompensationMonthly(Money.of(5000.0));
|
||||
nc4.setRestrictedIndustries("同行业财务管理/同类ERP系统服务商");
|
||||
nc4.setStatus("在职-未触发");
|
||||
nc4.setSignedDate("2022-07-01");
|
||||
nc4.setCreatedAt(Instant.now());
|
||||
|
||||
HrNonCompete nc5 = new HrNonCompete();
|
||||
nc5.setEmployeeId("EMP-2020-0112");
|
||||
nc5.setEmployeeName("周晨光");
|
||||
nc5.setDept("市场部");
|
||||
nc5.setPosition("市场总监");
|
||||
nc5.setRestrictionPeriod(18);
|
||||
nc5.setStartDate("2023-11-15");
|
||||
nc5.setEndDate("2025-05-14");
|
||||
nc5.setCompensationMonthly(Money.of(9500.0));
|
||||
nc5.setRestrictedIndustries("水务行业市场推广,客户范围包括原有的50家签约客户");
|
||||
nc5.setStatus("已到期");
|
||||
nc5.setSignedDate("2023-11-10");
|
||||
nc5.setCreatedAt(Instant.now());
|
||||
|
||||
nonCompeteRepo.save(nc1);
|
||||
nonCompeteRepo.save(nc2);
|
||||
nonCompeteRepo.save(nc3);
|
||||
nonCompeteRepo.save(nc4);
|
||||
nonCompeteRepo.save(nc5);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 3. 合规预警规则库
|
||||
// ========================================================================
|
||||
private void seedComplianceAlertRules() {
|
||||
if (ruleRepo.count() > 0) return;
|
||||
|
||||
ComplianceAlertRule r1 = new ComplianceAlertRule();
|
||||
r1.setRuleCode("RULE-PROC-001");
|
||||
r1.setRuleName("单笔采购超预算比例预警");
|
||||
r1.setTriggerScene("采购");
|
||||
r1.setCondition("单笔采购金额超过年度采购预算的30%");
|
||||
r1.setThreshold("30");
|
||||
r1.setAlertLevel("高");
|
||||
r1.setEnabled(Boolean.TRUE);
|
||||
r1.setNotifyRoles("法务,合规专员,财务总监");
|
||||
r1.setTriggerCount(3);
|
||||
r1.setLastTriggeredAt("2025-04-20");
|
||||
r1.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r2 = new ComplianceAlertRule();
|
||||
r2.setRuleCode("RULE-SALE-001");
|
||||
r2.setRuleName("销售合同客户涉诉风险预警");
|
||||
r2.setTriggerScene("销售");
|
||||
r2.setCondition("新签销售合同甲方在法务诉讼台账中存在未结案的被告案件");
|
||||
r2.setThreshold("");
|
||||
r2.setAlertLevel("高");
|
||||
r2.setEnabled(Boolean.TRUE);
|
||||
r2.setNotifyRoles("法务,销售总监,总经理");
|
||||
r2.setTriggerCount(1);
|
||||
r2.setLastTriggeredAt("2025-03-12");
|
||||
r2.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r3 = new ComplianceAlertRule();
|
||||
r3.setRuleCode("RULE-CONTRACT-001");
|
||||
r3.setRuleName("合同无授权审批预警");
|
||||
r3.setTriggerScene("合同");
|
||||
r3.setCondition("合同金额超出签署人授权限额,未经上级审批");
|
||||
r3.setThreshold("500000");
|
||||
r3.setAlertLevel("紧急");
|
||||
r3.setEnabled(Boolean.TRUE);
|
||||
r3.setNotifyRoles("法务,总经理,董事会秘书");
|
||||
r3.setTriggerCount(0);
|
||||
r3.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r4 = new ComplianceAlertRule();
|
||||
r4.setRuleCode("RULE-HR-001");
|
||||
r4.setRuleName("高岗位员工离职竞业限制触发预警");
|
||||
r4.setTriggerScene("人事");
|
||||
r4.setCondition("研发/销售/财务等关键岗位人员离职,竞业限制协议状态仍为「在职-未触发」");
|
||||
r4.setThreshold("");
|
||||
r4.setAlertLevel("高");
|
||||
r4.setEnabled(Boolean.TRUE);
|
||||
r4.setNotifyRoles("法务,HR总监");
|
||||
r4.setTriggerCount(2);
|
||||
r4.setLastTriggeredAt("2025-02-28");
|
||||
r4.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r5 = new ComplianceAlertRule();
|
||||
r5.setRuleCode("RULE-FIN-001");
|
||||
r5.setRuleName("大额资金异常转账预警");
|
||||
r5.setTriggerScene("财务");
|
||||
r5.setCondition("单笔对外付款超过200万元且无合同关联");
|
||||
r5.setThreshold("2000000");
|
||||
r5.setAlertLevel("紧急");
|
||||
r5.setEnabled(Boolean.TRUE);
|
||||
r5.setNotifyRoles("法务,财务总监,总经理");
|
||||
r5.setTriggerCount(0);
|
||||
r5.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r6 = new ComplianceAlertRule();
|
||||
r6.setRuleCode("RULE-IP-001");
|
||||
r6.setRuleName("知识产权年费到期未缴预警");
|
||||
r6.setTriggerScene("知识产权");
|
||||
r6.setCondition("专利年费缴纳截止日前30天,费用状态仍为待缴");
|
||||
r6.setThreshold("30");
|
||||
r6.setAlertLevel("中");
|
||||
r6.setEnabled(Boolean.TRUE);
|
||||
r6.setNotifyRoles("法务,知识产权专员");
|
||||
r6.setTriggerCount(5);
|
||||
r6.setLastTriggeredAt("2025-05-31");
|
||||
r6.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r7 = new ComplianceAlertRule();
|
||||
r7.setRuleCode("RULE-PROC-002");
|
||||
r7.setRuleName("制裁名单供应商交易预警");
|
||||
r7.setTriggerScene("采购");
|
||||
r7.setCondition("采购供应商名称出现在公司维护的制裁名单/黑名单中");
|
||||
r7.setThreshold("");
|
||||
r7.setAlertLevel("紧急");
|
||||
r7.setEnabled(Boolean.TRUE);
|
||||
r7.setNotifyRoles("法务,合规专员,采购总监");
|
||||
r7.setTriggerCount(0);
|
||||
r7.setCreatedAt(Instant.now());
|
||||
|
||||
ComplianceAlertRule r8 = new ComplianceAlertRule();
|
||||
r8.setRuleCode("RULE-ENVI-001");
|
||||
r8.setRuleName("环保违规风险预警");
|
||||
r8.setTriggerScene("其他");
|
||||
r8.setCondition("环保监测数据超标且无整改记录,可能产生行政处罚");
|
||||
r8.setThreshold("");
|
||||
r8.setAlertLevel("高");
|
||||
r8.setEnabled(Boolean.TRUE);
|
||||
r8.setNotifyRoles("法务,安全环保部,总经理");
|
||||
r8.setTriggerCount(1);
|
||||
r8.setLastTriggeredAt("2024-12-15");
|
||||
r8.setCreatedAt(Instant.now());
|
||||
|
||||
ruleRepo.save(r1);
|
||||
ruleRepo.save(r2);
|
||||
ruleRepo.save(r3);
|
||||
ruleRepo.save(r4);
|
||||
ruleRepo.save(r5);
|
||||
ruleRepo.save(r6);
|
||||
ruleRepo.save(r7);
|
||||
ruleRepo.save(r8);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 4. 法务专项客户信用调查记录
|
||||
// ========================================================================
|
||||
private void seedLegalCreditInvest() {
|
||||
if (investRepo.count() > 0) return;
|
||||
|
||||
LegalCreditInvest inv1 = new LegalCreditInvest();
|
||||
inv1.setCustomerName("华南水务集团股份有限公司");
|
||||
inv1.setInvestigationType("综合调查");
|
||||
inv1.setScope("工商信息、涉诉记录、行政处罚、失信被执行人");
|
||||
inv1.setCourtRecord("存在3起历史诉讼,已全部结案,其中1起因工程款纠纷败诉");
|
||||
inv1.setBlacklisted(false);
|
||||
inv1.setLitigationHistory("2022年(2022)粤03民终4421号-货款纠纷(结案胜诉);\n2021年(2021)粤0305民初1183号-建工合同(调解结案)");
|
||||
inv1.setCreditScore("AA");
|
||||
inv1.setInvestigator("陈建国");
|
||||
inv1.setTargetCompletionDate("2025-02-28");
|
||||
inv1.setStatus("已结案");
|
||||
inv1.setConclusion("通过");
|
||||
inv1.setConclusionOpinion("整体信用状况良好,历史诉讼均已结案,无失信记录,可建立长期合作关系,建议合同金额不超过2000万元。");
|
||||
inv1.setConclusionDate("2025-02-26");
|
||||
inv1.setRemark("该客户为粤港澳大湾区重点国有水务企业,授信风险可控。");
|
||||
inv1.setCreatedAt(Instant.now());
|
||||
|
||||
LegalCreditInvest inv2 = new LegalCreditInvest();
|
||||
inv2.setCustomerName("珠海绿科环保技术有限公司");
|
||||
inv2.setInvestigationType("诉讼记录核查");
|
||||
inv2.setScope("全量涉诉记录及行政处罚记录");
|
||||
inv2.setCourtRecord("存在5起正在进行的诉讼,其中2起为知识产权侵权指控");
|
||||
inv2.setBlacklisted(false);
|
||||
inv2.setLitigationHistory("(2025)粤0305民初5012号-专利侵权(进行中);\n(2024)粤03民终6601号-技术转让纠纷(进行中)");
|
||||
inv2.setCreditScore("B");
|
||||
inv2.setInvestigator("李晓燕");
|
||||
inv2.setTargetCompletionDate("2025-06-15");
|
||||
inv2.setStatus("已结案");
|
||||
inv2.setConclusion("驳回");
|
||||
inv2.setConclusionOpinion("该公司存在多起知识产权侵权诉讼记录,与我司涉及专利侵权案件,建议中止合作谈判,列入法务重点监控名单。");
|
||||
inv2.setConclusionDate("2025-06-10");
|
||||
inv2.setRemark("已通知销售部暂停与该公司的合作洽谈,销售合同系统已标记风险。");
|
||||
inv2.setCreatedAt(Instant.now());
|
||||
|
||||
LegalCreditInvest inv3 = new LegalCreditInvest();
|
||||
inv3.setCustomerName("深圳博拓工程有限公司");
|
||||
inv3.setInvestigationType("失信被执行人核查");
|
||||
inv3.setScope("最高人民法院失信被执行人名单、执行案件记录");
|
||||
inv3.setCourtRecord("查询到2条失信被执行人记录,涉及2023年至今未履行的建工合同判决");
|
||||
inv3.setBlacklisted(true);
|
||||
inv3.setLitigationHistory("(2024)粤0305民初2241号-合同纠纷(进行中/我司原告);\n2023年被列入失信被执行人名单");
|
||||
inv3.setCreditScore("D");
|
||||
inv3.setInvestigator("陈建国");
|
||||
inv3.setTargetCompletionDate("2025-04-30");
|
||||
inv3.setStatus("已结案");
|
||||
inv3.setConclusion("驳回");
|
||||
inv3.setConclusionOpinion("该企业已被列入失信被执行人名单,建议冻结合作、追缴应收款,已向财务部出具法律意见书。");
|
||||
inv3.setConclusionDate("2025-04-25");
|
||||
inv3.setRemark("已同步供应商黑名单,采购系统已拦截该供应商新增采购订单。");
|
||||
inv3.setCreatedAt(Instant.now());
|
||||
|
||||
LegalCreditInvest inv4 = new LegalCreditInvest();
|
||||
inv4.setCustomerName("广州恒盛科技投资有限公司");
|
||||
inv4.setInvestigationType("工商信用核查");
|
||||
inv4.setScope("工商注册信息、股权结构、企业年报、司法拍卖记录");
|
||||
inv4.setCourtRecord("未发现涉诉记录");
|
||||
inv4.setBlacklisted(false);
|
||||
inv4.setLitigationHistory("无历史诉讼记录");
|
||||
inv4.setCreditScore("AAA");
|
||||
inv4.setInvestigator("王志远");
|
||||
inv4.setTargetCompletionDate("2025-07-31");
|
||||
inv4.setStatus("调查中");
|
||||
inv4.setRemark("新引入战略合作客户,正在开展全面尽职调查,预计下月出具调查报告。");
|
||||
inv4.setCreatedAt(Instant.now());
|
||||
|
||||
investRepo.save(inv1);
|
||||
investRepo.save(inv2);
|
||||
investRepo.save(inv3);
|
||||
investRepo.save(inv4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.ComplianceAnonReport;
|
||||
import com.kaidi.oa.domain.LegalPolicyRevision;
|
||||
import com.kaidi.oa.repository.ComplianceAnonReportRepository;
|
||||
import com.kaidi.oa.repository.LegalPolicyRevisionRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 制度修订审批 + 匿名合规举报演示数据播种器(法务风险部)。
|
||||
*
|
||||
* 补齐缺口 2(匿名举报)和缺口 8(制度修订 OA 引擎接入)的活体演示数据。
|
||||
* @Order(21),在 LegalDemoSeeder(@Order 20) 之后;幂等。
|
||||
*/
|
||||
@Order(21)
|
||||
@Component
|
||||
public class LegalPolicyAnonSeeder implements CommandLineRunner {
|
||||
|
||||
private final LegalPolicyRevisionRepository revisionRepo;
|
||||
private final ComplianceAnonReportRepository anonReportRepo;
|
||||
|
||||
public LegalPolicyAnonSeeder(LegalPolicyRevisionRepository revisionRepo,
|
||||
ComplianceAnonReportRepository anonReportRepo) {
|
||||
this.revisionRepo = revisionRepo;
|
||||
this.anonReportRepo = anonReportRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
seedPolicyRevisions();
|
||||
seedAnonReports();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 1. 制度修订申请(跨状态:草稿/审批中/已生效/已废止)
|
||||
// ======================================================================
|
||||
private void seedPolicyRevisions() {
|
||||
if (revisionRepo.count() > 0) return;
|
||||
|
||||
LegalPolicyRevision r1 = new LegalPolicyRevision();
|
||||
r1.setRevisionNo("PR-2025-001");
|
||||
r1.setPolicyNo("ZD-HR-015");
|
||||
r1.setTitle("员工竞业限制与保密协议管理制度");
|
||||
r1.setRevisionType("修订");
|
||||
r1.setCategory("管理制度");
|
||||
r1.setContent("根据2024年《劳动合同法》司法解释新规,修订竞业限制补偿标准为月薪30%,并扩展限制范围至供应商合作关系,增加违约金双倍赔偿条款。");
|
||||
r1.setReason("最高法院2024年发布关于竞业限制纠纷案件审理的新司法解释,原制度与新规不符,存在合规风险。");
|
||||
r1.setChangePoints("1. 补偿标准:≥月薪25% → 月薪30%; 2. 新增:禁止入职供应商; 3. 违约赔偿:1倍 → 2倍月薪×限制月数");
|
||||
r1.setApplicantDept("法务部");
|
||||
r1.setApplicant("陈建国");
|
||||
r1.setEffectiveDate("2025-07-01");
|
||||
r1.setStatus("已生效");
|
||||
r1.setCreatedAt(Instant.now());
|
||||
r1.setUpdatedAt(Instant.now());
|
||||
|
||||
LegalPolicyRevision r2 = new LegalPolicyRevision();
|
||||
r2.setRevisionNo("PR-2025-002");
|
||||
r2.setPolicyNo("ZD-PROC-008");
|
||||
r2.setTitle("采购合同授权审批管理办法");
|
||||
r2.setRevisionType("修订");
|
||||
r2.setCategory("授权规则");
|
||||
r2.setContent("调整各级授权金额上限:部门经理 50万→80万、总经理 200万→300万,新增电子合同签署授权,明确超授权行为追责机制。");
|
||||
r2.setReason("原授权标准制定于2022年,随公司业务规模扩大,现有金额上限已不匹配实际需求,导致大量合同需要越级审批,影响效率。");
|
||||
r2.setChangePoints("1. 部门经理授权额度上调至80万; 2. 总经理上调至300万; 3. 新增电子签章授权流程; 4. 超授权追责条款细化");
|
||||
r2.setApplicantDept("法务部");
|
||||
r2.setApplicant("李晓燕");
|
||||
r2.setEffectiveDate("2025-09-01");
|
||||
r2.setStatus("审批中");
|
||||
r2.setCreatedAt(Instant.now());
|
||||
r2.setUpdatedAt(Instant.now());
|
||||
|
||||
LegalPolicyRevision r3 = new LegalPolicyRevision();
|
||||
r3.setRevisionNo("PR-2025-003");
|
||||
r3.setPolicyNo("ZD-DATA-003");
|
||||
r3.setTitle("数据安全与个人信息保护管理制度");
|
||||
r3.setRevisionType("新建");
|
||||
r3.setCategory("管理制度");
|
||||
r3.setContent("根据《数据安全法》《个人信息保护法》制度要求,新建数据安全管理制度,覆盖数据分级保护、访问控制、泄露应急响应、删除和销毁规程。");
|
||||
r3.setReason("我司目前尚无专项数据安全管理制度,监管机构检查时被要求整改。《个保法》实施已近4年,该项制度为合规基础要求。");
|
||||
r3.setChangePoints("全新制度,主要内容:数据分级(公开/内部/机密/绝密)、敏感数据访问须双人核准、个人信息去标识化要求、数据出境审查流程");
|
||||
r3.setApplicantDept("法务部");
|
||||
r3.setApplicant("王志远");
|
||||
r3.setEffectiveDate("2025-10-01");
|
||||
r3.setStatus("草稿");
|
||||
r3.setCreatedAt(Instant.now());
|
||||
r3.setUpdatedAt(Instant.now());
|
||||
|
||||
LegalPolicyRevision r4 = new LegalPolicyRevision();
|
||||
r4.setRevisionNo("PR-2024-006");
|
||||
r4.setPolicyNo("ZD-PROC-003");
|
||||
r4.setTitle("物资采购管理流程(2020版)");
|
||||
r4.setRevisionType("废止");
|
||||
r4.setCategory("业务流程");
|
||||
r4.setContent("原2020年版物资采购流程已由2024年新版本取代,正式废止旧版本,防止双版本并行导致执行混乱。");
|
||||
r4.setReason("2024年采购管理流程完成修订并发布,原版本已过时,需正式废止以避免混用。");
|
||||
r4.setChangePoints("无变更,仅为废止旧版本。");
|
||||
r4.setApplicantDept("采购部");
|
||||
r4.setApplicant("赵明华");
|
||||
r4.setDeprecationDate("2025-01-01");
|
||||
r4.setStatus("已废止");
|
||||
r4.setCreatedAt(Instant.now());
|
||||
r4.setUpdatedAt(Instant.now());
|
||||
|
||||
LegalPolicyRevision r5 = new LegalPolicyRevision();
|
||||
r5.setRevisionNo("PR-2025-004");
|
||||
r5.setPolicyNo("ZD-HR-022");
|
||||
r5.setTitle("高管绩效激励与股权激励管理办法");
|
||||
r5.setRevisionType("修订");
|
||||
r5.setCategory("管理制度");
|
||||
r5.setContent("引入限制性股票激励计划,调整高管年度绩效考核权重(短期绩效40%+长期价值60%),与公司战略转型目标挂钩。");
|
||||
r5.setReason("公司战略转型需要更长期的激励机制,现有以年为周期的绩效考核已不足以激励高管关注长期价值创造。");
|
||||
r5.setChangePoints("1. 新增限制性股票激励计划(3年期); 2. 绩效权重调整; 3. 退出机制完善(反稀释条款)");
|
||||
r5.setApplicantDept("战略发展部");
|
||||
r5.setApplicant("张勇");
|
||||
r5.setEffectiveDate("2025-12-01");
|
||||
r5.setStatus("已驳回");
|
||||
r5.setRemark("董事会审议认为当前不宜引入股权激励,建议待上市计划明确后重新评估,本次申请驳回。");
|
||||
r5.setCreatedAt(Instant.now());
|
||||
r5.setUpdatedAt(Instant.now());
|
||||
|
||||
revisionRepo.save(r1);
|
||||
revisionRepo.save(r2);
|
||||
revisionRepo.save(r3);
|
||||
revisionRepo.save(r4);
|
||||
revisionRepo.save(r5);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 2. 匿名合规举报(跨状态:待受理/调查中/已处理/已关闭)
|
||||
// ======================================================================
|
||||
private void seedAnonReports() {
|
||||
if (anonReportRepo.count() > 0) return;
|
||||
|
||||
ComplianceAnonReport a1 = new ComplianceAnonReport();
|
||||
a1.setReportNo("AR-2025-001");
|
||||
a1.setReportType("财务异常");
|
||||
a1.setSuspectDept("采购部");
|
||||
a1.setSuspectPerson("某采购经理");
|
||||
a1.setReportContent("发现某采购经理与特定供应商存在利益输送:该供应商报价始终明显高于市场价15-20%,但每次仍中标,且该采购经理与供应商负责人有私下宴请记录。建议调查该供应商的实际控制人与采购部人员的关联关系。");
|
||||
a1.setReporterInfo("(匿名举报,不留联系方式)");
|
||||
a1.setSeverity("严重");
|
||||
a1.setStatus("调查中");
|
||||
a1.setHandler("陈建国");
|
||||
a1.setHandleResult("已启动内部调查,正在核查采购台账及供应商关系,预计2025年8月底完成初步报告。");
|
||||
a1.setQueryToken("a1b2c3d4e5f6g7h8");
|
||||
a1.setCreatedAt(Instant.now());
|
||||
a1.setUpdatedAt(Instant.now());
|
||||
|
||||
ComplianceAnonReport a2 = new ComplianceAnonReport();
|
||||
a2.setReportNo("AR-2025-002");
|
||||
a2.setReportType("安全隐患");
|
||||
a2.setSuspectDept("生产车间");
|
||||
a2.setSuspectPerson("(部门整体)");
|
||||
a2.setReportContent("生产车间B区消防通道长期被货架占用,消防设施存在遮挡,安全出口标识灯不亮。已发现超过3个月,多次口头提醒但无人处理,担心发生事故时无法及时疏散。");
|
||||
a2.setReporterInfo("可以通过内网邮件联系:anon@kaidi.internal");
|
||||
a2.setSeverity("紧急");
|
||||
a2.setStatus("已处理");
|
||||
a2.setHandler("王志远");
|
||||
a2.setHandleResult("已通知安全生产部现场整改。2025年6月15日复查确认:消防通道已清空,消防设施遮挡已移除,安全出口标识灯全部更换完毕。记录为安全隐患整改案例,纳入月度安全检查。");
|
||||
a2.setQueryToken("h8g7f6e5d4c3b2a1");
|
||||
a2.setCreatedAt(Instant.now());
|
||||
a2.setUpdatedAt(Instant.now());
|
||||
|
||||
ComplianceAnonReport a3 = new ComplianceAnonReport();
|
||||
a3.setReportNo("AR-2025-003");
|
||||
a3.setReportType("劳动违规");
|
||||
a3.setSuspectDept("研发部");
|
||||
a3.setSuspectPerson("(不便透露)");
|
||||
a3.setReportContent("研发部存在长期强制加班未支付加班费的情况,员工打卡记录被删除,绩效考核与加班时长挂钩形成隐性强制。希望公司核查过去12个月的加班记录和薪酬发放情况。");
|
||||
a3.setReporterInfo("");
|
||||
a3.setSeverity("严重");
|
||||
a3.setStatus("待受理");
|
||||
a3.setQueryToken("z9y8x7w6v5u4t3s2");
|
||||
a3.setCreatedAt(Instant.now());
|
||||
a3.setUpdatedAt(Instant.now());
|
||||
|
||||
ComplianceAnonReport a4 = new ComplianceAnonReport();
|
||||
a4.setReportNo("AR-2024-009");
|
||||
a4.setReportType("合规违规");
|
||||
a4.setSuspectDept("市场部");
|
||||
a4.setSuspectPerson("(团队行为)");
|
||||
a4.setReportContent("市场部在政府项目投标中存在提前获取标底信息的行为,怀疑有内线。已有2次投标中标价格与标底极为接近,偏差在0.3%以内,统计上不太可能出现。");
|
||||
a4.setReporterInfo("");
|
||||
a4.setSeverity("严重");
|
||||
a4.setStatus("已关闭");
|
||||
a4.setHandler("李晓燕");
|
||||
a4.setHandleResult("经调查,未发现实质性证据表明存在非法获取标底行为。2次中标均有合理解释(市场调研能力强+竞争对手报价较保守)。将加强投标廉洁合规培训,本案关闭。");
|
||||
a4.setQueryToken("r2q1p0o9n8m7l6k5");
|
||||
a4.setCreatedAt(Instant.now());
|
||||
a4.setUpdatedAt(Instant.now());
|
||||
|
||||
anonReportRepo.save(a1);
|
||||
anonReportRepo.save(a2);
|
||||
anonReportRepo.save(a3);
|
||||
anonReportRepo.save(a4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.MarketCampaign;
|
||||
import com.kaidi.oa.repository.MarketCampaignRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 市场推广活动演示数据(需求 §2 AI 营销复盘 — 补全缺口:活体无数据)。
|
||||
*
|
||||
* 为 campaign-review / funnel / summary 三个端点提供真实演示数据,
|
||||
* 让 AI 营销复盘结论能从真实记录自动聚合填入,覆盖"接口骨架但无数据联动"缺口。
|
||||
*
|
||||
* 数据覆盖:2025-2026 年多类型活动(展会/推介会/线上)、多状态(筹备中/进行中/已结束)、
|
||||
* 合理的预算金额(BigDecimal)和商机数量,供漏斗可视化和 AI 复盘分析使用。
|
||||
*
|
||||
* @Order(10) — 在 DataSeeder(@Order 1) 之后独立运行,幂等(count > 0 则跳过)。
|
||||
*/
|
||||
@Component
|
||||
@Order(10)
|
||||
public class MarketCampaignSeeder implements ApplicationRunner {
|
||||
|
||||
private final MarketCampaignRepository campaignRepo;
|
||||
|
||||
public MarketCampaignSeeder(MarketCampaignRepository campaignRepo) {
|
||||
this.campaignRepo = campaignRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
if (campaignRepo.count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<MarketCampaign> seeds = new ArrayList<>();
|
||||
|
||||
// 2025 年活动(已结束)
|
||||
seeds.add(campaign("2025年华东区建筑行业技术推介会", "推介会", "王建国", "市场部·华东办",
|
||||
"68000.00", "2025-03-15", 12, "已结束"));
|
||||
|
||||
seeds.add(campaign("2025年全国城市基础设施展(CIIF)", "展会", "李雪梅", "市场部",
|
||||
"152000.00", "2025-04-20", 28, "已结束"));
|
||||
|
||||
seeds.add(campaign("2025年Q2线上产品发布直播", "线上", "张伟", "市场部·数字化",
|
||||
"18500.00", "2025-05-10", 35, "已结束"));
|
||||
|
||||
seeds.add(campaign("2025年华南区政府BT/BOT项目推介", "推介会", "陈志远", "市场部·华南办",
|
||||
"45000.00", "2025-07-08", 9, "已结束"));
|
||||
|
||||
seeds.add(campaign("2025年下半年建材供应链展览会", "展会", "刘芳芳", "市场部",
|
||||
"88000.00", "2025-09-18", 21, "已结束"));
|
||||
|
||||
seeds.add(campaign("2025年Q4年终客户答谢推介会", "推介会", "王建国", "市场部",
|
||||
"55000.00", "2025-11-25", 16, "已结束"));
|
||||
|
||||
// 2026 年活动(进行中 / 筹备中)
|
||||
seeds.add(campaign("2026年智慧城市与绿色建筑论坛", "展会", "李雪梅", "市场部",
|
||||
"198000.00", "2026-03-22", 42, "已结束"));
|
||||
|
||||
seeds.add(campaign("2026年Q1区域市政项目对接会(西南)", "推介会", "赵明磊", "市场部·西南办",
|
||||
"36000.00", "2026-04-05", 8, "已结束"));
|
||||
|
||||
seeds.add(campaign("2026年线上招投标政策宣讲直播", "线上", "张伟", "市场部·数字化",
|
||||
"12000.00", "2026-05-16", 29, "进行中"));
|
||||
|
||||
seeds.add(campaign("2026年重点客户战略合作伙伴峰会", "推介会", "陈志远", "市场部",
|
||||
"120000.00", "2026-06-18", 18, "进行中"));
|
||||
|
||||
seeds.add(campaign("2026年下半年省级招标采购博览会", "展会", "王建国", "市场部",
|
||||
"165000.00", "2026-09-10", 0, "筹备中"));
|
||||
|
||||
seeds.add(campaign("2026年双11工程建材线上优惠专场", "线上", "刘芳芳", "市场部·数字化",
|
||||
"22000.00", "2026-11-01", 0, "筹备中"));
|
||||
|
||||
campaignRepo.saveAll(seeds);
|
||||
}
|
||||
|
||||
private MarketCampaign campaign(String theme, String type, String owner, String orgUnit,
|
||||
String budgetYuan, String holdDate, int leads, String status) {
|
||||
MarketCampaign c = new MarketCampaign();
|
||||
c.setTheme(theme);
|
||||
c.setCampaignType(type);
|
||||
c.setOwner(owner);
|
||||
c.setOrgUnit(orgUnit);
|
||||
c.setBudget(Money.of(Double.parseDouble(budgetYuan)));
|
||||
c.setHoldDate(holdDate);
|
||||
c.setLeads(leads);
|
||||
c.setStatus(status);
|
||||
c.setCreatedAt(Instant.now());
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.RdDeclPlan;
|
||||
import com.kaidi.oa.repository.RdDeclPlanRepository;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 申报服务部——年度/季度申报计划种子数据(Gap 1 [low] 补完)。
|
||||
*
|
||||
* 原缺口:rd-decl-plans 台账为 0 条数据,前端页面 rddeclplan.vue 虽已存在但列表空白。
|
||||
* 本 Seeder 在表为空时注入 4 条演示计划(跨状态:执行中/已批准/草稿/已完成),
|
||||
* 使 rddeclplan 页面首次打开即有数据可见。
|
||||
*
|
||||
* 注意:本 seeder 独立于 DataSeeder(Order=2 在 DataSeeder Order=1 之后运行),
|
||||
* 不修改 DataSeeder 的大构造函数。
|
||||
*/
|
||||
@Component
|
||||
@Order(2)
|
||||
public class RdDeclPlanSeeder implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RdDeclPlanSeeder.class);
|
||||
|
||||
private final RdDeclPlanRepository planRepo;
|
||||
|
||||
public RdDeclPlanSeeder(RdDeclPlanRepository planRepo) {
|
||||
this.planRepo = planRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (planRepo.count() > 0) {
|
||||
return; // 已有数据,跳过
|
||||
}
|
||||
log.info("Seeding RdDeclPlan demonstration data...");
|
||||
|
||||
// 1. 执行中:高新技术企业认定申报(主力计划)
|
||||
RdDeclPlan p1 = new RdDeclPlan();
|
||||
p1.setName("2026年度高新技术企业认定申报计划");
|
||||
p1.setPeriodType("年度");
|
||||
p1.setPlanYear(2026);
|
||||
p1.setPolicyCategory("资质类");
|
||||
p1.setProgramName("高新技术企业");
|
||||
p1.setDepartment("申报服务部");
|
||||
p1.setOwner("李申报");
|
||||
p1.setBudget(Money.of(80000.0));
|
||||
p1.setExpectedBenefit(Money.of(500000.0));
|
||||
p1.setPlanApplyDate("2026-08-01");
|
||||
p1.setPlanEndDate("2026-11-30");
|
||||
p1.setStatus("执行中");
|
||||
p1.setProgress(45);
|
||||
p1.setRemark("每三年复评一次,本次为第二轮;需财务审计报告+知识产权数量满足要求");
|
||||
p1.setCreatedAt(Instant.now());
|
||||
p1.setUpdatedAt(Instant.now());
|
||||
planRepo.save(p1);
|
||||
|
||||
// 2. 已批准:专精特新中小企业申报
|
||||
RdDeclPlan p2 = new RdDeclPlan();
|
||||
p2.setName("2026年度专精特新中小企业认定申报计划");
|
||||
p2.setPeriodType("年度");
|
||||
p2.setPlanYear(2026);
|
||||
p2.setPolicyCategory("资质类");
|
||||
p2.setProgramName("专精特新中小企业");
|
||||
p2.setDepartment("申报服务部");
|
||||
p2.setOwner("王申报");
|
||||
p2.setBudget(Money.of(30000.0));
|
||||
p2.setExpectedBenefit(Money.of(200000.0));
|
||||
p2.setPlanApplyDate("2026-09-01");
|
||||
p2.setPlanEndDate("2026-12-31");
|
||||
p2.setStatus("已批准");
|
||||
p2.setProgress(0);
|
||||
p2.setRemark("省级专精特新首次申报,需研发费用占比≥3%、知识产权≥2项");
|
||||
p2.setCreatedAt(Instant.now());
|
||||
p2.setUpdatedAt(Instant.now());
|
||||
planRepo.save(p2);
|
||||
|
||||
// 3. 草稿:研发费用加计扣除季度申请
|
||||
RdDeclPlan p3 = new RdDeclPlan();
|
||||
p3.setName("2026年Q3研发费用加计扣除预缴申请计划");
|
||||
p3.setPeriodType("季度");
|
||||
p3.setPlanYear(2026);
|
||||
p3.setPlanQuarter("Q3");
|
||||
p3.setPolicyCategory("资金类");
|
||||
p3.setProgramName("研发费用加计扣除");
|
||||
p3.setDepartment("申报服务部");
|
||||
p3.setOwner("张申报");
|
||||
p3.setBudget(Money.of(5000.0));
|
||||
p3.setExpectedBenefit(Money.of(120000.0));
|
||||
p3.setPlanApplyDate("2026-07-15");
|
||||
p3.setPlanEndDate("2026-07-31");
|
||||
p3.setStatus("草稿");
|
||||
p3.setProgress(0);
|
||||
p3.setRemark("每季度预缴税款时享受,需财务提供研发费用辅助账");
|
||||
p3.setCreatedAt(Instant.now());
|
||||
p3.setUpdatedAt(Instant.now());
|
||||
planRepo.save(p3);
|
||||
|
||||
// 4. 已完成:科技型中小企业评价(已结案)
|
||||
RdDeclPlan p4 = new RdDeclPlan();
|
||||
p4.setName("2025年度科技型中小企业评价申报计划");
|
||||
p4.setPeriodType("年度");
|
||||
p4.setPlanYear(2025);
|
||||
p4.setPolicyCategory("资质类");
|
||||
p4.setProgramName("科技型中小企业");
|
||||
p4.setDepartment("申报服务部");
|
||||
p4.setOwner("李申报");
|
||||
p4.setBudget(Money.of(10000.0));
|
||||
p4.setExpectedBenefit(Money.of(80000.0));
|
||||
p4.setPlanApplyDate("2025-05-01");
|
||||
p4.setPlanEndDate("2025-06-30");
|
||||
p4.setStatus("已完成");
|
||||
p4.setProgress(100);
|
||||
p4.setRemark("已完成在科技部门户网站填报并获评,有效期至2026年底");
|
||||
p4.setCreatedAt(Instant.now().minusSeconds(86400L * 180));
|
||||
p4.setUpdatedAt(Instant.now().minusSeconds(86400L * 30));
|
||||
planRepo.save(p4);
|
||||
|
||||
log.info("Seeded {} RdDeclPlan records.", planRepo.count());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.domain.SewageCostSavingProject;
|
||||
import com.kaidi.oa.domain.SewagePlantBenchmark;
|
||||
import com.kaidi.oa.repository.SewageCostSavingProjectRepository;
|
||||
import com.kaidi.oa.repository.SewagePlantBenchmarkRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 城镇污水运营中心·横向对标 + 降本增效项目台账 演示种子数据。
|
||||
*
|
||||
* 补深审计 low 缺口(8.绩效与成本分析):
|
||||
* 1. SewagePlantBenchmark:同区域同类水厂外部横向对标数据(6家水厂,含本厂,湖南区域一级A口径);
|
||||
* 2. SewageCostSavingProject:节能技改项目独立台账(5个项目,多状态多类型,含基期+实际节约额自动计算)。
|
||||
*
|
||||
* 幂等:各表 count == 0 才插入。
|
||||
*/
|
||||
@Component
|
||||
@Order(206)
|
||||
public class SewageBenchmarkSeeder implements ApplicationRunner {
|
||||
|
||||
private final SewagePlantBenchmarkRepository benchmarkRepo;
|
||||
private final SewageCostSavingProjectRepository savingRepo;
|
||||
|
||||
public SewageBenchmarkSeeder(SewagePlantBenchmarkRepository benchmarkRepo,
|
||||
SewageCostSavingProjectRepository savingRepo) {
|
||||
this.benchmarkRepo = benchmarkRepo;
|
||||
this.savingRepo = savingRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedBenchmarks();
|
||||
seedCostSavingProjects();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 同类水厂横向对标
|
||||
|
||||
private void seedBenchmarks() {
|
||||
if (benchmarkRepo.count() > 0) return;
|
||||
|
||||
// 本厂(凯迪科技·城镇污水一厂,标注 isSelf=true)
|
||||
benchmark("凯迪科技城镇污水一厂", "湖南省·岳阳市", 5.0, "一级A", "2025",
|
||||
0.312, 18.5, "0.058", "1.85",
|
||||
97.2, 96.8, "自填", true, "王芳");
|
||||
|
||||
// 区域同类水厂 A(处理规模相近)
|
||||
benchmark("岳阳滨水污水处理有限公司", "湖南省·岳阳市", 4.0, "一级A", "2025",
|
||||
0.298, 16.2, "0.052", "1.72",
|
||||
98.1, 97.5, "政府公开", false, "刘洋");
|
||||
|
||||
// 区域同类水厂 B(区域最优,吨水电耗最低)
|
||||
benchmark("岳阳君山水务股份有限公司", "湖南省·岳阳市", 6.0, "一级A", "2025",
|
||||
0.275, 15.8, "0.048", "1.63",
|
||||
98.5, 98.2, "行业报告·住建部2025年城镇排水年报", false, "刘洋");
|
||||
|
||||
// 区域同类水厂 C(规模稍大)
|
||||
benchmark("湘阴城乡水务集团第二污水厂", "湖南省·岳阳市", 8.0, "一级A", "2025",
|
||||
0.305, 17.3, "0.055", "1.78",
|
||||
96.8, 95.5, "集团汇总", false, "陈静");
|
||||
|
||||
// 区域同类水厂 D(较差样本,拉开分布)
|
||||
benchmark("临湘市污水处理厂", "湖南省·岳阳市", 3.0, "一级A", "2025",
|
||||
0.338, 20.6, "0.068", "2.05",
|
||||
94.2, 93.1, "政府公开", false, "陈静");
|
||||
|
||||
// 省级对标样本(规模最大,指标优异)
|
||||
benchmark("长沙第二污水处理厂", "湖南省·长沙市", 20.0, "地方特别限值", "2025",
|
||||
0.258, 14.1, "0.042", "1.52",
|
||||
99.0, 98.8, "行业报告·2025湖南省城镇污水处理年报", false, "王芳");
|
||||
}
|
||||
|
||||
private void benchmark(String plantName, String region, double capacity, String dischargeStd,
|
||||
String statPeriod, double unitPower, double unitReagent,
|
||||
String unitRepairCost, String unitTotalCost,
|
||||
double complianceRate, double equipIntactRate,
|
||||
String dataSrc, boolean isSelf, String maintainer) {
|
||||
SewagePlantBenchmark b = new SewagePlantBenchmark();
|
||||
b.setPlantName(plantName);
|
||||
b.setRegion(region);
|
||||
b.setCapacity(capacity);
|
||||
b.setDischargeStd(dischargeStd);
|
||||
b.setStatPeriod(statPeriod);
|
||||
b.setUnitPower(unitPower);
|
||||
b.setUnitReagent(unitReagent);
|
||||
b.setUnitRepairCost(new java.math.BigDecimal(unitRepairCost));
|
||||
b.setUnitTotalCost(new java.math.BigDecimal(unitTotalCost));
|
||||
b.setComplianceRate(complianceRate);
|
||||
b.setEquipIntactRate(equipIntactRate);
|
||||
b.setDataSrc(dataSrc);
|
||||
b.setIsSelf(isSelf);
|
||||
b.setMaintainer(maintainer);
|
||||
b.setCreatedAt(Instant.now());
|
||||
b.setUpdatedAt(Instant.now());
|
||||
benchmarkRepo.save(b);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 降本增效项目台账
|
||||
|
||||
private void seedCostSavingProjects() {
|
||||
if (savingRepo.count() > 0) return;
|
||||
|
||||
// 项目1:曝气变频改造——已完结,节约可观
|
||||
// 基期:吨水电耗 0.32 kWh/m³,月均水量 150000 m³
|
||||
// 改造后:吨水电耗 0.275 kWh/m³,验证 6 个月
|
||||
// 节约电量/月 = (0.32-0.275) × 150000 = 6750 kWh/月
|
||||
// 节约电费 = 6750 × 6 × 0.58 = 23490 元
|
||||
costSavingProject("JNJ-0001", "曝气系统变频节能改造", "节能改造", "水厂1号",
|
||||
"2024-03-01", "2024-08-31", "2024-09-15",
|
||||
"98000", 3,
|
||||
0.32, 22.5, 150000.0,
|
||||
6, 0.275, 22.0, 155000.0,
|
||||
"0.58", "8.50",
|
||||
"已完结", "王芳",
|
||||
"对3台提升泵+曝气风机加装变频器,实现精确曝气控制。已通过3个月基期+6个月效果验证,累计节约电量40500 kWh,节约电费23490元,静态回收期约25个月。");
|
||||
|
||||
// 项目2:精确加药系统升级——已完结,药耗降低
|
||||
// 基期:吨水药耗 20 g/m³,月均水量 150000 m³,药剂单价 8.5 元/kg
|
||||
// 改造后:吨水药耗 17.5 g/m³,验证 3 个月
|
||||
// 节约药量/月 = (20-17.5)/1000 × 150000 = 375 kg/月
|
||||
// 节约药费 = 375 × 3 × 8.5 = 9562.5 元
|
||||
costSavingProject("JNJ-0002", "在线水质联动精确加药系统", "精确加药", "水厂1号",
|
||||
"2024-06-01", "2024-10-31", "2024-11-10",
|
||||
"65000", 3,
|
||||
0.31, 20.0, 150000.0,
|
||||
3, 0.31, 17.5, 152000.0,
|
||||
"0.58", "8.50",
|
||||
"已完结", "刘洋",
|
||||
"引入在线氨氮/TP传感器与加药泵PLC联动,实现PAC/PAM按需投加。药耗下降12.5%,月节约药剂375 kg,节约费用约3187元/月。");
|
||||
|
||||
// 项目3:PAM替换为改性淀粉基助凝剂(药剂替代)——效果验证中
|
||||
// 基期:吨水药耗 5.2 g/m³(PAM),药剂单价 12 元/kg
|
||||
// 替换后:预期吨水药耗 4.5 g/m³,药剂单价 7.8 元/kg(改性淀粉)
|
||||
costSavingProject("JNJ-0003", "PAM替代:改性淀粉基助凝剂中试", "药剂替代", "水厂1号",
|
||||
"2025-03-01", "2025-08-31", null,
|
||||
"28000", 3,
|
||||
0.31, 5.2, 148000.0,
|
||||
3, 0.31, 4.5, 148000.0,
|
||||
"0.58", "7.80",
|
||||
"效果验证", "陈静",
|
||||
"试用改性淀粉基绿色助凝剂替代PAM,环保指标更优且成本更低。目前已完成3个月中试,吨水药耗从5.2降至4.5 g/m³,进入效果验证阶段。");
|
||||
|
||||
// 项目4:污泥脱水系统能效提升——改造中
|
||||
costSavingProject("JNJ-0004", "污泥脱水机变频节能改造", "节能改造", "水厂2号",
|
||||
"2025-08-01", "2025-12-31", null,
|
||||
"45000", 3,
|
||||
0.29, 18.0, 120000.0,
|
||||
3, 0.265, 18.0, 120000.0,
|
||||
"0.58", "8.50",
|
||||
"改造中", "王芳",
|
||||
"对2台带式压滤机驱动电机实施变频改造,预计吨水电耗降低0.025 kWh/m³,月节约电量3000 kWh。目前设备已进场,安装调试中。");
|
||||
|
||||
// 项目5:峰谷用电策略优化——立项中
|
||||
costSavingProject("JNJ-0005", "峰谷电价优化运行策略", "工艺优化", "全站",
|
||||
"2025-11-01", "2026-03-31", null,
|
||||
"5000", 3,
|
||||
0.32, 19.5, 270000.0,
|
||||
6, 0.305, 19.5, 270000.0,
|
||||
"0.58", "8.50",
|
||||
"立项中", "刘洋",
|
||||
"分析峰谷用电时段,将非核心用电负荷(污泥脱水、曝气辅机)移至谷段运行。通过调整运行策略降低电费(综合电价从0.58降至平均0.55元/kWh),无设备改造,主要是运行制度优化。");
|
||||
}
|
||||
|
||||
private void costSavingProject(String code, String projectName, String projectType, String plant,
|
||||
String kickoffDate, String planFinishDate, String actualFinishDate,
|
||||
String investCost, int baseMonths,
|
||||
double baseUnitPower, double baseUnitReagent, double baseMonthlyWater,
|
||||
int verifyMonths, double actualUnitPower, double actualUnitReagent, double actualMonthlyWater,
|
||||
String powerPrice, String reagentPrice,
|
||||
String projStatus, String owner, String remark) {
|
||||
SewageCostSavingProject p = new SewageCostSavingProject();
|
||||
p.setCode(code);
|
||||
p.setProjectName(projectName);
|
||||
p.setProjectType(projectType);
|
||||
p.setPlant(plant);
|
||||
p.setKickoffDate(kickoffDate);
|
||||
p.setPlanFinishDate(planFinishDate);
|
||||
p.setActualFinishDate(actualFinishDate);
|
||||
p.setInvestCost(new BigDecimal(investCost));
|
||||
p.setBaseMonths(baseMonths);
|
||||
p.setBaseUnitPower(baseUnitPower);
|
||||
p.setBaseUnitReagent(baseUnitReagent);
|
||||
p.setBaseMonthlyWater(baseMonthlyWater);
|
||||
p.setVerifyMonths(verifyMonths);
|
||||
p.setActualUnitPower(actualUnitPower);
|
||||
p.setActualUnitReagent(actualUnitReagent);
|
||||
p.setActualMonthlyWater(actualMonthlyWater);
|
||||
p.setPowerPrice(new BigDecimal(powerPrice));
|
||||
p.setReagentPrice(new BigDecimal(reagentPrice));
|
||||
p.setProjStatus(projStatus);
|
||||
p.setOwner(owner);
|
||||
p.setRemark(remark);
|
||||
p.setCreatedAt(Instant.now());
|
||||
p.setUpdatedAt(Instant.now());
|
||||
|
||||
// 自动计算节约量
|
||||
double deltaUnitPower = baseUnitPower - actualUnitPower;
|
||||
double savedKwhPerM = Math.max(0, deltaUnitPower * actualMonthlyWater);
|
||||
double savedKwhTotal = savedKwhPerM * verifyMonths;
|
||||
double deltaUnitReagent = baseUnitReagent - actualUnitReagent;
|
||||
double savedReagentKgPerM = Math.max(0, deltaUnitReagent / 1000.0 * actualMonthlyWater);
|
||||
BigDecimal pp = new BigDecimal(powerPrice);
|
||||
BigDecimal rp = new BigDecimal(reagentPrice);
|
||||
BigDecimal savedPowerCost = BigDecimal.valueOf(savedKwhTotal).multiply(pp)
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal savedReagentCost = BigDecimal.valueOf(savedReagentKgPerM * verifyMonths).multiply(rp)
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal savedTotal = savedPowerCost.add(savedReagentCost);
|
||||
p.setSavedKwhPerMonth(Math.round(savedKwhPerM * 100.0) / 100.0);
|
||||
p.setSavedKwhTotal(Math.round(savedKwhTotal * 100.0) / 100.0);
|
||||
p.setSavedReagentKgPerMonth(Math.round(savedReagentKgPerM * 100.0) / 100.0);
|
||||
p.setSavedPowerCost(savedPowerCost);
|
||||
p.setSavedReagentCost(savedReagentCost);
|
||||
p.setSavedTotalCost(savedTotal);
|
||||
|
||||
BigDecimal invest = new BigDecimal(investCost);
|
||||
if (savedTotal.compareTo(BigDecimal.ZERO) > 0 && verifyMonths > 0) {
|
||||
BigDecimal monthlyRate = savedTotal.divide(BigDecimal.valueOf(verifyMonths), 4, RoundingMode.HALF_UP);
|
||||
if (monthlyRate.compareTo(BigDecimal.ZERO) > 0) {
|
||||
double pb = invest.divide(monthlyRate, 2, RoundingMode.HALF_UP).doubleValue();
|
||||
p.setPaybackMonths(pb);
|
||||
}
|
||||
}
|
||||
|
||||
savingRepo.save(p);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.SewageCrossDeptLinkage;
|
||||
import com.kaidi.oa.domain.SewageExtLabCommission;
|
||||
import com.kaidi.oa.repository.SewageCrossDeptLinkageRepository;
|
||||
import com.kaidi.oa.repository.SewageExtLabCommissionRepository;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 城镇污水运营·跨部门联动触发器 & 外部实验室委托检测 演示种子数据。
|
||||
*
|
||||
* 补深审计 low 缺口(11.与其他部门接口要求):让联动台账与外委检测台账活体可见,
|
||||
* 覆盖多种状态(成功/失败/待触发 / 合格/超标/待判定),演示完整业务链路。
|
||||
*
|
||||
* 幂等:linkageRepo.count() == 0 才插入;commRepo.count() == 0 才插入。
|
||||
*/
|
||||
@Component
|
||||
@Order(30)
|
||||
public class SewageCrossDeptSeeder implements ApplicationRunner {
|
||||
|
||||
private final SewageCrossDeptLinkageRepository linkageRepo;
|
||||
private final SewageExtLabCommissionRepository commRepo;
|
||||
|
||||
public SewageCrossDeptSeeder(SewageCrossDeptLinkageRepository linkageRepo,
|
||||
SewageExtLabCommissionRepository commRepo) {
|
||||
this.linkageRepo = linkageRepo;
|
||||
this.commRepo = commRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(ApplicationArguments args) {
|
||||
seedLinkages();
|
||||
seedExtLabCommissions();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 跨部门联动触发器演示数据
|
||||
|
||||
private void seedLinkages() {
|
||||
if (linkageRepo.count() > 0) return;
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 1. 成功联动:药剂低库存→采购工单(真实写入)
|
||||
SewageCrossDeptLinkage lk1 = new SewageCrossDeptLinkage();
|
||||
lk1.setEventKey("REAGENT_LOW_STOCK");
|
||||
lk1.setSourceId(1L);
|
||||
lk1.setSourceModule("sewage_reagent_stock");
|
||||
lk1.setEventDesc("聚合氯化铝(PAC)(凯迪一污水厂)库存 280 kg 低于安全库存 500 kg,需补货");
|
||||
lk1.setTargetDept("采购部");
|
||||
lk1.setLinkageAction("生成采购工单");
|
||||
lk1.setStatus("成功");
|
||||
lk1.setTargetEntityType("mfg_purchase_order");
|
||||
lk1.setTargetEntityId(1001L);
|
||||
lk1.setResultNote("已真实创建采购工单 MfgPurchaseOrder#1001(来源:sewage_reagent_stock/1)");
|
||||
lk1.setOwner("系统自动");
|
||||
lk1.setTriggeredAt(now.minus(3, ChronoUnit.DAYS));
|
||||
lk1.setCompletedAt(now.minus(3, ChronoUnit.DAYS).plus(2, ChronoUnit.SECONDS));
|
||||
lk1.setCreatedAt(now.minus(3, ChronoUnit.DAYS));
|
||||
lk1.setUpdatedAt(now.minus(3, ChronoUnit.DAYS));
|
||||
linkageRepo.save(lk1);
|
||||
|
||||
// 2. 成功联动:月度成本归集完成→财务凭证(真实写入)
|
||||
SewageCrossDeptLinkage lk2 = new SewageCrossDeptLinkage();
|
||||
lk2.setEventKey("COST_MONTH_COMPLETE");
|
||||
lk2.setSourceId(2L);
|
||||
lk2.setSourceModule("sewage_cost_month");
|
||||
lk2.setEventDesc("2026-04 凯迪一污水厂 月度完全成本 ¥386500.00 完成,触发财务过账");
|
||||
lk2.setTargetDept("财务部");
|
||||
lk2.setLinkageAction("成本过账");
|
||||
lk2.setStatus("成功");
|
||||
lk2.setTargetEntityType("voucher");
|
||||
lk2.setTargetEntityId(2001L);
|
||||
lk2.setResultNote("已真实创建记账凭证 Voucher#2001(来源:sewage_cost_month/2)科目:5001-运营成本←2201-应付账款");
|
||||
lk2.setOwner("系统自动");
|
||||
lk2.setTriggeredAt(now.minus(10, ChronoUnit.DAYS));
|
||||
lk2.setCompletedAt(now.minus(10, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
lk2.setCreatedAt(now.minus(10, ChronoUnit.DAYS));
|
||||
lk2.setUpdatedAt(now.minus(10, ChronoUnit.DAYS));
|
||||
linkageRepo.save(lk2);
|
||||
|
||||
// 3. 成功联动:检测报告签发→申报服务部数据包
|
||||
SewageCrossDeptLinkage lk3 = new SewageCrossDeptLinkage();
|
||||
lk3.setEventKey("DETECT_REPORT_SIGNED");
|
||||
lk3.setSourceId(5L);
|
||||
lk3.setSourceModule("sewage_detection_report");
|
||||
lk3.setEventDesc("2026-05 月度水质检测报告已签发,COD削减量 42.8吨,氨氮削减量 8.6吨");
|
||||
lk3.setTargetDept("申报服务部");
|
||||
lk3.setLinkageAction("发送运营数据包");
|
||||
lk3.setStatus("成功");
|
||||
lk3.setTargetEntityType("ops_data_package");
|
||||
lk3.setTargetEntityId(5L);
|
||||
lk3.setResultNote("已将运营数据(检测报告 sourceId=5)打包推送至申报服务部,含减排贡献数据");
|
||||
lk3.setOwner("王运营");
|
||||
lk3.setTriggeredAt(now.minus(5, ChronoUnit.DAYS));
|
||||
lk3.setCompletedAt(now.minus(5, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
lk3.setCreatedAt(now.minus(5, ChronoUnit.DAYS));
|
||||
lk3.setUpdatedAt(now.minus(5, ChronoUnit.DAYS));
|
||||
linkageRepo.save(lk3);
|
||||
|
||||
// 4. 失败联动:月度成本归集→财务部(待重试)
|
||||
SewageCrossDeptLinkage lk4 = new SewageCrossDeptLinkage();
|
||||
lk4.setEventKey("COST_MONTH_COMPLETE");
|
||||
lk4.setSourceId(3L);
|
||||
lk4.setSourceModule("sewage_cost_month");
|
||||
lk4.setEventDesc("2026-05 凯迪二污水厂 月度完全成本 ¥312000.00 完成,触发财务过账");
|
||||
lk4.setTargetDept("财务部");
|
||||
lk4.setLinkageAction("成本过账");
|
||||
lk4.setStatus("失败");
|
||||
lk4.setResultNote("联动执行失败: 财务部凭证接口超时,请稍后重试");
|
||||
lk4.setOwner("系统自动");
|
||||
lk4.setTriggeredAt(now.minus(1, ChronoUnit.DAYS));
|
||||
lk4.setCreatedAt(now.minus(1, ChronoUnit.DAYS));
|
||||
lk4.setUpdatedAt(now.minus(1, ChronoUnit.DAYS));
|
||||
linkageRepo.save(lk4);
|
||||
|
||||
// 5. 待触发:运营记录自动归档
|
||||
SewageCrossDeptLinkage lk5 = new SewageCrossDeptLinkage();
|
||||
lk5.setEventKey("DETECTION_REPORT_ARCHIVE");
|
||||
lk5.setSourceId(7L);
|
||||
lk5.setSourceModule("sewage_detection_report");
|
||||
lk5.setEventDesc("2026-05 水质检测原始记录待归档至资料室");
|
||||
lk5.setTargetDept("资料室");
|
||||
lk5.setLinkageAction("自动归档");
|
||||
lk5.setStatus("待触发");
|
||||
lk5.setOwner("李化验");
|
||||
lk5.setTriggeredAt(now);
|
||||
lk5.setCreatedAt(now);
|
||||
lk5.setUpdatedAt(now);
|
||||
linkageRepo.save(lk5);
|
||||
|
||||
// 6. 成功联动:外委检测超标→法务风险部(真实写入 LegalRiskEvent)
|
||||
SewageCrossDeptLinkage lk6 = new SewageCrossDeptLinkage();
|
||||
lk6.setEventKey("EXT_LAB_EXCEED_LEGAL");
|
||||
lk6.setSourceId(1L);
|
||||
lk6.setSourceModule("sewage_ext_lab_commission");
|
||||
lk6.setEventDesc("EXT-LAB-0001 超标(总磷 0.82mg/L),已推送至法务风险部(LegalRiskEvent#3001)");
|
||||
lk6.setTargetDept("法务风险部");
|
||||
lk6.setLinkageAction("创建环保处罚风险事件");
|
||||
lk6.setStatus("成功");
|
||||
lk6.setTargetEntityType("legal_risk_event");
|
||||
lk6.setTargetEntityId(3001L);
|
||||
lk6.setResultNote("真实写入 LegalRiskEvent id=3001,类型=行政处罚,状态=待分析");
|
||||
lk6.setOwner("张水质");
|
||||
lk6.setTriggeredAt(now.minus(2, ChronoUnit.DAYS));
|
||||
lk6.setCompletedAt(now.minus(2, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
lk6.setCreatedAt(now.minus(2, ChronoUnit.DAYS));
|
||||
lk6.setUpdatedAt(now.minus(2, ChronoUnit.DAYS));
|
||||
linkageRepo.save(lk6);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 外部实验室委托检测演示数据
|
||||
|
||||
private void seedExtLabCommissions() {
|
||||
if (commRepo.count() > 0) return;
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 1. 超标整改状态(总磷超标,已推送法务)
|
||||
SewageExtLabCommission c1 = new SewageExtLabCommission();
|
||||
c1.setCommissionNo("EXT-LAB-0001");
|
||||
c1.setPlant("凯迪一污水厂");
|
||||
c1.setCommissionDate("2026-05-10");
|
||||
c1.setPlannedReportDate("2026-05-18");
|
||||
c1.setActualReportDate("2026-05-17");
|
||||
c1.setLabName("省环境检测技术中心");
|
||||
c1.setLabCertNo("CMA(2024)川字第12345号");
|
||||
c1.setSampleType("出水");
|
||||
c1.setSampleDate("2026-05-10");
|
||||
c1.setSampleNo("SMP-20260510-001");
|
||||
c1.setTestItems("COD,氨氮,总磷,总氮,SS,pH");
|
||||
c1.setResultSummary("COD=8.5mg/L;氨氮=0.42mg/L;总磷=0.82mg/L;总氮=10.6mg/L;SS=6mg/L;pH=7.2");
|
||||
c1.setComplianceResult("超标");
|
||||
c1.setExceedItems("总磷");
|
||||
c1.setCorrectionNote("总磷实测0.82 mg/L,超出GB18918一级A标准0.5 mg/L,需检查深度除磷单元,加强PAC加药量");
|
||||
c1.setStatus("超标整改");
|
||||
c1.setAmount(Money.of(3800.0));
|
||||
c1.setLinkageId(3001L);
|
||||
c1.setOwner("张水质");
|
||||
c1.setRemark("第三方年度水质监督性抽检");
|
||||
c1.setCreatedAt(now.minus(25, ChronoUnit.DAYS));
|
||||
c1.setUpdatedAt(now.minus(8, ChronoUnit.DAYS));
|
||||
commRepo.save(c1);
|
||||
|
||||
// 2. 合格状态,已归档
|
||||
SewageExtLabCommission c2 = new SewageExtLabCommission();
|
||||
c2.setCommissionNo("EXT-LAB-0002");
|
||||
c2.setPlant("凯迪二污水厂");
|
||||
c2.setCommissionDate("2026-04-15");
|
||||
c2.setPlannedReportDate("2026-04-22");
|
||||
c2.setActualReportDate("2026-04-21");
|
||||
c2.setLabName("市水质监测站");
|
||||
c2.setLabCertNo("CNAS L1234");
|
||||
c2.setSampleType("出水");
|
||||
c2.setSampleDate("2026-04-15");
|
||||
c2.setSampleNo("SMP-20260415-002");
|
||||
c2.setTestItems("COD,氨氮,总磷,总氮,SS");
|
||||
c2.setResultSummary("COD=6.2mg/L;氨氮=0.18mg/L;总磷=0.32mg/L;总氮=8.4mg/L;SS=4mg/L");
|
||||
c2.setComplianceResult("合格");
|
||||
c2.setStatus("已归档");
|
||||
c2.setAmount(Money.of(2500.0));
|
||||
c2.setOwner("李化验");
|
||||
c2.setRemark("季度合规性检测");
|
||||
c2.setCreatedAt(now.minus(60, ChronoUnit.DAYS));
|
||||
c2.setUpdatedAt(now.minus(45, ChronoUnit.DAYS));
|
||||
commRepo.save(c2);
|
||||
|
||||
// 3. 检测中(刚发样,尚未出结果)
|
||||
SewageExtLabCommission c3 = new SewageExtLabCommission();
|
||||
c3.setCommissionNo("EXT-LAB-0003");
|
||||
c3.setPlant("凯迪一污水厂");
|
||||
c3.setCommissionDate("2026-06-05");
|
||||
c3.setPlannedReportDate("2026-06-15");
|
||||
c3.setLabName("省环境检测技术中心");
|
||||
c3.setLabCertNo("CMA(2024)川字第12345号");
|
||||
c3.setSampleType("工艺段(A/O末端)");
|
||||
c3.setSampleDate("2026-06-05");
|
||||
c3.setSampleNo("SMP-20260605-003");
|
||||
c3.setTestItems("溶解氧,氨氮,总磷,总氮,MLSS");
|
||||
c3.setComplianceResult("待判定");
|
||||
c3.setStatus("检测中");
|
||||
c3.setAmount(Money.of(4200.0));
|
||||
c3.setOwner("张水质");
|
||||
c3.setRemark("A/O生化段专项监测,配合工艺优化调整");
|
||||
c3.setCreatedAt(now.minus(10, ChronoUnit.DAYS));
|
||||
c3.setUpdatedAt(now.minus(10, ChronoUnit.DAYS));
|
||||
commRepo.save(c3);
|
||||
|
||||
// 4. 待发样(刚创建)
|
||||
SewageExtLabCommission c4 = new SewageExtLabCommission();
|
||||
c4.setCommissionNo("EXT-LAB-0004");
|
||||
c4.setPlant("凯迪三污水厂");
|
||||
c4.setCommissionDate("2026-06-12");
|
||||
c4.setPlannedReportDate("2026-06-20");
|
||||
c4.setLabName("某市第三方水质检测机构");
|
||||
c4.setSampleType("污泥滤液");
|
||||
c4.setSampleDate("2026-06-12");
|
||||
c4.setSampleNo("SMP-20260612-004");
|
||||
c4.setTestItems("COD,氨氮,重金属(铅,镉,铬)");
|
||||
c4.setComplianceResult("待判定");
|
||||
c4.setStatus("待发样");
|
||||
c4.setAmount(Money.of(5600.0));
|
||||
c4.setOwner("赵厂长");
|
||||
c4.setRemark("污泥处置前合规性检测,确认重金属含量符合处置要求");
|
||||
c4.setCreatedAt(now.minus(3, ChronoUnit.DAYS));
|
||||
c4.setUpdatedAt(now.minus(3, ChronoUnit.DAYS));
|
||||
commRepo.save(c4);
|
||||
|
||||
// 5. 已出报告-合格(重金属检测通过)
|
||||
SewageExtLabCommission c5 = new SewageExtLabCommission();
|
||||
c5.setCommissionNo("EXT-LAB-0005");
|
||||
c5.setPlant("凯迪二污水厂");
|
||||
c5.setCommissionDate("2026-05-20");
|
||||
c5.setPlannedReportDate("2026-05-28");
|
||||
c5.setActualReportDate("2026-05-27");
|
||||
c5.setLabName("省环境检测技术中心");
|
||||
c5.setLabCertNo("CMA(2024)川字第12345号");
|
||||
c5.setSampleType("进水");
|
||||
c5.setSampleDate("2026-05-20");
|
||||
c5.setSampleNo("SMP-20260520-005");
|
||||
c5.setTestItems("COD,氨氮,总磷,总氮,SS,重金属筛查");
|
||||
c5.setResultSummary("COD=245mg/L;氨氮=28.6mg/L;总磷=3.2mg/L;总氮=36.4mg/L;SS=180mg/L;重金属未检出");
|
||||
c5.setComplianceResult("合格");
|
||||
c5.setStatus("已出报告");
|
||||
c5.setAmount(Money.of(6800.0));
|
||||
c5.setOwner("李化验");
|
||||
c5.setRemark("重要工业用户纳管排查配套检测");
|
||||
c5.setCreatedAt(now.minus(26, ChronoUnit.DAYS));
|
||||
c5.setUpdatedAt(now.minus(19, ChronoUnit.DAYS));
|
||||
commRepo.save(c5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.domain.MeetingActionItem;
|
||||
import com.kaidi.oa.domain.MeetingMinute;
|
||||
import com.kaidi.oa.domain.SupervisionProject;
|
||||
import com.kaidi.oa.domain.SvIndepClaim;
|
||||
import com.kaidi.oa.repository.MeetingActionItemRepository;
|
||||
import com.kaidi.oa.repository.MeetingMinuteRepository;
|
||||
import com.kaidi.oa.repository.SupervisionProjectRepository;
|
||||
import com.kaidi.oa.repository.SvIndepClaimRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工程监理部 · 缺口补完演示数据播种器。
|
||||
*
|
||||
* <p>补完两个 PARTIAL→MET 缺口:
|
||||
* <ol>
|
||||
* <li>缺口 5(索赔独立状态机):播种 SvIndepClaim 多阶段演示数据,
|
||||
* 覆盖 INTENT/INCIDENT/ASSESSMENT/OPINION/CLOSED/REJECTED 各阶段,
|
||||
* 包含关键线路影响分析字段。</li>
|
||||
* <li>缺口 7(监理例会纪要按项目过滤+待办任务结构化):
|
||||
* 播种 MeetingMinute(含 supervisionProjectId)及配套 MeetingActionItem
|
||||
* (含 assignee+dueDate,关联责任人)。</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>@Order(210) 在 DataSeeder(@Order 1) 之后运行;幂等(SvIndepClaim.count>0 则跳过)。
|
||||
*/
|
||||
@Order(210)
|
||||
@Component
|
||||
public class SvSupervisionMinuteSeeder implements CommandLineRunner {
|
||||
|
||||
private final SvIndepClaimRepository claimRepo;
|
||||
private final SupervisionProjectRepository projectRepo;
|
||||
private final MeetingMinuteRepository minuteRepo;
|
||||
private final MeetingActionItemRepository actionItemRepo;
|
||||
|
||||
public SvSupervisionMinuteSeeder(SvIndepClaimRepository claimRepo,
|
||||
SupervisionProjectRepository projectRepo,
|
||||
MeetingMinuteRepository minuteRepo,
|
||||
MeetingActionItemRepository actionItemRepo) {
|
||||
this.claimRepo = claimRepo;
|
||||
this.projectRepo = projectRepo;
|
||||
this.minuteRepo = minuteRepo;
|
||||
this.actionItemRepo = actionItemRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
seedIndepClaims();
|
||||
seedSupervisionMinutes();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 1. 索赔独立状态机演示数据
|
||||
// ========================================================================
|
||||
private void seedIndepClaims() {
|
||||
if (claimRepo.count() > 0) return;
|
||||
|
||||
// 获取第一个监理项目做关联(如无则不关联)
|
||||
List<SupervisionProject> projects = projectRepo.findAll();
|
||||
Long projectId1 = projects.isEmpty() ? null : projects.get(0).getId();
|
||||
String projectCode1 = projects.isEmpty() ? "JL-2026-001" : projects.get(0).getCode();
|
||||
Long projectId2 = projects.size() > 1 ? projects.get(1).getId() : projectId1;
|
||||
String projectCode2 = projects.size() > 1 ? projects.get(1).getCode() : projectCode1;
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 索赔1:已完成四阶段,状态 CLOSED(赶工费用索赔,关键线路影响=5天,核定3天)
|
||||
SvIndepClaim c1 = new SvIndepClaim();
|
||||
c1.setSupervisionProjectId(projectId1);
|
||||
c1.setSupervisionProjectCode(projectCode1);
|
||||
c1.setClaimCode("SYC-1");
|
||||
c1.setContractor("湖南城建建设有限公司");
|
||||
c1.setIntentDate("2026-03-10");
|
||||
c1.setIntentSummary("因设计变更导致基础换填工程量增加,施工方提出工期延误及费用增加索赔");
|
||||
c1.setIncidentStartDate("2026-02-15");
|
||||
c1.setIncidentEndDate("2026-03-08");
|
||||
c1.setIncidentDetail("2月15日建设单位下达设计变更通知单,要求将原持力层由3米加深至5米,导致土方开挖量增加约1800m³,工期顺延。施工方已按新标准施工,3月8日完成隐蔽验收。");
|
||||
c1.setEvidenceRef("变更通知单BG-2026-003,隐蔽验收记录HB-0308");
|
||||
c1.setDurationImpactDays(8);
|
||||
c1.setCriticalPathImpactDays(5);
|
||||
c1.setCriticalPathImpact("该延误位于关键路径(基础→主体结构),关键线路延误5天,其余3天属非关键工序可平行赶工");
|
||||
c1.setClaimAmount(Money.of(125000.00));
|
||||
c1.setCostImpactDetail("增加土方开挖1800m³×35元/m³=63000元,换填级配碎石580m³×107元/m³=62060元,合计125060元,舍尾为125000元");
|
||||
c1.setReviewer("张建国");
|
||||
c1.setReviewConclusion("部分支持");
|
||||
c1.setApprovedDurationDays(3);
|
||||
c1.setApprovedAmount(Money.of(98000.00));
|
||||
c1.setReviewOpinion("经复核,设计变更工程量属实,但部分换填单价偏高。核定费用补偿98000元,工期补偿3天(关键线路)。");
|
||||
c1.setReviewDate("2026-03-20");
|
||||
c1.setClaimStatus("CLOSED");
|
||||
c1.setCreatedAt(now);
|
||||
c1.setUpdatedAt(now);
|
||||
claimRepo.save(c1);
|
||||
|
||||
// 索赔2:处于 ASSESSMENT 阶段(甲方原因停工索赔)
|
||||
SvIndepClaim c2 = new SvIndepClaim();
|
||||
c2.setSupervisionProjectId(projectId1);
|
||||
c2.setSupervisionProjectCode(projectCode1);
|
||||
c2.setClaimCode("SYC-2");
|
||||
c2.setContractor("湖南城建建设有限公司");
|
||||
c2.setIntentDate("2026-04-18");
|
||||
c2.setIntentSummary("因建设单位资金周转问题导致工程暂停施工16天,施工方提出窝工费用及固定设施费索赔");
|
||||
c2.setIncidentStartDate("2026-04-01");
|
||||
c2.setIncidentEndDate("2026-04-17");
|
||||
c2.setIncidentDetail("建设单位4月1日口头通知暂停施工,4月17日恢复。停工期间共有工人42人待命,塔吊2台、混凝土泵1台未能正常作业,产生窝工损失及机械停滞费用。");
|
||||
c2.setEvidenceRef("停工令(口头转书面)BT-2026-001,考勤记录2026-04-01至04-17");
|
||||
c2.setDurationImpactDays(16);
|
||||
c2.setCriticalPathImpactDays(16);
|
||||
c2.setCriticalPathImpact("停工期间为主体结构关键施工阶段,全部16天均影响关键线路,计划竣工日期相应顺延16天");
|
||||
c2.setClaimAmount(Money.of(287500.00));
|
||||
c2.setCostImpactDetail("工人窝工42人×16天×60元/工日=40320元,塔吊停滞2台×16天×2200元/台天=70400元,泵车停滞1台×16天×3500元/天=56000元,现场管理费=120780元,合计287500元");
|
||||
c2.setClaimStatus("ASSESSMENT");
|
||||
c2.setCreatedAt(now);
|
||||
c2.setUpdatedAt(now);
|
||||
claimRepo.save(c2);
|
||||
|
||||
// 索赔3:处于 INCIDENT 阶段(不可抗力索赔)
|
||||
SvIndepClaim c3 = new SvIndepClaim();
|
||||
c3.setSupervisionProjectId(projectId2);
|
||||
c3.setSupervisionProjectCode(projectCode2);
|
||||
c3.setClaimCode("SYC-3");
|
||||
c3.setContractor("中建八局第二建设有限公司");
|
||||
c3.setIntentDate("2026-05-05");
|
||||
c3.setIntentSummary("因持续暴雨(超50年一遇)导致施工区域积水,基坑边坡出现局部滑塌,申请工期顺延及处置费用");
|
||||
c3.setIncidentStartDate("2026-04-28");
|
||||
c3.setIncidentEndDate("2026-05-03");
|
||||
c3.setIncidentDetail("4月28日至5月3日连续6天强降雨,日均降水量超过80mm,累计降水量512mm,超过50年一遇设计标准。基坑东侧边坡出现约15m范围局部滑塌,需紧急处置后方可继续施工。");
|
||||
c3.setEvidenceRef("气象局降雨记录证明,现场影像资料IMG-2026-0428至0503,应急处置方案EP-2026-001");
|
||||
c3.setClaimStatus("INCIDENT");
|
||||
c3.setCreatedAt(now);
|
||||
c3.setUpdatedAt(now);
|
||||
claimRepo.save(c3);
|
||||
|
||||
// 索赔4:处于 INTENT 阶段(材料涨价索赔)
|
||||
SvIndepClaim c4 = new SvIndepClaim();
|
||||
c4.setSupervisionProjectId(projectId2);
|
||||
c4.setSupervisionProjectCode(projectCode2);
|
||||
c4.setClaimCode("SYC-4");
|
||||
c4.setContractor("中建八局第二建设有限公司");
|
||||
c4.setIntentDate("2026-05-28");
|
||||
c4.setIntentSummary("螺纹钢价格较签合同时上涨约18%,申请材料差价补偿");
|
||||
c4.setClaimStatus("INTENT");
|
||||
c4.setCreatedAt(now);
|
||||
c4.setUpdatedAt(now);
|
||||
claimRepo.save(c4);
|
||||
|
||||
// 索赔5:已驳回(无有效证据)
|
||||
SvIndepClaim c5 = new SvIndepClaim();
|
||||
c5.setSupervisionProjectId(projectId1);
|
||||
c5.setSupervisionProjectCode(projectCode1);
|
||||
c5.setClaimCode("SYC-5");
|
||||
c5.setContractor("湖南城建建设有限公司");
|
||||
c5.setIntentDate("2026-01-15");
|
||||
c5.setIntentSummary("施工方申请因图纸延迟发放导致的窝工损失补偿");
|
||||
c5.setIncidentStartDate("2026-01-08");
|
||||
c5.setIncidentEndDate("2026-01-14");
|
||||
c5.setIncidentDetail("施工方称图纸未及时到位导致7天停工,但经查实:图纸已于1月7日发放,停工系施工方自身组织问题。");
|
||||
c5.setEvidenceRef("图纸签收记录20260107");
|
||||
c5.setClaimStatus("REJECTED");
|
||||
c5.setCreatedAt(now);
|
||||
c5.setUpdatedAt(now);
|
||||
claimRepo.save(c5);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 2. 监理例会纪要(含 supervisionProjectId + 结构化待办任务)
|
||||
// ========================================================================
|
||||
private void seedSupervisionMinutes() {
|
||||
// 只有纪要数<=2时才补种(DataSeeder可能已种1条)
|
||||
if (minuteRepo.count() > 5) return;
|
||||
|
||||
List<SupervisionProject> projects = projectRepo.findAll();
|
||||
Long projectId1 = projects.isEmpty() ? null : projects.get(0).getId();
|
||||
Long projectId2 = projects.size() > 1 ? projects.get(1).getId() : projectId1;
|
||||
Instant now = Instant.now();
|
||||
|
||||
// 纪要1:靖州县污水处理厂第8次监理例会,有3条待办
|
||||
MeetingMinute m1 = new MeetingMinute();
|
||||
m1.setSupervisionProjectId(projectId1);
|
||||
m1.setTitle("靖州县污水处理厂工程第8次监理例会纪要");
|
||||
m1.setContent("会议时间:2026-04-07 09:00\n会议地点:项目部会议室\n\n" +
|
||||
"【质量情况】本周完成生化池底板混凝土浇筑,坍落度、温度控制合格。" +
|
||||
"但北侧剪力墙模板拼缝偏差超标(实测3-5mm,规范≤2mm),需整改。\n\n" +
|
||||
"【进度情况】本周形象进度完成计划的95%,主体结构进度正常。\n\n" +
|
||||
"【安全情况】发现1处高处作业未佩戴安全带,已现场整改。\n\n" +
|
||||
"【计量支付】第5期进度款已提交监理审核,监理将于7日内出具支付证书。");
|
||||
m1.setStatus("已发布");
|
||||
m1.setDecisions("北侧剪力墙模板拼缝超标,施工方3天内完成整改并提交整改报告;\n" +
|
||||
"高处作业安全带配戴问题,安全员每日专项巡查并记录;\n" +
|
||||
"第5期进度款审核,监理4月10日前出具支付证书");
|
||||
m1.setCreatedAt(now);
|
||||
MeetingMinute savedM1 = minuteRepo.save(m1);
|
||||
|
||||
// 为纪要1生成3条待办任务(结构化,含assignee+dueDate)
|
||||
actionItem(savedM1.getId(), savedM1.getTitle(),
|
||||
"北侧剪力墙模板拼缝超标,完成整改并提交整改报告",
|
||||
"李明(施工方质量负责人)", "2026-04-10", now);
|
||||
actionItem(savedM1.getId(), savedM1.getTitle(),
|
||||
"高处作业安全带配戴情况每日专项巡查记录",
|
||||
"王安全(安全监理)", "2026-04-14", now);
|
||||
actionItem(savedM1.getId(), savedM1.getTitle(),
|
||||
"第5期进度款审核,出具支付证书",
|
||||
"张建国(总监)", "2026-04-10", now);
|
||||
|
||||
// 纪要2:岳阳市排水管网第3次专题会议(安全专题)
|
||||
MeetingMinute m2 = new MeetingMinute();
|
||||
m2.setSupervisionProjectId(projectId2);
|
||||
m2.setTitle("岳阳市排水管网改造工程雨季施工安全专题会议纪要");
|
||||
m2.setContent("会议时间:2026-05-12 14:30\n会议地点:施工现场临时会议室\n\n" +
|
||||
"【会议背景】气象部门预报5月下旬将迎来持续强降雨,为确保雨季施工安全,专门召开安全专题会。\n\n" +
|
||||
"【主要风险】①基坑周边地表水渗流;②临时排水沟排水能力不足;③脚手架基础积水沉降风险。\n\n" +
|
||||
"【应对措施】施工方需在5月20日前完成基坑周边截水沟加宽施工,增设3台潜水泵24小时备用," +
|
||||
"对全部脚手架基础进行加固处理,并建立每日雨前雨后巡查机制。");
|
||||
m2.setStatus("已发布");
|
||||
m2.setDecisions("基坑周边截水沟加宽施工,5月20日前完工;\n" +
|
||||
"增设潜水泵3台24小时备用,5月18日前就位;\n" +
|
||||
"全部脚手架基础加固处理,5月22日前完成并复验");
|
||||
m2.setCreatedAt(now);
|
||||
MeetingMinute savedM2 = minuteRepo.save(m2);
|
||||
|
||||
// 为纪要2生成3条待办任务
|
||||
actionItem(savedM2.getId(), savedM2.getTitle(),
|
||||
"基坑周边截水沟加宽施工完工",
|
||||
"赵强(施工方项目经理)", "2026-05-20", now);
|
||||
actionItem(savedM2.getId(), savedM2.getTitle(),
|
||||
"增设潜水泵3台就位并调试",
|
||||
"赵强(施工方项目经理)", "2026-05-18", now);
|
||||
actionItem(savedM2.getId(), savedM2.getTitle(),
|
||||
"脚手架基础加固完成后监理复验",
|
||||
"刘华(专业监理)", "2026-05-23", now);
|
||||
|
||||
// 纪要3:草稿状态(尚未发布)
|
||||
MeetingMinute m3 = new MeetingMinute();
|
||||
m3.setSupervisionProjectId(projectId1);
|
||||
m3.setTitle("靖州县污水处理厂工程第9次监理例会纪要(草稿)");
|
||||
m3.setContent("会议时间:2026-04-14 09:00\n会议地点:项目部会议室\n\n【待补充...】");
|
||||
m3.setStatus("草稿");
|
||||
m3.setCreatedAt(now);
|
||||
minuteRepo.save(m3);
|
||||
}
|
||||
|
||||
private void actionItem(Long minuteId, String minuteTitle, String content,
|
||||
String assignee, String dueDate, Instant now) {
|
||||
MeetingActionItem item = new MeetingActionItem();
|
||||
item.setMinuteId(minuteId);
|
||||
item.setMinuteTitle(minuteTitle);
|
||||
item.setContent(content);
|
||||
item.setAssignee(assignee);
|
||||
item.setDueDate(dueDate);
|
||||
item.setStatus("待办");
|
||||
item.setCreatedAt(now);
|
||||
item.setUpdatedAt(now);
|
||||
actionItemRepo.save(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.kaidi.oa.domain.FormTemplate;
|
||||
import com.kaidi.oa.repository.FormTemplateRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Loads extra form templates from classpath:seed-templates/*.json on startup and
|
||||
* upserts them (by id) as built-in templates. Each JSON file is an array of
|
||||
* { id, name, category, org, instructions, form, flow } objects whose form/flow
|
||||
* mirror the frontend FormSchema / FlowSchema. Runs after the main DataSeeder
|
||||
* and is idempotent (re-running re-applies the latest JSON), so dropping a new
|
||||
* JSON file under resources/seed-templates and rebuilding adds templates without
|
||||
* any code change. This is how 凯迪科技's real approval processes are catalogued.
|
||||
*/
|
||||
@Component
|
||||
@Order(100)
|
||||
public class TemplateJsonSeeder implements CommandLineRunner {
|
||||
|
||||
private final FormTemplateRepository repo;
|
||||
private final ObjectMapper om;
|
||||
|
||||
public TemplateJsonSeeder(FormTemplateRepository repo, ObjectMapper om) {
|
||||
this.repo = repo;
|
||||
this.om = om;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
Resource[] files;
|
||||
try {
|
||||
files = new PathMatchingResourcePatternResolver()
|
||||
.getResources("classpath*:seed-templates/*.json");
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
int upserted = 0;
|
||||
for (Resource f : files) {
|
||||
try (InputStream in = f.getInputStream()) {
|
||||
JsonNode arr = om.readTree(in);
|
||||
if (arr == null || !arr.isArray()) {
|
||||
continue;
|
||||
}
|
||||
for (JsonNode t : arr) {
|
||||
String id = t.path("id").asText(null);
|
||||
if (id == null || id.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
FormTemplate tpl = repo.findById(id).orElseGet(FormTemplate::new);
|
||||
tpl.setId(id);
|
||||
tpl.setName(t.path("name").asText(""));
|
||||
tpl.setCategory(t.path("category").asText(""));
|
||||
tpl.setOrg(t.path("org").asText(""));
|
||||
tpl.setInstructions(t.path("instructions").asText(""));
|
||||
tpl.setFormSchemaJson(t.has("form") ? t.get("form").toString() : "{}");
|
||||
tpl.setFlowSchemaJson(t.has("flow") ? t.get("flow").toString() : "{}");
|
||||
tpl.setBuiltin(true);
|
||||
if (tpl.getPublishedAt() == null || tpl.getPublishedAt().isBlank()) {
|
||||
tpl.setPublishedAt("2026-06-11");
|
||||
}
|
||||
repo.save(tpl);
|
||||
upserted++;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// skip malformed file, keep going
|
||||
}
|
||||
}
|
||||
if (upserted > 0) {
|
||||
System.out.println("TemplateJsonSeeder: upserted " + upserted
|
||||
+ " templates from seed-templates/*.json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
package com.kaidi.oa.seed;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.kaidi.oa.domain.FormTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builds the built-in form template catalog, porting the frontend
|
||||
* src/oa/engine/templates/*.ts catalog (11 templates). Each template's form and
|
||||
* flow schemas are serialized to JSON TEXT with the exact same structure the
|
||||
* frontend produces (FormSchema{title,rows,subTables}, FlowSchema{nodes,edges,
|
||||
* parallels,branches}).
|
||||
*
|
||||
* Order mirrors the frontend catalog array:
|
||||
* baohan-payment, expense-reimburse, leave-apply, seal-apply, general-collab,
|
||||
* supplier-access, business-trip, purchase-contract, payment-apply,
|
||||
* overtime-apply, expense-cond.
|
||||
*/
|
||||
public final class TemplateSeedData {
|
||||
|
||||
private final ObjectMapper om;
|
||||
|
||||
public TemplateSeedData(ObjectMapper om) {
|
||||
this.om = om;
|
||||
}
|
||||
|
||||
// ---- small schema-builder DSL mirroring the frontend types ----
|
||||
|
||||
private ObjectNode field(String id, String label, String type) {
|
||||
ObjectNode f = om.createObjectNode();
|
||||
f.put("id", id);
|
||||
f.put("label", label);
|
||||
f.put("type", type);
|
||||
return f;
|
||||
}
|
||||
|
||||
private ObjectNode req(ObjectNode f) {
|
||||
f.put("required", true);
|
||||
return f;
|
||||
}
|
||||
|
||||
private ObjectNode full(ObjectNode f) {
|
||||
f.put("fullWidth", true);
|
||||
return f;
|
||||
}
|
||||
|
||||
private ObjectNode opts(ObjectNode f, String... options) {
|
||||
ArrayNode arr = f.putArray("options");
|
||||
for (String o : options) {
|
||||
arr.add(o);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private ObjectNode col(String id, String label, String type, Integer width) {
|
||||
ObjectNode c = om.createObjectNode();
|
||||
c.put("id", id);
|
||||
c.put("label", label);
|
||||
c.put("type", type);
|
||||
if (width != null) {
|
||||
c.put("width", width);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private ObjectNode subTable(String id, String title, ObjectNode... columns) {
|
||||
ObjectNode st = om.createObjectNode();
|
||||
st.put("id", id);
|
||||
st.put("title", title);
|
||||
ArrayNode cols = st.putArray("columns");
|
||||
for (ObjectNode c : columns) {
|
||||
cols.add(c);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
private ObjectNode node(String id, String label, String type) {
|
||||
ObjectNode n = om.createObjectNode();
|
||||
n.put("id", id);
|
||||
n.put("label", label);
|
||||
n.put("type", type);
|
||||
return n;
|
||||
}
|
||||
|
||||
private ArrayNode edge(String from, String to) {
|
||||
ArrayNode e = om.createArrayNode();
|
||||
e.add(from);
|
||||
e.add(to);
|
||||
return e;
|
||||
}
|
||||
|
||||
/** Assemble a form schema node. rows is a list of row arrays of fields. */
|
||||
private String formJson(String title, List<ObjectNode[]> rows, ObjectNode... subTables) {
|
||||
ObjectNode form = om.createObjectNode();
|
||||
form.put("title", title);
|
||||
ArrayNode rowsArr = form.putArray("rows");
|
||||
for (ObjectNode[] row : rows) {
|
||||
ArrayNode r = rowsArr.addArray();
|
||||
for (ObjectNode f : row) {
|
||||
r.add(f);
|
||||
}
|
||||
}
|
||||
if (subTables.length > 0) {
|
||||
ArrayNode sts = form.putArray("subTables");
|
||||
for (ObjectNode st : subTables) {
|
||||
sts.add(st);
|
||||
}
|
||||
}
|
||||
return form.toString();
|
||||
}
|
||||
|
||||
private FormTemplate tpl(String id, String name, String category, String org,
|
||||
String publishedAt, String instructions,
|
||||
String formJson, String flowJson) {
|
||||
FormTemplate t = new FormTemplate();
|
||||
t.setId(id);
|
||||
t.setName(name);
|
||||
t.setCategory(category);
|
||||
t.setOrg(org);
|
||||
t.setPublishedAt(publishedAt);
|
||||
t.setInstructions(instructions);
|
||||
t.setFormSchemaJson(formJson);
|
||||
t.setFlowSchemaJson(flowJson);
|
||||
t.setBuiltin(true);
|
||||
return t;
|
||||
}
|
||||
|
||||
private List<ObjectNode[]> rows(ObjectNode[]... rows) {
|
||||
return List.of(rows);
|
||||
}
|
||||
|
||||
private ObjectNode[] row(ObjectNode... fields) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Build all 14 built-in templates (含申报计划审批模板 + 合同法务审批模板). */
|
||||
public List<FormTemplate> all() {
|
||||
List<FormTemplate> list = new ArrayList<>();
|
||||
list.add(baohan());
|
||||
list.add(reimburse());
|
||||
list.add(leave());
|
||||
list.add(seal());
|
||||
list.add(general());
|
||||
list.add(supplier());
|
||||
list.add(businessTrip());
|
||||
list.add(purchaseContract());
|
||||
list.add(paymentApply());
|
||||
list.add(overtime());
|
||||
list.add(expenseCond());
|
||||
list.add(financingApply());
|
||||
list.add(rdDeclPlanApprove());
|
||||
list.add(legalContractReview());
|
||||
return list;
|
||||
}
|
||||
|
||||
// ===================== individual templates =====================
|
||||
|
||||
private FormTemplate baohan() {
|
||||
String form = formJson("保函付款申请单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDate", "申请时间", "date"))),
|
||||
row(req(field("applyDept", "申请部门", "dept")), req(field("deadline", "截止付款时间", "date"))),
|
||||
row(full(req(opts(field("payType", "付款类型", "radio"), "投标保函", "预付款保函", "履约保函", "代理费")))),
|
||||
row(full(req(field("payUnit", "付款单位(保函开具单位)", "text")))),
|
||||
row(full(req(field("payAccount", "付款银行账号", "text")))),
|
||||
row(full(field("office", "办事处", "text"))),
|
||||
row(full(req(field("projectName", "项目名称", "text")))),
|
||||
row(full(field("payReason", "付款事由", "textarea")))
|
||||
), subTable("payeeDetails", "收款明细",
|
||||
col("payeeName", "收款人户名", "text", null),
|
||||
col("payeeAccount", "收款人账号", "text", null),
|
||||
col("payeeBank", "收款人开户行", "text", null),
|
||||
col("amount", "金额", "money", 140),
|
||||
col("voucher", "凭证附件", "attachment", 100)));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("deptHead", "发起者部门主管", "审批"));
|
||||
nodes.add(node("cashierMaker", "制单出纳", "知会"));
|
||||
nodes.add(node("cashierReview", "复核出纳", "知会"));
|
||||
nodes.add(node("settleAccount", "结算会计", "审批"));
|
||||
nodes.add(node("financeHead", "财务部负责人", "审批"));
|
||||
nodes.add(node("chairman", "董事长", "审批"));
|
||||
nodes.add(node("firstCashier", "第一出纳", "协同"));
|
||||
nodes.add(node("settleNotify", "结算会计", "知会"));
|
||||
nodes.add(node("reviewApprove", "复核出纳", "审批"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "deptHead"));
|
||||
edges.add(edge("deptHead", "cashierMaker"));
|
||||
edges.add(edge("deptHead", "cashierReview"));
|
||||
edges.add(edge("cashierMaker", "settleAccount"));
|
||||
edges.add(edge("cashierReview", "settleAccount"));
|
||||
edges.add(edge("settleAccount", "financeHead"));
|
||||
edges.add(edge("financeHead", "chairman"));
|
||||
edges.add(edge("chairman", "firstCashier"));
|
||||
edges.add(edge("firstCashier", "settleNotify"));
|
||||
edges.add(edge("settleNotify", "reviewApprove"));
|
||||
edges.add(edge("reviewApprove", "end"));
|
||||
ArrayNode parallels = flow.putArray("parallels");
|
||||
ArrayNode p = parallels.addArray();
|
||||
p.add("cashierMaker");
|
||||
p.add("cashierReview");
|
||||
|
||||
String instructions =
|
||||
"一、适用范围:本流程适用于投标保函、预付款保函、履约保函及代理费的对外付款申请。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 申请人需完整填写付款单位、付款银行账号、办事处及项目名称;\n" +
|
||||
" 2. 收款明细子表须逐行登记收款人户名、账号、开户行、金额,并上传凭证附件;\n" +
|
||||
" 3. 付款事由应说明保函用途与付款依据。\n" +
|
||||
"三、流程节点说明:\n" +
|
||||
" 发起者提交后先经发起者部门主管审批;随后由制单出纳、复核出纳并行知会;\n" +
|
||||
" 经结算会计、财务部负责人、董事长逐级审批;最后由第一出纳协同制单、\n" +
|
||||
" 结算会计知会、复核出纳审批后办结归档。\n" +
|
||||
"四、注意事项:金额较大或跨境付款的,应附合同及保函正本扫描件。";
|
||||
return tpl("baohan-payment", "保函付款申请单", "财务审批", "信息中心", "2024-09-12",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate reimburse() {
|
||||
String form = formJson("费用报销单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("reimburseDept", "报销部门", "dept"))),
|
||||
row(req(opts(field("expenseType", "费用类型", "select"), "差旅费", "招待费", "办公费", "培训费", "其他")),
|
||||
req(field("amount", "金额", "money")))
|
||||
), subTable("expenseItems", "报销明细",
|
||||
col("item", "费用项", "text", null),
|
||||
col("itemAmount", "金额", "money", 140),
|
||||
col("remark", "备注", "text", null)));
|
||||
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"financeAudit", "财务审核", "审批"},
|
||||
{"gm", "总经理", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"费用报销流程:申请人填写报销部门、费用类型、金额及报销明细后提交,\n" +
|
||||
"经部门主管审批、财务审核、总经理审批后办结。明细金额合计须与申请金额一致,\n" +
|
||||
"并附合规发票。";
|
||||
return tpl("expense-reimburse", "费用报销", "财务审批", "财务部", "2024-07-21",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate leave() {
|
||||
String form = formJson("请假申请", rows(
|
||||
row(req(field("applicant", "申请人", "person"))),
|
||||
row(full(req(opts(field("leaveType", "请假类型", "radio"), "事假", "病假", "年假")))),
|
||||
row(req(field("startTime", "开始时间", "datetime")), req(field("endTime", "结束时间", "datetime"))),
|
||||
row(full(req(field("reason", "请假事由", "textarea"))))
|
||||
));
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"hr", "人力资源部", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"请假申请流程:员工填写请假类型、起止时间与事由后提交,经部门主管审批、\n" +
|
||||
"人力资源部审批后生效。事假/病假需提前一个工作日提交,病假应附就诊证明。";
|
||||
return tpl("leave-apply", "请假申请", "人力资源", "行政部", "2024-06-03",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate seal() {
|
||||
String form = formJson("用印申请单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDate", "申请时间", "date"))),
|
||||
row(req(opts(field("sealType", "印章类型", "select"), "公章", "合同章", "财务章", "法人章")),
|
||||
req(field("copies", "份数", "number"))),
|
||||
row(full(req(field("docName", "文件名称", "text")))),
|
||||
row(full(req(field("reason", "用印事由", "textarea"))))
|
||||
));
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"adminHead", "行政负责人", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"用印申请:填写用印事由、印章类型、文件名称及份数,经部门主管、行政负责人审批后用印。";
|
||||
return tpl("seal-apply", "用印申请", "行政部", "行政部", "2024-05-16",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate general() {
|
||||
String form = formJson("通用事项", rows(
|
||||
row(full(req(field("title", "标题", "text")))),
|
||||
row(req(field("applicant", "发起人", "person"))),
|
||||
row(full(req(field("content", "正文", "textarea"))))
|
||||
));
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"通用协同事项:填写标题与正文后提交,经部门主管审批后办结,适用于无固定表单的一般性事项。";
|
||||
return tpl("general-collab", "通用事项", "公用模板", "信息中心", "2024-01-08",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate supplier() {
|
||||
String form = formJson("供应商准入申请单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDate", "申请时间", "date"))),
|
||||
row(full(req(field("supplierName", "供应商名称", "text")))),
|
||||
row(req(field("creditCode", "统一社会信用代码", "text")), field("regCapital", "注册资本", "money")),
|
||||
row(req(field("contact", "联系人", "text")), req(field("phone", "联系电话", "text"))),
|
||||
row(full(req(opts(field("supplyCategory", "供应类别", "checkbox"),
|
||||
"原材料", "设备", "工程服务", "办公用品", "其他")))),
|
||||
row(full(req(opts(field("cooperation", "合作意向", "radio"), "长期合作", "单次采购", "战略合作")))),
|
||||
row(full(req(field("reason", "准入理由", "textarea"))))
|
||||
), subTable("qualifications", "资质证照明细",
|
||||
col("certName", "证照名称", "text", null),
|
||||
col("certNo", "证照编号", "text", null),
|
||||
col("validUntil", "有效期至", "date", 140),
|
||||
col("attach", "扫描件", "attachment", 100)));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("purchaseLead", "采购主管", "审批"));
|
||||
nodes.add(node("qualityDept", "质量部", "知会"));
|
||||
nodes.add(node("financeDept", "财务部", "知会"));
|
||||
nodes.add(node("purchaseDirector", "采购总监", "审批"));
|
||||
nodes.add(node("vp", "分管副总", "审批"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "purchaseLead"));
|
||||
edges.add(edge("purchaseLead", "qualityDept"));
|
||||
edges.add(edge("purchaseLead", "financeDept"));
|
||||
edges.add(edge("qualityDept", "purchaseDirector"));
|
||||
edges.add(edge("financeDept", "purchaseDirector"));
|
||||
edges.add(edge("purchaseDirector", "vp"));
|
||||
edges.add(edge("vp", "end"));
|
||||
ArrayNode parallels = flow.putArray("parallels");
|
||||
ArrayNode p = parallels.addArray();
|
||||
p.add("qualityDept");
|
||||
p.add("financeDept");
|
||||
|
||||
String instructions =
|
||||
"一、适用范围:本流程适用于新增供应商的准入审核及合格供应商名录登记。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 申请人需完整填写供应商名称、统一社会信用代码、联系人及联系电话;\n" +
|
||||
" 2. 资质证照子表须逐行登记证照名称、证照编号、有效期,并上传扫描件附件;\n" +
|
||||
" 3. 供应类别与合作意向应如实勾选,准入理由须说明引入背景。\n" +
|
||||
"三、流程节点说明:\n" +
|
||||
" 发起者提交后先经采购主管审批;随后由质量部、财务部并行知会进行资质与\n" +
|
||||
" 信用初核;经采购总监、分管副总逐级审批后完成准入登记。\n" +
|
||||
"四、注意事项:证照缺失或在有效期外的,不予准入。";
|
||||
return tpl("supplier-access", "供应商准入申请", "采购管理", "采购部", "2024-10-18",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate businessTrip() {
|
||||
String form = formJson("出差申请单", rows(
|
||||
row(req(field("applicant", "出差人", "person")), req(field("applyDept", "所属部门", "dept"))),
|
||||
row(req(field("destination", "出差目的地", "text")),
|
||||
req(opts(field("transport", "主要交通工具", "select"), "飞机", "高铁", "汽车", "其他"))),
|
||||
row(req(field("startTime", "开始时间", "datetime")), req(field("endTime", "结束时间", "datetime"))),
|
||||
row(full(field("companions", "同行人员", "person"))),
|
||||
row(req(field("budget", "预算金额", "money"))),
|
||||
row(full(req(field("reason", "出差事由", "textarea"))))
|
||||
), subTable("itinerary", "行程明细",
|
||||
col("date", "日期", "date", 140),
|
||||
col("fromTo", "行程(出发-到达)", "text", null),
|
||||
col("wayTransport", "交通方式", "text", 120),
|
||||
col("estCost", "预计费用", "money", 140)));
|
||||
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"adminDept", "行政部", "知会"},
|
||||
{"gm", "总经理", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"出差申请流程:申请人填写出差事由、目的地、起止时间、同行人员及预算后提交,\n" +
|
||||
"经部门主管审批、行政部知会安排行程后,由总经理审批办结。行程明细须逐段登记\n" +
|
||||
"交通方式与预计费用,单次预算超过 5000 元的应附说明。";
|
||||
return tpl("business-trip", "出差申请", "行政部", "行政部", "2024-08-09",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate purchaseContract() {
|
||||
String form = formJson("采购合同会签单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDept", "申请部门", "dept"))),
|
||||
row(full(req(field("contractName", "合同名称", "text")))),
|
||||
row(full(req(field("counterparty", "对方单位", "text")))),
|
||||
row(req(field("amount", "合同金额", "money")), req(field("signDate", "签订日期", "date"))),
|
||||
row(req(opts(field("payMethod", "付款方式", "select"), "预付款", "货到付款", "分期付款", "验收后付款")),
|
||||
opts(field("contractType", "合同类型", "radio"), "框架协议", "一次性采购", "长期供货")),
|
||||
row(full(field("remark", "会签说明", "textarea")))
|
||||
), subTable("purchaseItems", "采购明细",
|
||||
col("material", "物料名称", "text", null),
|
||||
col("spec", "规格型号", "text", null),
|
||||
col("qty", "数量", "number", 100),
|
||||
col("unitPrice", "单价", "money", 120),
|
||||
col("itemAmount", "金额", "money", 140)));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("purchaseLead", "采购主管", "审批"));
|
||||
nodes.add(node("legalDept", "法务部", "协同"));
|
||||
nodes.add(node("financeDept", "财务部", "知会"));
|
||||
nodes.add(node("purchaseDirector", "采购总监", "审批"));
|
||||
nodes.add(node("vp", "分管副总", "审批"));
|
||||
nodes.add(node("gm", "总经理", "审批"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "purchaseLead"));
|
||||
edges.add(edge("purchaseLead", "legalDept"));
|
||||
edges.add(edge("purchaseLead", "financeDept"));
|
||||
edges.add(edge("legalDept", "purchaseDirector"));
|
||||
edges.add(edge("financeDept", "purchaseDirector"));
|
||||
edges.add(edge("purchaseDirector", "vp"));
|
||||
edges.add(edge("vp", "gm"));
|
||||
edges.add(edge("gm", "end"));
|
||||
ArrayNode parallels = flow.putArray("parallels");
|
||||
ArrayNode p = parallels.addArray();
|
||||
p.add("legalDept");
|
||||
p.add("financeDept");
|
||||
|
||||
String instructions =
|
||||
"一、适用范围:本流程适用于采购合同签订前的多部门会签与用印审批。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 申请人需完整填写合同名称、对方单位、合同金额、付款方式及签订日期;\n" +
|
||||
" 2. 采购明细子表须逐行登记物料名称、规格、数量、单价、金额;\n" +
|
||||
" 3. 合同正本扫描件应作为附件随单上传。\n" +
|
||||
"三、流程节点说明:\n" +
|
||||
" 发起者提交后先经采购主管审批;随后由法务部协同审查条款、财务部并行知会\n" +
|
||||
" 核对资金;经采购总监、分管副总、总经理逐级审批后用印签订。\n" +
|
||||
"四、注意事项:合同金额与采购明细合计须一致,重大合同应附立项依据。";
|
||||
return tpl("purchase-contract", "采购合同会签", "采购管理", "采购部", "2024-11-05",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate paymentApply() {
|
||||
String form = formJson("付款申请单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDept", "申请部门", "dept"))),
|
||||
row(full(req(field("payee", "收款单位", "text")))),
|
||||
row(req(field("amount", "付款金额", "money")), req(field("planDate", "计划付款日期", "date"))),
|
||||
row(req(opts(field("payMethod", "付款方式", "select"), "银行转账", "电汇", "承兑汇票", "现金")),
|
||||
field("contractNo", "关联合同号", "text")),
|
||||
row(full(req(opts(field("payCategory", "付款类别", "radio"),
|
||||
"货款", "服务费", "保证金", "预付款", "其他")))),
|
||||
row(full(req(field("purpose", "付款用途", "textarea"))))
|
||||
), subTable("payDetails", "付款明细",
|
||||
col("payeeName", "收款户名", "text", null),
|
||||
col("payeeBank", "开户行", "text", null),
|
||||
col("payeeAccount", "账号", "text", null),
|
||||
col("detailAmount", "金额", "money", 140),
|
||||
col("invoice", "发票附件", "attachment", 100)));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("deptHead", "部门主管", "审批"));
|
||||
nodes.add(node("financeAudit", "财务审核", "审批"));
|
||||
nodes.add(node("cashierMaker", "制单出纳", "知会"));
|
||||
nodes.add(node("cashierReview", "复核出纳", "知会"));
|
||||
nodes.add(node("financeHead", "财务负责人", "审批"));
|
||||
nodes.add(node("gm", "总经理", "审批"));
|
||||
nodes.add(node("cashierPay", "出纳付款", "协同"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "deptHead"));
|
||||
edges.add(edge("deptHead", "financeAudit"));
|
||||
edges.add(edge("financeAudit", "cashierMaker"));
|
||||
edges.add(edge("financeAudit", "cashierReview"));
|
||||
edges.add(edge("cashierMaker", "financeHead"));
|
||||
edges.add(edge("cashierReview", "financeHead"));
|
||||
edges.add(edge("financeHead", "gm"));
|
||||
edges.add(edge("gm", "cashierPay"));
|
||||
edges.add(edge("cashierPay", "end"));
|
||||
ArrayNode parallels = flow.putArray("parallels");
|
||||
ArrayNode p = parallels.addArray();
|
||||
p.add("cashierMaker");
|
||||
p.add("cashierReview");
|
||||
|
||||
String instructions =
|
||||
"一、适用范围:本流程适用于供应商货款、服务费、保证金等对外付款申请。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 申请人需完整填写收款单位、付款用途、付款方式及计划付款日期;\n" +
|
||||
" 2. 付款明细子表须逐行登记收款户名、开户行、账号、金额,并上传发票附件;\n" +
|
||||
" 3. 关联合同号应如实填写,以便财务核对。\n" +
|
||||
"三、流程节点说明:\n" +
|
||||
" 发起者提交后经部门主管、财务审核逐级审批;随后由制单出纳、复核出纳并行\n" +
|
||||
" 知会;经财务负责人、总经理审批后,由出纳协同完成付款。\n" +
|
||||
"四、注意事项:付款金额与明细合计须一致,无合同支撑的付款不予受理。";
|
||||
return tpl("payment-apply", "付款申请", "财务审批", "财务部", "2024-09-28",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate overtime() {
|
||||
String form = formJson("加班申请单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("applyDept", "所属部门", "dept"))),
|
||||
row(full(req(opts(field("overtimeType", "加班类型", "radio"),
|
||||
"工作日加班", "休息日加班", "节假日加班")))),
|
||||
row(req(field("startTime", "开始时间", "datetime")), req(field("endTime", "结束时间", "datetime"))),
|
||||
row(req(opts(field("compensation", "补偿方式", "select"), "调休", "加班费")),
|
||||
req(field("totalHours", "合计时长(小时)", "number"))),
|
||||
row(full(req(field("reason", "加班事由", "textarea"))))
|
||||
), subTable("overtimeItems", "加班明细",
|
||||
col("date", "加班日期", "date", 140),
|
||||
col("fromTime", "开始时间", "datetime", 180),
|
||||
col("toTime", "结束时间", "datetime", 180),
|
||||
col("hours", "时长(小时)", "number", 120),
|
||||
col("workContent", "工作内容", "text", null)));
|
||||
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "发起者", "start"},
|
||||
{"deptHead", "部门主管", "审批"},
|
||||
{"hr", "人力资源部", "知会"},
|
||||
{"leader", "分管领导", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
String instructions =
|
||||
"加班申请流程:申请人填写加班类型、起止时间、加班事由及补偿方式后提交,\n" +
|
||||
"经部门主管审批、人力资源部知会登记后,由分管领导审批生效。加班明细须逐日\n" +
|
||||
"登记起止时间与时长,调休与加班费不可同时申报。";
|
||||
return tpl("overtime-apply", "加班申请", "人力资源", "人力资源部", "2024-07-30",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
private FormTemplate expenseCond() {
|
||||
String form = formJson("费用审批单", rows(
|
||||
row(req(field("applicant", "申请人", "person")), req(field("amount", "金额", "money"))),
|
||||
row(req(opts(field("expenseType", "费用类型", "select"),
|
||||
"差旅费", "招待费", "办公费", "培训费", "其他"))),
|
||||
row(full(req(field("reason", "费用说明", "textarea"))))
|
||||
));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("deptHead", "部门主管", "审批"));
|
||||
nodes.add(node("amountGate", "金额判断", "审批"));
|
||||
nodes.add(node("gm", "总经理", "审批"));
|
||||
nodes.add(node("directPass", "直接通过", "知会"));
|
||||
nodes.add(node("financeReview", "财务复核", "审批"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "deptHead"));
|
||||
edges.add(edge("deptHead", "amountGate"));
|
||||
edges.add(edge("amountGate", "gm"));
|
||||
edges.add(edge("amountGate", "directPass"));
|
||||
edges.add(edge("gm", "financeReview"));
|
||||
edges.add(edge("directPass", "financeReview"));
|
||||
edges.add(edge("financeReview", "end"));
|
||||
ArrayNode branches = flow.putArray("branches");
|
||||
ObjectNode br = branches.addObject();
|
||||
br.put("fromId", "amountGate");
|
||||
ArrayNode cases = br.putArray("cases");
|
||||
ObjectNode c1 = cases.addObject();
|
||||
c1.put("label", "金额 ≥ 1万元");
|
||||
c1.put("toId", "gm");
|
||||
ObjectNode c2 = cases.addObject();
|
||||
c2.put("label", "金额 < 1万元");
|
||||
c2.put("toId", "directPass");
|
||||
|
||||
String instructions =
|
||||
"费用审批(条件分支演示):部门主管审批后,系统按报销金额自动分流——\n" +
|
||||
"金额 ≥ 1万元 的走「总经理审批」分支,< 1万元 的直接进入财务复核,\n" +
|
||||
"两条分支最终汇合到财务复核节点后办结。";
|
||||
return tpl("expense-cond", "费用审批(条件分支)", "财务审批", "财务部", "2025-03-18",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 融资申请审批单:金融办初审 → 财务部 → 风控负责人 → 高管/董事长。
|
||||
* templateId = "financing-apply",供 FinancingController.submitApproval() 调用。
|
||||
*/
|
||||
private FormTemplate financingApply() {
|
||||
String form = formJson("融资申请审批单", rows(
|
||||
row(req(field("applicant", "经办人", "person")),
|
||||
req(field("companySubject", "融资主体", "text"))),
|
||||
row(req(field("financingCode", "融资编号", "text")),
|
||||
req(field("financingType", "融资品种", "text"))),
|
||||
row(req(field("lender", "融资机构", "text")),
|
||||
req(field("amount", "融资金额(元)", "money"))),
|
||||
row(req(field("rate", "年化利率(%)", "number")),
|
||||
req(field("termMonths", "期限(月)", "number"))),
|
||||
row(req(field("repayMethod", "还款方式", "text")),
|
||||
field("purpose", "资金用途", "text")),
|
||||
row(full(field("schemeCompareNote", "方案比选说明", "textarea"))),
|
||||
row(full(field("creditCheckNote", "授信充足性自查", "textarea")))
|
||||
));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者(金融办)", "start"));
|
||||
nodes.add(node("financeOfficeReview", "金融办初审", "审批"));
|
||||
nodes.add(node("financeDept", "财务部审批", "审批"));
|
||||
nodes.add(node("riskControl", "风控负责人会签", "会签"));
|
||||
nodes.add(node("gm", "总经理审批", "审批"));
|
||||
nodes.add(node("chairman", "董事长审批", "审批"));
|
||||
nodes.add(node("financeNotify", "财务部知会", "知会"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "financeOfficeReview"));
|
||||
edges.add(edge("financeOfficeReview", "financeDept"));
|
||||
edges.add(edge("financeDept", "riskControl"));
|
||||
edges.add(edge("riskControl", "gm"));
|
||||
edges.add(edge("gm", "chairman"));
|
||||
edges.add(edge("chairman", "financeNotify"));
|
||||
edges.add(edge("financeNotify", "end"));
|
||||
|
||||
String instructions =
|
||||
"融资申请审批流程:\n" +
|
||||
"一、适用范围:本流程适用于各类融资申请(银行贷款/融资租赁/债券/票据等)。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 经办人须完整填写融资编号、融资机构、融资金额、年化利率及期限;\n" +
|
||||
" 2. 方案比选说明一栏需附多机构比选依据(综合成本率对比);\n" +
|
||||
" 3. 授信充足性自查一栏须确认该机构授信额度剩余足以覆盖本次融资。\n" +
|
||||
"三、审批节点:\n" +
|
||||
" 金融办初审 → 财务部审批 → 风控负责人会签 → 总经理 → 董事长 → 财务部知会 → 办结。\n" +
|
||||
"四、注意事项:\n" +
|
||||
" 审批通过后,融资台账状态自动由「审批中」推进到「已放款」(到账确认后手动更新)。";
|
||||
return tpl("financing-apply", "融资申请审批单", "财务审批", "金融办", "2025-01-15",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 申报服务部 — 年度/季度申报计划审批模板(需求 §1 年度/季度申报计划,Gap 1 [low])。
|
||||
* RdDeclPlanController 固定引用 templateId="rd-decl-plan-approve";本模板确保种子注册。
|
||||
*/
|
||||
private FormTemplate rdDeclPlanApprove() {
|
||||
String form = formJson("申报计划审批单", rows(
|
||||
row(req(field("applicant", "经办人", "person")),
|
||||
req(field("planName", "计划名称", "text"))),
|
||||
row(req(opts(field("periodType", "计划周期", "radio"), "年度", "季度")),
|
||||
req(field("planYear", "计划年度", "number"))),
|
||||
row(req(opts(field("policyCategory", "政策类别", "select"),
|
||||
"资质类", "资金类", "荣誉类", "人才类")),
|
||||
field("programName", "申报项目名称", "text")),
|
||||
row(field("budget", "申报预算(元)", "money"),
|
||||
field("expectedBenefit", "预期获批金额(元)", "money")),
|
||||
row(field("planApplyDate", "计划申报日期", "date"),
|
||||
field("planEndDate", "计划结束日期", "date")),
|
||||
row(full(field("owner", "负责人", "person"))),
|
||||
row(full(field("remark", "备注说明", "textarea")))
|
||||
));
|
||||
|
||||
ObjectNode flow = simpleFlow(new String[][]{
|
||||
{"start", "申报专员(发起)", "start"},
|
||||
{"deptHead", "申报服务部负责人", "审批"},
|
||||
{"rdDirector", "创新研发中心主任", "审批"},
|
||||
{"gm", "分管副总", "审批"},
|
||||
{"end", "结束", "end"}
|
||||
});
|
||||
|
||||
String instructions =
|
||||
"申报计划审批流程:\n" +
|
||||
"一、适用范围:本流程适用于创新研发中心·申报服务部编制的年度/季度政策申报计划。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 计划名称须明确年度(季度)和申报项目,如「2026年高新技术企业认定申报计划」;\n" +
|
||||
" 2. 申报预算应包含咨询费、审计费、材料费合计;\n" +
|
||||
" 3. 预期获批金额填写政策奖励或补贴估算值(不含税收减免);\n" +
|
||||
" 4. 计划申报日期与计划结束日期须在政策申报截止日之前。\n" +
|
||||
"三、审批节点:\n" +
|
||||
" 申报专员提交 → 申报服务部负责人审批 → 创新研发中心主任审批 → 分管副总审批 → 办结。\n" +
|
||||
"四、注意事项:\n" +
|
||||
" 审批通过后,计划状态自动变为「已批准」,系统同步在政策申报台账创建对应申报记录。";
|
||||
|
||||
return tpl("rd-decl-plan-approve", "申报计划审批单", "研发申报", "申报服务部", "2026-01-01",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 合同法务审批模板(templateId = "legal-contract-review")。
|
||||
* 由 LegalContractFlowController.submitReview() 引用,补齐审批引擎对接缺口。
|
||||
* 审批链:业务负责人 → 法务审核 → 财务审核(并行)→ 合规审核 → 高管审批 → 办结。
|
||||
*/
|
||||
private FormTemplate legalContractReview() {
|
||||
String form = formJson("合同法务审批单", rows(
|
||||
row(req(field("applicant", "发起人", "person")), req(field("applyDept", "发起部门", "dept"))),
|
||||
row(full(req(field("contractName", "合同名称", "text")))),
|
||||
row(full(req(field("counterparty", "对方单位", "text")))),
|
||||
row(req(field("amount", "合同金额", "money")), req(field("signDate", "签订日期", "date"))),
|
||||
row(req(opts(field("contractType", "合同类型", "select"),
|
||||
"采购合同", "销售合同", "技术合同", "劳务合同", "保密协议", "NDA", "其他")),
|
||||
req(opts(field("riskLevel", "风险等级", "radio"), "高", "中", "低"))),
|
||||
row(full(field("riskPoints", "主要风险点说明", "textarea"))),
|
||||
row(full(field("remark", "备注", "textarea")))
|
||||
));
|
||||
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
nodes.add(node("start", "发起者", "start"));
|
||||
nodes.add(node("bizLead", "业务负责人", "审批"));
|
||||
nodes.add(node("legal", "法务审核", "审批"));
|
||||
nodes.add(node("finance", "财务审核", "知会"));
|
||||
nodes.add(node("compliance", "合规审核", "审批"));
|
||||
nodes.add(node("gm", "高管审批", "审批"));
|
||||
nodes.add(node("end", "结束", "end"));
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
edges.add(edge("start", "bizLead"));
|
||||
edges.add(edge("bizLead", "legal"));
|
||||
edges.add(edge("bizLead", "finance"));
|
||||
edges.add(edge("legal", "compliance"));
|
||||
edges.add(edge("finance", "compliance"));
|
||||
edges.add(edge("compliance", "gm"));
|
||||
edges.add(edge("gm", "end"));
|
||||
ArrayNode parallels = flow.putArray("parallels");
|
||||
ArrayNode par = parallels.addArray();
|
||||
par.add("legal");
|
||||
par.add("finance");
|
||||
|
||||
String instructions =
|
||||
"合同法务审批流程:\n" +
|
||||
"一、适用范围:适用于各类型合同(采购/销售/技术/劳务/NDA)签订前的法务合规审批。\n" +
|
||||
"二、填报要求:\n" +
|
||||
" 1. 合同名称、对方单位、合同金额、签订日期必填;\n" +
|
||||
" 2. 合同类型须从下拉列表选择,风险等级(高/中/低)须如实填写;\n" +
|
||||
" 3. 主要风险点说明(如付款条款、违约责任、适用法律等)须简要描述。\n" +
|
||||
"三、审批节点:\n" +
|
||||
" 业务负责人确认 → 法务审核(条款)+ 财务知会(金额)并行 → 合规审核 → 高管审批 → 办结。\n" +
|
||||
"四、注意事项:高风险合同建议额外附法律意见书;合同正本扫描件须随单上传。";
|
||||
return tpl("legal-contract-review", "合同法务审批单", "合同管理", "法务风险部", "2026-01-01",
|
||||
instructions, form, flow.toString());
|
||||
}
|
||||
|
||||
/** Linear flow builder: nodes connected start -> ... -> end in order. */
|
||||
private ObjectNode simpleFlow(String[][] nodeDefs) {
|
||||
ObjectNode flow = om.createObjectNode();
|
||||
ArrayNode nodes = flow.putArray("nodes");
|
||||
for (String[] nd : nodeDefs) {
|
||||
nodes.add(node(nd[0], nd[1], nd[2]));
|
||||
}
|
||||
ArrayNode edges = flow.putArray("edges");
|
||||
for (int i = 0; i < nodeDefs.length - 1; i++) {
|
||||
edges.add(edge(nodeDefs[i][0], nodeDefs[i + 1][0]));
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
@@ -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. WwMsdsFile:MSDS 化学品安全技术说明书文件库(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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user