Files
ERP/oa-backend/src/main/java/com/kaidi/oa/seed/DataSeeder.java
T
Qiufeng 1affcd9a5e
Signed Release / release (push) Failing after 5m1s
feat: add PostgreSQL web installation wizard
2026-08-04 07:42:20 +08:00

3365 lines
190 KiB
Java

package com.kaidi.oa.seed;
import com.kaidi.oa.common.Money;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kaidi.oa.common.PasswordUtil;
import com.kaidi.oa.domain.Announcement;
import com.kaidi.oa.domain.Archive;
import com.kaidi.oa.domain.ArchiveComment;
import com.kaidi.oa.domain.BankAccount;
import com.kaidi.oa.domain.Bid;
import com.kaidi.oa.domain.BomItem;
import com.kaidi.oa.domain.Budget;
import com.kaidi.oa.domain.Certification;
import com.kaidi.oa.domain.CollabDoc;
import com.kaidi.oa.domain.CompanySubject;
import com.kaidi.oa.domain.Contract;
import com.kaidi.oa.domain.CrawlJob;
import com.kaidi.oa.domain.CrawlSource;
import com.kaidi.oa.domain.ContractChange;
import com.kaidi.oa.domain.ContractTemplate;
import com.kaidi.oa.domain.CostCenter;
import com.kaidi.oa.domain.Declaration;
import com.kaidi.oa.domain.DeclarationStep;
import com.kaidi.oa.domain.DeclarationTemplate;
import com.kaidi.oa.domain.Expert;
import com.kaidi.oa.domain.Handover;
import com.kaidi.oa.domain.RdExpense;
import com.kaidi.oa.domain.ContractMilestone;
import com.kaidi.oa.domain.Customer;
import com.kaidi.oa.domain.Discussion;
import com.kaidi.oa.domain.DocFile;
import com.kaidi.oa.domain.DocFolder;
import com.kaidi.oa.domain.FlowTask;
import com.kaidi.oa.domain.FlowTrace;
import com.kaidi.oa.domain.FormInstance;
import com.kaidi.oa.domain.FormTemplate;
import com.kaidi.oa.domain.HealthRecord;
import com.kaidi.oa.domain.Intel;
import com.kaidi.oa.domain.Invoice;
import com.kaidi.oa.domain.LeaderTrip;
import com.kaidi.oa.domain.Meeting;
import com.kaidi.oa.domain.MeetingMinute;
import com.kaidi.oa.domain.MeetingRoom;
import com.kaidi.oa.domain.Opportunity;
import com.kaidi.oa.domain.OpportunityFollowup;
import com.kaidi.oa.domain.Patent;
import com.kaidi.oa.domain.Payment;
import com.kaidi.oa.domain.PersonnelCert;
import com.kaidi.oa.domain.PriceItem;
import com.kaidi.oa.domain.Project;
import com.kaidi.oa.domain.QmsRecord;
import com.kaidi.oa.domain.ProjectTask;
import com.kaidi.oa.domain.RdProject;
import com.kaidi.oa.domain.SafetyCheck;
import com.kaidi.oa.domain.ScheduleEvent;
import com.kaidi.oa.domain.Seal;
import com.kaidi.oa.domain.SealUse;
import com.kaidi.oa.domain.Supplier;
import com.kaidi.oa.domain.Survey;
import com.kaidi.oa.domain.SysDept;
import com.kaidi.oa.domain.SysRole;
import com.kaidi.oa.domain.SysUser;
import com.kaidi.oa.domain.SysUserRole;
import com.kaidi.oa.domain.WorkPlan;
import com.kaidi.oa.domain.WastewaterClient;
import com.kaidi.oa.domain.WaterQualityRecord;
import com.kaidi.oa.domain.WorkOrder;
import com.kaidi.oa.domain.InventoryItem;
import com.kaidi.oa.domain.Account;
import com.kaidi.oa.domain.Voucher;
import com.kaidi.oa.domain.LabSample;
import com.kaidi.oa.domain.LabInstrument;
import com.kaidi.oa.domain.DesignDoc;
import com.kaidi.oa.domain.DesignChange;
import com.kaidi.oa.domain.SupervisionLog;
import com.kaidi.oa.domain.AuditProject;
import com.kaidi.oa.domain.ItAsset;
import com.kaidi.oa.repository.AnnouncementRepository;
import com.kaidi.oa.repository.ArchiveRepository;
import com.kaidi.oa.repository.ArchiveCommentRepository;
import com.kaidi.oa.repository.BankAccountRepository;
import com.kaidi.oa.repository.BidRepository;
import com.kaidi.oa.repository.BomItemRepository;
import com.kaidi.oa.repository.BudgetRepository;
import com.kaidi.oa.repository.CertificationRepository;
import com.kaidi.oa.repository.CollabDocRepository;
import com.kaidi.oa.repository.CompanySubjectRepository;
import com.kaidi.oa.repository.ContractChangeRepository;
import com.kaidi.oa.repository.ContractMilestoneRepository;
import com.kaidi.oa.repository.ContractRepository;
import com.kaidi.oa.repository.ContractTemplateRepository;
import com.kaidi.oa.repository.CostCenterRepository;
import com.kaidi.oa.repository.CrawlJobRepository;
import com.kaidi.oa.repository.CrawlSourceRepository;
import com.kaidi.oa.repository.CustomerRepository;
import com.kaidi.oa.repository.DeclarationRepository;
import com.kaidi.oa.repository.DeclarationStepRepository;
import com.kaidi.oa.repository.DeclarationTemplateRepository;
import com.kaidi.oa.repository.ExpertRepository;
import com.kaidi.oa.repository.HandoverRepository;
import com.kaidi.oa.repository.RdExpenseRepository;
import com.kaidi.oa.repository.DiscussionRepository;
import com.kaidi.oa.repository.DocFileRepository;
import com.kaidi.oa.repository.DocFolderRepository;
import com.kaidi.oa.repository.FlowTaskRepository;
import com.kaidi.oa.repository.FlowTraceRepository;
import com.kaidi.oa.repository.FormInstanceRepository;
import com.kaidi.oa.repository.FormTemplateRepository;
import com.kaidi.oa.repository.HealthRecordRepository;
import com.kaidi.oa.repository.IntelRepository;
import com.kaidi.oa.repository.InvoiceRepository;
import com.kaidi.oa.repository.LeaderTripRepository;
import com.kaidi.oa.repository.MeetingMinuteRepository;
import com.kaidi.oa.repository.MeetingRepository;
import com.kaidi.oa.repository.MeetingRoomRepository;
import com.kaidi.oa.repository.OpportunityFollowupRepository;
import com.kaidi.oa.repository.OpportunityRepository;
import com.kaidi.oa.repository.PatentRepository;
import com.kaidi.oa.repository.PaymentRepository;
import com.kaidi.oa.repository.PersonnelCertRepository;
import com.kaidi.oa.repository.PriceItemRepository;
import com.kaidi.oa.repository.ProjectRepository;
import com.kaidi.oa.repository.ProjectTaskRepository;
import com.kaidi.oa.repository.QmsRecordRepository;
import com.kaidi.oa.repository.RdProjectRepository;
import com.kaidi.oa.repository.SafetyCheckRepository;
import com.kaidi.oa.repository.ScheduleEventRepository;
import com.kaidi.oa.repository.SealRepository;
import com.kaidi.oa.repository.SealUseRepository;
import com.kaidi.oa.repository.SupplierRepository;
import com.kaidi.oa.repository.SurveyRepository;
import com.kaidi.oa.repository.SysDeptRepository;
import com.kaidi.oa.repository.SysRoleRepository;
import com.kaidi.oa.repository.SysUserRepository;
import com.kaidi.oa.repository.SysUserRoleRepository;
import com.kaidi.oa.repository.WorkPlanRepository;
import com.kaidi.oa.repository.WastewaterClientRepository;
import com.kaidi.oa.repository.WaterQualityRecordRepository;
import com.kaidi.oa.repository.WorkOrderRepository;
import com.kaidi.oa.repository.InventoryItemRepository;
import com.kaidi.oa.repository.AccountRepository;
import com.kaidi.oa.repository.VoucherRepository;
import com.kaidi.oa.repository.LabSampleRepository;
import com.kaidi.oa.repository.LabInstrumentRepository;
import com.kaidi.oa.repository.DesignDocRepository;
import com.kaidi.oa.repository.DesignChangeRepository;
import com.kaidi.oa.repository.SupervisionLogRepository;
import com.kaidi.oa.repository.AuditProjectRepository;
import com.kaidi.oa.repository.ItAssetRepository;
import com.kaidi.oa.domain.InventoryValuationConfig;
import com.kaidi.oa.domain.CostAdjustLog;
import com.kaidi.oa.repository.InventoryValuationConfigRepository;
import com.kaidi.oa.repository.CostAdjustLogRepository;
import com.kaidi.oa.service.WorkflowService;
import com.kaidi.oa.service.WorkflowService.ChainNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
* Seeds demonstration data on first startup (runs only when the relevant tables
* are empty). Ports the frontend template catalog and a representative set of
* in-flight form instances, plus org/users/roles, meetings, documents,
* announcements and schedule events.
*/
@DemoSeed
@Order(1)
public class DataSeeder implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(DataSeeder.class);
private static final String CURRENT_USER = "我(当前用户)";
private final SysDeptRepository deptRepo;
private final SysUserRepository userRepo;
private final SysRoleRepository roleRepo;
private final SysUserRoleRepository userRoleRepo;
private final FormTemplateRepository templateRepo;
private final FormInstanceRepository instanceRepo;
private final FlowTraceRepository traceRepo;
private final FlowTaskRepository taskRepo;
private final DocFolderRepository folderRepo;
private final DocFileRepository fileRepo;
private final MeetingRepository meetingRepo;
private final MeetingRoomRepository roomRepo;
private final MeetingMinuteRepository minuteRepo;
private final ScheduleEventRepository scheduleRepo;
private final AnnouncementRepository announcementRepo;
private final ProjectRepository projectRepo;
private final ProjectTaskRepository projectTaskRepo;
private final WorkPlanRepository workPlanRepo;
private final CollabDocRepository collabDocRepo;
private final DiscussionRepository discussionRepo;
private final SurveyRepository surveyRepo;
private final LeaderTripRepository leaderTripRepo;
private final CompanySubjectRepository companySubjectRepo;
private final ContractRepository contractRepo;
private final ContractMilestoneRepository contractMilestoneRepo;
private final ContractTemplateRepository contractTemplateRepo;
private final ContractChangeRepository contractChangeRepo;
private final SupplierRepository supplierRepo;
private final CustomerRepository customerRepo;
private final BankAccountRepository bankAccountRepo;
private final SealRepository sealRepo;
private final SealUseRepository sealUseRepo;
private final InvoiceRepository invoiceRepo;
private final PaymentRepository paymentRepo;
private final ArchiveRepository archiveRepo;
private final ArchiveCommentRepository archiveCommentRepo;
private final OpportunityRepository opportunityRepo;
private final OpportunityFollowupRepository opportunityFollowupRepo;
private final SafetyCheckRepository safetyCheckRepo;
private final CertificationRepository certificationRepo;
private final BudgetRepository budgetRepo;
private final BidRepository bidRepo;
private final IntelRepository intelRepo;
private final RdProjectRepository rdProjectRepo;
private final PatentRepository patentRepo;
private final DeclarationRepository declarationRepo;
private final DeclarationTemplateRepository declarationTemplateRepo;
private final RdExpenseRepository rdExpenseRepo;
private final CostCenterRepository costCenterRepo;
private final PriceItemRepository priceItemRepo;
private final PersonnelCertRepository personnelCertRepo;
private final CrawlSourceRepository crawlSourceRepo;
private final CrawlJobRepository crawlJobRepo;
private final BomItemRepository bomItemRepo;
private final HealthRecordRepository healthRecordRepo;
private final QmsRecordRepository qmsRecordRepo;
private final WastewaterClientRepository wastewaterClientRepo;
private final WaterQualityRecordRepository waterQualityRecordRepo;
private final WorkOrderRepository workOrderRepo;
private final InventoryItemRepository inventoryItemRepo;
private final AccountRepository accountRepo;
private final VoucherRepository voucherRepo;
private final LabSampleRepository labSampleRepo;
private final LabInstrumentRepository labInstrumentRepo;
private final DesignDocRepository designDocRepo;
private final DesignChangeRepository designChangeRepo;
private final SupervisionLogRepository supervisionLogRepo;
private final AuditProjectRepository auditProjectRepo;
private final ItAssetRepository itAssetRepo;
private final HandoverRepository handoverRepo;
private final DeclarationStepRepository declarationStepRepo;
private final ExpertRepository expertRepo;
private final InventoryValuationConfigRepository inventoryValuationConfigRepo;
private final CostAdjustLogRepository costAdjustLogRepo;
private final WorkflowService workflowService;
private final ObjectMapper objectMapper;
public DataSeeder(SysDeptRepository deptRepo, SysUserRepository userRepo, SysRoleRepository roleRepo,
SysUserRoleRepository userRoleRepo, FormTemplateRepository templateRepo,
FormInstanceRepository instanceRepo, FlowTraceRepository traceRepo,
FlowTaskRepository taskRepo, DocFolderRepository folderRepo, DocFileRepository fileRepo,
MeetingRepository meetingRepo, MeetingRoomRepository roomRepo,
MeetingMinuteRepository minuteRepo, ScheduleEventRepository scheduleRepo,
AnnouncementRepository announcementRepo, ProjectRepository projectRepo,
ProjectTaskRepository projectTaskRepo, WorkPlanRepository workPlanRepo,
CollabDocRepository collabDocRepo, DiscussionRepository discussionRepo,
SurveyRepository surveyRepo, LeaderTripRepository leaderTripRepo,
CompanySubjectRepository companySubjectRepo, ContractRepository contractRepo,
ContractMilestoneRepository contractMilestoneRepo,
ContractTemplateRepository contractTemplateRepo,
ContractChangeRepository contractChangeRepo,
SupplierRepository supplierRepo, CustomerRepository customerRepo,
BankAccountRepository bankAccountRepo, SealRepository sealRepo,
SealUseRepository sealUseRepo, InvoiceRepository invoiceRepo,
PaymentRepository paymentRepo, ArchiveRepository archiveRepo,
ArchiveCommentRepository archiveCommentRepo,
OpportunityRepository opportunityRepo,
OpportunityFollowupRepository opportunityFollowupRepo,
SafetyCheckRepository safetyCheckRepo,
CertificationRepository certificationRepo,
BudgetRepository budgetRepo,
BidRepository bidRepo,
IntelRepository intelRepo,
RdProjectRepository rdProjectRepo,
PatentRepository patentRepo,
DeclarationRepository declarationRepo,
DeclarationTemplateRepository declarationTemplateRepo,
RdExpenseRepository rdExpenseRepo,
CostCenterRepository costCenterRepo,
PriceItemRepository priceItemRepo,
PersonnelCertRepository personnelCertRepo,
CrawlSourceRepository crawlSourceRepo,
CrawlJobRepository crawlJobRepo,
BomItemRepository bomItemRepo,
HealthRecordRepository healthRecordRepo,
QmsRecordRepository qmsRecordRepo,
WastewaterClientRepository wastewaterClientRepo,
WaterQualityRecordRepository waterQualityRecordRepo,
WorkOrderRepository workOrderRepo,
InventoryItemRepository inventoryItemRepo,
AccountRepository accountRepo,
VoucherRepository voucherRepo,
LabSampleRepository labSampleRepo,
LabInstrumentRepository labInstrumentRepo,
DesignDocRepository designDocRepo,
DesignChangeRepository designChangeRepo,
SupervisionLogRepository supervisionLogRepo,
AuditProjectRepository auditProjectRepo,
ItAssetRepository itAssetRepo,
HandoverRepository handoverRepo,
DeclarationStepRepository declarationStepRepo,
ExpertRepository expertRepo,
InventoryValuationConfigRepository inventoryValuationConfigRepo,
CostAdjustLogRepository costAdjustLogRepo,
WorkflowService workflowService, ObjectMapper objectMapper) {
this.deptRepo = deptRepo;
this.userRepo = userRepo;
this.roleRepo = roleRepo;
this.userRoleRepo = userRoleRepo;
this.templateRepo = templateRepo;
this.instanceRepo = instanceRepo;
this.traceRepo = traceRepo;
this.taskRepo = taskRepo;
this.folderRepo = folderRepo;
this.fileRepo = fileRepo;
this.meetingRepo = meetingRepo;
this.roomRepo = roomRepo;
this.minuteRepo = minuteRepo;
this.scheduleRepo = scheduleRepo;
this.announcementRepo = announcementRepo;
this.projectRepo = projectRepo;
this.projectTaskRepo = projectTaskRepo;
this.workPlanRepo = workPlanRepo;
this.collabDocRepo = collabDocRepo;
this.discussionRepo = discussionRepo;
this.surveyRepo = surveyRepo;
this.leaderTripRepo = leaderTripRepo;
this.companySubjectRepo = companySubjectRepo;
this.contractRepo = contractRepo;
this.contractMilestoneRepo = contractMilestoneRepo;
this.contractTemplateRepo = contractTemplateRepo;
this.contractChangeRepo = contractChangeRepo;
this.supplierRepo = supplierRepo;
this.customerRepo = customerRepo;
this.bankAccountRepo = bankAccountRepo;
this.sealRepo = sealRepo;
this.sealUseRepo = sealUseRepo;
this.invoiceRepo = invoiceRepo;
this.paymentRepo = paymentRepo;
this.archiveRepo = archiveRepo;
this.archiveCommentRepo = archiveCommentRepo;
this.opportunityRepo = opportunityRepo;
this.opportunityFollowupRepo = opportunityFollowupRepo;
this.safetyCheckRepo = safetyCheckRepo;
this.certificationRepo = certificationRepo;
this.budgetRepo = budgetRepo;
this.bidRepo = bidRepo;
this.intelRepo = intelRepo;
this.rdProjectRepo = rdProjectRepo;
this.patentRepo = patentRepo;
this.declarationRepo = declarationRepo;
this.declarationTemplateRepo = declarationTemplateRepo;
this.rdExpenseRepo = rdExpenseRepo;
this.costCenterRepo = costCenterRepo;
this.priceItemRepo = priceItemRepo;
this.personnelCertRepo = personnelCertRepo;
this.crawlSourceRepo = crawlSourceRepo;
this.crawlJobRepo = crawlJobRepo;
this.bomItemRepo = bomItemRepo;
this.healthRecordRepo = healthRecordRepo;
this.qmsRecordRepo = qmsRecordRepo;
this.wastewaterClientRepo = wastewaterClientRepo;
this.waterQualityRecordRepo = waterQualityRecordRepo;
this.workOrderRepo = workOrderRepo;
this.inventoryItemRepo = inventoryItemRepo;
this.accountRepo = accountRepo;
this.voucherRepo = voucherRepo;
this.labSampleRepo = labSampleRepo;
this.labInstrumentRepo = labInstrumentRepo;
this.designDocRepo = designDocRepo;
this.designChangeRepo = designChangeRepo;
this.supervisionLogRepo = supervisionLogRepo;
this.auditProjectRepo = auditProjectRepo;
this.itAssetRepo = itAssetRepo;
this.handoverRepo = handoverRepo;
this.declarationStepRepo = declarationStepRepo;
this.expertRepo = expertRepo;
this.inventoryValuationConfigRepo = inventoryValuationConfigRepo;
this.costAdjustLogRepo = costAdjustLogRepo;
this.workflowService = workflowService;
this.objectMapper = objectMapper;
}
@Override
public void run(String... args) {
if (templateRepo.count() > 0 || userRepo.count() > 0) {
log.info("Seed data already present; skipping seeding.");
return;
}
log.info("Seeding OA demonstration data...");
seedOrg();
seedTemplates();
seedInstances();
seedDocuments();
seedMeetings();
seedSchedule();
seedAnnouncements();
seedProjects();
seedWorkPlans();
seedCollabDocs();
seedDiscussions();
seedSurveys();
seedLeaderTrips();
seedMasterData();
log.info("Seeding complete: {} templates, {} instances.",
templateRepo.count(), instanceRepo.count());
}
// ----- org / users / roles -----
private void seedOrg() {
SysDept root = dept("开迪集团", null, 1);
SysDept admin = dept("行政部", root.getId(), 3);
SysDept it = dept("信息中心", root.getId(), 4);
dept("采购部", root.getId(), 5);
// Two-level org expansion (Request.MD 2): 11 first-level centers under
// the group root, each carrying 1-3 second-level units.
SysDept market = dept("市场部", root.getId(), 6);
dept("经营部", market.getId(), 1);
dept("招投标部", market.getId(), 2);
dept("办事处管理", market.getId(), 3);
SysDept engMgmt = dept("工程管理中心", root.getId(), 7);
dept("项目管理部", engMgmt.getId(), 1);
dept("工程监理部", engMgmt.getId(), 2);
// Request.MD 2 (a): missing second-level units under engineering center.
dept("质安部", engMgmt.getId(), 3);
dept("专利工法办", engMgmt.getId(), 4);
SysDept rdCenter = dept("创新研发中心", root.getId(), 8);
dept("研发项目部", rdCenter.getId(), 1);
dept("知识产权部", rdCenter.getId(), 2);
// Request.MD 2 (c): design research center second-level units renamed.
SysDept designCenter = dept("设计研究中心", root.getId(), 9);
dept("咨询可研院", designCenter.getId(), 1);
dept("规划设计部", designCenter.getId(), 2);
SysDept mfgCenter = dept("制造管理中心", root.getId(), 10);
dept("生物质肥料制造中心", mfgCenter.getId(), 1);
dept("水处理药剂制造中心", mfgCenter.getId(), 2);
// Request.MD 2 (d): operation management center second-level units renamed.
SysDept opsCenter = dept("运营管理中心", root.getId(), 11);
dept("城镇污水运营中心", opsCenter.getId(), 1);
dept("工业废水运营中心", opsCenter.getId(), 2);
SysDept payCenter = dept("支付中心", root.getId(), 12);
dept("资金结算部", payCenter.getId(), 1);
dept("票据管理部", payCenter.getId(), 2);
// Request.MD 2 (b): finance department moved from top level under pay
// center; (a): cost control department added under pay center.
SysDept fin = dept("财务部", payCenter.getId(), 3);
dept("成本控制部", payCenter.getId(), 4);
SysDept internalControl = dept("内控部", root.getId(), 13);
dept("法务风险部", internalControl.getId(), 1);
dept("审计监察部", internalControl.getId(), 2);
SysDept adminOffice = dept("行政办公", root.getId(), 14);
dept("综合部", adminOffice.getId(), 1);
dept("后勤部", adminOffice.getId(), 2);
dept("信息部", adminOffice.getId(), 3);
dept("档案室", adminOffice.getId(), 4);
// Request.MD 2 (a): archives reading room added under admin office.
dept("资料室", adminOffice.getId(), 5);
SysDept brand = dept("品牌推广", root.getId(), 15);
dept("品牌策划部", brand.getId(), 1);
dept("媒体运营部", brand.getId(), 2);
SysDept dataCenter = dept("数据中心", root.getId(), 16);
dept("统一数据库", dataCenter.getId(), 1);
dept("情报采集", dataCenter.getId(), 2);
dept("数据分析", dataCenter.getId(), 3);
SysRole adminRole = role("系统管理员", "ADMIN");
SysRole userRole = role("普通用户", "USER");
SysRole approverRole = role("审批人", "APPROVER");
SysUser adminUser = user("admin", "系统管理员", it.getId(), "信息中心主任",
"13800000000", "admin@kaidi.com");
SysUser u1 = user("zhangwei", "张伟", fin.getId(), "财务部经理", "13800000001", "zhangwei@kaidi.com");
SysUser u2 = user("lina", "李娜", admin.getId(), "行政主管", "13800000002", "lina@kaidi.com");
SysUser u3 = user("wangfang", "王芳", it.getId(), "软件工程师", "13800000003", "wangfang@kaidi.com");
SysUser u4 = user("liuyang", "刘洋", fin.getId(), "会计", "13800000004", "liuyang@kaidi.com");
SysUser u5 = user("chenjing", "陈静", admin.getId(), "人事专员", "13800000005", "chenjing@kaidi.com");
link(adminUser.getId(), adminRole.getId());
link(u1.getId(), approverRole.getId());
link(u2.getId(), approverRole.getId());
link(u3.getId(), userRole.getId());
link(u4.getId(), userRole.getId());
link(u5.getId(), userRole.getId());
}
private SysDept dept(String name, Long parentId, int sortNo) {
SysDept d = new SysDept();
d.setName(name);
d.setParentId(parentId);
d.setSortNo(sortNo);
return deptRepo.save(d);
}
private SysRole role(String name, String code) {
SysRole r = new SysRole();
r.setName(name);
r.setCode(code);
return roleRepo.save(r);
}
private SysUser user(String loginName, String displayName, Long deptId, String title,
String phone, String email) {
SysUser u = new SysUser();
u.setLoginName(loginName);
u.setDisplayName(displayName);
u.setDeptId(deptId);
u.setTitle(title);
u.setPhone(phone);
u.setEmail(email);
u.setPassword(PasswordUtil.hash("123456"));
u.setEnabled(true);
return userRepo.save(u);
}
private void link(Long userId, Long roleId) {
userRoleRepo.save(new SysUserRole(userId, roleId));
}
// ----- form templates -----
private void seedTemplates() {
TemplateSeedData seed = new TemplateSeedData(objectMapper);
templateRepo.saveAll(seed.all());
}
// ----- form instances (mirror frontend seedDemoItems) -----
private void seedInstances() {
// In-flight items (待办 / 办理中 / 已退回).
seedInstance("baohan-payment", "关于投标保函付款的申请",
"{\"applicant\":\"我(当前用户)\",\"payType\":\"投标保函\",\"payUnit\":\"中国建设银行\",\"projectName\":\"滨江路桥工程\",\"payReason\":\"投标保函付款\"}",
0, CURRENT_USER, 2, false, false);
seedInstance("expense-reimburse", "市场部第二季度推广费用报销",
"{\"applicant\":\"我(当前用户)\",\"reimburseDept\":\"市场部\",\"expenseType\":\"差旅费\",\"amount\":12800}",
1, CURRENT_USER, 4, false, false);
seedInstance("leave-apply", "王芳的年假申请",
"{\"applicant\":\"王芳\",\"leaveType\":\"年假\",\"reason\":\"回乡探亲\"}",
0, "王芳", 1, false, false);
seedInstance("purchase-contract", "采购部办公设备采购合同会签",
"{\"title\":\"采购部办公设备采购合同会签\"}",
1, CURRENT_USER, 5, false, false);
seedInstance("seal-apply", "法务部对外合同用印申请",
"{\"applicant\":\"我(当前用户)\",\"sealType\":\"合同章\",\"copies\":2,\"docName\":\"战略合作框架协议\",\"reason\":\"对外签约用印\"}",
0, CURRENT_USER, 1, false, false);
seedInstance("business-trip", "研发部赴上海出差申请",
"{\"title\":\"研发部赴上海出差申请\"}",
0, "刘洋", 2, false, false);
seedInstance("payment-apply", "供应商货款付款申请",
"{\"title\":\"供应商货款付款申请\"}",
2, CURRENT_USER, 6, false, false);
seedInstance("overtime-apply", "陈静的周末加班申请",
"{\"title\":\"陈静的周末加班申请\"}",
0, "陈静", 1, false, false);
seedInstance("supplier-access", "新供应商资质准入会签",
"{\"title\":\"新供应商资质准入会签\"}",
1, CURRENT_USER, 3, false, false);
seedInstance("general-collab", "关于完善信息安全制度的请示",
"{\"title\":\"关于完善信息安全制度的请示\",\"applicant\":\"我(当前用户)\",\"content\":\"建议完善信息安全管理制度。\"}",
0, CURRENT_USER, 1, false, true);
// Finished items (已办结).
seedInstance("leave-apply", "我的事假申请",
"{\"applicant\":\"我(当前用户)\",\"leaveType\":\"事假\",\"reason\":\"处理家庭事务\"}",
0, CURRENT_USER, 8, true, false);
seedInstance("expense-reimburse", "办公用品报销",
"{\"applicant\":\"我(当前用户)\",\"reimburseDept\":\"行政部\",\"expenseType\":\"办公费\",\"amount\":3200}",
0, CURRENT_USER, 9, true, false);
seedInstance("seal-apply", "业务合同用印申请",
"{\"applicant\":\"黄磊\",\"sealType\":\"公章\",\"copies\":1,\"docName\":\"采购合同\",\"reason\":\"采购签约\"}",
0, "黄磊", 10, true, false);
seedInstance("general-collab", "关于季度经营分析会的通知",
"{\"title\":\"关于季度经营分析会的通知\",\"applicant\":\"我(当前用户)\",\"content\":\"定于本月下旬召开季度经营分析会。\"}",
0, CURRENT_USER, 11, true, false);
// One draft (草稿).
seedDraft("business-trip", "赴北京客户拜访出差申请(暂存)",
"{\"title\":\"赴北京客户拜访出差申请(暂存)\"}", CURRENT_USER);
}
/**
* Seed an instance at a given main-chain index, with prior 同意 trace steps
* filled in. finished -> jump to 已办结; returned -> mark 已退回.
*/
private void seedInstance(String templateId, String title, String dataJson, int atIndex,
String origin, int dayOffset, boolean finished, boolean returned) {
FormTemplate template = templateRepo.findById(templateId).orElse(null);
if (template == null) {
return;
}
List<ChainNode> chain = workflowService.mainChain(template.getFlowSchemaJson());
Instant startTs = daysAgo(dayOffset, 9, 15);
FormInstance inst = new FormInstance();
inst.setTemplateId(template.getId());
inst.setTemplateName(template.getName());
inst.setCategory(template.getCategory());
inst.setTitle(title);
inst.setDataJson(dataJson);
inst.setOriginUser(origin);
inst.setCreatedAt(startTs);
int idx = finished ? chain.size() : Math.min(atIndex, Math.max(0, chain.size() - 1));
if (finished) {
inst.setNodeIndex(chain.size());
inst.setStatus("已办结");
inst.setCurrentNode("办结");
inst.setCurrentNodeId(endId(template.getFlowSchemaJson()));
} else {
inst.setNodeIndex(idx);
ChainNode node = chain.isEmpty() ? null : chain.get(idx);
inst.setCurrentNode(node == null ? "办结" : node.label());
inst.setCurrentNodeId(node == null ? "end" : node.id());
inst.setStatus(returned ? "已退回" : (atIndex == 0 ? "待办" : "办理中"));
}
inst.setUpdatedAt(daysAgo(Math.max(0, dayOffset - atIndex), 11, 5));
instanceRepo.save(inst);
Long instId = inst.getId();
// Trace chain.
addTrace(instId, "发起", origin, "提交申请,请各级领导审批。", "发起", startTs);
for (int k = 0; k < idx && k < chain.size(); k++) {
addTrace(instId, chain.get(k).label(), CURRENT_USER,
"情况属实,同意,按流程办理。", "同意", daysAgo(Math.max(0, dayOffset - k - 1), 10 + k, 20));
}
if (finished) {
addTrace(instId, "办结", CURRENT_USER, "流程办结,归档。", "办结",
daysAgo(Math.max(0, dayOffset - chain.size() - 1), 16, 30));
}
if (returned) {
String nodeLabel = chain.isEmpty() ? "审批" : chain.get(idx).label();
addTrace(instId, nodeLabel, CURRENT_USER, "材料需补充,退回修改。", "退回",
daysAgo(Math.max(0, dayOffset - atIndex), 15, 0));
}
// Open task for the active node when in-flight.
if (!finished && !returned && !chain.isEmpty()) {
addTask(instId, chain.get(idx).label(), CURRENT_USER, inst.getUpdatedAt());
}
}
private void seedDraft(String templateId, String title, String dataJson, String origin) {
FormTemplate template = templateRepo.findById(templateId).orElse(null);
if (template == null) {
return;
}
List<ChainNode> chain = workflowService.mainChain(template.getFlowSchemaJson());
Instant ts = daysAgo(0, 9, 20);
FormInstance inst = new FormInstance();
inst.setTemplateId(template.getId());
inst.setTemplateName(template.getName());
inst.setCategory(template.getCategory());
inst.setTitle(title);
inst.setDataJson(dataJson);
inst.setOriginUser(origin);
inst.setStatus("草稿");
inst.setNodeIndex(0);
inst.setCurrentNode("草稿");
inst.setCurrentNodeId(chain.isEmpty() ? "start" : chain.get(0).id());
inst.setCreatedAt(ts);
inst.setUpdatedAt(ts);
instanceRepo.save(inst);
}
private void addTrace(Long instanceId, String node, String who, String opinion,
String type, Instant when) {
FlowTrace t = new FlowTrace();
t.setInstanceId(instanceId);
t.setNode(node);
t.setWho(who);
t.setOpinion(opinion);
t.setType(type);
t.setCreatedAt(when);
traceRepo.save(t);
}
private void addTask(Long instanceId, String nodeLabel, String assignee, Instant when) {
FlowTask task = new FlowTask();
task.setInstanceId(instanceId);
task.setNodeLabel(nodeLabel);
task.setAssignee(assignee);
task.setStatus("todo");
task.setCreatedAt(when);
taskRepo.save(task);
}
private String endId(String flowJson) {
try {
var nodes = objectMapper.readTree(flowJson).path("nodes");
if (nodes.isArray()) {
for (var n : nodes) {
if ("end".equals(n.path("type").asText(""))) {
return n.path("id").asText("end");
}
}
}
} catch (Exception ignored) {
// fall through to default
}
return "end";
}
// ----- documents -----
private void seedDocuments() {
DocFolder root = folder("公司文档", null);
DocFolder policy = folder("规章制度", root.getId());
DocFolder finance = folder("财务资料", root.getId());
folder("项目档案", root.getId());
file(policy.getId(), "员工手册", "pdf", 2_400_000L, 3, "李娜", "已发布");
file(policy.getId(), "考勤管理办法", "docx", 180_000L, 2, "陈静", "已发布");
file(finance.getId(), "2025年度预算表", "xlsx", 540_000L, 1, "张伟", "草稿");
file(root.getId(), "公司组织架构图", "png", 320_000L, 1, "系统管理员", "已发布");
}
private DocFolder folder(String name, Long parentId) {
DocFolder f = new DocFolder();
f.setName(name);
f.setParentId(parentId);
return folderRepo.save(f);
}
private void file(Long folderId, String name, String ext, long size, int version,
String uploader, String status) {
DocFile f = new DocFile();
f.setFolderId(folderId);
f.setName(name);
f.setExt(ext);
f.setSize(size);
f.setVersion(version);
f.setUploader(uploader);
f.setStatus(status);
f.setUpdatedAt(Instant.now());
fileRepo.save(f);
}
// ----- meetings -----
private void seedMeetings() {
MeetingRoom r1 = room("第一会议室", 20, "A座3层");
MeetingRoom r2 = room("视频会议室", 10, "B座5层");
room("洽谈室", 6, "A座1层");
Instant base = Instant.now().plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.HOURS);
Meeting m1 = meeting("季度经营分析会", base, base.plus(2, ChronoUnit.HOURS),
r1.getId(), "张伟", "已预定");
meeting("产品迭代评审会", base.plus(1, ChronoUnit.DAYS), base.plus(1, ChronoUnit.DAYS).plus(90, ChronoUnit.MINUTES),
r2.getId(), "王芳", "已预定");
minute(m1.getId(), "季度经营分析会纪要", "一、上季度经营回顾;二、本季度目标分解;三、行动项与责任人。");
}
private MeetingRoom room(String name, int capacity, String location) {
MeetingRoom r = new MeetingRoom();
r.setName(name);
r.setCapacity(capacity);
r.setLocation(location);
return roomRepo.save(r);
}
private Meeting meeting(String subject, Instant start, Instant end, Long roomId,
String organizer, String status) {
Meeting m = new Meeting();
m.setSubject(subject);
m.setStartTime(start);
m.setEndTime(end);
m.setRoomId(roomId);
m.setOrganizer(organizer);
m.setStatus(status);
return meetingRepo.save(m);
}
private void minute(Long meetingId, String title, String content) {
MeetingMinute mm = new MeetingMinute();
mm.setMeetingId(meetingId);
mm.setTitle(title);
mm.setContent(content);
mm.setCreatedAt(Instant.now());
minuteRepo.save(mm);
}
// ----- schedule -----
private void seedSchedule() {
Instant base = Instant.now().truncatedTo(ChronoUnit.HOURS);
event("项目周例会", "会议", base.plus(2, ChronoUnit.HOURS), base.plus(3, ChronoUnit.HOURS), CURRENT_USER);
event("提交月度报表", "任务", base.plus(1, ChronoUnit.DAYS), base.plus(1, ChronoUnit.DAYS).plus(1, ChronoUnit.HOURS), "张伟");
event("客户拜访", "外出", base.plus(2, ChronoUnit.DAYS), base.plus(2, ChronoUnit.DAYS).plus(4, ChronoUnit.HOURS), "刘洋");
}
private void event(String title, String type, Instant start, Instant end, String owner) {
ScheduleEvent e = new ScheduleEvent();
e.setTitle(title);
e.setType(type);
e.setStartTime(start);
e.setEndTime(end);
e.setOwner(owner);
scheduleRepo.save(e);
}
// ----- announcements -----
private void seedAnnouncements() {
announcement("公告", "关于2025年端午节放假安排的通知",
"根据国家法定节假日安排,端午节放假三天,请各部门提前做好工作安排。", "行政部", true);
announcement("公告", "关于启用新版OA系统的通知",
"新版协同办公系统正式上线,原系统将于本月底停用,请及时迁移待办事项。", "信息中心", true);
announcement("新闻", "公司荣获年度优秀供应商称号",
"在年度供应链大会上,公司凭借稳定的交付质量荣获优秀供应商称号。", "市场部", false);
announcement("新闻", "第三季度经营业绩稳步增长",
"公司第三季度营收同比增长18%,各项业务指标稳中向好。", "财务部", false);
}
private void announcement(String category, String title, String content, String author, boolean top) {
Announcement a = new Announcement();
a.setCategory(category);
a.setTitle(title);
a.setContent(content);
a.setAuthor(author);
a.setTop(top);
a.setPublishedAt(Instant.now().minus(Duration.ofDays(1)));
announcementRepo.save(a);
}
// ----- projects -----
private void seedProjects() {
Project p1 = project("靖州供水项目工程信息化", "张伟", 4L, "2026-04-01", "2026-09-30", 65, "进行中", "执行");
Project p2 = project("新版OA系统上线推广", "王芳", 4L, "2026-05-10", "2026-06-30", 80, "进行中", "中标");
Project p3 = project("2026年度财务预算系统升级", "刘洋", 2L, "2026-03-01", "2026-05-31", 100, "已完成", "验收");
project("行政后勤管理平台建设", "李娜", 3L, "2026-02-01", "2026-04-30", 40, "已超期", "执行");
projectTask(p1.getId(), "完成现场管网数据采集", "王芳", "已完成", "2026-05-20", "高");
projectTask(p1.getId(), "搭建供水调度监控模块", "刘洋", "进行中", "2026-07-15", "高");
projectTask(p1.getId(), "编写运维操作手册", "陈静", "待办", "2026-08-31", "中");
projectTask(p2.getId(), "整理各部门待办迁移清单", "李娜", "进行中", "2026-06-18", "高");
projectTask(p2.getId(), "组织全员使用培训", "王芳", "待办", "2026-06-25", "中");
projectTask(p3.getId(), "预算模板字段重构", "刘洋", "已完成", "2026-04-30", "低");
}
private Project project(String name, String owner, Long deptId, String startDate,
String endDate, int progress, String status, String phase) {
Project p = new Project();
p.setName(name);
p.setOwner(owner);
p.setDeptId(deptId);
p.setStartDate(startDate);
p.setEndDate(endDate);
p.setProgress(progress);
p.setStatus(status);
p.setPhase(phase);
return projectRepo.save(p);
}
private void projectTask(Long projectId, String title, String assignee, String status,
String dueDate, String priority) {
ProjectTask t = new ProjectTask();
t.setProjectId(projectId);
t.setTitle(title);
t.setAssignee(assignee);
t.setStatus(status);
t.setDueDate(dueDate);
t.setPriority(priority);
projectTaskRepo.save(t);
}
// ----- work plans -----
private void seedWorkPlans() {
workPlan("信息中心本周工作计划", "王芳", "本周", "执行中",
"一、推进OA上线推广;二、完成供水项目数据采集;三、处理日常运维工单。");
workPlan("财务部六月工作计划", "张伟", "本月", "执行中",
"一、完成半年度结账;二、推进预算系统升级验收;三、组织费用报销专项核查。");
workPlan("行政部第二季度工作计划", "李娜", "本季", "已完成",
"一、完成办公场地调整;二、组织员工体检;三、修订考勤管理办法。");
workPlan("人事招聘月度计划", "陈静", "本月", "草稿",
"一、梳理各部门用人需求;二、发布社会招聘公告;三、安排面试与入职。");
}
private void workPlan(String title, String owner, String period, String status, String content) {
WorkPlan p = new WorkPlan();
p.setTitle(title);
p.setOwner(owner);
p.setPeriod(period);
p.setStatus(status);
p.setContent(content);
workPlanRepo.save(p);
}
// ----- collab docs -----
private void seedCollabDocs() {
collabDoc("OA上线推广方案", "王芳", "张伟,李娜,陈静", "编辑中",
"围绕新版OA系统上线,制定分阶段推广、培训与答疑计划,确保各部门平稳迁移。");
collabDoc("靖州供水项目实施方案", "张伟", "王芳,刘洋", "已定稿",
"明确供水项目信息化建设的范围、里程碑、责任分工与验收标准。");
collabDoc("公司年度经营分析报告", "刘洋", "张伟,李娜", "编辑中",
"汇总各部门经营数据,分析上半年经营成果与下半年改进方向。");
collabDoc("信息安全管理制度(修订稿)", "系统管理员", "王芳,陈静", "已定稿",
"对账号权限、数据备份、终端安全等条款进行修订,强化信息安全管理。");
}
private void collabDoc(String title, String owner, String participants, String status, String content) {
CollabDoc d = new CollabDoc();
d.setTitle(title);
d.setOwner(owner);
d.setParticipants(participants);
d.setStatus(status);
d.setContent(content);
d.setUpdatedAt(Instant.now().minus(Duration.ofDays(1)));
collabDocRepo.save(d);
}
// ----- discussions -----
private void seedDiscussions() {
discussion("关于新版OA的使用反馈", "李娜", "系统建议",
"新版OA整体体验不错,建议在待办列表增加批量处理功能,提高审批效率。", 8);
discussion("供水项目现场协作经验分享", "王芳", "项目交流",
"分享靖州供水项目现场数据采集的协作经验,欢迎大家补充改进建议。", 5);
discussion("食堂就餐时间调整讨论", "陈静", "行政事务",
"近期就餐高峰拥挤,提议错峰用餐,请各部门发表意见。", 12);
discussion("费用报销流程优化建议", "刘洋", "财务话题",
"建议简化小额报销审批环节,缩短到账周期,欢迎讨论。", 6);
}
private void discussion(String title, String author, String category, String content, int replyCount) {
Discussion d = new Discussion();
d.setTitle(title);
d.setAuthor(author);
d.setCategory(category);
d.setContent(content);
d.setReplyCount(replyCount);
d.setCreatedAt(Instant.now().minus(Duration.ofDays(2)));
discussionRepo.save(d);
}
// ----- surveys -----
private void seedSurveys() {
survey("OA满意度调查", "信息中心", "进行中",
"[{\"label\":\"非常满意\",\"votes\":18},{\"label\":\"满意\",\"votes\":24},{\"label\":\"一般\",\"votes\":9},{\"label\":\"不满意\",\"votes\":3}]",
54, "2026-06-20");
survey("年度团建活动地点投票", "行政部", "进行中",
"[{\"label\":\"张家界\",\"votes\":31},{\"label\":\"长沙周边\",\"votes\":15},{\"label\":\"省外自驾\",\"votes\":12}]",
58, "2026-06-15");
survey("员工培训需求调研", "陈静", "已结束",
"[{\"label\":\"办公软件技能\",\"votes\":22},{\"label\":\"项目管理\",\"votes\":17},{\"label\":\"财税知识\",\"votes\":11}]",
50, "2026-05-31");
}
private void survey(String title, String author, String status, String optionsJson,
int responseCount, String deadline) {
Survey s = new Survey();
s.setTitle(title);
s.setAuthor(author);
s.setStatus(status);
s.setOptionsJson(optionsJson);
s.setResponseCount(responseCount);
s.setDeadline(deadline);
surveyRepo.save(s);
}
// ----- leader trips -----
private void seedLeaderTrips() {
Instant base = Instant.now().truncatedTo(ChronoUnit.HOURS);
leaderTrip("张伟", "长沙", base.plus(2, ChronoUnit.DAYS), base.plus(3, ChronoUnit.DAYS),
"赴长沙参加行业峰会", "计划中");
leaderTrip("李娜", "靖州", base.plus(1, ChronoUnit.DAYS), base.plus(1, ChronoUnit.DAYS).plus(8, ChronoUnit.HOURS),
"现场调研供水项目进展", "计划中");
leaderTrip("王芳", "北京", base.minus(2, ChronoUnit.DAYS), base.plus(1, ChronoUnit.HOURS),
"参加信息化技术交流会", "进行中");
leaderTrip("刘洋", "广州", base.minus(6, ChronoUnit.DAYS), base.minus(4, ChronoUnit.DAYS),
"对接审计与财税合规事项", "已完成");
}
private void leaderTrip(String leader, String destination, Instant startTime, Instant endTime,
String purpose, String status) {
LeaderTrip t = new LeaderTrip();
t.setLeader(leader);
t.setDestination(destination);
t.setStartTime(startTime);
t.setEndTime(endTime);
t.setPurpose(purpose);
t.setStatus(status);
leaderTripRepo.save(t);
}
// ----- core master data -----
private void seedMasterData() {
seedCompanySubjects();
seedContracts();
seedContractTemplates();
seedContractChanges();
seedSuppliers();
seedCustomers();
seedBankAccounts();
seedSeals();
seedInvoices();
seedPayments();
seedSealUses();
seedArchives();
seedArchiveComments();
seedOpportunities();
seedOpportunityFollowups();
seedSafetyChecks();
seedCertifications();
seedBudgets();
seedBids();
seedIntel();
seedRd();
seedDeclarationTemplates();
seedRdExpenses();
seedCostCenters();
seedPriceItems();
seedPersonnelCerts();
seedCrawlSources();
seedCrawlJobs();
seedBomItems();
seedHealthRecords();
seedQmsRecords();
seedWastewaterClients();
seedWaterQualityRecords();
seedWorkOrders();
seedInventoryItems();
seedAccounts();
seedVouchers();
seedLabSamples();
seedLabInstruments();
seedDesignDocs();
seedDesignChanges();
seedSupervisionLogs();
seedAuditProjects();
seedItAssets();
seedHandovers();
seedDeclarationSteps();
seedExperts();
seedInventoryValuationConfigs();
seedCostAdjustLogs();
}
private void seedCompanySubjects() {
companySubject("湖南凯迪工程科技有限公司", "湖南凯迪", "总公司",
"91430100MA4L8K0X1A", "张伟", "正常");
companySubject("凯迪长沙办公室", "长沙办", "办事处",
"91430100MA4L8K0X2B", "李娜", "正常");
companySubject("智铖造(湖南)建设有限公司", "智铖造", "集团小公司",
"91430100MA4L8K0X3C", "王芳", "正常");
companySubject("江山春锦置业有限公司", "江山春锦", "集团小公司",
"91430100MA4L8K0X4D", "刘洋", "正常");
companySubject("湖南凯迪工程科技有限公司岳阳分公司", "岳阳分公司", "分公司",
"91430600MA4L8K0X5E", "陈静", "正常");
}
private CompanySubject companySubject(String name, String shortName, String type,
String creditCode, String legalPerson, String status) {
CompanySubject c = new CompanySubject();
c.setName(name);
c.setShortName(shortName);
c.setType(type);
c.setCreditCode(creditCode);
c.setLegalPerson(legalPerson);
c.setStatus(status);
return companySubjectRepo.save(c);
}
private void seedContracts() {
// paidAmount is kept self-consistent: it equals the sum of the 已付
// payments that reference this contract (see seedPayments). invoicedAmount
// equals the sum of the invoice amounts that reference this contract via
// Invoice.contractId (see seedInvoices). Pending/rejected payments and
// invoices without a contractId do not contribute.
// c1: no 已付 payment (FK-0004 is 待付) -> paid 0; invoice 00231001 amount.
Contract c1 = contract("KD-JY-2026-001", "靖州供水项目信息化建设合同", "经营合同",
"靖州县水利局", "湖南凯迪工程科技有限公司", 8600000d, 1L,
"湖南凯迪工程科技有限公司", "2026-04-01", "履约中",
0d, 1761467.89d, "李娜");
// c2: FK-0001 已付 1600000 (FK-0005 待付 excluded); invoice 00125679 amount.
Contract c2 = contract("KD-CB-2026-002", "供水调度监控设备成本合同", "成本合同",
"湖南凯迪工程科技有限公司", "智铖造(湖南)建设有限公司", 3200000d, 1L,
"湖南凯迪工程科技有限公司", "2026-04-15", "履约中",
1600000d, 283185.84d, "刘洋");
// c3: FK-0002 已付 480000; invoice 00125678 amount.
Contract c3 = contract("KD-CG-2026-003", "新版OA系统服务器采购合同", "采购合同",
"湖南凯迪工程科技有限公司", "长沙华信网络科技有限公司", 480000d, 2L,
"凯迪长沙办公室", "2026-05-12", "已结算",
480000d, 424778.76d, "陈静");
// c4: FK-0003 已付 600000 (FK-0009 待付 excluded); no invoice references it.
Contract c4 = contract("KD-JY-2026-004", "江山春锦项目工程咨询合同", "经营合同",
"江山春锦置业有限公司", "湖南凯迪工程科技有限公司", 1500000d, null,
"江山春锦置业有限公司", "2026-03-20", "履约中",
600000d, 0d, "李娜");
// c5: FK-0010 已付 130000; no invoice references it.
Contract c5 = contract("KD-CG-2026-005", "行政后勤平台软件采购合同", "采购合同",
"湖南凯迪工程科技有限公司", "湖南云图软件有限公司", 260000d, null,
"湖南凯迪工程科技有限公司", "2026-02-10", "已终止",
130000d, 0d, "陈静");
seedContractMilestones(c1, c2, c3, c4, c5);
}
private Contract contract(String code, String name, String type, String partyA, String partyB,
double amount, Long projectId, String companySubject, String signDate,
String status, double paidAmount, double invoicedAmount, String custodian) {
Contract c = new Contract();
c.setCode(code);
c.setName(name);
c.setType(type);
c.setPartyA(partyA);
c.setPartyB(partyB);
c.setAmount(Money.of(amount));
c.setProjectId(projectId);
c.setCompanySubject(companySubject);
c.setSignDate(signDate);
c.setStatus(status);
c.setPaidAmount(Money.of(paidAmount));
c.setInvoicedAmount(Money.of(invoicedAmount));
c.setCustodian(custodian);
return contractRepo.save(c);
}
private void seedContractMilestones(Contract c1, Contract c2, Contract c3, Contract c4, Contract c5) {
// c1 靖州供水项目信息化建设合同 (履约中) - 4 nodes.
milestone(c1.getId(), "合同签订", "2026-04-01", 0d, "已完成");
milestone(c1.getId(), "进场", "2026-04-20", 1720000d, "已完成");
milestone(c1.getId(), "中期验收", "2026-07-15", 3440000d, "待履约");
milestone(c1.getId(), "竣工验收", "2026-09-30", 3440000d, "待履约");
// c2 供水调度监控设备成本合同 (履约中) - 3 nodes, includes 逾期.
milestone(c2.getId(), "合同签订", "2026-04-15", 0d, "已完成");
milestone(c2.getId(), "设备进场", "2026-05-30", 1600000d, "逾期");
milestone(c2.getId(), "竣工验收", "2026-08-31", 1600000d, "待履约");
// c3 新版OA系统服务器采购合同 (已结算) - 3 nodes, all 已完成.
milestone(c3.getId(), "合同签订", "2026-05-12", 0d, "已完成");
milestone(c3.getId(), "到货验收", "2026-05-28", 480000d, "已完成");
milestone(c3.getId(), "质保到期", "2026-12-31", 0d, "待履约");
// c4 江山春锦项目工程咨询合同 (履约中) - 4 nodes, includes 逾期.
milestone(c4.getId(), "合同签订", "2026-03-20", 0d, "已完成");
milestone(c4.getId(), "中期成果提交", "2026-05-15", 750000d, "已完成");
milestone(c4.getId(), "终期成果提交", "2026-05-31", 750000d, "逾期");
milestone(c4.getId(), "竣工验收", "2026-10-31", 0d, "待履约");
// c5 行政后勤平台软件采购合同 (已终止) - 3 nodes.
milestone(c5.getId(), "合同签订", "2026-02-10", 0d, "已完成");
milestone(c5.getId(), "上线验收", "2026-04-30", 130000d, "已完成");
milestone(c5.getId(), "质保到期", "2027-04-30", 0d, "待履约");
}
private void milestone(Long contractId, String name, String dueDate, double amount, String status) {
ContractMilestone m = new ContractMilestone();
m.setContractId(contractId);
m.setName(name);
m.setDueDate(dueDate);
m.setAmount(Money.of(amount));
m.setStatus(status);
contractMilestoneRepo.save(m);
}
private void seedSuppliers() {
supplier("智铖造(湖南)建设有限公司", "91430100MA4L8K0X3C", "劳务", "王芳",
"13800000003", "中国建设银行长沙分行", "4300012345678900001", "合格");
supplier("长沙华信网络科技有限公司", "91430100MA4LX1234A", "设备", "周强",
"13900000001", "中国工商银行长沙分行", "6222021234567890001", "合格");
supplier("湖南云图软件有限公司", "91430100MA4LX5678B", "服务", "赵敏",
"13900000002", "招商银行长沙分行", "6225881234567890002", "准入中");
supplier("湖南宏发建材有限公司", "91430100MA4LX9012C", "材料", "孙磊",
"13900000003", "中国农业银行长沙分行", "6228481234567890003", "合格");
supplier("岳阳通达设备租赁有限公司", "91430600MA4LX3456D", "设备", "钱伟",
"13900000004", "中国银行岳阳分行", "6217001234567890004", "停用");
}
private Supplier supplier(String name, String creditCode, String category, String contact,
String phone, String bankName, String bankAccount, String status) {
Supplier s = new Supplier();
s.setName(name);
s.setCreditCode(creditCode);
s.setCategory(category);
s.setContact(contact);
s.setPhone(phone);
s.setBankName(bankName);
s.setBankAccount(bankAccount);
s.setStatus(status);
return supplierRepo.save(s);
}
private void seedCustomers() {
customer("靖州县水利局", "政府", "杨建国", "13700000001",
"湖南省怀化市靖州县渠阳镇政府路1号", "合作中");
customer("江山春锦置业有限公司", "企业", "胡敏", "13700000002",
"湖南省长沙市岳麓区滨江路88号", "合作中");
customer("湖南省交通建设投资集团", "企业", "罗成", "13700000003",
"湖南省长沙市芙蓉区五一大道100号", "合作中");
customer("岳阳市城乡建设事务中心", "事业单位", "彭辉", "13700000004",
"湖南省岳阳市岳阳楼区巴陵中路18号", "潜在");
customer("怀化市自然资源局", "政府", "唐勇", "13700000005",
"湖南省怀化市鹤城区迎丰中路66号", "停止");
}
private Customer customer(String name, String type, String contact, String phone,
String address, String status) {
Customer c = new Customer();
c.setName(name);
c.setType(type);
c.setContact(contact);
c.setPhone(phone);
c.setAddress(address);
c.setStatus(status);
return customerRepo.save(c);
}
private void seedBankAccounts() {
bankAccount("湖南凯迪工程科技有限公司", "中国建设银行长沙分行营业部", "4300012345678900088",
"湖南凯迪工程科技有限公司", "人民币", "基本户");
bankAccount("湖南凯迪工程科技有限公司", "中国工商银行长沙分行", "6222021234567899001",
"湖南凯迪工程科技有限公司", "人民币", "项目专用户");
bankAccount("智铖造(湖南)建设有限公司", "招商银行长沙分行", "6225881234567899002",
"智铖造(湖南)建设有限公司", "人民币", "一般户");
bankAccount("江山春锦置业有限公司", "中国农业银行长沙分行", "6228481234567899003",
"江山春锦置业有限公司", "人民币", "基本户");
bankAccount("湖南凯迪工程科技有限公司岳阳分公司", "中国银行岳阳分行", "6217001234567899004",
"湖南凯迪工程科技有限公司岳阳分公司", "人民币", "一般户");
}
private BankAccount bankAccount(String accountName, String bankName, String accountNo,
String companySubject, String currency, String purpose) {
BankAccount b = new BankAccount();
b.setAccountName(accountName);
b.setBankName(bankName);
b.setAccountNo(accountNo);
b.setCompanySubject(companySubject);
b.setCurrency(currency);
b.setPurpose(purpose);
return bankAccountRepo.save(b);
}
private void seedSeals() {
seal("湖南凯迪公章", "公章", "湖南凯迪工程科技有限公司", "李娜", "在用");
seal("湖南凯迪合同专用章", "合同章", "湖南凯迪工程科技有限公司", "陈静", "在用");
seal("湖南凯迪财务专用章", "财务章", "湖南凯迪工程科技有限公司", "刘洋", "在用");
// 法人代表章 must stay 在用: seal-use YZ-2026-0009 (法人授权用印) references
// it. The seal-use create endpoint now rejects 封存/停用 seals, so a sealed
// seal here would make the seed self-inconsistent with that validation.
seal("湖南凯迪法人代表章", "法人章", "湖南凯迪工程科技有限公司", "张伟", "在用");
seal("靖州供水项目部项目章", "项目章", "湖南凯迪工程科技有限公司", "王芳", "在用");
}
private Seal seal(String name, String type, String companySubject, String custodian, String status) {
Seal s = new Seal();
s.setName(name);
s.setType(type);
s.setCompanySubject(companySubject);
s.setCustodian(custodian);
s.setStatus(status);
return sealRepo.save(s);
}
private void seedInvoices() {
// contractId ties an invoice back to a contract so invoicedAmount is
// traceable. The contract invoicedAmount seed values equal the sum of the
// amounts of the invoices that reference each contract (see seedContracts):
// invoice 00125678 -> contract 3, invoice 00125679 -> contract 2,
// invoice 00231001 -> contract 1. Other invoices carry no contractId.
invoice("4300203130", "00125678", "进项", 424778.76d, 55221.24d, 480000d,
"长沙华信网络科技有限公司", 2L, 3L, "已认证", "2026-05-15");
invoice("4300203130", "00125679", "进项", 283185.84d, 36814.16d, 320000d,
"智铖造(湖南)建设有限公司", 1L, 2L, "已认证", "2026-04-20");
invoice("4300202130", "00231001", "销项", 1761467.89d, 158532.11d, 1920000d,
"靖州县水利局", 1L, 1L, "已开", "2026-05-28");
invoice("4300202130", "00231002", "销项", 137614.68d, 12385.32d, 150000d,
"江山春锦置业有限公司", null, null, "已开", "2026-04-30");
invoice("4300203130", "00125680", "进项", 230088.50d, 29911.50d, 260000d,
"湖南云图软件有限公司", null, null, "作废", "2026-02-18");
invoice("4300202130", "00231003", "销项", 91743.12d, 8256.88d, 100000d,
"湖南省交通建设投资集团", null, null, "已认证", "2026-06-05");
}
private Invoice invoice(String code, String number, String type, double amount, double taxAmount,
double total, String partyName, Long projectId, Long contractId,
String status, String issueDate) {
Invoice v = new Invoice();
v.setCode(code);
v.setNumber(number);
v.setType(type);
v.setAmount(Money.of(amount));
v.setTaxAmount(Money.of(taxAmount));
v.setTotal(Money.of(total));
v.setPartyName(partyName);
v.setProjectId(projectId);
v.setContractId(contractId);
v.setStatus(status);
v.setIssueDate(issueDate);
return invoiceRepo.save(v);
}
private void seedPayments() {
// Seed payments directly (no contract paidAmount write-back here); the
// contract seed values already reflect cumulative paid. Write-back only
// happens on the POST create endpoint.
payment("FK-2026-0001", "湖南凯迪工程科技有限公司", "进度款", "智铖造(湖南)建设有限公司",
1600000d, 2L, 2L, 1L, "6225881234567899002", "张伟", "已付", "2026-05-18");
payment("FK-2026-0002", "湖南凯迪工程科技有限公司", "货款", "长沙华信网络科技有限公司",
480000d, 1L, 3L, 2L, "6222021234567890001", "刘洋", "已付", "2026-05-20");
payment("FK-2026-0003", "江山春锦置业有限公司", "进度款", "湖南凯迪工程科技有限公司",
600000d, null, 4L, null, "6228481234567899003", "李娜", "已付", "2026-05-22");
payment("FK-2026-0004", "湖南凯迪工程科技有限公司", "进度款", "靖州供水项目施工队",
1720000d, null, 1L, 1L, "4300012345678900088", "张伟", "待付", "2026-06-15");
payment("FK-2026-0005", "智铖造(湖南)建设有限公司", "保函", "中国建设银行长沙分行",
32000d, null, 2L, 1L, "4300012345678900001", "刘洋", "待付", "2026-06-20");
payment("FK-2026-0006", "岳阳分公司", "费用报销", "陈静",
12800d, null, null, null, "6217001234567899004", "陈静", "待付", "2026-06-08");
payment("FK-2026-0007", "湖南凯迪工程科技有限公司岳阳分公司", "货款", "岳阳通达设备租赁有限公司",
88000d, null, null, null, "6217001234567890004", "陈静", "已付", "2026-05-30");
payment("FK-2026-0008", "智铖造(湖南)建设有限公司", "费用报销", "刘洋",
6400d, null, null, null, "6225881234567890002", "刘洋", "已驳回", "2026-06-03");
payment("FK-2026-0009", "江山春锦置业有限公司", "保函", "招商银行长沙分行",
15000d, null, 4L, null, "6225881234567899002", "李娜", "待付", "2026-06-12");
payment("FK-2026-0010", "湖南凯迪工程科技有限公司", "货款", "湖南宏发建材有限公司",
130000d, null, 5L, null, "6228481234567890003", "张伟", "已付", "2026-04-28");
}
private void payment(String code, String subject, String payType, String payeeName, double amount,
Long invoiceId, Long contractId, Long projectId, String bankAccountNo,
String applicant, String status, String payDate) {
Payment p = new Payment();
p.setCode(code);
p.setSubject(subject);
p.setPayType(payType);
p.setPayeeName(payeeName);
p.setAmount(Money.of(amount));
p.setInvoiceId(invoiceId);
p.setContractId(contractId);
p.setProjectId(projectId);
p.setBankAccountNo(bankAccountNo);
p.setApplicant(applicant);
p.setStatus(status);
p.setPayDate(payDate);
p.setCreatedAt(Instant.now());
paymentRepo.save(p);
}
private void seedSealUses() {
// sealId/sealName follow the seedSeals order: 1 湖南凯迪公章,
// 2 湖南凯迪合同专用章, 3 湖南凯迪财务专用章, 4 湖南凯迪法人代表章,
// 5 靖州供水项目部项目章. status is one of 已用印 / 待用印 / 已驳回.
sealUse("YZ-2026-0001", 2L, "湖南凯迪合同专用章", "湖南凯迪工程科技有限公司",
1L, 1L, "靖州供水项目施工合同", 2, "李娜", "对外签约用印", "2026-04-01", "已用印");
sealUse("YZ-2026-0002", 1L, "湖南凯迪公章", "湖南凯迪工程科技有限公司",
1L, null, "靖州供水项目中标通知书", 1, "张伟", "投标响应用印", "2026-03-28", "已用印");
sealUse("YZ-2026-0003", 2L, "湖南凯迪合同专用章", "智铖造(湖南)建设有限公司",
1L, 2L, "供水调度监控设备成本合同", 2, "刘洋", "采购签约用印", "2026-04-15", "已用印");
sealUse("YZ-2026-0004", 2L, "湖南凯迪合同专用章", "湖南凯迪工程科技有限公司",
2L, 3L, "新版OA系统服务器采购合同", 1, "陈静", "设备采购用印", "2026-05-12", "已用印");
sealUse("YZ-2026-0005", 3L, "湖南凯迪财务专用章", "湖南凯迪工程科技有限公司",
null, null, "银行开户申请", 1, "刘洋", "项目专用户开户用印", "2026-05-06", "待用印");
sealUse("YZ-2026-0006", 2L, "湖南凯迪合同专用章", "江山春锦置业有限公司",
null, 4L, "江山春锦项目工程咨询合同", 2, "李娜", "对外签约用印", "2026-03-20", "已用印");
sealUse("YZ-2026-0007", 5L, "靖州供水项目部项目章", "湖南凯迪工程科技有限公司",
1L, null, "靖州供水项目现场签证单", 3, "王芳", "现场签证用印", "2026-05-20", "已用印");
sealUse("YZ-2026-0008", 1L, "湖南凯迪公章", "湖南凯迪工程科技有限公司岳阳分公司",
null, null, "岳阳分公司资质申报材料", 2, "陈静", "资质申报用印", "2026-06-08", "待用印");
sealUse("YZ-2026-0009", 4L, "湖南凯迪法人代表章", "湖南凯迪工程科技有限公司",
null, null, "授权委托书", 1, "张伟", "法人授权用印", "2026-06-05", "已驳回");
sealUse("YZ-2026-0010", 2L, "湖南凯迪合同专用章", "智铖造(湖南)建设有限公司",
null, null, "劳务分包补充协议", 2, "刘洋", "对外签约用印", "2026-06-10", "待用印");
}
private void sealUse(String code, Long sealId, String sealName, String subject,
Long projectId, Long contractId, String docName, int copies,
String applicant, String purpose, String useDate, String status) {
SealUse s = new SealUse();
s.setCode(code);
s.setSealId(sealId);
s.setSealName(sealName);
s.setSubject(subject);
s.setProjectId(projectId);
s.setContractId(contractId);
s.setDocName(docName);
s.setCopies(copies);
s.setApplicant(applicant);
s.setPurpose(purpose);
s.setUseDate(useDate);
s.setStatus(status);
s.setCreatedAt(Instant.now());
sealUseRepo.save(s);
}
private void seedArchives() {
// sourceId links: contracts 1..5, projects 1,2, declarations are 手动.
// category covers 项目档案 / 合同档案 / 申报成果 / 制度文件 / 会议纪要 / 技术资料.
archive("项目档案", "靖州供水项目竣工验收报告", "项目", 1L,
"靖州供水项目竣工验收报告.pdf", "pdf", 4_820_000L, "张伟", "2026-06-05",
"靖州供水,竣工验收,2026", "靖州供水项目信息化建设完成竣工验收,各项指标合格。",
"内部", "https://oa.kaidi.com/files/jingzhou-acceptance-report.pdf");
archive("项目档案", "新版OA系统上线推广实施方案", "项目", 2L,
"OA上线推广实施方案.docx", "docx", 360_000L, "李娜", "2026-05-15",
"OA系统,上线推广,实施方案", "新版OA系统分阶段推广、培训与答疑的总体实施方案。",
"内部", "https://oa.kaidi.com/files/oa-rollout-plan.docx");
archive("项目档案", "靖州供水项目现场管网数据采集成果", "项目", 1L,
"供水管网数据采集汇总表.xlsx", "xlsx", 1_280_000L, "刘洋", "2026-05-22",
"靖州供水,管网数据,数据采集", "现场管网普查数据汇总,供调度监控模块建库使用。",
"内部", "https://oa.kaidi.com/files/pipeline-survey-data.xlsx");
archive("合同档案", "靖州供水项目信息化建设合同", "合同", 1L,
"KD-JY-2026-001-靖州供水信息化建设合同.pdf", "pdf", 2_640_000L, "李娜", "2026-04-02",
"靖州供水,经营合同,2026", "靖州县水利局与凯迪签订的供水项目信息化建设主合同。",
"机密", "https://oa.kaidi.com/files/kd-jy-2026-001-contract.pdf");
archive("合同档案", "供水调度监控设备成本合同", "合同", 2L,
"KD-CB-2026-002-供水调度监控设备成本合同.pdf", "pdf", 1_980_000L, "刘洋", "2026-04-16",
"供水调度,成本合同,设备采购", "凯迪与智铖造就供水调度监控设备签订的成本合同。",
"机密", "https://oa.kaidi.com/files/kd-cb-2026-002-contract.pdf");
archive("合同档案", "新版OA系统服务器采购合同", "合同", 3L,
"KD-CG-2026-003-服务器采购合同.pdf", "pdf", 1_120_000L, "陈静", "2026-05-13",
"OA系统,采购合同,服务器", "向长沙华信采购OA系统服务器的采购合同,已结算。",
"机密", "https://oa.kaidi.com/files/kd-cg-2026-003-contract.pdf");
archive("申报成果", "高新技术企业认定申报书", "申报", null,
"高新技术企业认定申报书.pdf", "pdf", 3_560_000L, "张伟", "2026-05-08",
"高新技术企业,申报,资质认定", "湖南凯迪高新技术企业认定整套申报材料。",
"内部", "https://oa.kaidi.com/files/hightech-application.pdf");
archive("申报成果", "供水智能调度系统软件著作权证书", "申报", null,
"软件著作权登记证书.png", "png", 680_000L, "王芳", "2026-04-26",
"软件著作权,申报成果,知识产权", "供水智能调度系统软件著作权登记证书扫描件。",
"公开", "https://oa.kaidi.com/files/software-copyright-cert.png");
archive("申报成果", "2026年度科技型中小企业评价申报材料", "申报", null,
"科技型中小企业评价申报材料.docx", "docx", 420_000L, "陈静", "2026-03-30",
"科技型中小企业,申报,2026", "科技型中小企业入库评价申报书及佐证材料。",
"内部", "https://oa.kaidi.com/files/sme-evaluation-application.docx");
archive("制度文件", "ISO9001质量管理体系文件", "手动", null,
"ISO9001质量管理体系文件.pdf", "pdf", 5_240_000L, "李娜", "2026-02-20",
"ISO9001,质量管理,体系文件", "公司质量管理体系程序文件与作业指导书汇编。",
"公开", "https://oa.kaidi.com/files/iso9001-quality-manual.pdf");
archive("制度文件", "信息安全管理制度(修订稿)", "手动", null,
"信息安全管理制度-修订稿.docx", "docx", 210_000L, "张伟", "2026-05-10",
"信息安全,管理制度,修订", "账号权限、数据备份、终端安全等条款的修订稿。",
"内部", "https://oa.kaidi.com/files/infosec-policy-revised.docx");
archive("制度文件", "费用报销管理办法", "手动", null,
"费用报销管理办法.pdf", "pdf", 290_000L, "刘洋", "2026-03-12",
"费用报销,管理办法,财务制度", "规范费用报销范围、标准与审批流程的管理办法。",
"机密", "https://oa.kaidi.com/files/expense-reimbursement-policy.pdf");
archive("会议纪要", "项目周例会纪要-2026年第23周", "会议", null,
"项目周例会纪要-2026W23.docx", "docx", 96_000L, "陈静", "2026-06-08",
"周例会,会议纪要,2026", "本周项目进展、风险与行动项汇总,明确责任人。",
"内部", "https://oa.kaidi.com/files/weekly-meeting-2026w23.docx");
archive("会议纪要", "季度经营分析会纪要", "会议", null,
"季度经营分析会纪要.docx", "docx", 132_000L, "李娜", "2026-04-10",
"经营分析,会议纪要,季度", "上季度经营回顾、本季度目标分解与行动项。",
"机密", "https://oa.kaidi.com/files/quarterly-business-review.docx");
archive("技术资料", "供水调度监控模块运维操作手册", "手动", 1L,
"供水调度监控运维操作手册.pdf", "pdf", 2_180_000L, "王芳", "2026-05-28",
"运维手册,供水调度,技术资料", "供水调度监控模块的部署、运维与故障处理操作手册。",
"公开", "https://oa.kaidi.com/files/dispatch-ops-manual.pdf");
}
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, String fileUrl) {
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.setFileUrl(fileUrl);
a.setCreatedAt(Instant.now());
archiveRepo.save(a);
}
private void seedArchiveComments() {
// archiveId points at the first few seeded archives (1..5).
archiveComment(1L, "张伟", "第3页竣工数据需与结算单核对,避免口径不一致。");
archiveComment(1L, "刘洋", "建议补充监理签字页,验收资料才算完整。");
archiveComment(2L, "王芳", "培训排期请提前同步到各部门,避免与月底结账冲突。");
archiveComment(3L, "刘洋", "管网坐标列有几处空值,请回到现场补测后再入库。");
archiveComment(5L, "张伟", "成本合同金额与采购台账存在差额,请核对后回复。");
archiveComment(5L, "王芳", "设备验收报告未见附件,麻烦补传一份扫描件。");
}
private void archiveComment(Long archiveId, String author, String content) {
ArchiveComment c = new ArchiveComment();
c.setArchiveId(archiveId);
c.setAuthor(author);
c.setContent(content);
c.setCreatedAt(Instant.now());
archiveCommentRepo.save(c);
}
// ----- CRM opportunities -----
private void seedOpportunities() {
// customerId follows the seedCustomers order: 1 靖州县水利局,
// 2 江山春锦置业有限公司, 3 湖南省交通建设投资集团,
// 4 岳阳市城乡建设事务中心, 5 怀化市自然资源局.
// stage covers 线索 / 初步接触 / 方案报价 / 商务谈判 / 赢单 / 输单;
// probability rises with stage (线索 10 -> 赢单 100); status is one of
// 进行中 / 已赢单 / 已输单.
// orgUnit (归属单位) covers 经营部 / 岳阳办事处 / 长沙办事处 / 湖南分公司 /
// 广东分公司 so the org-tree domain filter has multiple buckets.
opportunity("靖州县农村供水智能调度二期项目", 1L, "靖州县水利局", "污水运营",
12000000d, "商务谈判", 75, "张伟", "老客户", "2026-08-31", "进行中", "经营部");
opportunity("怀化市政道路智慧化改造项目", 5L, "怀化市自然资源局", "市政",
8600000d, "方案报价", 50, "刘洋", "招标网", "2026-09-30", "进行中", "湖南分公司");
opportunity("岳阳城乡污水处理厂运营服务项目", 4L, "岳阳市城乡建设事务中心", "污水运营",
26000000d, "初步接触", 30, "王芳", "转介绍", "2026-12-31", "进行中", "岳阳办事处");
opportunity("湖南省高速公路机电EPC总承包项目", 3L, "湖南省交通建设投资集团", "EPC",
58000000d, "线索", 10, "张伟", "官网", "2027-03-31", "进行中", "长沙办事处");
opportunity("江山春锦住宅小区房建工程", 2L, "江山春锦置业有限公司", "房建",
4300000d, "赢单", 100, "李娜", "老客户", "2026-05-20", "已赢单", "广东分公司");
opportunity("怀化经开区污水管网咨询服务项目", 5L, "怀化市自然资源局", "咨询",
760000d, "输单", 0, "刘洋", "招标网", "2026-04-15", "已输单", "湖南分公司");
opportunity("靖州县水利局环保设备采购项目", 1L, "靖州县水利局", "环保设备",
1850000d, "方案报价", 50, "王芳", "老客户", "2026-07-31", "进行中", "经营部");
opportunity("岳阳楼区市政排水管网改造项目", 4L, "岳阳市城乡建设事务中心", "市政",
3200000d, "初步接触", 30, "刘洋", "招标网", "2026-10-15", "进行中", "岳阳办事处");
opportunity("湖南交投园区道路工程信息化项目", 3L, "湖南省交通建设投资集团", "公路",
6700000d, "商务谈判", 75, "张伟", "转介绍", "2026-08-10", "进行中", "长沙办事处");
}
private void opportunity(String name, Long customerId, String customerName, String projectType,
double amount, String stage, int probability, String owner,
String source, String expectedDate, String status, String orgUnit) {
Opportunity o = new Opportunity();
o.setName(name);
o.setCustomerId(customerId);
o.setCustomerName(customerName);
o.setProjectType(projectType);
o.setAmount(Money.of(amount));
o.setStage(stage);
o.setProbability(probability);
o.setOwner(owner);
o.setSource(source);
o.setExpectedDate(expectedDate);
o.setStatus(status);
o.setOrgUnit(orgUnit);
o.setCreatedAt(Instant.now());
opportunityRepo.save(o);
}
private void seedOpportunityFollowups() {
// opportunityId links to the seedOpportunities order (1..9).
// type is one of 电话 / 拜访 / 邮件 / 其他.
followup(1L, "拜访", "现场拜访靖州县水利局,确认二期调度范围与预算上限。",
"2026-05-12", "张伟", "本月内提交正式商务方案并报价。");
followup(1L, "电话", "与业主对接负责人电话沟通付款节点,倾向分三期支付。",
"2026-05-28", "张伟", "下周准备合同条款初稿与业主法务对接。");
followup(2L, "邮件", "向怀化市自然资源局发送智慧化改造技术方案与报价清单。",
"2026-05-20", "刘洋", "等待评标反馈,准备答疑材料。");
followup(2L, "拜访", "走访项目现场,核实道路里程与机电点位数量。",
"2026-06-02", "刘洋", "据现场情况修订工程量清单后重新报价。");
followup(3L, "电话", "首次电话接触岳阳城乡建设事务中心,了解运营服务采购意向。",
"2026-05-15", "王芳", "约定下月上门做初步技术交流。");
followup(3L, "拜访", "上门做污水处理厂运营服务初步交流,介绍公司业绩。",
"2026-06-05", "王芳", "整理同类运营案例资料发对方参考。");
followup(4L, "邮件", "收到高速机电EPC项目招标预告,发送公司资质与意向函。",
"2026-05-30", "张伟", "持续跟踪招标公告发布时间。");
followup(5L, "拜访", "与江山春锦置业完成房建工程合同条款最终确认并签约。",
"2026-05-18", "李娜", "推进首期进度款开票与回款。");
followup(5L, "电话", "电话回访签约后施工进场安排,业主表示满意。",
"2026-05-25", "李娜", "建立项目跟进台账,定期回访。");
followup(6L, "邮件", "收到怀化经开区咨询项目落标通知,竞争对手报价更低。",
"2026-04-15", "刘洋", "复盘报价策略,维护客户关系备战后续标段。");
followup(7L, "拜访", "拜访靖州县水利局确认环保设备选型与技术参数。",
"2026-05-22", "王芳", "完成设备方案比选后提交正式报价。");
followup(8L, "电话", "电话初步接触岳阳楼区排水管网改造需求,了解资金来源。",
"2026-06-03", "刘洋", "约定现场踏勘时间,准备初步方案。");
followup(9L, "拜访", "与湖南交投就园区道路信息化项目进入商务谈判阶段。",
"2026-06-06", "张伟", "敲定付款方式与质保条款,争取本月内签约。");
followup(9L, "邮件", "发送商务谈判要点纪要及合同主要条款给业主确认。",
"2026-06-09", "张伟", "等待业主反馈后安排正式签约。");
}
private void followup(Long opportunityId, String type, String content, String followDate,
String operator, String nextPlan) {
OpportunityFollowup f = new OpportunityFollowup();
f.setOpportunityId(opportunityId);
f.setType(type);
f.setContent(content);
f.setFollowDate(followDate);
f.setOperator(operator);
f.setNextPlan(nextPlan);
f.setCreatedAt(Instant.now());
opportunityFollowupRepo.save(f);
}
// ----- EHS safety checks -----
private void seedSafetyChecks() {
// type covers 安全 / 质量 / 环境; result covers 合格 / 隐患 / 不合格;
// level matches result (合格=无, 隐患/不合格=一般 or 重大);
// rectifyStatus covers 待整改 / 整改中 / 已闭环. projectId 1,2 on part of them.
safetyCheck("EHS-2026-0001", "安全", "靖州供水项目部", 1L, "施工现场临时用电规范检查",
"隐患", "一般", "配电箱未配漏电保护装置,存在触电风险。", "刘洋",
"整改中", "2026-06-20", "2026-06-05");
safetyCheck("EHS-2026-0002", "安全", "靖州供水项目部", 1L, "高处作业安全防护检查",
"不合格", "重大", "管廊高处作业未设置安全网及生命线,作业人员未系安全带。", "张伟",
"待整改", "2026-06-18", "2026-06-06");
safetyCheck("EHS-2026-0003", "质量", "吉首武水水环境治理工程", 2L, "管道焊接质量抽检",
"合格", "无", null, "陈静",
"已闭环", null, "2026-05-28");
safetyCheck("EHS-2026-0004", "质量", "吉首武水水环境治理工程", 2L, "混凝土试块强度检测",
"隐患", "一般", "部分试块28天强度低于设计值,需复检并加强养护。", "欧经理",
"整改中", "2026-06-25", "2026-06-02");
safetyCheck("EHS-2026-0005", "环境", "城镇污水运营中心", null, "污水处理出水水质监测",
"合格", "无", null, "刘洋",
"已闭环", null, "2026-05-30");
safetyCheck("EHS-2026-0006", "环境", "城镇污水运营中心", null, "污泥外运处置台账核查",
"隐患", "一般", "污泥转运联单记录不完整,缺少接收单位签收回执。", "陈静",
"待整改", "2026-06-22", "2026-06-07");
safetyCheck("EHS-2026-0007", "安全", "城镇污水运营中心", null, "有限空间作业审批检查",
"不合格", "重大", "进入泵站集水井作业前未办理有限空间作业票、未做气体检测。", "欧经理",
"整改中", "2026-06-16", "2026-06-08");
safetyCheck("EHS-2026-0008", "安全", "靖州供水项目部", 1L, "消防器材配置与有效期检查",
"合格", "无", null, "张伟",
"已闭环", null, "2026-05-26");
safetyCheck("EHS-2026-0009", "质量", "靖州供水项目部", 1L, "原材料进场验收检查",
"隐患", "重大", "一批管材无出厂合格证及检测报告即进场,存在质量风险。", "欧经理",
"待整改", "2026-06-19", "2026-06-09");
safetyCheck("EHS-2026-0010", "环境", "吉首武水水环境治理工程", 2L, "施工扬尘与噪声防控检查",
"隐患", "一般", "现场裸土未覆盖、未配备喷淋降尘设备,扬尘超标。", "刘洋",
"已闭环", "2026-06-01", "2026-05-24");
}
private void safetyCheck(String code, String type, String location, Long projectId, String item,
String result, String level, String finding, String responsible,
String rectifyStatus, String dueDate, String checkDate) {
SafetyCheck s = new SafetyCheck();
s.setCode(code);
s.setType(type);
s.setLocation(location);
s.setProjectId(projectId);
s.setItem(item);
s.setResult(result);
s.setLevel(level);
s.setFinding(finding);
s.setResponsible(responsible);
s.setRectifyStatus(rectifyStatus);
s.setDueDate(dueDate);
s.setCheckDate(checkDate);
s.setCreatedAt(Instant.now());
safetyCheckRepo.save(s);
}
// ----- three-system certifications -----
private void seedCertifications() {
// name covers ISO9001 / ISO14001 / ISO45001 / 安全生产标准化 / 施工资质;
// status covers 有效 / 即将到期 (at least one 即将到期 expiring in 2026 H2).
certification("ISO9001质量管理体系", "00126Q31580R0M", "方圆标志认证集团有限公司",
"供水、污水处理及水环境治理工程的设计、施工与运营服务",
"2024-08-15", "2027-08-14", "有效");
certification("ISO14001环境管理体系", "00126E31581R0M", "方圆标志认证集团有限公司",
"供水、污水处理及水环境治理工程相关活动的环境管理",
"2024-08-15", "2027-08-14", "有效");
certification("ISO45001职业健康安全管理体系", "00126S31582R0M", "方圆标志认证集团有限公司",
"供水、污水处理及水环境治理工程施工与运营的职业健康安全管理",
"2023-11-10", "2026-11-09", "即将到期");
certification("安全生产标准化", "AQBZH-2023-HN-0428", "湖南省住房和城乡建设厅",
"市政公用工程施工安全生产标准化(二级)",
"2023-12-20", "2026-12-19", "即将到期");
certification("施工资质", "D243011568-SG-2022", "湖南省住房和城乡建设厅",
"市政公用工程施工总承包二级、水利水电工程施工总承包三级",
"2022-06-01", "2027-05-31", "有效");
}
private void certification(String name, String certNo, String issuer, String scope,
String issueDate, String expireDate, String status) {
Certification c = new Certification();
c.setName(name);
c.setCertNo(certNo);
c.setIssuer(issuer);
c.setScope(scope);
c.setIssueDate(issueDate);
c.setExpireDate(expireDate);
c.setStatus(status);
c.setCreatedAt(Instant.now());
certificationRepo.save(c);
}
// ----- unified budget / cost center -----
private void seedBudgets() {
// element covers 人工费 / 材料费 / 机械费 / 差旅费 / 管理费 / 外协费 / 分包费;
// period covers 年度 / 季度 / 月度; status covers 执行中 / 超支 / 已结.
// projectId 1,2 on part of them; companySubject covers
// 湖南凯迪工程科技有限公司 / 智铖造(湖南)建设有限公司 / 江山春锦置业有限公司.
// budgetAmount vs actualAmount designed so execution rate (actual/budget)
// spreads high and low; 3 rows are 超支 (actual>budget) and 2 are 已结.
budget("靖州供水项目人工费预算", 1L, "湖南凯迪工程科技有限公司", "人工费",
"2026", "年度", 1800000d, 1260000d, "张伟", "执行中");
budget("靖州供水项目材料费预算", 1L, "湖南凯迪工程科技有限公司", "材料费",
"2026", "年度", 2600000d, 2860000d, "刘洋", "超支");
budget("靖州供水项目机械费预算", 1L, "湖南凯迪工程科技有限公司", "机械费",
"2026", "季度", 760000d, 418000d, "刘洋", "执行中");
budget("靖州供水项目分包费预算", 1L, "智铖造(湖南)建设有限公司", "分包费",
"2026", "年度", 1600000d, 1600000d, "刘洋", "已结");
budget("新版OA推广项目外协费预算", 2L, "湖南凯迪工程科技有限公司", "外协费",
"2026", "季度", 480000d, 504000d, "陈静", "超支");
budget("新版OA推广项目差旅费预算", 2L, "湖南凯迪工程科技有限公司", "差旅费",
"2026", "月度", 60000d, 28500d, "龚丽娟", "执行中");
budget("江山春锦项目人工费预算", null, "江山春锦置业有限公司", "人工费",
"2026", "年度", 900000d, 612000d, "李娜", "执行中");
budget("江山春锦项目管理费预算", null, "江山春锦置业有限公司", "管理费",
"2026", "年度", 320000d, 352000d, "李娜", "超支");
budget("集团本部管理费预算", null, "湖南凯迪工程科技有限公司", "管理费",
"2026", "年度", 4200000d, 1890000d, "张伟", "执行中");
budget("集团本部差旅费预算", null, "湖南凯迪工程科技有限公司", "差旅费",
"2026", "季度", 240000d, 226800d, "龚丽娟", "执行中");
budget("智铖造劳务外协费预算", null, "智铖造(湖南)建设有限公司", "外协费",
"2026", "月度", 150000d, 97500d, "刘洋", "执行中");
budget("供水调度设备机械费预算", 1L, "智铖造(湖南)建设有限公司", "机械费",
"2026", "年度", 520000d, 520000d, "龚丽娟", "已结");
}
private void budget(String name, Long projectId, String companySubject, String element,
String year, String period, double budgetAmount, double actualAmount,
String owner, String status) {
Budget b = new Budget();
b.setName(name);
b.setProjectId(projectId);
b.setCompanySubject(companySubject);
b.setElement(element);
b.setYear(year);
b.setPeriod(period);
b.setBudgetAmount(Money.of(budgetAmount));
b.setActualAmount(Money.of(actualAmount));
b.setOwner(owner);
b.setStatus(status);
b.setCreatedAt(Instant.now());
budgetRepo.save(b);
}
// ----- unified bidding projects -----
private void seedBids() {
// projectType covers 房建 / 市政 / 公路 / EPC / 咨询 / 环保设备 / 污水运营;
// bidMethod covers 公开招标 / 邀请招标 / 竞争性谈判; status covers
// 投标准备 / 已投标 / 中标 / 未中标 / 废标 (中标 has 3 rows).
// tenderee uses 靖州县水利局 / 湖南省交通建设投资集团 / 岳阳市城乡建设事务中心 /
// 怀化市自然资源局. controlPrice spans millions to over 100M; bidAmount is
// slightly below controlPrice (winning bids quote more competitively);
// deposit is 1-2% of bidAmount. opportunityId links to seedOpportunities
// (1..9); winning bids link to projectId (1,2). owner uses 张伟/刘洋/王芳.
// orgUnit (归属单位) covers 经营部 / 岳阳办事处 / 长沙办事处 / 湖南分公司 /
// 广东分公司 so the org-tree domain filter has multiple buckets.
bid("靖州县农村供水智能调度二期项目", "JZSL-ZB-2026-018", "靖州县水利局", "污水运营",
12000000d, 11600000d, 232000d, "公开招标", "中标",
"2026-05-15", "2026-05-20", 1L, 1L, "张伟", "经营部");
bid("湖南省高速公路机电EPC总承包项目", "HNJT-ZB-2026-007", "湖南省交通建设投资集团", "EPC",
120000000d, 118500000d, 2370000d, "公开招标", "投标准备",
"2026-07-10", "2026-07-15", 4L, null, "张伟", "长沙办事处");
bid("岳阳城乡污水处理厂运营服务项目", "YYCX-ZB-2026-031", "岳阳市城乡建设事务中心", "污水运营",
26000000d, 25400000d, 508000d, "公开招标", "已投标",
"2026-06-02", "2026-06-08", 3L, null, "王芳", "岳阳办事处");
bid("怀化市政道路智慧化改造项目", "HHZR-ZB-2026-022", "怀化市自然资源局", "市政",
8600000d, 8450000d, 126750d, "公开招标", "已投标",
"2026-06-05", "2026-06-12", 2L, null, "刘洋", "湖南分公司");
bid("岳阳楼区市政排水管网改造项目", "YYCX-ZB-2026-040", "岳阳市城乡建设事务中心", "市政",
3200000d, 3120000d, 46800d, "邀请招标", "中标",
"2026-05-26", "2026-05-30", 8L, 2L, "刘洋", "岳阳办事处");
bid("靖州县水利局环保设备采购项目", "JZSL-ZB-2026-026", "靖州县水利局", "环保设备",
1850000d, 1788000d, 26820d, "竞争性谈判", "中标",
"2026-06-01", "2026-06-04", 7L, 1L, "王芳", "经营部");
bid("湖南交投园区道路工程信息化项目", "HNJT-ZB-2026-015", "湖南省交通建设投资集团", "公路",
6700000d, 6620000d, 99300d, "邀请招标", "投标准备",
"2026-08-05", "2026-08-10", 9L, null, "张伟", "长沙办事处");
bid("怀化经开区污水管网咨询服务项目", "HHZR-ZB-2026-009", "怀化市自然资源局", "咨询",
760000d, 745000d, 11175d, "公开招标", "未中标",
"2026-04-08", "2026-04-15", 6L, null, "刘洋", "湖南分公司");
bid("岳阳房建保障性住房工程项目", "YYCX-ZB-2026-013", "岳阳市城乡建设事务中心", "房建",
43000000d, 42600000d, 639000d, "公开招标", "未中标",
"2026-03-20", "2026-03-26", null, null, "王芳", "广东分公司");
bid("湖南交投绕城高速养护工程项目", "HNJT-ZB-2026-003", "湖南省交通建设投资集团", "公路",
9800000d, 9750000d, 146250d, "竞争性谈判", "废标",
"2026-02-18", "2026-02-22", null, null, "张伟", "长沙办事处");
}
private void bid(String projectName, String bidNo, String tenderee, String projectType,
double controlPrice, double bidAmount, double deposit, String bidMethod,
String status, String bidDate, String openDate, Long opportunityId,
Long projectId, String owner, String orgUnit) {
Bid b = new Bid();
b.setProjectName(projectName);
b.setBidNo(bidNo);
b.setTenderee(tenderee);
b.setProjectType(projectType);
b.setControlPrice(Money.of(controlPrice));
b.setBidAmount(Money.of(bidAmount));
b.setDeposit(Money.of(deposit));
b.setBidMethod(bidMethod);
b.setStatus(status);
b.setBidDate(bidDate);
b.setOpenDate(openDate);
b.setOpportunityId(opportunityId);
b.setProjectId(projectId);
b.setOwner(owner);
b.setOrgUnit(orgUnit);
b.setCreatedAt(Instant.now());
bidRepo.save(b);
}
// ----- unified intelligence center -----
private void seedIntel() {
// category covers 招标公告 / 中标公示 / 政策法规 / 竞品动态 / 客户动态 / 行业资讯
// (招标公告 has 5 rows around 凯迪主业: 农村供水 / 市政污水 / 水利EPC / 环保设备).
// status covers 待评估 / 跟进中 / 已转商机 / 已忽略; the 3 已转商机 rows carry
// opportunityId linking to seedOpportunities (1..3). source/region span
// 湖南省公共资源交易中心 / 中国政府采购网 / 全国公共资源交易平台 / 水利部网站 /
// 客户官网 and 湖南 / 怀化 / 岳阳 / 靖州 / 全国. owner uses 张伟 / 刘洋 / 王芳.
intel("靖州县2026年农村供水维修养护项目公开招标公告", "招标公告",
"湖南省公共资源交易中心", "靖州", "2026-06-03", "2026-06-04",
"农村供水",
"https://www.hnsggzy.com/jyxx/zbgg/2026/JZNCGS-018.html",
"靖州县2026年农村供水维修养护项目公开招标,预算约860万,投标截止6月底,要求具备市政或水利施工资质。",
"已转商机", 1L, "张伟");
intel("怀化市鹤城区市政污水管网改造工程招标公告", "招标公告",
"怀化市公共资源交易中心", "怀化", "2026-06-05", "2026-06-06",
"污水处理",
"https://www.hhggzy.gov.cn/jyxx/zbgg/2026/HHSZ-022.html",
"怀化市鹤城区市政污水管网改造工程公开招标,控制价约2300万,采用EPC总承包方式,重点考察同类业绩。",
"跟进中", null, "刘洋");
intel("岳阳市城乡污水处理厂运营服务项目采购公告", "招标公告",
"中国政府采购网", "岳阳", "2026-05-28", "2026-05-30",
"污水处理",
"https://www.ccgp.gov.cn/cggg/dfgg/gkzb/2026/YYCX-031.html",
"岳阳市城乡污水处理厂三年运营服务采购,年运营费约2600万,要求具备污水运营业绩与环保资质。",
"已转商机", 3L, "王芳");
intel("湖南省高速公路机电EPC总承包项目招标预公告", "招标公告",
"全国公共资源交易平台", "湖南", "2026-06-01", "2026-06-02",
"EPC",
"https://ggzy.hunan.gov.cn/jyxx/zbyg/2026/HNJT-007.html",
"湖南省高速公路机电系统EPC总承包项目发布招标预公告,估算投资约1.2亿,正式招标预计7月发布。",
"待评估", null, "张伟");
intel("靖州县水利局环保设备采购竞争性谈判公告", "招标公告",
"靖州县公共资源交易平台", "靖州", "2026-05-30", "2026-06-01",
"环保设备",
"https://www.hnsggzy.com/jyxx/jtgg/2026/JZSL-026.html",
"靖州县水利局拟采购一批水质监测与污水处理环保设备,预算约185万,采用竞争性谈判方式。",
"已转商机", 2L, "王芳");
intel("靖州县农村供水智能调度二期项目中标公示", "中标公示",
"湖南省公共资源交易中心", "靖州", "2026-05-22", "2026-05-23",
"农村供水",
"https://www.hnsggzy.com/jyxx/zbgs/2026/JZNCGS-018-result.html",
"靖州县农村供水智能调度二期项目中标公示,湖南凯迪工程科技有限公司以1160万中标,公示期三天。",
"已忽略", null, "张伟");
intel("怀化经开区污水管网咨询服务项目中标公示", "中标公示",
"怀化市公共资源交易中心", "怀化", "2026-04-16", "2026-04-17",
"污水处理",
"https://www.hhggzy.gov.cn/jyxx/zbgs/2026/HHZR-009-result.html",
"怀化经开区污水管网咨询服务项目由本地竞品以低价中标,凯迪未入围,可作为后续报价参考。",
"已忽略", null, "刘洋");
intel("水利部关于加快推进农村供水高质量发展的指导意见", "政策法规",
"水利部网站", "全国", "2026-05-18", "2026-05-19",
"农村供水",
"http://www.mwr.gov.cn/zwgk/zcfg/2026/ncgsfz.html",
"水利部印发指导意见,明确十四五末农村自来水普及率目标,鼓励城乡供水一体化与智能化运维,利好供水项目储备。",
"跟进中", null, "张伟");
intel("湖南省财政厅下达2026年城镇污水处理提质增效专项资金", "政策法规",
"中国政府采购网", "湖南", "2026-05-10", "2026-05-12",
"污水处理",
"https://www.ccgp.gov.cn/zcfg/dffg/2026/hncz-wszl.html",
"湖南省财政厅下达污水处理提质增效专项资金,重点支持市州污水管网与处理厂改造,相关招标有望陆续释放。",
"待评估", null, "刘洋");
intel("中国水利水电第八工程局中标怀化重点水利工程", "竞品动态",
"全国公共资源交易平台", "怀化", "2026-05-25", "2026-05-26",
"水利EPC",
"https://ggzy.hunan.gov.cn/jyxx/zbgs/2026/sdj-hh.html",
"竞品中国水利水电第八工程局中标怀化重点水利枢纽工程,金额约3.8亿,显示其在湘西水利市场持续发力。",
"待评估", null, "刘洋");
intel("靖州县水利局领导调研城乡供水一体化建设", "客户动态",
"客户官网", "靖州", "2026-06-06", "2026-06-07",
"农村供水",
"http://www.jingzhou.gov.cn/slj/dt/2026/cxgsyth.html",
"靖州县水利局主要领导调研城乡供水一体化建设,提出年内启动三期智能调度项目,建议提前对接需求。",
"跟进中", null, "张伟");
intel("2026年湖南省水务环保产业发展趋势研究报告发布", "行业资讯",
"中国政府采购网", "湖南", "2026-05-15", "2026-05-16",
"污水处理",
"https://www.ccgp.gov.cn/hyzx/2026/hnswhb-report.html",
"行业报告指出湖南县域供水与污水运营市场年增长超两成,PPP与EPC模式成为主流,建议加大区域布局。",
"待评估", null, "王芳");
}
private void intel(String title, String category, String source, String region,
String publishDate, String captureDate, String keyword, String refUrl,
String summary, String status, Long opportunityId, String owner) {
Intel i = new Intel();
i.setTitle(title);
i.setCategory(category);
i.setSource(source);
i.setRegion(region);
i.setPublishDate(publishDate);
i.setCaptureDate(captureDate);
i.setKeyword(keyword);
i.setRefUrl(refUrl);
i.setSummary(summary);
i.setStatus(status);
i.setOpportunityId(opportunityId);
i.setOwner(owner);
i.setCreatedAt(Instant.now());
intelRepo.save(i);
}
// ----- R&D innovation center (business chain 6) -----
private void seedRd() {
// Chain 6: RdProject (研发立项) -> Patent (知识产权/专利) ->
// Declaration (政府项目申报), linked by rdProjectId. RdProjects are saved
// first so their generated ids (1..8) can be referenced as foreign keys
// by the patents and declarations below.
// field covers 农村供水 / 污水处理 / 生物质能 / 智能装备 / 环保材料 /
// 水环境治理; stage covers 立项 / 研发中 / 中试 / 结题 / 验收; progress
// spans 20..100; budget spans hundreds of thousands to a few million;
// department uses 创新研发中心 / 设计研究中心 / 制造管理中心; leader uses
// 张伟 / 刘洋 / 王芳 / 欧阳明.
RdProject r1 = rdProject("农村供水智能调度算法研究", "RD-2026-001", "农村供水", "张伟",
"创新研发中心", 2600000d, "2025-09-01", "2026-12-31", "研发中", 55,
"已完成调度模型构建与仿真验证,进入算法迭代阶段。");
RdProject r2 = rdProject("村镇污水处理工艺优化研究", "RD-2026-002", "污水处理", "刘洋",
"创新研发中心", 1800000d, "2025-06-01", "2026-09-30", "中试", 70,
"新型组合工艺中试出水稳定达标,处理能耗下降约15%。");
RdProject r3 = rdProject("生物质燃料发酵工艺研究", "RD-2026-003", "生物质能", "欧阳明",
"设计研究中心", 3200000d, "2024-12-01", "2026-06-30", "验收", 100,
"完成发酵工艺定型与示范线建设,产气率达到设计指标,通过结题验收。");
RdProject r4 = rdProject("供水管网漏损监测装备研发", "RD-2026-004", "智能装备", "王芳",
"制造管理中心", 1500000d, "2025-08-15", "2027-03-31", "研发中", 45,
"完成监测终端样机与定位算法初版,进入现场试点准备。");
RdProject r5 = rdProject("环保滤料研发与产业化", "RD-2026-005", "环保材料", "刘洋",
"设计研究中心", 980000d, "2025-10-01", "2026-12-31", "中试", 60,
"新型复合滤料中试性能优于同类产品,启动量产工艺设计。");
RdProject r6 = rdProject("城乡水环境治理一体化技术研究", "RD-2026-006", "水环境治理", "张伟",
"创新研发中心", 4200000d, "2025-03-01", "2027-06-30", "研发中", 38,
"完成流域水环境模型搭建与方案比选,进入关键技术攻关。");
RdProject r7 = rdProject("农村供水水质在线监测系统", "RD-2026-007", "农村供水", "王芳",
"制造管理中心", 760000d, "2026-01-10", "2026-11-30", "立项", 20,
"完成需求分析与系统总体方案,启动核心模块设计。");
RdProject r8 = rdProject("污泥资源化处理装备开发", "RD-2026-008", "智能装备", "欧阳明",
"设计研究中心", 2100000d, "2025-05-01", "2026-08-31", "结题", 90,
"完成成套装备研制与连续运行考核,整理结题材料申请验收。");
// Patents: type covers 发明专利 / 实用新型 / 外观设计 / 软件著作权 / 商标;
// status covers 已授权 (4) / 申请中 / 已受理 / 已驳回; 8 carry a real
// rdProjectId (r1..r8) and 2 are null. applicant uses 湖南凯迪工程科技有限公司.
// feeDueDate / annualFee (专利年费) are filled for 已授权 patents only;
// some due dates fall in 2026 H2 (临近) and one is already past (已过期);
// not-yet-granted patents leave feeDueDate null and annualFee 0.
patent("一种农村供水智能调度方法及系统", "发明专利", "ZL2026100012345.6",
"湖南凯迪工程科技有限公司", "张伟,陈志强,刘洋", r1.getId(), "2026-02-18", "已授权",
"2026-09-30", 2700d);
patent("一种村镇污水处理组合工艺装置", "实用新型", "ZL2026200023456.7",
"湖南凯迪工程科技有限公司", "刘洋,王芳", r2.getId(), "2026-03-05", "已授权",
"2026-11-15", 900d);
patent("生物质燃料发酵反应器", "发明专利", "ZL2025100034567.8",
"湖南凯迪工程科技有限公司", "欧阳明,周敏", r3.getId(), "2025-08-12", "已授权",
"2026-03-31", 2400d);
patent("供水管网漏损监测终端外观设计", "外观设计", "ZL2026300045678.9",
"湖南凯迪工程科技有限公司", "王芳", r4.getId(), "2026-04-20", "申请中",
null, 0d);
patent("环保复合滤料制备方法", "发明专利", "ZL2026100056789.0",
"湖南凯迪工程科技有限公司", "刘洋,孙磊", r5.getId(), "2026-05-08", "已受理",
null, 0d);
patent("供水管网漏损监测分析软件", "软件著作权", "软著登字第8312456号",
"湖南凯迪工程科技有限公司", "王芳,赵敏", r4.getId(), "2026-03-28", "已授权",
"2026-12-20", 600d);
patent("水环境治理一体化调控平台", "软件著作权", "软著登字第8327891号",
"湖南凯迪工程科技有限公司", "张伟,陈志强", r6.getId(), "2026-04-15", "申请中",
null, 0d);
patent("一种农村供水水质在线监测仪", "实用新型", "ZL2026200067890.1",
"湖南凯迪工程科技有限公司", "王芳", r7.getId(), "2026-05-22", "已受理",
null, 0d);
patent("凯迪水务环保商标", "商标", "注册号62318745",
"湖南凯迪工程科技有限公司", "湖南凯迪工程科技有限公司", null, "2026-01-15", "申请中",
null, 0d);
patent("一种污泥脱水装置", "实用新型", "ZL2025200078901.2",
"湖南凯迪工程科技有限公司", "欧阳明", null, "2025-09-30", "已驳回",
null, 0d);
// Declarations: program covers all 5 categories 高新技术企业 /
// 科技型中小企业 / 省重点研发计划 / 专精特新 / 创新平台; status covers
// 准备中 / 已申报 / 已立项 / 已结题 / 未通过; most carry a real rdProjectId.
// authority uses 湖南省科技厅 / 湖南省工信厅 / 怀化市科技局 /
// 国家高新技术企业认定管理工作领导小组; amount spans 20万..500万; deadline
// in 2026. owner uses 张伟 / 王芳.
declaration("2026年度高新技术企业认定申报", "高新技术企业",
"国家高新技术企业认定管理工作领导小组", r1.getId(), 0d, "2026-08-31", "准备中", "张伟");
declaration("农村供水智能调度省重点研发计划项目申报", "省重点研发计划",
"湖南省科技厅", r1.getId(), 5000000d, "2026-04-30", "已立项", "张伟");
declaration("村镇污水处理工艺优化省重点研发计划申报", "省重点研发计划",
"湖南省科技厅", r2.getId(), 3000000d, "2026-03-31", "已结题", "张伟");
declaration("生物质能装备专精特新中小企业申报", "专精特新",
"湖南省工信厅", r3.getId(), 800000d, "2026-06-30", "已申报", "王芳");
declaration("2026年度科技型中小企业评价入库", "科技型中小企业",
"湖南省科技厅", r5.getId(), 200000d, "2026-09-30", "已申报", "王芳");
declaration("水环境治理工程技术研究中心创新平台申报", "创新平台",
"怀化市科技局", r6.getId(), 1500000d, "2026-05-31", "未通过", "张伟");
declaration("智能水务装备制造业创新平台申报", "创新平台",
"湖南省工信厅", r8.getId(), 2000000d, "2026-10-31", "准备中", "王芳");
}
private RdProject rdProject(String name, String code, String field, String leader,
String department, double budget, String startDate, String endDate,
String stage, int progress, String achievement) {
RdProject p = new RdProject();
p.setName(name);
p.setCode(code);
p.setField(field);
p.setLeader(leader);
p.setDepartment(department);
p.setBudget(Money.of(budget));
p.setStartDate(startDate);
p.setEndDate(endDate);
p.setStage(stage);
p.setProgress(progress);
p.setAchievement(achievement);
p.setCreatedAt(Instant.now());
return rdProjectRepo.save(p);
}
private void patent(String name, String type, String patentNo, String applicant,
String inventors, Long rdProjectId, String applyDate, String status,
String feeDueDate, double annualFee) {
Patent p = new Patent();
p.setName(name);
p.setType(type);
p.setPatentNo(patentNo);
p.setApplicant(applicant);
p.setInventors(inventors);
p.setRdProjectId(rdProjectId);
p.setApplyDate(applyDate);
p.setStatus(status);
p.setFeeDueDate(feeDueDate);
p.setAnnualFee(Money.of(annualFee));
p.setCreatedAt(Instant.now());
patentRepo.save(p);
}
private void declaration(String name, String program, String authority, Long rdProjectId,
double amount, String deadline, String status, String owner) {
Declaration d = new Declaration();
d.setName(name);
d.setProgram(program);
d.setAuthority(authority);
d.setRdProjectId(rdProjectId);
d.setAmount(Money.of(amount));
d.setDeadline(deadline);
d.setStatus(status);
d.setOwner(owner);
d.setCreatedAt(Instant.now());
declarationRepo.save(d);
}
// ----- declaration template library (申报模板自动搭建, business chain 6) -----
private void seedDeclarationTemplates() {
// program covers all 5 categories 高新技术企业 / 科技型中小企业 /
// 省重点研发计划 / 专精特新 / 创新平台; status is 启用 for 4 and 停用 for 1;
// version uses V1.0 / V2.0; updatedBy uses 王芳 / 张伟 / 知识产权部.
// requiredMaterials and autoCheckRules are comma-separated; bodyTemplate
// carries placeholders such as {企业名称} {申报年度} {研发投入}.
declarationTemplate("高新技术企业认定申报模板", "高新技术企业",
"国家高新技术企业认定管理工作领导小组",
"营业执照,审计报告,研发费用专项审计,高新技术产品收入专项审计,知识产权清单,科技人员名单,研发项目立项书",
"研发费用占比≥3%,科技人员占比≥10%,高新技术产品收入占比≥60%,知识产权≥1项,近三年研发费用归集完整",
"{企业名称}申请认定国家高新技术企业。{申报年度}企业研发投入为{研发投入}元,"
+ "拥有自主知识产权及核心技术,主营高新技术产品(服务)收入占比符合认定标准。",
"V2.0", "启用", "王芳");
declarationTemplate("科技型中小企业评价入库模板", "科技型中小企业",
"湖南省科技厅",
"营业执照,上年度财务报表,研发费用辅助账,科技人员名单,知识产权或科技成果证明",
"职工总数≤500人,年销售收入≤2亿元,研发费用占比达标,科技人员占比≥10%",
"{企业名称}申请{申报年度}科技型中小企业评价入库。企业研发投入{研发投入}元,"
+ "科技人员及研发费用占比符合评价指标要求。",
"V1.0", "启用", "张伟");
declarationTemplate("省重点研发计划项目申报模板", "省重点研发计划",
"湖南省科技厅",
"项目申报书,研发立项书,可行性研究报告,经费预算表,研发团队简历,前期成果证明,配套资金证明",
"立项研发项目≥1项,经费预算合理,研发团队人员齐备,前期成果支撑充分",
"{企业名称}申报{申报年度}省重点研发计划项目。项目总投入{研发投入}元,"
+ "围绕关键技术攻关设置研究内容、技术指标与考核节点。",
"V2.0", "启用", "张伟");
declarationTemplate("专精特新中小企业申报模板", "专精特新",
"湖南省工信厅",
"营业执照,近两年财务报表,主导产品说明,知识产权清单,质量管理体系认证,研发投入证明",
"主营业务收入占比≥70%,研发费用占比达标,有效知识产权≥2项,经营稳定无失信记录",
"{企业名称}申报专精特新中小企业。{申报年度}研发投入{研发投入}元,"
+ "主导产品市场占有率与专业化、精细化、特色化、新颖化水平突出。",
"V1.0", "启用", "知识产权部");
declarationTemplate("工程技术研究中心创新平台申报模板", "创新平台",
"湖南省工信厅",
"申报书,平台建设方案,研发场地及设备清单,研发人员名单,合作单位协议,已承担项目清单",
"研发场地及设备满足要求,固定研发人员≥10人,近三年承担研发项目≥3项,有产学研合作",
"{企业名称}申报工程技术研究中心创新平台。{申报年度}计划投入{研发投入}元,"
+ "围绕行业共性关键技术开展研发、中试与成果转化。",
"V1.0", "停用", "知识产权部");
}
private void declarationTemplate(String name, String program, String authority,
String requiredMaterials, String autoCheckRules,
String bodyTemplate, String version, String status,
String updatedBy) {
DeclarationTemplate t = new DeclarationTemplate();
t.setName(name);
t.setProgram(program);
t.setAuthority(authority);
t.setRequiredMaterials(requiredMaterials);
t.setAutoCheckRules(autoCheckRules);
t.setBodyTemplate(bodyTemplate);
t.setVersion(version);
t.setStatus(status);
t.setUpdatedBy(updatedBy);
t.setCreatedAt(Instant.now());
declarationTemplateRepo.save(t);
}
// ----- R&D expense collection (研发立项-费用-凭证 evidence chain, business chain 6) -----
private void seedRdExpenses() {
// rdProjectId points at the RdProjects seeded in seedRd (1..8);
// rdProjectName mirrors the linked project name. category covers all 6
// kinds 人工费 / 直接投入 / 折旧费用 / 无形资产摊销 / 设计费 / 其他; amount
// spans a few thousand to a few hundred thousand; occurDate is in 2026;
// voucher carries 凭证号 plus the supporting evidence (发票/工时单/合同);
// status covers 已归集 (7) / 待审核 (3) / 已驳回 (2); recorder uses
// 张伟 / 刘洋 / 王芳. Multiple expenses point at the same rdProject to form
// the 一个研发项目归集多笔费用 evidence chain (r1 carries 3, r2 carries 2).
rdExpense(1L, "农村供水智能调度算法研究", "人工费",
"研发团队2026年第一季度工时人工费", 186000d, "2026-03-31",
"PZ-2026-0101 + 工时单/工资表", "已归集", "张伟");
rdExpense(1L, "农村供水智能调度算法研究", "直接投入",
"算法仿真测试服务器及云资源采购", 92000d, "2026-02-18",
"PZ-2026-0112 + 发票/采购合同", "已归集", "刘洋");
rdExpense(1L, "农村供水智能调度算法研究", "设计费",
"外委调度模型外观与交互设计费", 45000d, "2026-04-10",
"PZ-2026-0156 + 发票/设计合同", "待审核", "王芳");
rdExpense(2L, "村镇污水处理工艺优化研究", "直接投入",
"中试装置药剂及耗材采购", 128000d, "2026-01-25",
"PZ-2026-0123 + 发票/工时单/合同", "已归集", "刘洋");
rdExpense(2L, "村镇污水处理工艺优化研究", "折旧费用",
"中试线设备2026年上半年折旧分摊", 76000d, "2026-06-30",
"PZ-2026-0210 + 折旧明细表", "已归集", "张伟");
rdExpense(3L, "生物质燃料发酵工艺研究", "人工费",
"发酵工艺研发人员人工费", 240000d, "2026-03-31",
"PZ-2026-0131 + 工时单/工资表", "已归集", "刘洋");
rdExpense(3L, "生物质燃料发酵工艺研究", "无形资产摊销",
"发酵工艺专利技术无形资产摊销", 58000d, "2026-05-20",
"PZ-2026-0188 + 摊销明细表", "待审核", "王芳");
rdExpense(4L, "供水管网漏损监测装备研发", "直接投入",
"监测终端样机元器件及试制材料", 153000d, "2026-04-08",
"PZ-2026-0162 + 发票/采购合同", "已归集", "王芳");
rdExpense(4L, "供水管网漏损监测装备研发", "设计费",
"漏损定位算法外委软件开发设计费", 88000d, "2026-05-15",
"PZ-2026-0199 + 发票/开发合同", "已驳回", "刘洋");
rdExpense(5L, "环保滤料研发与产业化", "直接投入",
"复合滤料原料及量产工艺试验材料", 67000d, "2026-03-12",
"PZ-2026-0145 + 发票/合同", "已归集", "张伟");
rdExpense(6L, "城乡水环境治理一体化技术研究", "其他",
"流域水环境治理方案专家评审及差旅费", 8500d, "2026-02-28",
"PZ-2026-0118 + 发票/费用单", "待审核", "王芳");
rdExpense(8L, "污泥资源化处理装备开发", "折旧费用",
"成套装备试制设备折旧分摊", 39000d, "2026-04-30",
"PZ-2026-0176 + 折旧明细表", "已驳回", "刘洋");
}
private void rdExpense(Long rdProjectId, String rdProjectName, String category,
String description, double amount, String occurDate, String voucher,
String status, String recorder) {
RdExpense e = new RdExpense();
e.setRdProjectId(rdProjectId);
e.setRdProjectName(rdProjectName);
e.setCategory(category);
e.setDescription(description);
e.setAmount(Money.of(amount));
e.setOccurDate(occurDate);
e.setVoucher(voucher);
e.setStatus(status);
e.setRecorder(recorder);
e.setCreatedAt(Instant.now());
rdExpenseRepo.save(e);
}
// ----- unified contract center: templates and changes (business chain 3) -----
private void seedContractTemplates() {
// contractType covers all 5 kinds 经营合同 / 采购合同 / 成本合同 / 销售合同 /
// 法务合同; applicableSubject spans 总公司 / 分公司 / 集团小公司 / 不限;
// version uses V1.0 / V2.0; status is 启用 for 4 and 停用 for 1; updatedBy
// uses 张伟 / 王芳 / 法务部. bodyTemplate carries placeholders such as
// {甲方} {乙方} {金额} {工期}; requiredClauses is comma-separated.
contractTemplate("经营合同标准模板", "经营合同", "总公司",
"本合同由{甲方}与{乙方}就供水信息化建设事宜,经友好协商,达成如下条款。"
+ "合同总价款为人民币{金额}元,工期为{工期}。乙方应按约定节点完成交付并通过验收。",
"标的范围,合同价款,质保期,违约责任,争议解决", "V2.0", "启用", "张伟");
contractTemplate("采购合同标准模板", "采购合同", "不限",
"本采购合同由{甲方}(采购方)与{乙方}(供货方)签订。采购标的及数量见附件,"
+ "合同金额{金额}元,交货期{工期},验收合格后付款。",
"采购清单,合同价款,交货期,质量标准,验收方式,违约责任", "V1.0", "启用", "王芳");
contractTemplate("成本合同标准模板", "成本合同", "集团小公司",
"本成本合同由{甲方}与{乙方}就劳务及设备投入达成约定。合同金额{金额}元,"
+ "履约期{工期},乙方应保证投入资源满足现场施工进度要求。",
"标的范围,合同价款,履约期限,安全责任,违约责任", "V1.0", "启用", "张伟");
contractTemplate("销售合同标准模板", "销售合同", "分公司",
"本销售合同由{甲方}(出卖方)与{乙方}(买受方)签订。标的产品及规格见清单,"
+ "销售总额{金额}元,交付周期{工期},款到发货。",
"标的范围,销售价款,交付方式,售后服务,违约责任", "V2.0", "启用", "王芳");
contractTemplate("法务合同审查模板", "法务合同", "不限",
"本模板用于{甲方}对外签署合同的法务审查。审查要点包括主体资格、权利义务对等、"
+ "金额{金额}与付款条件、工期{工期}及违约救济等条款的合规性。",
"主体资格,权利义务,合同价款,保密条款,违约责任,争议解决", "V1.0", "停用", "法务部");
}
private void contractTemplate(String name, String contractType, String applicableSubject,
String bodyTemplate, String requiredClauses, String version,
String status, String updatedBy) {
ContractTemplate t = new ContractTemplate();
t.setName(name);
t.setContractType(contractType);
t.setApplicableSubject(applicableSubject);
t.setBodyTemplate(bodyTemplate);
t.setRequiredClauses(requiredClauses);
t.setVersion(version);
t.setStatus(status);
t.setUpdatedBy(updatedBy);
t.setCreatedAt(Instant.now());
contractTemplateRepo.save(t);
}
private void seedContractChanges() {
// contractId points at the contracts seeded in seedContracts (1..5);
// contractName mirrors the linked contract name. changeType covers
// 变更 / 终止 / 续签. status uses only 审批中 / 草稿 / 已驳回 here so the
// seed never writes back onto the contracts (已生效 write-back is left to
// live POST testing). amountBefore equals the linked contract amount;
// effectiveDate is in 2026; applicant uses 张伟 / 刘洋.
contractChange(1L, "靖州供水项目信息化建设合同", "变更",
"新增管网数据采集子系统,合同范围扩大", 8600000d, 9200000d,
"2026-07-01", "审批中", "张伟");
contractChange(2L, "供水调度监控设备成本合同", "变更",
"设备选型升级,单价调整", 3200000d, 3450000d,
"2026-06-20", "草稿", "刘洋");
contractChange(4L, "江山春锦项目工程咨询合同", "终止",
"项目方调整建设计划,咨询服务提前终止", 1500000d, 0d,
"2026-08-15", "已驳回", "张伟");
contractChange(1L, "靖州供水项目信息化建设合同", "续签",
"一期运维到期,续签二期运维服务", 8600000d, 8600000d,
"2026-09-30", "审批中", "刘洋");
}
private void contractChange(Long contractId, String contractName, String changeType,
String reason, double amountBefore, double amountAfter,
String effectiveDate, String status, String applicant) {
ContractChange c = new ContractChange();
c.setContractId(contractId);
c.setContractName(contractName);
c.setChangeType(changeType);
c.setReason(reason);
c.setAmountBefore(Money.of(amountBefore));
c.setAmountAfter(Money.of(amountAfter));
c.setEffectiveDate(effectiveDate);
c.setStatus(status);
c.setApplicant(applicant);
c.setCreatedAt(Instant.now());
contractChangeRepo.save(c);
}
// ----- cost-center master data (成本中心主档) -----
private void seedCostCenters() {
// ccType covers all 4 kinds 项目 / 部门 / 公司 / 工序; owner uses
// 张伟 / 刘洋 / 王芳; parentName is filled on the lower-level centers and
// left null on the top-level ones; status is 启用 for most and 停用 for one.
// budgetTotal vs. actualTotal is kept realistic, with 2 centers overspent
// (actualTotal > budgetTotal) and the rest under budget.
costCenter("CC-XM-001", "靖州供水项目成本中心", "项目", "张伟",
"工程管理中心", 8600000d, 4820000d, "启用");
costCenter("CC-XM-002", "江山春锦项目成本中心", "项目", "刘洋",
"工程管理中心", 1500000d, 1680000d, "启用");
costCenter("CC-BM-001", "工程管理中心", "部门", "张伟",
"湖南凯迪工程科技有限公司", 3200000d, 2150000d, "启用");
costCenter("CC-BM-002", "财务部成本中心", "部门", "刘洋",
"湖南凯迪工程科技有限公司", 600000d, 410000d, "启用");
costCenter("CC-BM-003", "信息中心成本中心", "部门", "王芳",
"湖南凯迪工程科技有限公司", 800000d, 920000d, "启用");
costCenter("CC-GS-001", "总公司管理费", "公司", "张伟",
null, 5000000d, 3260000d, "启用");
costCenter("CC-GX-001", "管网施工工序成本中心", "工序", "刘洋",
"靖州供水项目成本中心", 2400000d, 1180000d, "启用");
costCenter("CC-GX-002", "顶管施工工序成本中心", "工序", "王芳",
"靖州供水项目成本中心", 900000d, 360000d, "停用");
}
private void costCenter(String code, String name, String ccType, String owner,
String parentName, double budgetTotal, double actualTotal, String status) {
CostCenter c = new CostCenter();
c.setCode(code);
c.setName(name);
c.setCcType(ccType);
c.setOwner(owner);
c.setParentName(parentName);
c.setBudgetTotal(Money.of(budgetTotal));
c.setActualTotal(Money.of(actualTotal));
c.setStatus(status);
c.setCreatedAt(Instant.now());
costCenterRepo.save(c);
}
// ----- labor / material / machinery price library (人材机价格库) -----
private void seedPriceItems() {
// category covers all 3 kinds with 4 人工 / 4 材料 / 4 机械; priceType
// covers all 4 kinds 信息价 / 市场价 / 历史中标价 / 企业标准价; region uses
// 湖南 / 怀化 / 全国; unit / spec / unitPrice are realistic; effectiveDate
// is in 2026.
// 人工 (labor): unit 工日.
priceItem("人工", "普工", "综合用工", "工日", 180d, "湖南",
"本地劳务", "信息价", "2026-01-01");
priceItem("人工", "钢筋工", "中级技工", "工日", 320d, "怀化",
"湘建劳务有限公司", "市场价", "2026-03-01");
priceItem("人工", "管道安装工", "中级技工", "工日", 300d, "湖南",
"本地劳务", "企业标准价", "2026-02-01");
priceItem("人工", "技术员", "现场技术管理", "工日", 450d, "湖南",
"公司内部", "企业标准价", "2026-01-01");
// 材料 (material).
priceItem("材料", "PE给水管", "dn200 PE100 SDR17", "米", 86.5d, "湖南",
"湖南振兴管业有限公司", "市场价", "2026-04-01");
priceItem("材料", "普通硅酸盐水泥", "P.O 42.5", "吨", 420d, "怀化",
"怀化南方水泥有限公司", "信息价", "2026-03-01");
priceItem("材料", "HRB400螺纹钢筋", "dn20", "吨", 3850d, "全国",
"华菱钢铁", "信息价", "2026-04-01");
priceItem("材料", "石英砂滤料", "0.5-1.0mm 过滤用", "吨", 680d, "湖南",
"巩义滤料厂", "历史中标价", "2026-02-15");
// 机械 (machinery): unit 台班.
priceItem("机械", "履带式挖掘机", "1.0立方米 斗容", "台班", 1350d, "湖南",
"三一重工租赁", "市场价", "2026-03-01");
priceItem("机械", "潜水排污泵", "100QW 流量100立方米每小时", "台班", 280d, "怀化",
"本地租赁", "企业标准价", "2026-02-01");
priceItem("机械", "汽车起重机", "25吨", "台班", 1600d, "湖南",
"中联重科租赁", "历史中标价", "2026-01-15");
priceItem("机械", "非开挖顶管设备", "dn800 泥水平衡", "台班", 4200d, "全国",
"顶管工程公司", "历史中标价", "2026-04-10");
}
private void priceItem(String category, String name, String spec, String unit,
double unitPrice, String region, String supplier, String priceType,
String effectiveDate) {
PriceItem p = new PriceItem();
p.setCategory(category);
p.setName(name);
p.setSpec(spec);
p.setUnit(unit);
p.setUnitPrice(Money.of(unitPrice));
p.setRegion(region);
p.setSupplier(supplier);
p.setPriceType(priceType);
p.setEffectiveDate(effectiveDate);
p.setCreatedAt(Instant.now());
priceItemRepo.save(p);
}
// ----- personnel certificate library (人员证件库) -----
private void seedPersonnelCerts() {
// certType covers all 7 kinds 身份证 / 一级建造师 / 二级建造师 /
// 安全员证 / 特种作业证 / 职称证书 / 学历证书; issuer is realistic
// (住房和城乡建设部 / 应急管理厅 / 人社厅 etc.); status is kept
// self-consistent with expireDate: 6 有效 / 2 即将到期 (expire in 2026
// 下半年) / 2 已过期 (expireDate already past). 身份证 / 学历证书 carry no
// expireDate (长期).
personnelCert("张伟", "工程管理中心", "一级建造师", "湘13201826",
"住房和城乡建设部", "2018-06-15", "2027-06-14", "有效");
personnelCert("刘洋", "工程管理中心", "二级建造师", "湘243010592",
"湖南省住房和城乡建设厅", "2020-09-01", "2026-09-30", "即将到期");
personnelCert("王芳", "信息中心", "职称证书", "高工202100783",
"湖南省人力资源和社会保障厅", "2021-12-20", null, "有效");
personnelCert("欧阳明", "设计研究中心", "一级建造师", "湘13205471",
"住房和城乡建设部", "2019-03-10", "2025-03-09", "已过期");
personnelCert("李娜", "行政部", "学历证书", "1043220190512345",
"教育部学位与研究生教育发展中心", "2012-07-01", null, "有效");
personnelCert("陈静", "行政部", "身份证", "430103199005120026",
"怀化市公安局", "2015-05-20", "2035-05-19", "有效");
personnelCert("孙磊", "工程管理中心", "安全员证", "湘建安C202300456",
"湖南省住房和城乡建设厅", "2023-04-18", "2026-10-17", "即将到期");
personnelCert("周敏", "工程管理中心", "特种作业证", "T430000202100987",
"湖南省应急管理厅", "2021-06-10", "2027-06-09", "有效");
personnelCert("赵敏", "信息中心", "特种作业证", "T430000202000321",
"湖南省应急管理厅", "2020-08-15", "2026-02-14", "已过期");
personnelCert("陈志强", "设计研究中心", "二级建造师", "湘243011267",
"湖南省住房和城乡建设厅", "2022-11-05", "2028-11-04", "有效");
}
private void personnelCert(String personName, String dept, String certType, String certNo,
String issuer, String issueDate, String expireDate, String status) {
PersonnelCert c = new PersonnelCert();
c.setPersonName(personName);
c.setDept(dept);
c.setCertType(certType);
c.setCertNo(certNo);
c.setIssuer(issuer);
c.setIssueDate(issueDate);
c.setExpireDate(expireDate);
c.setStatus(status);
c.setCreatedAt(Instant.now());
personnelCertRepo.save(c);
}
// ----- intelligence collection pipeline (查网爬取 + 人工筛选) -----
private void seedCrawlSources() {
// sourceType covers all four kinds 政府采购平台 / 公共资源交易中心 /
// 企业官网 / 行业门户; region uses 湖南 / 怀化 / 靖州 / 全国; keywords stick
// to the core business (农村供水 / 污水处理 / 水利EPC / 管网 / 环保设备);
// frequency varies across 每日 / 每周 / 手动; enabled is mostly true with one
// false; lastRunAt is filled for some 2026 sources and left empty for others.
crawlSource("湖南省公共资源交易中心", "https://www.hncgp.gov.cn", "公共资源交易中心",
"湖南", "农村供水,污水处理,水利EPC,管网", "每日", true, "2026-06-10 08:30");
crawlSource("中国政府采购网", "http://www.ccgp.gov.cn", "政府采购平台",
"全国", "农村供水,污水处理,环保设备", "每日", true, "2026-06-10 08:35");
crawlSource("怀化市公共资源交易中心", "https://www.hhggzy.gov.cn", "公共资源交易中心",
"怀化", "市政污水,管网,环保设备", "每周", true, "2026-06-08 09:10");
crawlSource("靖州县政府采购平台", "https://www.jingzhou.gov.cn/zfcg", "政府采购平台",
"靖州", "农村供水,水利EPC", "每周", true, null);
crawlSource("中国水利水电第八工程局官网", "https://www.sinohydro8.com", "企业官网",
"全国", "水利EPC,管网", "手动", false, null);
crawlSource("中国水利工程协会行业门户", "http://www.cweun.org.cn", "行业门户",
"全国", "农村供水,污水处理,环保设备", "每周", true, "2026-06-05 14:20");
}
private void crawlSource(String name, String url, String sourceType, String region,
String keywords, String frequency, boolean enabled, String lastRunAt) {
CrawlSource s = new CrawlSource();
s.setName(name);
s.setUrl(url);
s.setSourceType(sourceType);
s.setRegion(region);
s.setKeywords(keywords);
s.setFrequency(frequency);
s.setEnabled(enabled);
s.setLastRunAt(lastRunAt);
s.setCreatedAt(Instant.now());
crawlSourceRepo.save(s);
}
private void seedCrawlJobs() {
// sourceId points to the seeded sources (1..6) with the matching sourceName;
// runAt uses 2026 times; fetched / screened / newIntel decrease sensibly;
// status is mostly 成功 with one 部分失败; operator uses 系统RPA / 张伟.
crawlJob(1L, "湖南省公共资源交易中心", "2026-06-10 08:30", 32, 19, 8, "成功", "系统RPA");
crawlJob(2L, "中国政府采购网", "2026-06-10 08:35", 28, 16, 6, "成功", "系统RPA");
crawlJob(3L, "怀化市公共资源交易中心", "2026-06-08 09:10", 21, 12, 5, "部分失败", "张伟");
crawlJob(6L, "中国水利工程协会行业门户", "2026-06-05 14:20", 18, 10, 4, "成功", "系统RPA");
crawlJob(1L, "湖南省公共资源交易中心", "2026-06-03 08:28", 26, 15, 6, "成功", "系统RPA");
}
private void crawlJob(Long sourceId, String sourceName, String runAt, int fetched,
int screened, int newIntel, String status, String operator) {
CrawlJob j = new CrawlJob();
j.setSourceId(sourceId);
j.setSourceName(sourceName);
j.setRunAt(runAt);
j.setFetched(fetched);
j.setScreened(screened);
j.setNewIntel(newIntel);
j.setStatus(status);
j.setOperator(operator);
j.setCreatedAt(Instant.now());
crawlJobRepo.save(j);
}
// ----- bill-of-quantities / BOM comparison (工程量清单/BOM 比对) -----
private void seedBomItems() {
// projectId points to the seeded projects (1 靖州供水项目工程信息化,
// 2 新版OA系统上线推广); category covers 分部分项 / 措施 / 其他; status is
// set to 超量 when actualQty exceeds bidQty (3 rows) and 正常 otherwise.
bomItem(1L, "靖州供水项目工程信息化", "PE给水管DN200", "m",
3200d, 3450d, 86d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "管沟土方开挖", "m3",
5800d, 6120d, 32d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "C20混凝土管基", "m3",
420d, 405d, 480d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "砂石垫层", "m3",
760d, 760d, 145d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "砖砌检查井", "座",
48d, 53d, 1850d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "潜水泵安装", "台",
12d, 12d, 6800d, "分部分项");
bomItem(1L, "靖州供水项目工程信息化", "施工围挡", "m",
560d, 560d, 95d, "措施");
bomItem(1L, "靖州供水项目工程信息化", "基坑降水", "天",
90d, 108d, 320d, "措施");
bomItem(2L, "新版OA系统上线推广", "球墨铸铁管DN300", "m",
1500d, 1500d, 260d, "分部分项");
bomItem(2L, "新版OA系统上线推广", "管道试压及冲洗消毒", "m",
1500d, 1500d, 18d, "其他");
}
private void bomItem(Long projectId, String projectName, String name, String unit,
double bidQty, double actualQty, double unitPrice, String category) {
BomItem b = new BomItem();
b.setProjectId(projectId);
b.setProjectName(projectName);
b.setName(name);
b.setUnit(unit);
b.setBidQty(bidQty);
b.setActualQty(actualQty);
b.setUnitPrice(Money.of(unitPrice));
b.setCategory(category);
b.setStatus(actualQty > bidQty ? "超量" : "正常");
b.setCreatedAt(Instant.now());
bomItemRepo.save(b);
}
// ----- occupational-health records (职业健康, HR) -----
private void seedHealthRecords() {
// hazardFactor covers 粉尘 / 噪声 / 高处作业 / 化学品 / 振动 / 无; examType
// covers 上岗前 / 在岗期间 / 离岗 (lifecycle 上岗前-在岗-离岗); result covers
// 正常 / 复查 / 疑似职业病; status covers 正常 (5) / 待复查 (2) / 重点监护 (1);
// some nextExamDate values fall in 2026 H2.
healthRecord("陈志强", "工程管理中心", "现场施工员", "粉尘", "在岗期间",
"2026-03-12", "正常", "2027-03-12", "正常");
healthRecord("周敏", "制造管理中心", "设备操作工", "噪声", "在岗期间",
"2026-02-28", "正常", "2026-08-28", "正常");
healthRecord("孙磊", "工程管理中心", "高处作业人员", "高处作业", "上岗前",
"2026-05-06", "正常", "2027-05-06", "正常");
healthRecord("赵敏", "创新研发中心", "化验分析员", "化学品", "在岗期间",
"2026-01-20", "复查", "2026-07-20", "待复查");
healthRecord("李建国", "制造管理中心", "钻探工", "振动", "在岗期间",
"2026-04-15", "复查", "2026-10-15", "待复查");
healthRecord("吴芳", "行政人事部", "办公室文员", "无", "上岗前",
"2026-05-22", "正常", null, "正常");
healthRecord("郑伟", "工程管理中心", "电焊工", "粉尘", "在岗期间",
"2026-02-10", "疑似职业病", "2026-09-10", "重点监护");
healthRecord("黄强", "制造管理中心", "装配钳工", "噪声", "离岗",
"2026-03-30", "正常", null, "正常");
}
private void healthRecord(String personName, String dept, String post, String hazardFactor,
String examType, String examDate, String result,
String nextExamDate, String status) {
HealthRecord h = new HealthRecord();
h.setPersonName(personName);
h.setDept(dept);
h.setPost(post);
h.setHazardFactor(hazardFactor);
h.setExamType(examType);
h.setExamDate(examDate);
h.setResult(result);
h.setNextExamDate(nextExamDate);
h.setStatus(status);
h.setCreatedAt(Instant.now());
healthRecordRepo.save(h);
}
// ----- quality-management-system records (质量管理体系, ISO9001) -----
private void seedQmsRecords() {
// type covers all 5 categories 内部审核 / 管理评审 / 不符合项 /
// 纠正预防措施 / 供方质量评价; status covers 开放 / 整改中 / 已关闭.
qmsRecord("内部审核", "2026年第一次质量体系内部审核", "质量管理部",
"对工程管理中心、制造管理中心等7个部门开展ISO9001内审,覆盖全部过程。",
"2026-07-31", "整改中", "李娜");
qmsRecord("管理评审", "2025年度质量管理体系管理评审", "总经理办公室",
"评审质量方针与目标达成情况,输出体系持续改进决议。",
"2026-04-30", "已关闭", "王芳");
qmsRecord("不符合项", "施工记录可追溯性不符合项", "工程管理中心",
"靖州供水项目部分隐蔽工程验收记录签字不全,不满足程序文件要求。",
"2026-07-15", "整改中", "陈志强");
qmsRecord("不符合项", "计量器具检定超期不符合项", "制造管理中心",
"2台压力表超过检定有效期仍在使用,不符合监视和测量资源控制要求。",
"2026-06-30", "开放", "周敏");
qmsRecord("纠正预防措施", "图纸会审遗漏纠正预防措施", "设计研究中心",
"针对图纸会审遗漏问题制定纠正措施并完善设计输出评审清单。",
"2026-08-20", "整改中", "刘洋");
qmsRecord("纠正预防措施", "客户投诉响应纠正预防措施", "市场经营部",
"针对客户对工期反馈制定预防措施,优化进度计划评审流程。",
"2026-05-31", "已关闭", "李娜");
qmsRecord("供方质量评价", "2026年上半年合格供方质量评价", "采购供应部",
"对管材、设备等关键供方进行质量、交付、服务综合评价并更新合格名录。",
"2026-09-30", "开放", "孙磊");
qmsRecord("供方质量评价", "土建分包商质量履约评价", "工程管理中心",
"对在建项目土建分包商履约质量评价,1家评定为限期整改。",
"2026-07-10", "已关闭", "陈志强");
}
private void qmsRecord(String type, String title, String dept, String finding,
String dueDate, String status, String owner) {
QmsRecord q = new QmsRecord();
q.setType(type);
q.setTitle(title);
q.setDept(dept);
q.setFinding(finding);
q.setDueDate(dueDate);
q.setStatus(status);
q.setOwner(owner);
q.setCreatedAt(Instant.now());
qmsRecordRepo.save(q);
}
// ----- operation management center: wastewater clients (运营管理中心, 污水运营) -----
private void seedWastewaterClients() {
// industry covers 印染 / 化工 / 食品 / 制药 / 电镀 / 造纸; dischargeType covers
// 工业废水 / 生活污水; pollutant (主要污染物) covers COD / 氨氮 / 总磷 / 重金属;
// serviceStatus covers 运营中 (5) / 停产 (1) / 欠费 (2). Seeded ids 1..8 are
// referenced by seedWaterQualityRecords via clientId. owner uses 王芳/刘洋/陈静.
wastewaterClient("岳阳恒昌印染有限公司", "印染", "工业废水", "WW-2026-001",
3200d, "COD", "运营中", "王芳");
wastewaterClient("湖南华盛化工股份有限公司", "化工", "工业废水", "WW-2026-002",
5800d, "重金属", "运营中", "刘洋");
wastewaterClient("怀化绿源食品加工厂", "食品", "工业废水", "WW-2026-003",
1500d, "COD", "运营中", "陈静");
wastewaterClient("靖州康宁制药有限公司", "制药", "工业废水", "WW-2026-004",
2100d, "氨氮", "欠费", "王芳");
wastewaterClient("长沙金属表面电镀厂", "电镀", "工业废水", "WW-2026-005",
900d, "重金属", "运营中", "刘洋");
wastewaterClient("岳阳纸业造纸有限公司", "造纸", "工业废水", "WW-2026-006",
7600d, "COD", "停产", "陈静");
wastewaterClient("湖南鸿运印染集团", "印染", "工业废水", "WW-2026-007",
4100d, "总磷", "运营中", "王芳");
wastewaterClient("怀化高新区生活污水处理站", "食品", "生活污水", "WW-2026-008",
12000d, "氨氮", "欠费", "刘洋");
}
private void wastewaterClient(String name, String industry, String dischargeType,
String contractNo, double dailyVolume, String pollutant,
String serviceStatus, String owner) {
WastewaterClient c = new WastewaterClient();
c.setName(name);
c.setIndustry(industry);
c.setDischargeType(dischargeType);
c.setContractNo(contractNo);
c.setDailyVolume(dailyVolume);
c.setPollutant(pollutant);
c.setServiceStatus(serviceStatus);
c.setOwner(owner);
c.setCreatedAt(Instant.now());
wastewaterClientRepo.save(c);
}
// ----- operation management center: water quality records (运营管理中心, 水质监测) -----
private void seedWaterQualityRecords() {
// clientId points to the seeded wastewater clients (1..8); result covers
// 达标 / 超标 (3 rows) / 预警 (2 rows). codValue is COD (mg/L), ammoniaValue
// is 氨氮, phValue is pH, tpValue is 总磷. inspector uses 王芳/刘洋/陈静.
waterQualityRecord(1L, "岳阳恒昌印染有限公司", "2026-05-08",
86d, 12.5d, 7.2d, 0.4d, "达标", "王芳");
waterQualityRecord(1L, "岳阳恒昌印染有限公司", "2026-06-08",
132d, 18.6d, 6.8d, 0.7d, "超标", "王芳");
waterQualityRecord(2L, "湖南华盛化工股份有限公司", "2026-05-12",
72d, 9.8d, 7.5d, 0.3d, "达标", "刘洋");
waterQualityRecord(2L, "湖南华盛化工股份有限公司", "2026-06-12",
95d, 14.2d, 7.0d, 0.55d, "预警", "刘洋");
waterQualityRecord(3L, "怀化绿源食品加工厂", "2026-05-15",
64d, 8.2d, 7.3d, 0.28d, "达标", "陈静");
waterQualityRecord(4L, "靖州康宁制药有限公司", "2026-05-18",
148d, 22.4d, 6.5d, 0.82d, "超标", "王芳");
waterQualityRecord(5L, "长沙金属表面电镀厂", "2026-05-20",
58d, 6.5d, 7.6d, 0.22d, "达标", "刘洋");
waterQualityRecord(5L, "长沙金属表面电镀厂", "2026-06-20",
91d, 13.1d, 7.1d, 0.49d, "预警", "刘洋");
waterQualityRecord(6L, "岳阳纸业造纸有限公司", "2026-04-28",
78d, 10.4d, 7.4d, 0.36d, "达标", "陈静");
waterQualityRecord(7L, "湖南鸿运印染集团", "2026-05-25",
156d, 24.8d, 6.3d, 0.95d, "超标", "王芳");
waterQualityRecord(7L, "湖南鸿运印染集团", "2026-06-25",
82d, 11.2d, 7.2d, 0.41d, "达标", "王芳");
waterQualityRecord(8L, "怀化高新区生活污水处理站", "2026-05-30",
68d, 9.1d, 7.5d, 0.31d, "达标", "刘洋");
}
private void waterQualityRecord(Long clientId, String clientName, String monitorDate,
double codValue, double ammoniaValue, double phValue,
double tpValue, String result, String inspector) {
WaterQualityRecord r = new WaterQualityRecord();
r.setClientId(clientId);
r.setClientName(clientName);
r.setMonitorDate(monitorDate);
r.setCodValue(codValue);
r.setAmmoniaValue(ammoniaValue);
r.setPhValue(phValue);
r.setTpValue(tpValue);
r.setResult(result);
r.setInspector(inspector);
r.setCreatedAt(Instant.now());
waterQualityRecordRepo.save(r);
}
// ----- manufacturing management center: work orders (制造管理中心, 生产工单) -----
private void seedWorkOrders() {
// productName covers 生物质有机肥 / 复合微生物肥 / 水处理药剂PAC / 缓释肥;
// unit covers 吨 / 袋; status covers all 5 stages
// 待生产 / 生产中 / 已完工 / 质检中 / 已入库. workshop covers
// 一号车间 / 二号车间 / 三号车间. owner uses 周敏/黄强/李建国.
workOrder("WO-2026-001", "生物质有机肥", 500d, 0d, "吨",
"2026-07-10", "待生产", "一号车间", "周敏");
workOrder("WO-2026-002", "复合微生物肥", 300d, 120d, "吨",
"2026-06-20", "生产中", "一号车间", "周敏");
workOrder("WO-2026-003", "水处理药剂PAC", 200d, 200d, "吨",
"2026-06-05", "已完工", "二号车间", "黄强");
workOrder("WO-2026-004", "缓释肥", 8000d, 8000d, "袋",
"2026-06-08", "质检中", "三号车间", "李建国");
workOrder("WO-2026-005", "生物质有机肥", 600d, 600d, "吨",
"2026-05-28", "已入库", "一号车间", "周敏");
workOrder("WO-2026-006", "复合微生物肥", 450d, 0d, "吨",
"2026-07-15", "待生产", "二号车间", "黄强");
workOrder("WO-2026-007", "水处理药剂PAC", 150d, 90d, "吨",
"2026-06-25", "生产中", "二号车间", "黄强");
workOrder("WO-2026-008", "缓释肥", 10000d, 10000d, "袋",
"2026-05-30", "已入库", "三号车间", "李建国");
}
private void workOrder(String orderNo, String productName, double planQty, double actualQty,
String unit, String plannedDate, String status, String workshop,
String owner) {
WorkOrder w = new WorkOrder();
w.setOrderNo(orderNo);
w.setProductName(productName);
w.setPlanQty(planQty);
w.setActualQty(actualQty);
w.setUnit(unit);
w.setPlannedDate(plannedDate);
w.setStatus(status);
w.setWorkshop(workshop);
w.setOwner(owner);
w.setCreatedAt(Instant.now());
workOrderRepo.save(w);
}
// ----- manufacturing management center: inventory items (制造管理中心, 库存) -----
private void seedInventoryItems() {
// category covers 原料 / 半成品 / 成品 / 辅料; status is set to 低于安全线 when
// quantity is below safetyStock (3 rows) and 正常 / 超储 otherwise. location
// (库位) covers multiple warehouse zones.
inventoryItem("腐殖酸原粉", "原料", "工业级", "吨",
85d, 30d, "A区-01", "正常");
inventoryItem("微生物菌剂", "原料", "活菌5亿/g", "kg",
120d, 200d, "A区-02", "低于安全线");
inventoryItem("氯化铝溶液", "原料", "含量30%", "吨",
42d, 20d, "A区-03", "正常");
inventoryItem("有机肥半成品料", "半成品", "发酵态", "吨",
60d, 40d, "B区-01", "正常");
inventoryItem("PAC浓缩液", "半成品", "液态", "吨",
15d, 25d, "B区-02", "低于安全线");
inventoryItem("生物质有机肥成品", "成品", "40kg/袋", "吨",
320d, 100d, "C区-01", "超储");
inventoryItem("复合微生物肥成品", "成品", "25kg/袋", "吨",
95d, 50d, "C区-02", "正常");
inventoryItem("缓释肥成品", "成品", "5kg/袋", "袋",
12000d, 4000d, "C区-03", "正常");
inventoryItem("编织包装袋", "辅料", "40kg规格", "条",
3500d, 5000d, "D区-01", "低于安全线");
inventoryItem("封口缝线", "辅料", "工业线轴", "卷",
280d, 100d, "D区-02", "正常");
}
private void inventoryItem(String materialName, String category, String spec, String unit,
double quantity, double safetyStock, String location, String status) {
InventoryItem i = new InventoryItem();
i.setMaterialName(materialName);
i.setCategory(category);
i.setSpec(spec);
i.setUnit(unit);
i.setQuantity(quantity);
i.setSafetyStock(safetyStock);
i.setLocation(location);
i.setStatus(quantity < safetyStock ? "低于安全线" : status);
i.setCreatedAt(Instant.now());
inventoryItemRepo.save(i);
}
// ----- finance general-ledger center: accounts (财务总账, 会计科目) -----
private void seedAccounts() {
// Standard top-level subjects covering all 5 categories
// 资产 / 负债 / 权益 / 成本 / 损益; direction (余额方向) is self-consistent
// with category (assets/costs/expenses 借, liabilities/equity/income 贷).
// All seeded subjects are level 1 with no parentCode.
account("1001", "库存现金", "资产", "借", null, 1, 58000d);
account("1002", "银行存款", "资产", "借", null, 1, 12600000d);
account("1122", "应收账款", "资产", "借", null, 1, 8450000d);
account("1403", "原材料", "资产", "借", null, 1, 2360000d);
account("1601", "固定资产", "资产", "借", null, 1, 18900000d);
account("2202", "应付账款", "负债", "贷", null, 1, 5320000d);
account("2211", "应付职工薪酬", "负债", "贷", null, 1, 1280000d);
account("2221", "应交税费", "负债", "贷", null, 1, 960000d);
account("4001", "实收资本", "权益", "贷", null, 1, 30000000d);
account("4104", "利润分配", "权益", "贷", null, 1, 4600000d);
account("6401", "主营业务成本", "成本", "借", null, 1, 21800000d);
account("6001", "主营业务收入", "损益", "贷", null, 1, 34500000d);
account("6601", "销售费用", "损益", "借", null, 1, 1860000d);
account("6602", "管理费用", "损益", "借", null, 1, 2740000d);
}
private void account(String code, String name, String category, String direction,
String parentCode, int level, double balance) {
Account a = new Account();
a.setCode(code);
a.setName(name);
a.setCategory(category);
a.setDirection(direction);
a.setParentCode(parentCode);
a.setLevel(level);
a.setBalance(Money.of(balance));
a.setCreatedAt(Instant.now());
accountRepo.save(a);
}
// ----- finance general-ledger center: vouchers (财务总账, 记账凭证) -----
private void seedVouchers() {
// voucherNo follows 记-2026-NNN; summary covers real business events
// (收到工程款 / 支付材料款 / 计提工资 / 缴纳税费 等); status covers
// 暂存 / 已审核 / 已记账. preparer uses 张伟/刘洋/陈静.
voucher("记-2026-001", "2026-06-01", "收到靖州供水项目工程款",
"银行存款", "应收账款", 2600000d, "已记账", "刘洋");
voucher("记-2026-002", "2026-06-02", "支付原材料采购款",
"应付账款", "银行存款", 860000d, "已记账", "刘洋");
voucher("记-2026-003", "2026-06-03", "计提本月职工工资",
"管理费用", "应付职工薪酬", 1280000d, "已审核", "陈静");
voucher("记-2026-004", "2026-06-05", "缴纳增值税及附加税费",
"应交税费", "银行存款", 960000d, "已审核", "张伟");
voucher("记-2026-005", "2026-06-08", "结转主营业务成本",
"主营业务成本", "原材料", 1540000d, "已记账", "刘洋");
voucher("记-2026-006", "2026-06-10", "确认岳阳排水管网项目收入",
"应收账款", "主营业务收入", 3120000d, "已记账", "刘洋");
voucher("记-2026-007", "2026-06-12", "报销销售部门差旅费",
"销售费用", "库存现金", 38000d, "暂存", "陈静");
voucher("记-2026-008", "2026-06-15", "支付办公管理费用",
"管理费用", "银行存款", 126000d, "暂存", "陈静");
voucher("记-2026-009", "2026-06-18", "购置生产设备入账",
"固定资产", "银行存款", 4200000d, "已审核", "张伟");
voucher("记-2026-010", "2026-06-20", "提现备用金",
"库存现金", "银行存款", 50000d, "已记账", "刘洋");
}
private void voucher(String voucherNo, String voucherDate, String summary, String debitAccount,
String creditAccount, double amount, String status, String preparer) {
Voucher v = new Voucher();
v.setVoucherNo(voucherNo);
v.setVoucherDate(voucherDate);
v.setSummary(summary);
v.setDebitAccount(debitAccount);
v.setCreditAccount(creditAccount);
v.setAmount(Money.of(amount));
v.setStatus(status);
v.setPreparer(preparer);
v.setCreatedAt(Instant.now());
voucherRepo.save(v);
}
// ----- laboratory center: test samples (实验室, 检测样品) -----
private void seedLabSamples() {
// sampleType covers 水样 / 泥样 / 气样 / 固废 / 土样; source covers
// 项目自检 / 客户委托 / 监督抽查; status covers 待检 / 检测中 / 已出报告 /
// 复检. sampleNo follows YP-2026-NNN.
labSample("YP-2026-001", "靖州污水厂进水水样", "水样", "项目自检", "2026-06-01",
"COD/氨氮/总磷/SS", "已出报告", "周敏");
labSample("YP-2026-002", "岳阳管网出水水样", "水样", "监督抽查", "2026-06-02",
"COD/总氮/pH", "检测中", "周敏");
labSample("YP-2026-003", "脱水污泥泥样", "泥样", "项目自检", "2026-06-03",
"含水率/有机质/重金属", "已出报告", "李强");
labSample("YP-2026-004", "生化池活性污泥泥样", "泥样", "项目自检", "2026-06-05",
"MLSS/SV30", "复检", "李强");
labSample("YP-2026-005", "锅炉烟气气样", "气样", "客户委托", "2026-06-06",
"二氧化硫/氮氧化物/颗粒物", "待检", "陈晨");
labSample("YP-2026-006", "厂界无组织废气气样", "气样", "监督抽查", "2026-06-07",
"硫化氢/氨/臭气浓度", "检测中", "陈晨");
labSample("YP-2026-007", "焚烧炉渣固废样", "固废", "客户委托", "2026-06-08",
"浸出毒性/重金属", "待检", "黄海");
labSample("YP-2026-008", "危废暂存间固废样", "固废", "监督抽查", "2026-06-09",
"腐蚀性/易燃性", "已出报告", "黄海");
labSample("YP-2026-009", "复绿区表层土样", "土样", "项目自检", "2026-06-10",
"pH/有机质/全氮/全磷", "检测中", "周敏");
labSample("YP-2026-010", "填埋场周边土样", "土样", "客户委托", "2026-06-10",
"重金属/有机污染物", "待检", "李强");
}
private void labSample(String sampleNo, String sampleName, String sampleType, String source,
String receiveDate, String testItems, String status, String assignee) {
LabSample s = new LabSample();
s.setSampleNo(sampleNo);
s.setSampleName(sampleName);
s.setSampleType(sampleType);
s.setSource(source);
s.setReceiveDate(receiveDate);
s.setTestItems(testItems);
s.setStatus(status);
s.setAssignee(assignee);
s.setCreatedAt(Instant.now());
labSampleRepo.save(s);
}
// ----- laboratory center: instruments (实验室, 仪器设备) -----
private void seedLabInstruments() {
// status covers 在用 / 维修 / 停用 / 待校准; nextCalibration of several
// instruments falls in the second half of 2026. assetNo follows YQ-NNN.
labInstrument("COD测定仪", "5B-3C", "YQ-001", "2026-01-15", "2027-01-15",
"在用", "化学分析室", "周敏");
labInstrument("原子吸收分光光度计", "AA-7000", "YQ-002", "2025-12-20", "2026-12-20",
"在用", "重金属分析室", "李强");
labInstrument("气相色谱仪", "GC-2030", "YQ-003", "2026-03-10", "2026-09-10",
"待校准", "有机分析室", "陈晨");
labInstrument("pH计", "PHS-3E", "YQ-004", "2026-05-01", "2026-11-01",
"在用", "化学分析室", "周敏");
labInstrument("紫外可见分光光度计", "UV-1900", "YQ-005", "2026-02-18", "2027-02-18",
"在用", "化学分析室", "黄海");
labInstrument("电子天平", "ME204", "YQ-006", "2026-04-22", "2026-10-22",
"维修", "天平室", "李强");
labInstrument("总有机碳分析仪", "TOC-L", "YQ-007", "2025-11-30", "2026-11-30",
"在用", "有机分析室", "陈晨");
labInstrument("烟气分析仪", "Testo 350", "YQ-008", "2026-03-25", "2026-09-25",
"停用", "废气检测组", "黄海");
}
private void labInstrument(String name, String model, String assetNo, String calibrationDate,
String nextCalibration, String status, String location, String manager) {
LabInstrument i = new LabInstrument();
i.setName(name);
i.setModel(model);
i.setAssetNo(assetNo);
i.setCalibrationDate(calibrationDate);
i.setNextCalibration(nextCalibration);
i.setStatus(status);
i.setLocation(location);
i.setManager(manager);
i.setCreatedAt(Instant.now());
labInstrumentRepo.save(i);
}
// ----- design research center: design documents (设计研究中心, 设计文档) -----
private void seedDesignDocs() {
// docType covers 可研报告 / 初步设计 / 施工图 / 计算书 / 专项方案;
// reviewStatus covers 编制中 / 校审中 / 已批准 / 已归档.
designDoc("靖州县污水处理厂可行性研究报告", "靖州县污水处理厂工程", "可研报告", "V2.0",
"王建国", "已批准", "2026-03-15");
designDoc("岳阳市排水管网初步设计", "岳阳市排水管网改造工程", "初步设计", "V1.1",
"刘志远", "已归档", "2026-02-20");
designDoc("怀化垃圾焚烧发电厂施工图", "怀化生活垃圾焚烧发电项目", "施工图", "V3.0",
"陈思", "校审中", "2026-05-10");
designDoc("污水厂工艺水力计算书", "靖州县污水处理厂工程", "计算书", "V1.0",
"赵敏", "已批准", "2026-04-01");
designDoc("深基坑开挖专项方案", "岳阳市排水管网改造工程", "专项方案", "V2.1",
"孙浩", "校审中", "2026-05-18");
designDoc("中水回用初步设计", "园区中水回用工程", "初步设计", "V1.0",
"周晴", "编制中", "");
designDoc("污泥处置施工图", "城西污泥处置中心项目", "施工图", "V1.2",
"陈思", "编制中", "");
designDoc("厂区总平面布置可研报告", "城西污泥处置中心项目", "可研报告", "V1.0",
"王建国", "已归档", "2026-01-12");
}
private void designDoc(String name, String projectName, String docType, String version,
String designer, String reviewStatus, String issueDate) {
DesignDoc d = new DesignDoc();
d.setName(name);
d.setProjectName(projectName);
d.setDocType(docType);
d.setVersion(version);
d.setDesigner(designer);
d.setReviewStatus(reviewStatus);
d.setIssueDate(issueDate);
d.setCreatedAt(Instant.now());
designDocRepo.save(d);
}
// ----- design research center: design changes (设计研究中心, 设计变更) -----
private void seedDesignChanges() {
// changeType covers 设计变更 / 技术核定 / 工程洽商; status covers
// 申请 / 审批中 / 已实施 / 已驳回.
designChange("怀化垃圾焚烧发电厂施工图", "怀化生活垃圾焚烧发电项目", "现场地质条件与勘察不符",
"设计变更", "桩基础形式调整, 增加造价约80万", "已实施", "陈思");
designChange("岳阳市排水管网初步设计", "岳阳市排水管网改造工程", "管线避让既有燃气管道",
"工程洽商", "局部管线改线120米", "审批中", "刘志远");
designChange("污水厂工艺水力计算书", "靖州县污水处理厂工程", "进水水质参数核定",
"技术核定", "调整生化池停留时间, 不影响出水", "已实施", "赵敏");
designChange("深基坑开挖专项方案", "岳阳市排水管网改造工程", "支护结构优化建议",
"设计变更", "改为钢板桩支护, 节省工期15天", "申请", "孙浩");
designChange("中水回用初步设计", "园区中水回用工程", "用户用水量预测偏差",
"技术核定", "回用规模由5000吨调整为3000吨", "审批中", "周晴");
designChange("污泥处置施工图", "城西污泥处置中心项目", "设备选型与场地冲突",
"工程洽商", "需重新核定设备布置, 暂缓实施", "已驳回", "陈思");
}
private void designChange(String docName, String projectName, String reason, String changeType,
String impact, String status, String applicant) {
DesignChange c = new DesignChange();
c.setDocName(docName);
c.setProjectName(projectName);
c.setReason(reason);
c.setChangeType(changeType);
c.setImpact(impact);
c.setStatus(status);
c.setApplicant(applicant);
c.setCreatedAt(Instant.now());
designChangeRepo.save(c);
}
// ----- engineering supervision center: supervision logs (工程监理, 监理记录) -----
private void seedSupervisionLogs() {
// logType covers 旁站 / 巡视 / 平行检验 / 监理通知 / 计量支付;
// handleStatus covers 待处理 / 处理中 / 已闭环.
supervisionLog("靖州县污水处理厂工程", "旁站", "生化池底板混凝土浇筑全程旁站",
"无", "已闭环", "李监理", "2026-06-01");
supervisionLog("靖州县污水处理厂工程", "巡视", "现场安全文明施工巡视",
"脚手架部分临边防护缺失", "处理中", "李监理", "2026-06-02");
supervisionLog("岳阳市排水管网改造工程", "平行检验", "回填土压实度平行检验",
"局部压实度未达标", "已闭环", "张监理", "2026-06-03");
supervisionLog("岳阳市排水管网改造工程", "监理通知", "下发雨季施工措施落实通知",
"排水沟未及时清理", "处理中", "张监理", "2026-06-04");
supervisionLog("怀化生活垃圾焚烧发电项目", "计量支付", "第6期工程进度计量审核",
"无", "已闭环", "王监理", "2026-06-05");
supervisionLog("怀化生活垃圾焚烧发电项目", "旁站", "钢结构主梁吊装旁站",
"吊装方案与现场不符", "待处理", "王监理", "2026-06-06");
supervisionLog("城西污泥处置中心项目", "巡视", "基坑降水运行情况巡视",
"降水井水位偏高", "处理中", "赵监理", "2026-06-07");
supervisionLog("城西污泥处置中心项目", "平行检验", "钢筋原材进场平行检验",
"无", "已闭环", "赵监理", "2026-06-08");
supervisionLog("园区中水回用工程", "监理通知", "下发隐蔽工程验收整改通知",
"管道接口未做闭水试验", "待处理", "孙监理", "2026-06-09");
supervisionLog("园区中水回用工程", "计量支付", "材料预付款支付审核",
"无", "已闭环", "孙监理", "2026-06-10");
}
private void supervisionLog(String projectName, String logType, String content, String problem,
String handleStatus, String supervisor, String logDate) {
SupervisionLog l = new SupervisionLog();
l.setProjectName(projectName);
l.setLogType(logType);
l.setContent(content);
l.setProblem(problem);
l.setHandleStatus(handleStatus);
l.setSupervisor(supervisor);
l.setLogDate(logDate);
l.setCreatedAt(Instant.now());
supervisionLogRepo.save(l);
}
// ----- audit and supervision center: audit projects (审计监察, 审计项目) -----
private void seedAuditProjects() {
// auditType covers 财务审计 / 工程审计 / 合规审计 / 内控评价 /
// 经济责任审计; status covers 计划 / 实施中 / 报告 / 整改 / 已结.
auditProject("2026年度财务收支审计", "财务审计", "财务部", "钱伟", "2025年度",
5, "已结");
auditProject("靖州污水厂工程结算审计", "工程审计", "工程管理中心", "孙立", "2026上半年",
8, "整改");
auditProject("招投标合规专项审计", "合规审计", "招投标中心", "周强", "2025-2026",
3, "报告");
auditProject("采购内部控制评价", "内控评价", "采购部", "钱伟", "2025年度",
4, "实施中");
auditProject("分公司负责人经济责任审计", "经济责任审计", "长沙分公司", "孙立", "2023-2025",
6, "实施中");
auditProject("2026年度合同管理合规审计", "合规审计", "法务部", "周强", "2025年度",
2, "计划");
auditProject("固定资产投资工程审计", "工程审计", "怀化项目部", "孙立", "2026上半年",
0, "计划");
auditProject("费用报销内控评价", "内控评价", "财务部", "钱伟", "2025年度",
7, "已结");
}
private void auditProject(String name, String auditType, String auditee, String leadAuditor,
String period, int findings, String status) {
AuditProject p = new AuditProject();
p.setName(name);
p.setAuditType(auditType);
p.setAuditee(auditee);
p.setLeadAuditor(leadAuditor);
p.setPeriod(period);
p.setFindings(findings);
p.setStatus(status);
p.setCreatedAt(Instant.now());
auditProjectRepo.save(p);
}
// ----- information department: IT assets (信息部, IT资产) -----
private void seedItAssets() {
// category covers 服务器 / 网络设备 / 终端 / 软件 / 办公设备; status covers
// 在用 / 闲置 / 维修 / 报废. assetNo follows IT-NNN; holder uses 字段 holder
// to avoid the SQL keyword user.
itAsset("IT-001", "Dell PowerEdge R750 服务器", "服务器", "2U/64G内存/4T",
"信息部机房", "信息中心", "2025-03-10", "在用", "总部机房A柜");
itAsset("IT-002", "华为 RH2288 服务器", "服务器", "2U/128G内存/8T",
"信息部机房", "信息中心", "2024-06-15", "在用", "总部机房A柜");
itAsset("IT-003", "华为 S5720 核心交换机", "网络设备", "48口千兆",
"信息部机房", "信息中心", "2024-06-15", "在用", "总部机房B柜");
itAsset("IT-004", "深信服 防火墙 AF-2000", "网络设备", "千兆下一代防火墙",
"信息部机房", "信息中心", "2025-01-20", "在用", "总部机房B柜");
itAsset("IT-005", "联想 ThinkPad T14 笔记本", "终端", "i7/16G/512G",
"王建国", "设计研究中心", "2025-04-08", "在用", "设计室");
itAsset("IT-006", "戴尔 OptiPlex 台式机", "终端", "i5/8G/256G",
"周敏", "实验室", "2024-09-12", "在用", "化学分析室");
itAsset("IT-007", "惠普 ProBook 笔记本", "终端", "i5/8G/256G",
"", "信息中心", "2023-05-30", "闲置", "信息部库房");
itAsset("IT-008", "用友 U8+ ERP 软件", "软件", "财务模块50用户",
"财务部", "财务部", "2024-01-10", "在用", "应用服务器");
itAsset("IT-009", "正版 Office 365 授权", "软件", "企业版200账号",
"信息部", "信息中心", "2025-02-01", "在用", "云端订阅");
itAsset("IT-010", "佳能 iR-ADV 复印机", "办公设备", "A3彩色多功能一体机",
"行政部", "行政部", "2023-07-22", "维修", "办公区打印间");
itAsset("IT-011", "爱普生 投影仪", "办公设备", "5000流明",
"行政部", "行政部", "2022-03-18", "报废", "信息部库房");
itAsset("IT-012", "TP-LINK 无线AP", "网络设备", "千兆双频吸顶式",
"信息部", "信息中心", "2025-05-06", "闲置", "信息部库房");
}
private void itAsset(String assetNo, String name, String category, String spec, String holder,
String dept, String purchaseDate, String status, String location) {
ItAsset a = new ItAsset();
a.setAssetNo(assetNo);
a.setName(name);
a.setCategory(category);
a.setSpec(spec);
a.setHolder(holder);
a.setDept(dept);
a.setPurchaseDate(purchaseDate);
a.setStatus(status);
a.setLocation(location);
a.setCreatedAt(Instant.now());
itAssetRepo.save(a);
}
// ----- work handover (工作交接) -----
private void seedHandovers() {
// handoverType covers 离职交接 / 调动交接 / 轮岗交接; status covers
// 进行中 / 已完成 / 已确认.
handover("刘洋", "陈静", "财务部", "离职交接",
"在手凭证移交, 网银U盾交回, 待报销单据清单", "进行中", "2026-06-05");
handover("王芳", "张伟", "信息中心", "调动交接",
"在研系统源码与文档, 服务器账号, 待办工单", "已完成", "2026-05-20");
handover("李娜", "陈静", "行政办公", "轮岗交接",
"印章保管登记本, 后勤合同台账, 车辆调度记录", "已确认", "2026-04-28");
handover("周敏", "孙磊", "创新研发中心", "离职交接",
"在研课题进度, 实验数据归档, 知识产权材料", "进行中", "2026-06-08");
handover("赵强", "钱进", "工程管理中心", "调动交接",
"在建项目台账, 监理资料, 安全检查整改单", "已完成", "2026-05-12");
handover("孙丽", "李娜", "支付中心", "轮岗交接",
"结算单据移交, 票据领用登记, 对账单清单", "已确认", "2026-03-30");
}
private void handover(String handoverPerson, String receiver, String dept,
String handoverType, String items, String status, String handoverDate) {
Handover h = new Handover();
h.setHandoverPerson(handoverPerson);
h.setReceiver(receiver);
h.setDept(dept);
h.setHandoverType(handoverType);
h.setItems(items);
h.setStatus(status);
h.setHandoverDate(handoverDate);
h.setCreatedAt(Instant.now());
handoverRepo.save(h);
}
// ----- declaration multi-step lifecycle (申报多步骤生命周期) -----
private void seedDeclarationSteps() {
// Two declarations (declarationId 1 and 2, names taken from the seeded
// Declaration list) each carry the full eight-step lifecycle. status
// reflects progress: early steps 已完成, a middle step 进行中, later
// steps 待办. Declaration 1 is further along than declaration 2.
String d1 = "2026年度高新技术企业认定申报";
declarationSteps(1L, d1, "张伟",
new String[]{"已完成", "已完成", "已完成", "已完成", "进行中", "待办", "待办", "待办"});
String d2 = "农村供水智能调度省重点研发计划项目申报";
declarationSteps(2L, d2, "王芳",
new String[]{"已完成", "已完成", "进行中", "待办", "待办", "待办", "待办", "待办"});
}
private void declarationSteps(Long declarationId, String declarationName, String owner,
String[] statuses) {
String[] names = {"立项", "材料协同", "数据采集", "内部审核",
"外部提交", "进度跟踪", "补正答辩", "结果登记"};
for (int i = 0; i < names.length; i++) {
DeclarationStep s = new DeclarationStep();
s.setDeclarationId(declarationId);
s.setDeclarationName(declarationName);
s.setStepName(names[i]);
s.setStepOrder(i + 1);
s.setStatus(statuses[i]);
s.setOwner(owner);
s.setDueDate("2026-0" + (i < 3 ? 3 + i : 6) + "-15");
s.setCreatedAt(Instant.now());
declarationStepRepo.save(s);
}
}
// ----- declaration expert pool (申报专家库) -----
private void seedExperts() {
// field covers 给排水 / 环境工程 / 市政 / 财务 / 法律 / 水利; expertType
// covers 评审专家 / 咨询专家 / 行业专家; status covers 在库 / 暂停.
expert("陈国华", "给排水", "湖南大学土木工程学院", "教授", "评审专家",
"13900000001", "在库");
expert("林志远", "环境工程", "湖南省环境科学研究院", "研究员", "评审专家",
"13900000002", "在库");
expert("王海涛", "市政", "中南市政设计研究院", "教授级高工", "咨询专家",
"13900000003", "在库");
expert("赵敏华", "财务", "天职国际会计师事务所", "高级会计师", "咨询专家",
"13900000004", "在库");
expert("孙立军", "法律", "湖南金州律师事务所", "高级律师", "咨询专家",
"13900000005", "在库");
expert("周建波", "水利", "湖南省水利水电勘测设计院", "教授级高工", "行业专家",
"13900000006", "在库");
expert("吴秀英", "环境工程", "怀化学院化学与材料工程学院", "副教授", "评审专家",
"13900000007", "在库");
expert("郑文斌", "给排水", "湖南省建筑设计院", "高级工程师", "行业专家",
"13900000008", "暂停");
}
private void expert(String name, String field, String org, String title,
String expertType, String phone, String status) {
Expert e = new Expert();
e.setName(name);
e.setField(field);
e.setOrg(org);
e.setTitle(title);
e.setExpertType(expertType);
e.setPhone(phone);
e.setStatus(status);
e.setCreatedAt(Instant.now());
expertRepo.save(e);
}
// ----- 存货计价方法配置种子 -----
private void seedInventoryValuationConfigs() {
if (inventoryValuationConfigRepo.count() > 0) return;
inventoryValuationConfig("全局", "原材料", "移动平均", "2025-01", null,
"已生效", "初始建立全局计价方法", "张会计", "总会计师");
inventoryValuationConfig("生物质肥料制造中心", "成品/半成品", "先进先出", "2025-01", null,
"已生效", "制造中心成品按先进先出管理", "李主管", "成本控制部长");
inventoryValuationConfig("水处理药剂制造中心", "化学品", "个别计价", "2025-03", null,
"已生效", "化学品价格差异大,采用个别计价", "王会计", "总会计师");
inventoryValuationConfig("全局", "设备/零部件", "移动平均", "2026-01", null,
"草稿", "2026年度更新计价方法草案", "赵分析师", null);
}
private void inventoryValuationConfig(String costCenter, String materialCategory,
String method, String from, String to,
String status, String reason,
String applicant, String approver) {
InventoryValuationConfig c = new InventoryValuationConfig();
c.setCostCenter(costCenter);
c.setMaterialCategory(materialCategory);
c.setMethod(method);
c.setEffectivePeriodFrom(from);
c.setEffectivePeriodTo(to);
c.setStatus(status);
c.setReason(reason);
c.setApplicant(applicant);
c.setApprover(approver);
c.setCreatedAt(Instant.now().minus(Duration.ofDays(60)));
if (approver != null) {
c.setApprovedAt(Instant.now().minus(Duration.ofDays(55)));
}
inventoryValuationConfigRepo.save(c);
}
// ----- 成本调整操作日志种子 -----
private void seedCostAdjustLogs() {
if (costAdjustLogRepo.count() > 0) return;
costAdjustLog("标准成本修订", "StandardCost", null, "原材料钢材 2025-06标准成本",
new java.math.BigDecimal("5800.00"), new java.math.BigDecimal("6100.00"),
null, null, "供应商提价导致原材料采购成本上升", "张会计", "2025-06", "总会计师", 90);
costAdjustLog("差异手工分摊", "BomVarianceDetail", null, "2025-06材料价格差异分摊",
new java.math.BigDecimal("12000.00"), new java.math.BigDecimal("0.00"),
null, null, "月末将材料价格差异按比例分摊至库存和销售成本", "李主管", "2025-06", "成本控制部长", 85);
costAdjustLog("预算修改", "ProjectBudgetLine", null, "凯迪环保项目材料费预算调增",
new java.math.BigDecimal("500000.00"), new java.math.BigDecimal("560000.00"),
null, null, "因设计变更导致材料用量增加,申请预算调增", "王分析师", "2025-05", "项目总监", 100);
costAdjustLog("计价方法变更", "InventoryValuationConfig", null, "水处理药剂由移动平均改为个别计价",
null, null, "移动平均", "个别计价",
"化学品价格波动较大,个别计价更准确反映实际成本", "赵会计", "2025-03", "总会计师", 75);
costAdjustLog("标准成本修订", "StandardCost", null, "人工费率2025年度调整",
new java.math.BigDecimal("85.00"), new java.math.BigDecimal("92.00"),
null, null, "根据人力资源部工时费率调整方案更新标准人工费率", "张会计", "2025-01", "总会计师", 180);
}
private void costAdjustLog(String adjustType, String entityType, Long entityId,
String entityDesc, java.math.BigDecimal amountBefore,
java.math.BigDecimal amountAfter,
String valueBefore, String valueAfter,
String reason, String operator, String period,
String approver, int daysAgoOffset) {
CostAdjustLog log = new CostAdjustLog();
log.setAdjustType(adjustType);
log.setEntityType(entityType);
log.setEntityId(entityId);
log.setEntityDesc(entityDesc);
log.setAmountBefore(amountBefore != null ? amountBefore : java.math.BigDecimal.ZERO);
log.setAmountAfter(amountAfter != null ? amountAfter : java.math.BigDecimal.ZERO);
log.setValueBefore(valueBefore);
log.setValueAfter(valueAfter);
log.setReason(reason);
log.setOperator(operator);
log.setPeriod(period);
log.setApprover(approver);
log.setOperatedAt(Instant.now().minus(Duration.ofDays(daysAgoOffset)));
costAdjustLogRepo.save(log);
}
// ----- helpers -----
private Instant daysAgo(int offset, int hour, int minute) {
return Instant.now()
.minus(Duration.ofDays(offset))
.truncatedTo(ChronoUnit.DAYS)
.plus(Duration.ofHours(hour))
.plus(Duration.ofMinutes(minute));
}
}