Files
ERP/oa-backend/src/main/java/com/kaidi/oa/seed/FinancingDomainSeeder.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

469 lines
19 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.kaidi.oa.seed;
import com.kaidi.oa.common.Money;
import com.kaidi.oa.domain.Financing;
import com.kaidi.oa.domain.FinancingPolicy;
import com.kaidi.oa.domain.PmtBankEvaluation;
import com.kaidi.oa.domain.PmtBankRelation;
import com.kaidi.oa.domain.RepaymentPlan;
import com.kaidi.oa.repository.FinancingPolicyRepository;
import com.kaidi.oa.repository.FinancingRepository;
import com.kaidi.oa.repository.PmtBankEvaluationRepository;
import com.kaidi.oa.repository.PmtBankRelationRepository;
import com.kaidi.oa.repository.RepaymentPlanRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import java.math.BigDecimal;
import java.time.Instant;
/**
* 融资域演示数据(金融办)。
* 补建 Gap-1/2/5 的活体演示数据:金融机构档案、融资台账、机构定期评价、融资政策规则、还款计划。
* 只在对应表为空时执行,防止重复。@Order(20) 在 DataSeeder(@Order(1)) 之后运行。
*/
@Order(20)
@DemoSeed
public class FinancingDomainSeeder implements CommandLineRunner {
private final PmtBankRelationRepository bankRelRepo;
private final FinancingRepository financingRepo;
private final RepaymentPlanRepository planRepo;
private final PmtBankEvaluationRepository evalRepo;
private final FinancingPolicyRepository policyRepo;
public FinancingDomainSeeder(PmtBankRelationRepository bankRelRepo,
FinancingRepository financingRepo,
RepaymentPlanRepository planRepo,
PmtBankEvaluationRepository evalRepo,
FinancingPolicyRepository policyRepo) {
this.bankRelRepo = bankRelRepo;
this.financingRepo = financingRepo;
this.planRepo = planRepo;
this.evalRepo = evalRepo;
this.policyRepo = policyRepo;
}
@Override
public void run(String... args) {
seedBankRelations();
seedFinancings();
seedPolicies();
seedEvaluations();
}
private void seedBankRelations() {
if (bankRelRepo.count() > 0) return;
PmtBankRelation r1 = new PmtBankRelation();
r1.setInstitutionName("工商银行长沙分行");
r1.setInstitutionType("商业银行");
r1.setBranch("长沙分行公司业务部");
r1.setAccountManager("张磊");
r1.setManagerPhone("0731-88882001");
r1.setTotalCreditLimit(Money.of(200000000.0));
r1.setCooperationStartDate("2019-03-01");
r1.setRating("A");
r1.setAvgApprovalDays(7);
r1.setRemark("主要合作银行,综合授信2亿,流贷+固贷均有合作历史。");
r1.setStatus("合作中");
r1.setCreatedBy("admin");
r1.setCreatedAt(Instant.now());
bankRelRepo.save(r1);
PmtBankRelation r2 = new PmtBankRelation();
r2.setInstitutionName("建设银行株洲支行");
r2.setInstitutionType("商业银行");
r2.setBranch("株洲高新区支行");
r2.setAccountManager("李梅");
r2.setManagerPhone("0733-77773001");
r2.setTotalCreditLimit(Money.of(100000000.0));
r2.setCooperationStartDate("2020-06-15");
r2.setRating("B");
r2.setAvgApprovalDays(10);
r2.setRemark("次要银行,主要为子公司株洲项目配套贷款。");
r2.setStatus("合作中");
r2.setCreatedBy("admin");
r2.setCreatedAt(Instant.now());
bankRelRepo.save(r2);
PmtBankRelation r3 = new PmtBankRelation();
r3.setInstitutionName("国家开发银行湖南省分行");
r3.setInstitutionType("政策性银行");
r3.setBranch("湖南省分行基础设施部");
r3.setAccountManager("王强");
r3.setManagerPhone("0731-66661001");
r3.setTotalCreditLimit(Money.of(500000000.0));
r3.setCooperationStartDate("2021-01-20");
r3.setRating("A");
r3.setAvgApprovalDays(15);
r3.setRemark("政策性长期资金来源,主要用于环保固定资产投资项目贷款,利率优惠。");
r3.setStatus("合作中");
r3.setCreatedBy("admin");
r3.setCreatedAt(Instant.now());
bankRelRepo.save(r3);
PmtBankRelation r4 = new PmtBankRelation();
r4.setInstitutionName("中信银行长沙分行");
r4.setInstitutionType("商业银行");
r4.setBranch("长沙分行企业金融部");
r4.setAccountManager("陈晓");
r4.setManagerPhone("0731-55550801");
r4.setTotalCreditLimit(Money.of(80000000.0));
r4.setCooperationStartDate("2022-09-01");
r4.setRating("B");
r4.setAvgApprovalDays(8);
r4.setRemark("近两年新增合作银行,流动资金贷款为主,银行承兑汇票业务活跃。");
r4.setStatus("合作中");
r4.setCreatedBy("admin");
r4.setCreatedAt(Instant.now());
bankRelRepo.save(r4);
}
private void seedFinancings() {
if (financingRepo.count() > 0) return;
Financing f1 = new Financing();
f1.setCode("RZ-2024-001");
f1.setLender("工商银行长沙分行");
f1.setFinancingType("银行贷款");
f1.setCreditLimit(Money.of(50000000.0));
f1.setAmount(Money.of(50000000.0));
f1.setCurrency("人民币");
f1.setRate(BigDecimal.valueOf(3.85));
f1.setStartDate("2024-01-15");
f1.setEndDate("2026-01-14");
f1.setTermMonths(24);
f1.setRepayMethod("按期付息到期还本");
f1.setStatus("还款中");
f1.setCompanySubject("凯迪科技股份有限公司");
f1.setPurpose("补充流动资金");
f1.setOwner("admin");
f1.setCreatedAt(Instant.now());
Financing sf1 = financingRepo.save(f1);
// 为 f1 生成还款计划(按期付息到期还本,24期,月息)
seedRepaymentPlan(sf1, 24, 3.85, "按期付息到期还本", "2024-01-15");
Financing f2 = new Financing();
f2.setCode("RZ-2024-002");
f2.setLender("国家开发银行湖南省分行");
f2.setFinancingType("银行贷款");
f2.setCreditLimit(Money.of(100000000.0));
f2.setAmount(Money.of(80000000.0));
f2.setCurrency("人民币");
f2.setRate(BigDecimal.valueOf(3.20));
f2.setStartDate("2024-03-01");
f2.setEndDate("2029-02-28");
f2.setTermMonths(60);
f2.setRepayMethod("等额本金");
f2.setStatus("还款中");
f2.setCompanySubject("凯迪科技股份有限公司");
f2.setPurpose("污水处理厂固定资产投资");
f2.setOwner("admin");
f2.setCreatedAt(Instant.now());
Financing sf2 = financingRepo.save(f2);
seedRepaymentPlan(sf2, 60, 3.20, "等额本金", "2024-03-01");
Financing f3 = new Financing();
f3.setCode("RZ-2024-003");
f3.setLender("建设银行株洲支行");
f3.setFinancingType("银行贷款");
f3.setCreditLimit(Money.of(20000000.0));
f3.setAmount(Money.of(18000000.0));
f3.setCurrency("人民币");
f3.setRate(BigDecimal.valueOf(4.35));
f3.setStartDate("2024-06-01");
f3.setEndDate("2025-05-31");
f3.setTermMonths(12);
f3.setRepayMethod("到期一次还本付息");
f3.setStatus("还款中");
f3.setCompanySubject("株洲凯迪环保科技有限公司");
f3.setPurpose("补充流动资金");
f3.setOwner("admin");
f3.setCreatedAt(Instant.now());
Financing sf3 = financingRepo.save(f3);
seedRepaymentPlan(sf3, 12, 4.35, "到期一次还本付息", "2024-06-01");
Financing f4 = new Financing();
f4.setCode("RZ-2025-001");
f4.setLender("中信银行长沙分行");
f4.setFinancingType("银行贷款");
f4.setCreditLimit(Money.of(30000000.0));
f4.setAmount(Money.of(30000000.0));
f4.setCurrency("人民币");
f4.setRate(BigDecimal.valueOf(4.10));
f4.setStartDate("2025-02-01");
f4.setEndDate("2026-01-31");
f4.setTermMonths(12);
f4.setRepayMethod("等额本息");
f4.setStatus("还款中");
f4.setCompanySubject("凯迪科技股份有限公司");
f4.setPurpose("采购原材料及备品备件");
f4.setOwner("admin");
f4.setCreatedAt(Instant.now());
Financing sf4 = financingRepo.save(f4);
seedRepaymentPlan(sf4, 12, 4.10, "等额本息", "2025-02-01");
// 一笔申请中
Financing f5 = new Financing();
f5.setCode("RZ-2025-002");
f5.setLender("工商银行长沙分行");
f5.setFinancingType("银行承兑汇票");
f5.setCreditLimit(Money.of(20000000.0));
f5.setAmount(Money.of(20000000.0));
f5.setCurrency("人民币");
f5.setRate(BigDecimal.valueOf(2.50));
f5.setStartDate("2025-07-01");
f5.setEndDate("2026-06-30");
f5.setTermMonths(12);
f5.setRepayMethod("到期一次还本付息");
f5.setStatus("申请中");
f5.setCompanySubject("凯迪科技股份有限公司");
f5.setPurpose("支付设备采购款(银承)");
f5.setOwner("admin");
f5.setCreatedAt(Instant.now());
financingRepo.save(f5);
// 一笔已结清
Financing f6 = new Financing();
f6.setCode("RZ-2023-003");
f6.setLender("建设银行株洲支行");
f6.setFinancingType("银行贷款");
f6.setCreditLimit(Money.of(10000000.0));
f6.setAmount(Money.of(10000000.0));
f6.setCurrency("人民币");
f6.setRate(BigDecimal.valueOf(4.60));
f6.setStartDate("2023-01-01");
f6.setEndDate("2024-12-31");
f6.setTermMonths(24);
f6.setRepayMethod("等额本息");
f6.setStatus("已结清");
f6.setCompanySubject("株洲凯迪环保科技有限公司");
f6.setPurpose("经营周转资金");
f6.setOwner("admin");
f6.setCreatedAt(Instant.now());
financingRepo.save(f6);
}
private void seedRepaymentPlan(Financing f, int term, double annualRate, String method, String startDate) {
if (planRepo.findByFinancingIdOrderByPeriodNoAsc(f.getId()).size() > 0) return;
double monthlyRate = annualRate / 100.0 / 12.0;
double principal = Money.nz(f.getAmount()).doubleValue();
java.time.LocalDate start;
try {
start = java.time.LocalDate.parse(startDate);
} catch (Exception e) {
start = java.time.LocalDate.now();
}
if ("到期一次还本付息".equals(method)) {
double interest = principal * monthlyRate * term;
RepaymentPlan p = buildPlan(f, 1, start.plusMonths(term).toString(),
new BigDecimal(principal).setScale(2, java.math.RoundingMode.HALF_UP),
Money.of(interest), "未还");
planRepo.save(p);
return;
}
if ("按期付息到期还本".equals(method)) {
BigDecimal totalPrincipal = Money.nz(f.getAmount());
for (int i = 1; i <= term; i++) {
double interest = principal * monthlyRate;
BigDecimal prin = (i == term) ? totalPrincipal : BigDecimal.ZERO;
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
Money.nz(prin), Money.of(interest), "未还");
planRepo.save(p);
}
return;
}
if ("等额本金".equals(method)) {
BigDecimal totalPrincipal = Money.nz(f.getAmount());
BigDecimal perPrincipal = totalPrincipal.divide(BigDecimal.valueOf(term), 2, java.math.RoundingMode.HALF_UP);
double balance = principal;
BigDecimal acc = BigDecimal.ZERO;
for (int i = 1; i <= term; i++) {
double interest = balance * monthlyRate;
BigDecimal prin = (i == term) ? Money.sub(totalPrincipal, acc) : perPrincipal;
acc = Money.add(acc, prin);
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
Money.nz(prin), Money.of(interest), "未还");
planRepo.save(p);
balance -= prin.doubleValue();
}
return;
}
// 等额本息
BigDecimal totalPrincipal = Money.nz(f.getAmount());
double installment;
if (monthlyRate == 0) {
installment = principal / term;
} else {
double pow = Math.pow(1 + monthlyRate, term);
installment = principal * monthlyRate * pow / (pow - 1);
}
double balance = principal;
BigDecimal acc = BigDecimal.ZERO;
for (int i = 1; i <= term; i++) {
double interest = balance * monthlyRate;
double prinD = installment - interest;
BigDecimal prin = (i == term) ? Money.sub(totalPrincipal, acc) : Money.of(prinD);
acc = Money.add(acc, prin);
RepaymentPlan p = buildPlan(f, i, start.plusMonths(i).toString(),
Money.nz(prin), Money.of(interest), "未还");
planRepo.save(p);
balance -= prin.doubleValue();
}
}
private RepaymentPlan buildPlan(Financing f, int no, String dueDate,
BigDecimal principal, BigDecimal interest, String status) {
RepaymentPlan p = new RepaymentPlan();
p.setFinancingId(f.getId());
p.setFinancingCode(f.getCode());
p.setPeriodNo(no);
p.setDueDate(dueDate);
p.setPrincipal(Money.nz(principal));
p.setInterest(Money.nz(interest));
p.setStatus(status);
p.setCreatedAt(Instant.now());
return p;
}
private void seedPolicies() {
if (policyRepo.count() > 0) return;
FinancingPolicy p1 = new FinancingPolicy();
p1.setRuleName("单笔流动资金贷款额度上限");
p1.setRuleType("额度上限");
p1.setLimitValue(Money.of(100000000.0));
p1.setScope("全集团");
p1.setHardBlock(true);
p1.setDescription("集团融资政策:单笔流动资金贷款不超过 1 亿元,超限须报董事会审批。");
p1.setStatus("启用");
p1.setCreatedBy("admin");
p1.setCreatedAt(Instant.now());
policyRepo.save(p1);
FinancingPolicy p2 = new FinancingPolicy();
p2.setRuleName("融资综合利率上限");
p2.setRuleType("利率上限");
p2.setLimitValue(Money.of(6.0));
p2.setScope("全集团");
p2.setHardBlock(false);
p2.setDescription("集团融资政策:融资年化利率超过 6% 视为高成本融资,需在申请中说明替代方案比选结果,硬拒以警告为主不阻断。");
p2.setStatus("启用");
p2.setCreatedBy("admin");
p2.setCreatedAt(Instant.now());
policyRepo.save(p2);
FinancingPolicy p3 = new FinancingPolicy();
p3.setRuleName("流动资金贷款期限上限");
p3.setRuleType("期限上限");
p3.setLimitValue(Money.of(12.0));
p3.setScope("流动资金");
p3.setHardBlock(true);
p3.setDescription("流动资金贷款期限原则上不超过 12 个月(1年),超限需升级审批至财务总监。");
p3.setStatus("启用");
p3.setCreatedBy("admin");
p3.setCreatedAt(Instant.now());
policyRepo.save(p3);
FinancingPolicy p4 = new FinancingPolicy();
p4.setRuleName("允许融资品种限制");
p4.setRuleType("品种限制");
p4.setAllowedValues("银行贷款,融资租赁,银行承兑汇票,信用证");
p4.setScope("全集团");
p4.setHardBlock(true);
p4.setDescription("集团融资政策:仅允许银行贷款、融资租赁、银行承兑汇票、信用证四种融资品种,其他品种须专项论证并报批。");
p4.setStatus("启用");
p4.setCreatedBy("admin");
p4.setCreatedAt(Instant.now());
policyRepo.save(p4);
FinancingPolicy p5 = new FinancingPolicy();
p5.setRuleName("固定资产贷款期限上限");
p5.setRuleType("期限上限");
p5.setLimitValue(Money.of(120.0));
p5.setScope("固定资产投资");
p5.setHardBlock(true);
p5.setDescription("固定资产贷款期限不超过 10 年(120 个月),与项目建设及投资回收期匹配。");
p5.setStatus("启用");
p5.setCreatedBy("admin");
p5.setCreatedAt(Instant.now());
policyRepo.save(p5);
}
private void seedEvaluations() {
if (evalRepo.count() > 0) return;
// 需要 bankRelRepo 已有数据
java.util.List<PmtBankRelation> rels = bankRelRepo.findAll();
if (rels.isEmpty()) return;
Instant now = Instant.now();
for (PmtBankRelation rel : rels) {
// 每个机构建 2 条历史评价(2024Q4 和 2025Q2
PmtBankEvaluation ev1 = new PmtBankEvaluation();
ev1.setBankRelId(rel.getId());
ev1.setInstitutionName(rel.getInstitutionName());
ev1.setEvalPeriod("2024Q4");
ev1.setEvalDate("2024-12-31");
int s1a = baseScore(rel.getRating()) - 5;
int s1b = baseScore(rel.getRating());
int s1c = baseScore(rel.getRating()) + 3;
int s1d = baseScore(rel.getRating()) - 2;
ev1.setScoreApprovalSpeed(clamp(s1a));
ev1.setScoreServiceQuality(clamp(s1b));
ev1.setScoreRateCompetitiveness(clamp(s1c));
ev1.setScoreCooperation(clamp(s1d));
int total1 = (clamp(s1a) + clamp(s1b) + clamp(s1c) + clamp(s1d)) / 4;
ev1.setTotalScore(total1);
ev1.setGrade(grade(total1));
ev1.setRemark("2024年四季度综合评价,审批效率和服务质量符合预期。");
ev1.setEvaluator("admin");
ev1.setStatus("已完成");
ev1.setCreatedAt(now);
evalRepo.save(ev1);
PmtBankEvaluation ev2 = new PmtBankEvaluation();
ev2.setBankRelId(rel.getId());
ev2.setInstitutionName(rel.getInstitutionName());
ev2.setEvalPeriod("2025Q2");
ev2.setEvalDate("2025-06-30");
int s2a = baseScore(rel.getRating()) + 2;
int s2b = baseScore(rel.getRating()) + 1;
int s2c = baseScore(rel.getRating());
int s2d = baseScore(rel.getRating()) + 4;
ev2.setScoreApprovalSpeed(clamp(s2a));
ev2.setScoreServiceQuality(clamp(s2b));
ev2.setScoreRateCompetitiveness(clamp(s2c));
ev2.setScoreCooperation(clamp(s2d));
int total2 = (clamp(s2a) + clamp(s2b) + clamp(s2c) + clamp(s2d)) / 4;
ev2.setTotalScore(total2);
ev2.setGrade(grade(total2));
ev2.setRemark("2025年二季度综合评价,各维度较上期有所提升。");
ev2.setEvaluator("admin");
ev2.setStatus("已完成");
ev2.setCreatedAt(now);
evalRepo.save(ev2);
}
}
private static int baseScore(String rating) {
if ("A".equals(rating)) return 85;
if ("B".equals(rating)) return 70;
if ("C".equals(rating)) return 55;
return 40;
}
private static int clamp(int v) {
return Math.max(0, Math.min(100, v));
}
private static String grade(int total) {
if (total >= 80) return "A";
if (total >= 60) return "B";
if (total >= 40) return "C";
return "D";
}
}