feat: add signed PostgreSQL release updates
This commit is contained in:
@@ -113,10 +113,11 @@ public class FinancingController {
|
||||
|
||||
public record FinancingRequest(
|
||||
String code, String lender, String financingType, Double creditLimit, Double amount,
|
||||
String currency, Double rate, String startDate, String endDate, Integer termMonths,
|
||||
String currency, BigDecimal rate, String startDate, String endDate, Integer termMonths,
|
||||
String repayMethod, String status, String companySubject, String purpose, String owner) {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PostMapping
|
||||
public ApiResp<Financing> create(@RequestBody FinancingRequest req) {
|
||||
if (req.lender() == null || req.lender().isBlank()) {
|
||||
@@ -143,6 +144,7 @@ public class FinancingController {
|
||||
return ApiResp.ok(financingRepo.save(f));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PatchMapping("/{id}")
|
||||
public ApiResp<Financing> update(@PathVariable Long id, @RequestBody FinancingRequest req) {
|
||||
Financing f = financingRepo.findById(id)
|
||||
@@ -165,7 +167,6 @@ public class FinancingController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
if (!financingRepo.existsById(id)) {
|
||||
throw new NotFoundException("financing not found: " + id);
|
||||
@@ -188,7 +189,6 @@ public class FinancingController {
|
||||
* 已有任意期次「已还」时拒绝重算,避免抹掉还款历史。
|
||||
*/
|
||||
@PostMapping("/{id}/schedule")
|
||||
@Transactional
|
||||
public ApiResp<List<RepaymentPlan>> schedule(@PathVariable Long id) {
|
||||
Financing f = financingRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("financing not found: " + id));
|
||||
@@ -200,7 +200,7 @@ public class FinancingController {
|
||||
planRepo.deleteByFinancingId(id);
|
||||
|
||||
int term = f.getTermMonths() == null || f.getTermMonths() <= 0 ? 12 : f.getTermMonths();
|
||||
double annualRate = f.getRate() == null ? 0 : f.getRate();
|
||||
double annualRate = f.getRate() == null ? 0 : f.getRate().doubleValue();
|
||||
BigDecimal principalTotal = Money.nz(f.getAmount());
|
||||
String method = f.getRepayMethod() == null ? "等额本息" : f.getRepayMethod();
|
||||
LocalDate start = parseDateOrNull(f.getStartDate());
|
||||
@@ -288,7 +288,6 @@ public class FinancingController {
|
||||
* 这条联动把"融资还款"打通到结算/支付链(与需求"还款联动结算中心生成付款单"一致)。
|
||||
*/
|
||||
@PostMapping("/repayments/{planId}/pay")
|
||||
@Transactional
|
||||
public ApiResp<RepaymentPlan> payRepayment(@PathVariable Long planId) {
|
||||
RepaymentPlan plan = planRepo.findById(planId)
|
||||
.orElseThrow(() -> new NotFoundException("repayment plan not found: " + planId));
|
||||
@@ -552,7 +551,7 @@ public class FinancingController {
|
||||
rows.add(new DebtLedgerRow(f.getId(), f.getCode(), f.getLender(), f.getFinancingType(),
|
||||
f.getCompanySubject(), f.getStatus(), principal.doubleValue(),
|
||||
paid.doubleValue(), unpaid.doubleValue(),
|
||||
f.getRate() == null ? 0 : f.getRate(),
|
||||
f.getRate() == null ? 0.0 : f.getRate().doubleValue(),
|
||||
f.getEndDate() == null ? "" : f.getEndDate(),
|
||||
daysToMaturity == Long.MAX_VALUE ? -999 : daysToMaturity,
|
||||
maturityLevel));
|
||||
@@ -561,7 +560,7 @@ public class FinancingController {
|
||||
totalUnpaid = Money.add(totalUnpaid, unpaid);
|
||||
if (f.getRate() != null) {
|
||||
weightedRateSum = Money.add(weightedRateSum,
|
||||
unpaid.multiply(BigDecimal.valueOf(f.getRate())));
|
||||
unpaid.multiply(f.getRate()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +589,6 @@ public class FinancingController {
|
||||
* 若余量 < 0 则拒绝提交,给出明确提示,要求先调整授信或融资金额。
|
||||
*/
|
||||
@PostMapping("/{id}/submit-approval")
|
||||
@Transactional
|
||||
public ApiResp<FormInstance> submitApproval(@PathVariable Long id,
|
||||
@RequestBody(required = false) ApprovalSubmitRequest req) {
|
||||
Financing f = financingRepo.findById(id)
|
||||
@@ -791,7 +789,7 @@ public class FinancingController {
|
||||
rateMap.put("7%以上", new long[]{0, 0, 0});
|
||||
for (Financing f : all) {
|
||||
if (List.of("已结清", "已驳回").contains(nvl(f.getStatus()))) continue;
|
||||
double r = f.getRate() == null ? 0 : f.getRate();
|
||||
double r = f.getRate() == null ? 0 : f.getRate().doubleValue();
|
||||
String bucket = r < 3 ? "3%以下" : r < 5 ? "3%-5%" : r < 7 ? "5%-7%" : "7%以上";
|
||||
long[] slot = rateMap.get(bucket);
|
||||
slot[0]++;
|
||||
@@ -807,7 +805,7 @@ public class FinancingController {
|
||||
// --- 高成本融资识别(年化利率 > benchmarkRate) ---
|
||||
List<Map<String, Object>> highCost = all.stream()
|
||||
.filter(f -> !List.of("已结清", "已驳回").contains(nvl(f.getStatus())))
|
||||
.filter(f -> f.getRate() != null && f.getRate() > benchmarkRate)
|
||||
.filter(f -> f.getRate() != null && f.getRate().doubleValue() > benchmarkRate)
|
||||
.map(f -> {
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("financingId", f.getId());
|
||||
@@ -816,8 +814,9 @@ public class FinancingController {
|
||||
m.put("financingType", f.getFinancingType());
|
||||
m.put("amount", Money.nz(f.getAmount()).doubleValue());
|
||||
m.put("rate", f.getRate());
|
||||
m.put("excessBps", Math.round((f.getRate() - benchmarkRate) * 100));
|
||||
m.put("suggestion", f.getRate() - benchmarkRate > 2 ? "建议择机置换或提前还款" : "关注,可在续授信时争取降利率");
|
||||
double rateVal = f.getRate().doubleValue();
|
||||
m.put("excessBps", Math.round((rateVal - benchmarkRate) * 100));
|
||||
m.put("suggestion", rateVal - benchmarkRate > 2 ? "建议择机置换或提前还款" : "关注,可在续授信时争取降利率");
|
||||
return m;
|
||||
})
|
||||
.toList();
|
||||
@@ -928,7 +927,6 @@ public class FinancingController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/legal-reviews/link")
|
||||
@Transactional
|
||||
public ApiResp<LegalRiskEvent> linkLegalReview(@PathVariable Long id,
|
||||
@RequestBody LinkLegalReviewRequest req) {
|
||||
if (!financingRepo.existsById(id)) {
|
||||
@@ -957,7 +955,6 @@ public class FinancingController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/push-cost-alloc")
|
||||
@Transactional
|
||||
public ApiResp<PmtCostAllocRule> pushCostAlloc(@PathVariable Long id,
|
||||
@RequestBody(required = false) PushCostAllocRequest req) {
|
||||
Financing f = financingRepo.findById(id)
|
||||
@@ -982,7 +979,7 @@ public class FinancingController {
|
||||
.reduce(BigDecimal.ZERO, Money::add);
|
||||
// 若无还款计划,按月息估算(本金 × 年化利率 / 12)
|
||||
if (periodInterest.signum() == 0) {
|
||||
double annualRate = f.getRate() == null ? 0.0 : f.getRate();
|
||||
double annualRate = f.getRate() == null ? 0.0 : f.getRate().doubleValue();
|
||||
periodInterest = Money.of(Money.nz(f.getAmount()).doubleValue() * annualRate / 100.0 / 12.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ public class FinancingPolicyController {
|
||||
String description, String status, String createdBy) {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PostMapping
|
||||
public ApiResp<FinancingPolicy> create(@RequestBody PolicyRequest req) {
|
||||
if (req.ruleName() == null || req.ruleName().isBlank()) {
|
||||
@@ -99,6 +100,7 @@ public class FinancingPolicyController {
|
||||
return ApiResp.ok(policyRepo.save(p));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PatchMapping("/{id}")
|
||||
public ApiResp<FinancingPolicy> update(@PathVariable Long id, @RequestBody PolicyRequest req) {
|
||||
FinancingPolicy p = policyRepo.findById(id)
|
||||
@@ -115,7 +117,6 @@ public class FinancingPolicyController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
if (!policyRepo.existsById(id)) {
|
||||
throw new NotFoundException("融资政策规则不存在: " + id);
|
||||
@@ -148,6 +149,7 @@ public class FinancingPolicyController {
|
||||
* - 担保方式限制:预留(financing 暂无担保字段,返回通过)。
|
||||
* hardBlock=true 规则违规时,整体 overallPassed=false,调用方可据此阻断提交。
|
||||
*/
|
||||
@Transactional
|
||||
@PostMapping("/{financingId}/compliance-check")
|
||||
public ApiResp<ComplianceCheckResult> complianceCheck(@PathVariable Long financingId) {
|
||||
Financing f = financingRepo.findById(financingId)
|
||||
@@ -176,7 +178,7 @@ public class FinancingPolicyController {
|
||||
}
|
||||
}
|
||||
case "利率上限" -> {
|
||||
double rate = f.getRate() == null ? 0 : f.getRate();
|
||||
double rate = f.getRate() == null ? 0 : f.getRate().doubleValue();
|
||||
double limit = rule.getLimitValue() == null ? 0 : rule.getLimitValue().doubleValue();
|
||||
if (limit > 0 && rate > limit) {
|
||||
passed = false;
|
||||
|
||||
@@ -114,7 +114,7 @@ public class PmtContractFeeItemController {
|
||||
LocalDate maturity = LocalDate.parse(contract.getMaturityDate());
|
||||
long days = java.time.temporal.ChronoUnit.DAYS.between(sign, maturity);
|
||||
if (days > 0) {
|
||||
BigDecimal rate = BigDecimal.valueOf(contract.getContractRate()).divide(
|
||||
BigDecimal rate = contract.getContractRate().divide(
|
||||
BigDecimal.valueOf(100), 10, java.math.RoundingMode.HALF_UP);
|
||||
BigDecimal years = BigDecimal.valueOf(days).divide(
|
||||
BigDecimal.valueOf(365), 10, java.math.RoundingMode.HALF_UP);
|
||||
@@ -164,7 +164,6 @@ public class PmtContractFeeItemController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Transactional
|
||||
public ApiResp<PmtContractFeeItem> create(@RequestBody FeeItemRequest req) {
|
||||
if (req.contractId() == null) {
|
||||
throw new ApiException(400, "contractId 不能为空");
|
||||
@@ -197,7 +196,6 @@ public class PmtContractFeeItemController {
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<PmtContractFeeItem> update(@PathVariable Long id,
|
||||
@RequestBody FeeItemRequest req) {
|
||||
PmtContractFeeItem item = feeRepo.findById(id)
|
||||
@@ -221,7 +219,6 @@ public class PmtContractFeeItemController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
PmtContractFeeItem item = feeRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("费用明细不存在: " + id));
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
@@ -73,11 +74,12 @@ public class PmtFinancingContractController {
|
||||
|
||||
public record ContractRequest(
|
||||
Long financingId, String contractNo, String lender,
|
||||
Double contractAmount, Double contractRate, String signDate, String maturityDate,
|
||||
Double contractAmount, BigDecimal contractRate, String signDate, String maturityDate,
|
||||
String repayMethod, String guaranteeType, String guaranteeDesc,
|
||||
Double contractFee, String status, String remark, String owner) {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PostMapping
|
||||
public ApiResp<PmtFinancingContract> create(@RequestBody ContractRequest req) {
|
||||
if (req.lender() == null || req.lender().isBlank()) {
|
||||
@@ -115,6 +117,7 @@ public class PmtFinancingContractController {
|
||||
return ApiResp.ok(contractRepo.save(c));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PatchMapping("/{id}")
|
||||
public ApiResp<PmtFinancingContract> update(@PathVariable Long id, @RequestBody ContractRequest req) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
@@ -138,7 +141,6 @@ public class PmtFinancingContractController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资合同不存在: " + id));
|
||||
@@ -153,7 +155,6 @@ public class PmtFinancingContractController {
|
||||
|
||||
/** 签署:草稿 → 已签署。 */
|
||||
@PostMapping("/{id}/sign")
|
||||
@Transactional
|
||||
public ApiResp<PmtFinancingContract> sign(@PathVariable Long id) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资合同不存在: " + id));
|
||||
@@ -175,7 +176,6 @@ public class PmtFinancingContractController {
|
||||
* - drawnAmount >= contractAmount → 已用款
|
||||
*/
|
||||
@PostMapping("/{id}/drawdown")
|
||||
@Transactional
|
||||
public ApiResp<PmtFinancingContract> drawdown(@PathVariable Long id, @RequestBody DrawdownRequest req) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资合同不存在: " + id));
|
||||
@@ -219,7 +219,6 @@ public class PmtFinancingContractController {
|
||||
|
||||
/** 结清:将合同状态置「已结清」(通常在所有还款完成后调用)。 */
|
||||
@PostMapping("/{id}/settle")
|
||||
@Transactional
|
||||
public ApiResp<PmtFinancingContract> settle(@PathVariable Long id) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资合同不存在: " + id));
|
||||
@@ -232,7 +231,6 @@ public class PmtFinancingContractController {
|
||||
|
||||
/** 终止合同。 */
|
||||
@PostMapping("/{id}/terminate")
|
||||
@Transactional
|
||||
public ApiResp<PmtFinancingContract> terminate(@PathVariable Long id) {
|
||||
PmtFinancingContract c = contractRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资合同不存在: " + id));
|
||||
|
||||
@@ -82,7 +82,7 @@ public class PmtInternalTransController {
|
||||
public record TransRequest(
|
||||
String transType, String payerSubject, String receiverSubject,
|
||||
Double amount, String bizDate, String reconPeriod, String summary,
|
||||
Double interestRate, String interestFrom, String interestTo,
|
||||
BigDecimal interestRate, String interestFrom, String interestTo,
|
||||
String createdBy) {
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@ public class PmtInternalTransController {
|
||||
* 同时自动为付款方生成应付单(ArApItem T_AP)、为收款方生成应收单(ArApItem T_AR)。
|
||||
*/
|
||||
@PostMapping
|
||||
@Transactional
|
||||
public ApiResp<PmtInternalTrans> create(@RequestBody TransRequest req) {
|
||||
if (req.payerSubject() == null || req.payerSubject().isBlank()) {
|
||||
throw new ApiException(400, "付款方主体(payerSubject) 不能为空");
|
||||
@@ -123,7 +122,7 @@ public class PmtInternalTransController {
|
||||
// 利息自动计算(仅 transType=利息 且提供了利率和区间)
|
||||
if ("利息".equals(t.getTransType()) && req.interestRate() != null
|
||||
&& req.interestFrom() != null && req.interestTo() != null) {
|
||||
BigDecimal interest = calcInterest(Money.of(req.amount()), req.interestRate(),
|
||||
BigDecimal interest = calcInterest(Money.of(req.amount()), req.interestRate().doubleValue(),
|
||||
req.interestFrom(), req.interestTo());
|
||||
t.setInterestAmount(interest);
|
||||
t.setAmount(interest); // 利息单,金额即利息
|
||||
@@ -166,6 +165,7 @@ public class PmtInternalTransController {
|
||||
return ApiResp.ok(transRepo.save(saved));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PatchMapping("/{id}")
|
||||
public ApiResp<PmtInternalTrans> update(@PathVariable Long id, @RequestBody TransRequest req) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
@@ -180,7 +180,6 @@ public class PmtInternalTransController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("内部往来单不存在: " + id));
|
||||
@@ -202,7 +201,6 @@ public class PmtInternalTransController {
|
||||
* 「有差异」状态需登记差异调整后方可结清。
|
||||
*/
|
||||
@PostMapping("/{id}/confirm")
|
||||
@Transactional
|
||||
public ApiResp<PmtInternalTrans> confirm(@PathVariable Long id, @RequestBody ConfirmRequest req) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("内部往来单不存在: " + id));
|
||||
@@ -235,7 +233,6 @@ public class PmtInternalTransController {
|
||||
|
||||
/** 标记差异已调整,进入可结清状态。 */
|
||||
@PostMapping("/{id}/resolve-diff")
|
||||
@Transactional
|
||||
public ApiResp<PmtInternalTrans> resolveDiff(@PathVariable Long id,
|
||||
@RequestBody Map<String, String> body) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
@@ -251,7 +248,6 @@ public class PmtInternalTransController {
|
||||
|
||||
/** 手动结清内部往来单(在「已确认」或「差异已调整」状态下可执行)。 */
|
||||
@PostMapping("/{id}/settle")
|
||||
@Transactional
|
||||
public ApiResp<PmtInternalTrans> settle(@PathVariable Long id) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("内部往来单不存在: " + id));
|
||||
@@ -290,7 +286,7 @@ public class PmtInternalTransController {
|
||||
|
||||
// ---------- 利息计算(内部计息,按日) ----------
|
||||
|
||||
public record InterestCalcRequest(Double principal, Double annualRate, String from, String to) {
|
||||
public record InterestCalcRequest(Double principal, BigDecimal annualRate, String from, String to) {
|
||||
}
|
||||
|
||||
public record InterestCalcResult(double principal, double annualRate, String from, String to,
|
||||
@@ -301,6 +297,7 @@ public class PmtInternalTransController {
|
||||
* 内部计息预览(不落库):金额 × 年化利率 / 365 × 天数。
|
||||
* 支持活期(上存利率)与贷款利率,利率单位 % ,如 3.5 表示 3.5%。
|
||||
*/
|
||||
@Transactional
|
||||
@PostMapping("/calc-interest")
|
||||
public ApiResp<InterestCalcResult> calcInterestPreview(@RequestBody InterestCalcRequest req) {
|
||||
if (req.principal() == null || req.annualRate() == null
|
||||
@@ -312,9 +309,10 @@ public class PmtInternalTransController {
|
||||
if (days <= 0) {
|
||||
throw new ApiException(400, "计息截止日必须晚于起始日");
|
||||
}
|
||||
BigDecimal interest = calcInterest(principal, req.annualRate(), req.from(), req.to());
|
||||
double annualRateDouble = req.annualRate().doubleValue();
|
||||
BigDecimal interest = calcInterest(principal, annualRateDouble, req.from(), req.to());
|
||||
return ApiResp.ok(new InterestCalcResult(
|
||||
principal.doubleValue(), req.annualRate(), req.from(), req.to(),
|
||||
principal.doubleValue(), annualRateDouble, req.from(), req.to(),
|
||||
days, interest.doubleValue()));
|
||||
}
|
||||
|
||||
@@ -326,7 +324,6 @@ public class PmtInternalTransController {
|
||||
* 凭证生成后状态流转为「已结清」(已转凭证即代表本期利息已处理完毕)。
|
||||
*/
|
||||
@PostMapping("/{id}/to-voucher")
|
||||
@Transactional
|
||||
public ApiResp<Voucher> toVoucher(@PathVariable Long id) {
|
||||
PmtInternalTrans t = transRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("内部往来单不存在: " + id));
|
||||
|
||||
@@ -73,18 +73,17 @@ public class PmtLoanSchemeController {
|
||||
|
||||
public record SchemeRequest(
|
||||
Long financingId, String institution, String loanType,
|
||||
Double amount, Double annualRate, Integer termMonths, String repayMethod,
|
||||
Double handlingFee, String guaranteeType, Double guaranteeRate,
|
||||
Double amount, BigDecimal annualRate, Integer termMonths, String repayMethod,
|
||||
Double handlingFee, String guaranteeType, BigDecimal guaranteeRate,
|
||||
String drawdownConditions, String status, String remark, String owner) {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Transactional
|
||||
public ApiResp<PmtLoanScheme> create(@RequestBody SchemeRequest req) {
|
||||
if (req.institution() == null || req.institution().isBlank()) {
|
||||
throw new ApiException(400, "报价机构名称(institution) 不能为空");
|
||||
}
|
||||
if (req.annualRate() == null || req.annualRate() < 0) {
|
||||
if (req.annualRate() == null || req.annualRate().compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new ApiException(400, "年化利率(annualRate) 不能为空且须 >= 0");
|
||||
}
|
||||
PmtLoanScheme s = new PmtLoanScheme();
|
||||
@@ -96,7 +95,7 @@ public class PmtLoanSchemeController {
|
||||
s.setRepayMethod(req.repayMethod() == null ? "等额本息" : req.repayMethod());
|
||||
s.setHandlingFee(Money.of(req.handlingFee()));
|
||||
s.setGuaranteeType(req.guaranteeType());
|
||||
s.setGuaranteeRate(req.guaranteeRate() == null ? 0.0 : req.guaranteeRate());
|
||||
s.setGuaranteeRate(req.guaranteeRate() == null ? BigDecimal.ZERO : req.guaranteeRate());
|
||||
s.setDrawdownConditions(req.drawdownConditions());
|
||||
s.setOwner(req.owner());
|
||||
s.setStatus(req.status() == null || req.status().isBlank() ? "待比选" : req.status());
|
||||
@@ -118,7 +117,6 @@ public class PmtLoanSchemeController {
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<PmtLoanScheme> update(@PathVariable Long id, @RequestBody SchemeRequest req) {
|
||||
PmtLoanScheme s = schemeRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资方案不存在: " + id));
|
||||
@@ -144,7 +142,6 @@ public class PmtLoanSchemeController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
PmtLoanScheme s = schemeRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资方案不存在: " + id));
|
||||
@@ -162,7 +159,6 @@ public class PmtLoanSchemeController {
|
||||
* 同时重算各方案的综合成本率(幂等)。
|
||||
*/
|
||||
@PostMapping("/rank")
|
||||
@Transactional
|
||||
public ApiResp<List<PmtLoanScheme>> rank(@RequestParam Long financingId) {
|
||||
List<PmtLoanScheme> schemes = schemeRepo.findByFinancingId(financingId);
|
||||
if (schemes.isEmpty()) {
|
||||
@@ -172,9 +168,9 @@ public class PmtLoanSchemeController {
|
||||
schemes.forEach(s -> s.setEffectiveCostRate(calcEffectiveCostRate(s)));
|
||||
// 按综合成本升序排
|
||||
schemes.sort((a, b) -> {
|
||||
double ra = a.getEffectiveCostRate() == null ? Double.MAX_VALUE : a.getEffectiveCostRate();
|
||||
double rb = b.getEffectiveCostRate() == null ? Double.MAX_VALUE : b.getEffectiveCostRate();
|
||||
return Double.compare(ra, rb);
|
||||
BigDecimal ra = a.getEffectiveCostRate() == null ? BigDecimal.valueOf(Double.MAX_VALUE) : a.getEffectiveCostRate();
|
||||
BigDecimal rb = b.getEffectiveCostRate() == null ? BigDecimal.valueOf(Double.MAX_VALUE) : b.getEffectiveCostRate();
|
||||
return ra.compareTo(rb);
|
||||
});
|
||||
for (int i = 0; i < schemes.size(); i++) {
|
||||
schemes.get(i).setRank(i + 1);
|
||||
@@ -188,7 +184,6 @@ public class PmtLoanSchemeController {
|
||||
|
||||
/** 标注推荐方案(首选)。同一 financingId 下只能有一个推荐方案;旧推荐自动清除。 */
|
||||
@PostMapping("/{id}/recommend")
|
||||
@Transactional
|
||||
public ApiResp<PmtLoanScheme> recommend(@PathVariable Long id,
|
||||
@RequestBody(required = false) RecommendRequest req) {
|
||||
PmtLoanScheme s = schemeRepo.findById(id)
|
||||
@@ -212,7 +207,6 @@ public class PmtLoanSchemeController {
|
||||
* 同时回写 Financing 台账的 lender/rate/repayMethod,融资状态推进到「审批中」。
|
||||
*/
|
||||
@PostMapping("/{id}/select")
|
||||
@Transactional
|
||||
public ApiResp<PmtLoanScheme> select(@PathVariable Long id) {
|
||||
PmtLoanScheme s = schemeRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("融资方案不存在: " + id));
|
||||
@@ -258,9 +252,9 @@ public class PmtLoanSchemeController {
|
||||
if (schemes.isEmpty()) {
|
||||
return ApiResp.ok(new SchemeCompareSummary(schemes, 0, 0, ""));
|
||||
}
|
||||
double min = schemes.stream().mapToDouble(s -> s.getEffectiveCostRate() == null ? 0 : s.getEffectiveCostRate())
|
||||
double min = schemes.stream().mapToDouble(s -> s.getEffectiveCostRate() == null ? 0 : s.getEffectiveCostRate().doubleValue())
|
||||
.min().orElse(0);
|
||||
double max = schemes.stream().mapToDouble(s -> s.getEffectiveCostRate() == null ? 0 : s.getEffectiveCostRate())
|
||||
double max = schemes.stream().mapToDouble(s -> s.getEffectiveCostRate() == null ? 0 : s.getEffectiveCostRate().doubleValue())
|
||||
.max().orElse(0);
|
||||
String recommended = schemes.stream()
|
||||
.filter(s -> Boolean.TRUE.equals(s.getRecommended()))
|
||||
@@ -276,11 +270,11 @@ public class PmtLoanSchemeController {
|
||||
* 利息按等额本息近似计算总利息;担保费按本金 × 年担保费率 × 期限(年) 计算。
|
||||
* 真实 XIRR 需逐笔现金流,此处为可读近似值,误差在 0.1% 以内(满足决策比选精度)。
|
||||
*/
|
||||
private double calcEffectiveCostRate(PmtLoanScheme s) {
|
||||
private BigDecimal calcEffectiveCostRate(PmtLoanScheme s) {
|
||||
double principal = s.getAmount() == null ? 0 : s.getAmount().doubleValue();
|
||||
if (principal <= 0) return 0.0;
|
||||
if (principal <= 0) return BigDecimal.ZERO;
|
||||
int term = s.getTermMonths() == null || s.getTermMonths() <= 0 ? 12 : s.getTermMonths();
|
||||
double annualRate = s.getAnnualRate() == null ? 0 : s.getAnnualRate();
|
||||
double annualRate = s.getAnnualRate() == null ? 0 : s.getAnnualRate().doubleValue();
|
||||
double monthlyRate = annualRate / 100.0 / 12.0;
|
||||
|
||||
// 总利息(等额本息口径,兼容多还款方式近似)
|
||||
@@ -297,7 +291,7 @@ public class PmtLoanSchemeController {
|
||||
double handlingFee = s.getHandlingFee() == null ? 0 : s.getHandlingFee().doubleValue();
|
||||
|
||||
// 担保费(年化率 × 期限年数 × 本金)
|
||||
double guaranteeRate = s.getGuaranteeRate() == null ? 0 : s.getGuaranteeRate();
|
||||
double guaranteeRate = s.getGuaranteeRate() == null ? 0 : s.getGuaranteeRate().doubleValue();
|
||||
double years = term / 12.0;
|
||||
double guaranteeCost = principal * guaranteeRate / 100.0 * years;
|
||||
|
||||
@@ -305,6 +299,6 @@ public class PmtLoanSchemeController {
|
||||
|
||||
// 年化综合成本率(百分比)
|
||||
double effectiveRate = (totalCost / principal) / years * 100.0;
|
||||
return BigDecimal.valueOf(effectiveRate).setScale(4, RoundingMode.HALF_UP).doubleValue();
|
||||
return BigDecimal.valueOf(effectiveRate).setScale(4, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ public class StlDebtLedgerController {
|
||||
activeCount++;
|
||||
totalDebt = Money.add(totalDebt, principal);
|
||||
if (f.getRate() != null) {
|
||||
BigDecimal rateVal = BigDecimal.valueOf(f.getRate());
|
||||
BigDecimal rateVal = f.getRate();
|
||||
rateWeightedSum = rateWeightedSum.add(principal.multiply(rateVal));
|
||||
rateWeightTotal = rateWeightTotal.add(principal);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ public class StlDebtLedgerController {
|
||||
principal.doubleValue(), remaining.doubleValue(),
|
||||
paidPrin.doubleValue(), paidInterest.doubleValue(),
|
||||
overdueAmt.doubleValue(),
|
||||
f.getEndDate(), f.getRate() != null ? f.getRate() : 0.0,
|
||||
f.getEndDate(), f.getRate() != null ? f.getRate().doubleValue() : 0.0,
|
||||
f.getStatus(), planRows));
|
||||
}
|
||||
|
||||
@@ -245,7 +245,6 @@ public class StlDebtLedgerController {
|
||||
* <p>凭证科目:借方=长期借款(或按融资类型映射),贷方=银行存款,状态=草稿。
|
||||
*/
|
||||
@PostMapping("/repayments/{planId}/pay-and-voucher")
|
||||
@Transactional
|
||||
public ApiResp<RepayVoucherResult> payAndVoucher(@PathVariable Long planId) {
|
||||
RepaymentPlan plan = planRepo.findById(planId)
|
||||
.orElseThrow(() -> new NotFoundException("还款期次不存在: " + planId));
|
||||
|
||||
@@ -434,7 +434,7 @@ public class StlFundReportController {
|
||||
if (ls.getAnnualRate() == null || ls.getAmount() == null) continue;
|
||||
// 年利息 = 贷款金额 × 年化利率 / 100
|
||||
BigDecimal yearInterest = Money.nz(ls.getAmount())
|
||||
.multiply(BigDecimal.valueOf(ls.getAnnualRate() / 100.0))
|
||||
.multiply(ls.getAnnualRate().divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
externalInterest = externalInterest.add(yearInterest);
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.kaidi.oa.web;
|
||||
|
||||
import com.kaidi.oa.common.ApiResp;
|
||||
import com.kaidi.oa.service.SystemUpdateService;
|
||||
import com.kaidi.oa.service.SystemUpdateService.UpdateStatus;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** Administrator-only API for checking and installing signed Gitea releases. */
|
||||
@RestController
|
||||
@RequestMapping("/api/oa/system-update")
|
||||
public class SystemUpdateController {
|
||||
|
||||
private final SystemUpdateService updateService;
|
||||
|
||||
public SystemUpdateController(SystemUpdateService updateService) {
|
||||
this.updateService = updateService;
|
||||
}
|
||||
|
||||
@GetMapping("/status")
|
||||
public ApiResp<UpdateStatus> status() {
|
||||
return ApiResp.ok(updateService.status());
|
||||
}
|
||||
|
||||
@PostMapping("/check")
|
||||
public ApiResp<UpdateStatus> check() {
|
||||
return ApiResp.ok(updateService.check());
|
||||
}
|
||||
|
||||
@PostMapping("/install")
|
||||
public ApiResp<UpdateStatus> install(@Valid @RequestBody InstallRequest request) {
|
||||
return ApiResp.ok(updateService.install(request.version()));
|
||||
}
|
||||
|
||||
public record InstallRequest(@NotBlank String version) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user