init
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package com.ruoyi.loanratepricing.controller;
|
||||
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.loanratepricing.domain.dto.FinalRateAdjustDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.FinalRateSubmitDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.OptInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.entity.LoanPricingApply;
|
||||
import com.ruoyi.loanratepricing.domain.vo.FinalRateAdjustVO;
|
||||
import com.ruoyi.loanratepricing.service.RatePricingService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/11/10
|
||||
**/
|
||||
@Api(tags = "莲都利率测算")
|
||||
@RestController
|
||||
@RequestMapping("/rate/pricing")
|
||||
public class LoanRatePricingController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private RatePricingService ratePricingService;
|
||||
|
||||
@ApiOperation("发起利率定价申请,或加载利率定价申请")
|
||||
@Anonymous
|
||||
@PostMapping("/load")
|
||||
public R<LoanPricingApply> loadRatePricingApply(@RequestBody OptInvokeDTO optInvokeDTO) {
|
||||
return R.ok(ratePricingService.loadRatePricingApply(optInvokeDTO));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("测算最终利率")
|
||||
@PostMapping("/invoke")
|
||||
public R<FinalRateAdjustVO> invokeFinalRate(@RequestBody FinalRateAdjustDTO finalRateAdjustDTO) {
|
||||
return R.ok(ratePricingService.invokeFinalRate(finalRateAdjustDTO));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("提交利率定价申请")
|
||||
@PostMapping("/submit")
|
||||
public R<String> submitFinalRate(@RequestBody FinalRateSubmitDTO finalRateSubmitDTO) {
|
||||
return R.ok(ratePricingService.submitFinalRate(finalRateSubmitDTO));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.ruoyi.loanratepricing.controller;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.loanratepricing.domain.entity.LoanPricingApply;
|
||||
import com.ruoyi.loanratepricing.service.ModelService;
|
||||
import com.ruoyi.loanratepricing.service.OptService;
|
||||
import com.ruoyi.loanratepricing.service.RatePricingService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/11/10
|
||||
**/
|
||||
@Api(tags = "莲都利率测算测试接口")
|
||||
@RestController
|
||||
@RequestMapping("/rate/pricing/mock")
|
||||
public class LoanRatePricingMockController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private RatePricingService ratePricingService;
|
||||
|
||||
@Resource
|
||||
private OptService optService;
|
||||
|
||||
@Resource
|
||||
private ModelService modelService;
|
||||
|
||||
@ApiOperation("发起利率定价流程")
|
||||
@Anonymous
|
||||
@PostMapping("/submit/application")
|
||||
public AjaxResult submitApplication() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
ObjectNode objectNode = objectMapper.createObjectNode();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
String formattedDateTime = now.format(formatter);
|
||||
objectNode.put("applicationId", Long.parseLong(formattedDateTime));
|
||||
objectNode.put("midEntEleDdc", "0");
|
||||
objectNode.put("midEntWaterDdc", "0");
|
||||
objectNode.put("midPerEleDdc", "1");
|
||||
objectNode.put("midPerQuickPay", "1");
|
||||
return new AjaxResult(0, "success", objectNode);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("提交利率测算结果")
|
||||
@PostMapping("/submit/result")
|
||||
public AjaxResult submitRatePricingResult(@RequestBody LoanPricingApply loanPricingApply) {
|
||||
return new AjaxResult(0, "success", null);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("查询议价池")
|
||||
@GetMapping("/pool/balance")
|
||||
public AjaxResult queryPoolBalance(@RequestParam String ownerId) {
|
||||
ObjectNode jsonNodes = loadJsonFromResource("data/pool_balance.json");
|
||||
return new AjaxResult(0, "success", jsonNodes);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("查询利率测算记录")
|
||||
@GetMapping("/application")
|
||||
public AjaxResult queryApplication(@RequestParam Long id) {
|
||||
LoanPricingApply loanPricingApply = ratePricingService.queryLoanPricingApplyFromRedis(id);
|
||||
return new AjaxResult(0, "success", loanPricingApply);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("调用模型获取测算利率")
|
||||
@PostMapping("/invokeModel")
|
||||
public AjaxResult invokeModel() {
|
||||
ObjectNode jsonNodes = loadJsonFromResource("data/model_output.json");
|
||||
return new AjaxResult(10000, "success", jsonNodes);
|
||||
}
|
||||
|
||||
private ObjectNode loadJsonFromResource(String resourcePath){
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourcePath);
|
||||
try (InputStream inputStream = classPathResource.getInputStream();){
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(inputStream, ObjectNode.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.ruoyi.loanratepricing.controller;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.loanratepricing.domain.dto.ModelInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.OptInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.entity.LoanPricingApply;
|
||||
import com.ruoyi.loanratepricing.domain.entity.ModelOutputFields;
|
||||
import com.ruoyi.loanratepricing.domain.vo.OptPoolBalance;
|
||||
import com.ruoyi.loanratepricing.service.ModelService;
|
||||
import com.ruoyi.loanratepricing.service.OptService;
|
||||
import com.ruoyi.loanratepricing.service.RatePricingService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/11/10
|
||||
**/
|
||||
@Api(tags = "莲都利率测算接口测试")
|
||||
@RestController
|
||||
@RequestMapping("/rate/pricing/opt")
|
||||
public class LoanRatePricingTestController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private RatePricingService ratePricingService;
|
||||
|
||||
@Resource
|
||||
private OptService optService;
|
||||
|
||||
@Resource
|
||||
private ModelService modelService;
|
||||
|
||||
@ApiOperation("发起利率定价流程")
|
||||
@Anonymous
|
||||
@PostMapping("/submit/application")
|
||||
public R<JSONObject> submitApplication(@RequestBody OptInvokeDTO optInvokeDTO) {
|
||||
return R.ok(optService.submitApplication(optInvokeDTO));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("提交利率测算结果")
|
||||
@PostMapping("/submit/result")
|
||||
public R<String> submitRatePricingResult(@RequestBody LoanPricingApply loanPricingApply) {
|
||||
return R.ok(optService.submitRatePricingResult(loanPricingApply));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("查询议价池")
|
||||
@GetMapping("/pool/balance")
|
||||
public R<OptPoolBalance> queryPoolBalance(@RequestParam String userCode) {
|
||||
return R.ok(optService.queryPoolBalance(userCode));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("查询利率测算记录")
|
||||
@GetMapping("/application")
|
||||
public R<LoanPricingApply> queryApplication(@RequestParam String id) {
|
||||
return R.ok(optService.queryLoanPricingApply(id));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@ApiOperation("调用模型获取测算利率")
|
||||
@PostMapping("/invokeModel")
|
||||
public R<ModelOutputFields> invokeModel(@RequestBody ModelInvokeDTO modelInvokeDTO) {
|
||||
return R.ok(modelService.invokeModel(modelInvokeDTO));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ruoyi.loanratepricing.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/23
|
||||
**/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "rate-pricing")
|
||||
public class RatePricingProperties {
|
||||
|
||||
private String oaUrl;
|
||||
|
||||
private String modelUrl;
|
||||
|
||||
private String applicationSubmitUrl;
|
||||
|
||||
private String pricingResultUrl;
|
||||
|
||||
private String poolBalanceUrl;
|
||||
|
||||
private String getApplicationUrl;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.loanratepricing.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/24
|
||||
**/
|
||||
@Data
|
||||
public class FinalRateAdjustDTO {
|
||||
|
||||
private String applicationId;
|
||||
|
||||
private BigDecimal finalRate;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.loanratepricing.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/24
|
||||
**/
|
||||
@Data
|
||||
public class FinalRateSubmitDTO {
|
||||
|
||||
private String applicationId;
|
||||
|
||||
private String finalRate;
|
||||
|
||||
private String branchUsage;
|
||||
private String outletUsage;
|
||||
private String managerUsage;
|
||||
|
||||
private String profitAmt;
|
||||
|
||||
// 审批状态
|
||||
private String approvalStatus; // 审批状态 0不需要审批 1需要审批
|
||||
private String approvalLevel; // 审批层级 0不需要审批 1网点 2支行
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.ruoyi.loanratepricing.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 贷款定价申请信息实体类
|
||||
* 包含申请信息、定价结果、客户信息、各类BP因子等
|
||||
*/
|
||||
@Data
|
||||
public class LoanPricingApplySubmitDTO {
|
||||
|
||||
// 申请基本信息
|
||||
private Long applicationId; // 申请编号
|
||||
private String orgCode; // 申请机构代码
|
||||
private String orgName; // 申请机构名称
|
||||
private String applyUserCode; // 申请人ID
|
||||
private String applyUserName; // 申请人姓名
|
||||
|
||||
// 定价结果
|
||||
private String baseLoanRate; // 基础利率
|
||||
private String finalRate; // 最终利率
|
||||
private String minRateBranch; // 支行最低利率
|
||||
private String minRateProduct; // 最低利率
|
||||
private String calculateRate; // 测算利率
|
||||
private String totalBp; // 浮动BP合计
|
||||
|
||||
// 客户基础信息
|
||||
private String custType; // 客户类型(个人/企业)
|
||||
private String custIsn; // 客户内码
|
||||
private String custName; // 客户名称
|
||||
private String idType; // 证件类型
|
||||
private String idNo; // 证件号
|
||||
private String guarType;
|
||||
private String loanType;
|
||||
private String loanRate;
|
||||
|
||||
|
||||
// 忠诚度因子BP
|
||||
private String isKeyClient; // 个人核心用户
|
||||
private String isFirstLoan; // 我行首贷客户
|
||||
private String faithDay; // 用信天数
|
||||
private String custAge; // 客户年龄
|
||||
private String bpFirstLoan; // BP_首贷
|
||||
private String bpAgeLoan; // BP_贷龄
|
||||
private String bpAge; // BP_年龄
|
||||
private String totalBpLoyalty; // TOTAL_BP_忠诚度
|
||||
|
||||
// 贡献度因子BP - 存款相关
|
||||
private String perAvg; // 借款个人年日均
|
||||
private String perSp; // 借款个人特殊类存款
|
||||
private String perFmyAvg; // 配偶_子女年日均
|
||||
private String perFmySp; // 配偶_子女特殊类存款
|
||||
private String perEntAvg; // 个人对应企业年日均
|
||||
private String perEntSp; // 个人对应企业特殊类存款
|
||||
private String entAvg; // 企业年日均
|
||||
private String entSp; // 企业特殊类存款
|
||||
private String entPerAvg; // 企业法人年日均
|
||||
private String entPerSp; // 企业法人特殊类存款
|
||||
private String entPerFmyAvg; // 企业法人配偶年日均
|
||||
private String entPerFmySp; // 企业法人配偶特殊类存款
|
||||
private String entFinAvg; // 企业财务年日均
|
||||
private String entFinSp; // 企业财务特殊类存款
|
||||
private String balanceAvg; // 存款年日均合计
|
||||
private String loanAvg; // 贷款年日均
|
||||
private String derivationRate; // 派生率
|
||||
private String totalBpContribution; // TOTAL_BP_贡献度
|
||||
|
||||
// 贡献度因子BP - 中间业务
|
||||
private String midPerCard; // 中间业务_个人_信用卡
|
||||
private String midPerPass; // 中间业务_个人_一码通
|
||||
private String midPerHarvest; // 中间业务_个人_丰收互联
|
||||
private String midPerEffect; // 中间业务_个人_有效客户
|
||||
private String midPerQuickPay; // 中间业务_个人_快捷支付
|
||||
private String midPerEleDdc; // 中间业务_个人_电费代扣
|
||||
private String midPerCitizencard; // 中间业务_个人_市民卡
|
||||
private String midEntConnect; // 中间业务_企业_企业互联
|
||||
private String midEntEffect; // 中间业务_企业_有效价值客户
|
||||
private String midEntPublicFund; // 中间业务_企业_职工缴纳公积金
|
||||
private String midEntEleDdc; // 中间业务_企业_电费代扣
|
||||
private String midEntWaterDdc; // 中间业务_企业_水费代扣
|
||||
private String midEntTax; // 中间业务_企业_税务代扣
|
||||
private String bpMid; // BP_中间业务
|
||||
|
||||
// 关联度因子BP
|
||||
private String payroll; // 代发工资户数
|
||||
private String invLoanAmount; // 存量贷款余额
|
||||
private String bpPayroll; // BP_代发工资
|
||||
|
||||
// 企业客户关联度
|
||||
private String isCleanEnt; // 净身企业
|
||||
private String hasSettleAcct; // 开立基本结算账户
|
||||
private String isManufacturing; // 制造业企业
|
||||
private String isAgriGuar; // 省农担担保贷款
|
||||
private String isTaxA; // 是否纳税信用等级A级
|
||||
private String isAgriLeading; // 是否县级及以上农业龙头企业
|
||||
private String isInclusiveFinance;
|
||||
private String bpEntType; // BP_企业客户类别
|
||||
private String totalBpRelevance; // TOTAL_BP_关联度
|
||||
|
||||
// 贷款用途因子BP
|
||||
private String applyAmt; // 申请金额(元)
|
||||
private String bpLoanAmount; // BP_贷款额度
|
||||
private String loanPurpose; // 贷款用途
|
||||
private String bizProof; // 是否有经营佐证
|
||||
private String bpLoanUse; // BP_贷款用途
|
||||
|
||||
// 抵押物质押因子BP
|
||||
private String collType; // 抵质押类型
|
||||
private String collThirdParty; // 抵质押物是否三方所有
|
||||
private String bpCollateral; // BP_抵押物
|
||||
|
||||
// 风险度因子BP
|
||||
private String greyCust; // 灰名单客户
|
||||
private String prinOverdue; // 本金逾期
|
||||
private String interestOverdue; // 利息逾期
|
||||
private String cardOverdue; // 信用卡逾期
|
||||
private String bpGreyOverdue; // BP_灰名单与逾期
|
||||
private String totalBpRisk; // TOTAL_BP_风险度
|
||||
|
||||
// 审批状态
|
||||
private String approvalStatus; // 审批状态
|
||||
private String approvalLevel; // 审批层级
|
||||
|
||||
private BigDecimal branchBalance;
|
||||
private BigDecimal outletBalance;
|
||||
private BigDecimal managerBalance;
|
||||
|
||||
private BigDecimal branchUsage;
|
||||
private BigDecimal outletUsage;
|
||||
private BigDecimal managerUsage;
|
||||
|
||||
private BigDecimal profitAmt;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.ruoyi.loanratepricing.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/23
|
||||
**/
|
||||
@Data
|
||||
public class ModelInvokeDTO {
|
||||
/**
|
||||
* 业务方流水号(必填)
|
||||
* 可使用时间戳,或自定义随机生成
|
||||
*/
|
||||
private String serialNum;
|
||||
|
||||
/**
|
||||
* 机构编码(必填)
|
||||
* 固定值:931000
|
||||
*/
|
||||
private String orgCode;
|
||||
|
||||
/**
|
||||
* 运行模式(必填)
|
||||
* 固定值:1:同步
|
||||
*/
|
||||
private String runType = "1";
|
||||
|
||||
/**
|
||||
* 客户内码(必填)
|
||||
*/
|
||||
private String custIsn;
|
||||
|
||||
/**
|
||||
* 客户类型(必填)
|
||||
* 可选值:个人/企业
|
||||
*/
|
||||
private String custType;
|
||||
|
||||
/**
|
||||
* 担保方式(必填)
|
||||
* 可选值:信用,保证,抵押,质押
|
||||
*/
|
||||
private String guarType;
|
||||
|
||||
/**
|
||||
* 中间业务_个人_快捷支付(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midPerQuickPay;
|
||||
|
||||
/**
|
||||
* 中间业务_个人_电费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midPerEleDdc;
|
||||
|
||||
/**
|
||||
* 中间业务_企业_电费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midEntEleDdc;
|
||||
|
||||
/**
|
||||
* 中间业务_企业_水费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midEntWaterDdc;
|
||||
|
||||
/**
|
||||
* 申请金额(必填)
|
||||
* 单位:元
|
||||
*/
|
||||
private String applyAmt;
|
||||
|
||||
/**
|
||||
* 净身企业(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String isCleanEnt;
|
||||
|
||||
/**
|
||||
* 开立基本结算账户(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String hasSettleAcct;
|
||||
|
||||
/**
|
||||
* 制造业企业(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String isManufacturing;
|
||||
|
||||
/**
|
||||
* 省农担担保贷款(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String isAgriGuar;
|
||||
|
||||
/**
|
||||
* 是否纳税信用等级A级(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String isTaxA;
|
||||
|
||||
/**
|
||||
* 是否县级及以上农业龙头企业(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String isAgriLeading;
|
||||
|
||||
private String isInclusiveFinance;
|
||||
|
||||
/**
|
||||
* 贷款用途(非必填)
|
||||
* 可选值:consumer/business
|
||||
*/
|
||||
private String loanPurpose;
|
||||
|
||||
/**
|
||||
* 是否有经营佐证(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String bizProof;
|
||||
|
||||
/**
|
||||
* 抵质押类型(非必填)
|
||||
* 可选值:抵押/质押
|
||||
*/
|
||||
private String collType;
|
||||
|
||||
/**
|
||||
* 抵质押物是否三方所有(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String collThirdParty;
|
||||
|
||||
// /**
|
||||
// * 贷款利率(必填)
|
||||
// */
|
||||
// private String loanRate;
|
||||
|
||||
/**
|
||||
* 客户名称(非必填)
|
||||
*/
|
||||
private String custName;
|
||||
|
||||
/**
|
||||
* 证件类型(非必填)
|
||||
*/
|
||||
private String idType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.ruoyi.loanratepricing.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/23
|
||||
**/
|
||||
@Data
|
||||
public class OptInvokeDTO {
|
||||
|
||||
private String applicationId;
|
||||
|
||||
/**
|
||||
* 客户类型(个人/企业)
|
||||
*/
|
||||
private String custType;
|
||||
|
||||
/**
|
||||
* 客户内码
|
||||
*/
|
||||
private String custIsn;
|
||||
|
||||
/**
|
||||
* 客户名称
|
||||
*/
|
||||
private String custName;
|
||||
|
||||
/**
|
||||
* 证件类型
|
||||
*/
|
||||
private String idType;
|
||||
|
||||
/**
|
||||
* 证件号
|
||||
*/
|
||||
private String idNo;
|
||||
|
||||
/**
|
||||
* 申请金额
|
||||
*/
|
||||
private String applyAmt;
|
||||
|
||||
/**
|
||||
* 担保方式
|
||||
*/
|
||||
private String guarType;
|
||||
|
||||
/**
|
||||
* 抵质押类型
|
||||
*/
|
||||
private String collType;
|
||||
|
||||
/**
|
||||
* 抵质押物是否三方所有 0-否,1-是
|
||||
*/
|
||||
private String collThirdParty;
|
||||
|
||||
/**
|
||||
* 贷款用途
|
||||
*/
|
||||
private String loanPurpose;
|
||||
|
||||
private String loanRate;
|
||||
|
||||
/**
|
||||
* 是否有经营佐证 0-否,1-是
|
||||
*/
|
||||
private String bizProof;
|
||||
|
||||
/**
|
||||
* 贷款产品ID
|
||||
*/
|
||||
private String loanProdId;
|
||||
|
||||
/**
|
||||
* 贷款产品
|
||||
*/
|
||||
private String loanProd;
|
||||
|
||||
/**
|
||||
* 当日办理业务
|
||||
*/
|
||||
private String todayBiz;
|
||||
|
||||
/**
|
||||
* 是否净身企业 0-否,1-是
|
||||
*/
|
||||
private String isCleanEnt;
|
||||
|
||||
/**
|
||||
* 是否制造业企业 0-否,1-是
|
||||
*/
|
||||
private String isManufacturing;
|
||||
|
||||
/**
|
||||
* 是否省农担担保贷款 0-否,1-是
|
||||
*/
|
||||
private String isAgriGuar;
|
||||
|
||||
|
||||
private String isInclusiveFinance;
|
||||
|
||||
/**
|
||||
* 是否纳税信用等级A级 0-否,1-是
|
||||
*/
|
||||
private String isTaxA;
|
||||
|
||||
/**
|
||||
* 是否县级及以上农业龙头企业 0-否,1-是
|
||||
*/
|
||||
private String isAgriLeading;
|
||||
|
||||
/**
|
||||
* 中间业务_个人_快捷支付(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midPerQuickPay;
|
||||
|
||||
/**
|
||||
* 中间业务_个人_电费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midPerEleDdc;
|
||||
|
||||
/**
|
||||
* 中间业务_企业_电费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midEntEleDdc;
|
||||
|
||||
/**
|
||||
* 中间业务_企业_水费代扣(非必填)
|
||||
* 可选值:true/false
|
||||
*/
|
||||
private String midEntWaterDdc;
|
||||
|
||||
/**
|
||||
* 申请机构ID
|
||||
*/
|
||||
private String orgCode;
|
||||
|
||||
/**
|
||||
* 申请机构名称
|
||||
*/
|
||||
private String orgName;
|
||||
|
||||
/**
|
||||
* 申请人ID
|
||||
*/
|
||||
private String applyUserCode;
|
||||
|
||||
/**
|
||||
* 申请人姓名
|
||||
*/
|
||||
private String applyUserName;
|
||||
|
||||
/**
|
||||
* 是否开立基本结算账户 0-否,1-是
|
||||
*/
|
||||
private String hasSettleAcct;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.ruoyi.loanratepricing.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 贷款定价申请信息实体类
|
||||
* 包含申请信息、定价结果、客户信息、各类BP因子等
|
||||
*/
|
||||
@Data
|
||||
public class LoanPricingApply {
|
||||
|
||||
// 申请基本信息
|
||||
private String applicationId; // 申请编号
|
||||
private String orgCode; // 申请机构代码
|
||||
private String orgName; // 申请机构名称
|
||||
private String applyUserCode; // 申请人ID
|
||||
private String applyUserName; // 申请人姓名
|
||||
|
||||
// 定价结果
|
||||
private String baseLoanRate; // 基础利率
|
||||
private String finalRate; // 最终利率
|
||||
private String minRateBranch; // 支行最低利率
|
||||
private String minRateProduct; // 最低利率
|
||||
private String calculateRate; // 测算利率
|
||||
private String totalBp; // 浮动BP合计
|
||||
|
||||
// 客户基础信息
|
||||
private String custType; // 客户类型(个人/企业)
|
||||
private String custIsn; // 客户内码
|
||||
private String custName; // 客户名称
|
||||
private String idType; // 证件类型
|
||||
private String idNo; // 证件号
|
||||
private String guarType;
|
||||
private String loanType;
|
||||
private String loanRate;
|
||||
|
||||
|
||||
// 忠诚度因子BP
|
||||
private String isKeyClient; // 个人核心用户
|
||||
private String isFirstLoan; // 我行首贷客户
|
||||
private String faithDay; // 用信天数
|
||||
private String custAge; // 客户年龄
|
||||
private String bpFirstLoan; // BP_首贷
|
||||
private String bpAgeLoan; // BP_贷龄
|
||||
private String bpAge; // BP_年龄
|
||||
private String totalBpLoyalty; // TOTAL_BP_忠诚度
|
||||
|
||||
// 贡献度因子BP - 存款相关
|
||||
private String perAvg; // 借款个人年日均
|
||||
private String perSp; // 借款个人特殊类存款
|
||||
private String perFmyAvg; // 配偶_子女年日均
|
||||
private String perFmySp; // 配偶_子女特殊类存款
|
||||
private String perEntAvg; // 个人对应企业年日均
|
||||
private String perEntSp; // 个人对应企业特殊类存款
|
||||
private String entAvg; // 企业年日均
|
||||
private String entSp; // 企业特殊类存款
|
||||
private String entPerAvg; // 企业法人年日均
|
||||
private String entPerSp; // 企业法人特殊类存款
|
||||
private String entPerFmyAvg; // 企业法人配偶年日均
|
||||
private String entPerFmySp; // 企业法人配偶特殊类存款
|
||||
private String entFinAvg; // 企业财务年日均
|
||||
private String entFinSp; // 企业财务特殊类存款
|
||||
private String balanceAvg; // 存款年日均合计
|
||||
private String loanAvg; // 贷款年日均
|
||||
private String derivationRate; // 派生率
|
||||
private String totalBpContribution; // TOTAL_BP_贡献度
|
||||
|
||||
// 贡献度因子BP - 中间业务
|
||||
private String midPerCard; // 中间业务_个人_信用卡
|
||||
private String midPerPass; // 中间业务_个人_一码通
|
||||
private String midPerHarvest; // 中间业务_个人_丰收互联
|
||||
private String midPerEffect; // 中间业务_个人_有效客户
|
||||
private String midPerQuickPay; // 中间业务_个人_快捷支付
|
||||
private String midPerEleDdc; // 中间业务_个人_电费代扣
|
||||
private String midPerCitizencard; // 中间业务_个人_市民卡
|
||||
private String midEntConnect; // 中间业务_企业_企业互联
|
||||
private String midEntEffect; // 中间业务_企业_有效价值客户
|
||||
private String midEntPublicFund; // 中间业务_企业_职工缴纳公积金
|
||||
private String midEntEleDdc; // 中间业务_企业_电费代扣
|
||||
private String midEntWaterDdc; // 中间业务_企业_水费代扣
|
||||
private String midEntTax; // 中间业务_企业_税务代扣
|
||||
private String bpMid; // BP_中间业务
|
||||
|
||||
// 关联度因子BP
|
||||
private String payroll; // 代发工资户数
|
||||
private String invLoanAmount; // 存量贷款余额
|
||||
private String bpPayroll; // BP_代发工资
|
||||
|
||||
// 企业客户关联度
|
||||
private String isCleanEnt; // 净身企业
|
||||
private String hasSettleAcct; // 开立基本结算账户
|
||||
private String isManufacturing; // 制造业企业
|
||||
private String isAgriGuar; // 省农担担保贷款
|
||||
private String isTaxA; // 是否纳税信用等级A级
|
||||
private String isAgriLeading; // 是否县级及以上农业龙头企业
|
||||
private String isInclusiveFinance;
|
||||
private String bpEntType; // BP_企业客户类别
|
||||
private String totalBpRelevance; // TOTAL_BP_关联度
|
||||
|
||||
// 贷款用途因子BP
|
||||
private String applyAmt; // 申请金额(元)
|
||||
private String bpLoanAmount; // BP_贷款额度
|
||||
private String loanPurpose; // 贷款用途
|
||||
private String bizProof; // 是否有经营佐证
|
||||
private String bpLoanUse; // BP_贷款用途
|
||||
|
||||
// 抵押物质押因子BP
|
||||
private String collType; // 抵质押类型
|
||||
private String collThirdParty; // 抵质押物是否三方所有
|
||||
private String bpCollateral; // BP_抵押物
|
||||
|
||||
// 风险度因子BP
|
||||
private String greyCust; // 灰名单客户
|
||||
private String prinOverdue; // 本金逾期
|
||||
private String interestOverdue; // 利息逾期
|
||||
private String cardOverdue; // 信用卡逾期
|
||||
private String bpGreyOverdue; // BP_灰名单与逾期
|
||||
private String totalBpRisk; // TOTAL_BP_风险度
|
||||
|
||||
// 审批状态
|
||||
private String approvalStatus; // 审批状态
|
||||
private String approvalLevel; // 审批层级
|
||||
|
||||
private BigDecimal branchBalance;
|
||||
private BigDecimal outletBalance;
|
||||
private BigDecimal managerBalance;
|
||||
|
||||
private BigDecimal branchUsage;
|
||||
private BigDecimal outletUsage;
|
||||
private BigDecimal managerUsage;
|
||||
|
||||
private BigDecimal profitAmt;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.ruoyi.loanratepricing.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/24
|
||||
**/
|
||||
|
||||
@Data
|
||||
public class ModelOutputFields {
|
||||
// 基础贷款信息
|
||||
/** 贷款合同号 */
|
||||
private String loanContractId;
|
||||
/** 客户内码 */
|
||||
private String custIsn;
|
||||
/** 客户类型 */
|
||||
private String custType;
|
||||
/** 客户年龄 */
|
||||
private String custAge;
|
||||
/** 贷款金额 */
|
||||
private String loanAmount;
|
||||
/** 贷款利率 */
|
||||
private String loanRate;
|
||||
/** 贷款日期 */
|
||||
private String loanDate;
|
||||
/** 贷款类型 */
|
||||
private String loanType;
|
||||
/** 贷款用途 */
|
||||
private String loanPurpose;
|
||||
|
||||
// 客户属性
|
||||
/** 个人核心用户 */
|
||||
private String isKeyClient;
|
||||
/** 我行首贷客户 */
|
||||
private String isFirstLoan;
|
||||
/** 用信天数 */
|
||||
private String faithDay;
|
||||
|
||||
// BP相关指标
|
||||
/** BP_首贷 */
|
||||
private String bpFirstLoan;
|
||||
/** BP_贷龄 */
|
||||
private String bpAgeLoan;
|
||||
/** BP_年龄 */
|
||||
private String bpAge;
|
||||
/** TOTAL_BP_忠诚度 */
|
||||
private String totalBpLoyalty;
|
||||
|
||||
// 存款相关指标
|
||||
/** 借款个人年日均 */
|
||||
private String perAvg;
|
||||
/** 借款个人特殊类存款 */
|
||||
private String perSp;
|
||||
/** 配偶_子女年日均 */
|
||||
private String perFmyAvg;
|
||||
/** 配偶_子女特殊类存款 */
|
||||
private String perFmySp;
|
||||
/** 个人对应企业年日均 */
|
||||
private String perEntAvg;
|
||||
/** 个人对应企业特殊类存款 */
|
||||
private String perEntSp;
|
||||
/** 企业年日均 */
|
||||
private String entAvg;
|
||||
/** 企业特殊类存款 */
|
||||
private String entSp;
|
||||
/** 企业法人年日均 */
|
||||
private String entPerAvg;
|
||||
/** 企业法人特殊类存款 */
|
||||
private String entPerSp;
|
||||
/** 企业法人配偶年日均 */
|
||||
private String entPerFmyAvg;
|
||||
/** 企业法人配偶特殊类存款 */
|
||||
private String entPerFmySp;
|
||||
/** 企业财务年日均 */
|
||||
private String entFinAvg;
|
||||
/** 企业财务特殊类存款 */
|
||||
private String entFinSp;
|
||||
/** 存款年日均 */
|
||||
private String balanceAvg;
|
||||
/** 贷款年日均 */
|
||||
private String loanAvg;
|
||||
/** 派生率 */
|
||||
private String derivationRate;
|
||||
|
||||
// 贡献度相关
|
||||
/** TOTAL_BP_贡献度 */
|
||||
private String totalBpContribution;
|
||||
|
||||
// 中间业务相关
|
||||
/** 中间业务_个人_信用卡 */
|
||||
private String midPerCard;
|
||||
/** 中间业务_个人_一码通 */
|
||||
private String midPerPass;
|
||||
/** 中间业务_个人_丰收互联 */
|
||||
private String midPerHarvest;
|
||||
/** 中间业务_个人_有效客户 */
|
||||
private String midPerEffect;
|
||||
/** 中间业务_个人_快捷支付 */
|
||||
private String midPerQuickPay;
|
||||
/** 中间业务_个人_电费代扣 */
|
||||
private String midPerEleDdc;
|
||||
/** 中间业务_个人_市民卡 */
|
||||
private String midPerCitizencard;
|
||||
/** 中间业务_企业_企业互联 */
|
||||
private String midEntConnect;
|
||||
/** 中间业务_企业_有效价值客户 */
|
||||
private String midEntEffect;
|
||||
/** 中间业务_企业_职工缴纳公积金 */
|
||||
private String midEntPublicFund;
|
||||
/** 中间业务_企业_电费代扣 */
|
||||
private String midEntEleDdc;
|
||||
/** 中间业务_企业_水费代扣 */
|
||||
private String midEntWaterDdc;
|
||||
/** 中间业务_企业_税务代扣 */
|
||||
private String midEntTax;
|
||||
/** BP_中间业务 */
|
||||
private String bpMid;
|
||||
|
||||
// 代发工资相关
|
||||
/** 代发工资户数 */
|
||||
private String payroll;
|
||||
/** BP_代发工资 */
|
||||
private String bpPayroll;
|
||||
|
||||
// 企业属性相关
|
||||
/** 净身企业 */
|
||||
private String isCleanEnt;
|
||||
/** 开立基本结算账户 */
|
||||
private String hasSettleAcct;
|
||||
/** 制造业企业 */
|
||||
private String isManufacturing;
|
||||
/** 省农担担保贷款 */
|
||||
private String isAgriGuar;
|
||||
/** 纳税信用等级 */
|
||||
private String isTaxA;
|
||||
/** 县级(含)以上农业龙头企业和示范性专业合作社 */
|
||||
private String isAgriLeading;
|
||||
/** BP_企业客户类别 */
|
||||
private String bpEntType;
|
||||
|
||||
// 关联度相关
|
||||
/** TOTAL_BP_关联度 */
|
||||
private String totalBpRelevance;
|
||||
|
||||
// 风控相关
|
||||
/** 经营佐证 */
|
||||
private String bizProof;
|
||||
/** 抵质押物类型 */
|
||||
private String collType;
|
||||
/** 灰名单客户 */
|
||||
private String greyCust;
|
||||
/** 本金逾期 */
|
||||
private String prinOverdue;
|
||||
/** 利息逾期 */
|
||||
private String interestOverdue;
|
||||
/** 信用卡逾期 */
|
||||
private String cardOverdue;
|
||||
/** BP_贷款额度 */
|
||||
private String bpLoanAmount;
|
||||
/** BP_贷款用途 */
|
||||
private String bpLoanUse;
|
||||
/** BP_抵押物 */
|
||||
private String bpCollateral;
|
||||
/** BP_灰名单与逾期 */
|
||||
private String bpGreyOverdue;
|
||||
/** TOTAL_BP_风险度 */
|
||||
private String totalBpRisk;
|
||||
/** TOTAL_BP */
|
||||
private String totalBp;
|
||||
/** 抵质押物是否三方所有 */
|
||||
private String collThirdParty;
|
||||
/** 担保方式 */
|
||||
private String guarType;
|
||||
/** 存量贷款余额 */
|
||||
private String invLoanAmount;
|
||||
/** 测算利率 */
|
||||
private String calculateRate;
|
||||
|
||||
private String baseLoanRate; // 基础利率
|
||||
|
||||
private String minRateBranch; // 支行最低利率
|
||||
|
||||
private String minRateProduct; // 最低利率
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.loanratepricing.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/24
|
||||
**/
|
||||
@Data
|
||||
public class FinalRateAdjustVO {
|
||||
|
||||
private String applicationId;
|
||||
|
||||
private BigDecimal branchBalance;
|
||||
private BigDecimal outletBalance;
|
||||
private BigDecimal managerBalance;
|
||||
|
||||
private BigDecimal branchUsage;
|
||||
private BigDecimal outletUsage;
|
||||
private BigDecimal managerUsage;
|
||||
|
||||
private BigDecimal profitAmt;
|
||||
|
||||
// 审批状态
|
||||
private String approvalStatus; // 审批状态 0不需要审批 1需要审批
|
||||
private String approvalLevel; // 审批层级 0不需要审批 1网点 2支行
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.loanratepricing.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/24
|
||||
**/
|
||||
@Data
|
||||
public class OptPoolBalance {
|
||||
|
||||
private BigDecimal branchBalance;
|
||||
|
||||
private BigDecimal outletBalance;
|
||||
|
||||
private BigDecimal managerBalance;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.ruoyi.loanratepricing.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.loanratepricing.domain.RatePricingProperties;
|
||||
import com.ruoyi.loanratepricing.domain.dto.ModelInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.entity.ModelOutputFields;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/11
|
||||
**/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ModelService {
|
||||
|
||||
@Resource
|
||||
private RatePricingProperties ratePricingProperties;
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
public ModelOutputFields invokeModel(ModelInvokeDTO modelInvokeDTO) {
|
||||
Map<String, String> requestBody = entityToMap(modelInvokeDTO);
|
||||
JSONObject response = HttpUtils.doPostFormUrlEncoded(ratePricingProperties.getModelUrl(), requestBody, null, JSONObject.class);
|
||||
log.info("------------------->调用模型返回结果:" + JSON.toJSONString(response));
|
||||
if(Objects.nonNull(response) && response.containsKey("code") && response.getInteger("code") == 10000){
|
||||
JSONObject mappingOutputFields = response.getJSONObject("data").getJSONObject("mappingOutputFields");
|
||||
return JSON.parseObject(mappingOutputFields.toJSONString(), ModelOutputFields.class);
|
||||
}else{
|
||||
log.error("------------------->调用模型失败,失败原因为:" + response.getString("message"));
|
||||
throw new ServiceException("调用模型失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用FastJSON将实体类转换为Map<String, String>
|
||||
* @param obj 待转换的实体类对象
|
||||
* @return 转换后的Map
|
||||
*/
|
||||
public static Map<String, String> entityToMap(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
// 先转为JSON字符串,再转换为指定类型的Map
|
||||
String jsonStr = JSON.toJSONString(obj);
|
||||
return JSON.parseObject(jsonStr, new TypeReference<Map<String, String>>() {});
|
||||
}
|
||||
|
||||
private void replaceIndicatorToVariableName(JSONObject jsonObject) {
|
||||
List<SysDictData> variableNameList = redisCache.getCacheObject("sys_dict:model_indicator_metric");
|
||||
variableNameList.forEach(sysDictData -> {
|
||||
if (jsonObject.containsKey(sysDictData.getDictLabel())) {
|
||||
jsonObject.put(sysDictData.getDictValue(), jsonObject.get(sysDictData.getDictLabel()));
|
||||
jsonObject.remove(sysDictData.getDictLabel());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.ruoyi.loanratepricing.service;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.loanratepricing.domain.RatePricingProperties;
|
||||
import com.ruoyi.loanratepricing.domain.dto.LoanPricingApplySubmitDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.OptInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.entity.LoanPricingApply;
|
||||
import com.ruoyi.loanratepricing.domain.vo.OptPoolBalance;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/23
|
||||
**/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OptService {
|
||||
|
||||
@Resource
|
||||
private RatePricingProperties ratePricingProperties;
|
||||
|
||||
|
||||
public JSONObject submitApplication(OptInvokeDTO optInvokeDTO) {
|
||||
JSONObject json = (JSONObject) JSON.toJSON(optInvokeDTO);
|
||||
JSONObject response = HttpUtils.doPostJson(ratePricingProperties.getOaUrl() + ratePricingProperties.getApplicationSubmitUrl(), json, null, JSONObject.class);
|
||||
log.info("------------>发起利率定价申请:" + JSON.toJSONString(response));
|
||||
if(Objects.nonNull(response) && response.containsKey("code") && response.getInteger("code") == 0){
|
||||
return response.getJSONObject("data");
|
||||
}else{
|
||||
log.error("申请利率定价流程失败" + response.toJSONString());
|
||||
throw new ServiceException("提交利率定价流程失败:" + response.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
public String submitRatePricingResult(LoanPricingApply loanPricingApply) {
|
||||
LoanPricingApplySubmitDTO loanPricingApplySubmitDTO = new LoanPricingApplySubmitDTO();
|
||||
BeanUtils.copyProperties(loanPricingApply, loanPricingApplySubmitDTO);
|
||||
loanPricingApplySubmitDTO.setApplicationId(Long.parseLong(loanPricingApply.getApplicationId()));
|
||||
|
||||
JSONObject json = (JSONObject) JSON.toJSON(loanPricingApplySubmitDTO);
|
||||
JSONObject response = HttpUtils.doPostJson(ratePricingProperties.getOaUrl() + ratePricingProperties.getPricingResultUrl(), json, null, JSONObject.class);
|
||||
log.info("------------>提交利率定价申请:" + JSON.toJSONString(response));
|
||||
if(Objects.nonNull(response) && response.containsKey("code") && response.getInteger("code") == 0){
|
||||
return "提交利率定价结果成功";
|
||||
}else{
|
||||
log.error("提交利率定价结果失败"+ response.toJSONString());
|
||||
throw new ServiceException("提交利率定价结果失败"+ response.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
public LoanPricingApply queryLoanPricingApply(String applicationId){
|
||||
Map<String, String> requestParam = new HashMap<>();
|
||||
requestParam.put("id", applicationId);
|
||||
JSONObject response = HttpUtils.doGet(ratePricingProperties.getOaUrl() + ratePricingProperties.getGetApplicationUrl(), requestParam, null, JSONObject.class);
|
||||
log.info("------------>查询利率定价申请信息:" + JSON.toJSONString(response));
|
||||
if(Objects.nonNull(response) && response.containsKey("code") && response.getInteger("code") == 0){
|
||||
JSONObject data = response.getJSONObject("data");
|
||||
return JSON.parseObject(data.toJSONString(), LoanPricingApply.class);
|
||||
}else{
|
||||
log.error("查询利率定价申请信息失败"+ response.toJSONString());
|
||||
throw new ServiceException("查询利率定价申请信息失败"+ response.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public OptPoolBalance queryPoolBalance(String userCode){
|
||||
Map<String, String> requestParam = new HashMap<>();
|
||||
requestParam.put("ownerId", userCode);
|
||||
requestParam.put("year", getCurrentYear());
|
||||
requestParam.put("quarter", getCurrentQuarter());
|
||||
JSONObject response = HttpUtils.doGet(ratePricingProperties.getOaUrl() + ratePricingProperties.getPoolBalanceUrl(), requestParam, null, JSONObject.class);
|
||||
log.info("------------>查询议价池:" + JSON.toJSONString(response));
|
||||
if(Objects.nonNull(response) && response.containsKey("code") && response.getInteger("code") == 0){
|
||||
OptPoolBalance optPoolBalance = new OptPoolBalance();
|
||||
JSONObject data = response.getJSONObject("data");
|
||||
if (Objects.isNull(data)){
|
||||
log.error("查询议价池失败" + response.toJSONString());
|
||||
return optPoolBalance;
|
||||
}
|
||||
if(data.containsKey("branch")){
|
||||
JSONObject branch = data.getJSONObject("branch");
|
||||
if(branch.containsKey("availableQuota")){
|
||||
BigDecimal availableQuota = new BigDecimal(branch.getString("availableQuota"));
|
||||
optPoolBalance.setBranchBalance(availableQuota);
|
||||
}else{
|
||||
optPoolBalance.setBranchBalance(BigDecimal.ZERO);
|
||||
}
|
||||
}else{
|
||||
optPoolBalance.setBranchBalance(BigDecimal.ZERO);
|
||||
}
|
||||
if(data.containsKey("network")){
|
||||
JSONObject network = data.getJSONObject("network");
|
||||
if(network.containsKey("availableQuota")){
|
||||
BigDecimal availableQuota = new BigDecimal(network.getString("availableQuota"));
|
||||
optPoolBalance.setOutletBalance(availableQuota);
|
||||
}else{
|
||||
optPoolBalance.setOutletBalance(BigDecimal.ZERO);
|
||||
}
|
||||
}else{
|
||||
optPoolBalance.setOutletBalance(BigDecimal.ZERO);
|
||||
}
|
||||
if(data.containsKey("manager")){
|
||||
JSONObject manager = data.getJSONObject("manager");
|
||||
if(manager.containsKey("availableQuota")){
|
||||
BigDecimal availableQuota = new BigDecimal(manager.getString("availableQuota"));
|
||||
optPoolBalance.setManagerBalance(availableQuota);
|
||||
}else{
|
||||
optPoolBalance.setManagerBalance(BigDecimal.ZERO);
|
||||
}
|
||||
}else{
|
||||
optPoolBalance.setManagerBalance(BigDecimal.ZERO);
|
||||
}
|
||||
return optPoolBalance;
|
||||
}else{
|
||||
log.error("查询议价池失败" + response.toJSONString());
|
||||
throw new ServiceException("查询议价池失败" + response.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
private String getCurrentYear(){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
return Integer.toString(year);
|
||||
}
|
||||
|
||||
private String getCurrentQuarter(){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
int quarter = (calendar.get(Calendar.MONTH) + 3) / 3;
|
||||
return Integer.toString(quarter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.ruoyi.loanratepricing.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.loanratepricing.domain.dto.FinalRateAdjustDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.FinalRateSubmitDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.ModelInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.dto.OptInvokeDTO;
|
||||
import com.ruoyi.loanratepricing.domain.entity.LoanPricingApply;
|
||||
import com.ruoyi.loanratepricing.domain.entity.ModelOutputFields;
|
||||
import com.ruoyi.loanratepricing.domain.vo.FinalRateAdjustVO;
|
||||
import com.ruoyi.loanratepricing.domain.vo.OptPoolBalance;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
* @Date 2025/12/23
|
||||
**/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class RatePricingService {
|
||||
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Resource
|
||||
private OptService optService;
|
||||
|
||||
@Resource
|
||||
private ModelService modelService;
|
||||
|
||||
private static String APPLICATION_ID_PREFIX = "LOAN_RATE_PRICING_APPLICATION_";
|
||||
|
||||
/**
|
||||
* 创建利率测算申请
|
||||
* 1. 表单信息提交
|
||||
* 2. 调用行社流程发起接口,取得applicationId
|
||||
* 3. 调用模型测算接口,获取大部分字段
|
||||
* 4. 保存流程到redis
|
||||
*
|
||||
* @param optInvokeDTO
|
||||
* @return
|
||||
*/
|
||||
public LoanPricingApply loadRatePricingApply(OptInvokeDTO optInvokeDTO){
|
||||
LoanPricingApply loanPricingApply;
|
||||
if (Objects.isNull(optInvokeDTO.getApplicationId())){
|
||||
JSONObject response = optService.submitApplication(optInvokeDTO);
|
||||
String applicationId = response.getString("applicationId");
|
||||
ModelInvokeDTO modelInvokeDTO = new ModelInvokeDTO();
|
||||
BeanUtils.copyProperties(optInvokeDTO, modelInvokeDTO);
|
||||
modelInvokeDTO.setRunType("1");
|
||||
modelInvokeDTO.setSerialNum(applicationId);
|
||||
modelInvokeDTO.setMidEntEleDdc(response.getString("midEntEleDdc"));
|
||||
modelInvokeDTO.setMidEntWaterDdc(response.getString("midEntWaterDdc"));
|
||||
modelInvokeDTO.setMidPerEleDdc(response.getString("midPerEleDdc"));
|
||||
modelInvokeDTO.setMidPerQuickPay(response.getString("midPerQuickPay"));
|
||||
ModelOutputFields modelOutputFields = modelService.invokeModel(modelInvokeDTO);
|
||||
|
||||
loanPricingApply = new LoanPricingApply();
|
||||
BeanUtils.copyProperties(optInvokeDTO, loanPricingApply);
|
||||
BeanUtils.copyProperties(modelOutputFields, loanPricingApply);
|
||||
log.info("--------------->applicationId:{}",applicationId);
|
||||
loanPricingApply.setApplicationId(applicationId);
|
||||
redisCache.setCacheObject(APPLICATION_ID_PREFIX + applicationId, loanPricingApply);
|
||||
}else{
|
||||
if (redisCache.hasKey(APPLICATION_ID_PREFIX + optInvokeDTO.getApplicationId())){
|
||||
loanPricingApply = redisCache.getCacheObject(APPLICATION_ID_PREFIX + optInvokeDTO.getApplicationId());
|
||||
log.info("--------------->通过redis查询记录:{}", loanPricingApply);
|
||||
}else{
|
||||
loanPricingApply = optService.queryLoanPricingApply(optInvokeDTO.getApplicationId());
|
||||
}
|
||||
}
|
||||
OptPoolBalance optPoolBalance = optService.queryPoolBalance(loanPricingApply.getApplyUserCode());
|
||||
BeanUtils.copyProperties(optPoolBalance, loanPricingApply);
|
||||
log.info("--------------->获取记录:{}", loanPricingApply);
|
||||
return loanPricingApply;
|
||||
}
|
||||
|
||||
public FinalRateAdjustVO invokeFinalRate(FinalRateAdjustDTO finalRateAdjustDTO){
|
||||
if (!redisCache.hasKey(APPLICATION_ID_PREFIX + finalRateAdjustDTO.getApplicationId())){
|
||||
throw new ServiceException("利率定价申请信息不存在,请重新发起");
|
||||
}
|
||||
LoanPricingApply loanPricingApply = redisCache.getCacheObject(APPLICATION_ID_PREFIX + finalRateAdjustDTO.getApplicationId());
|
||||
if (Objects.isNull(loanPricingApply.getMinRateBranch())){
|
||||
throw new ServiceException("利率定价模型信息不存在,请重新发起");
|
||||
}
|
||||
FinalRateAdjustVO finalRateAdjustVO = new FinalRateAdjustVO();
|
||||
OptPoolBalance optPoolBalance = optService.queryPoolBalance(loanPricingApply.getApplyUserCode());
|
||||
BeanUtils.copyProperties(optPoolBalance, finalRateAdjustVO);
|
||||
|
||||
BigDecimal minRateBranch = new BigDecimal(loanPricingApply.getMinRateBranch());
|
||||
if (finalRateAdjustDTO.getFinalRate().compareTo(minRateBranch) < 0){
|
||||
BigDecimal interestAmt = new BigDecimal(loanPricingApply.getApplyAmt()).multiply(minRateBranch.subtract(finalRateAdjustDTO.getFinalRate()));
|
||||
BigDecimal totalBalance = optPoolBalance.getBranchBalance().add(optPoolBalance.getOutletBalance()).add(optPoolBalance.getManagerBalance());
|
||||
if (interestAmt.compareTo(totalBalance) > 0){
|
||||
throw new ServiceException("总利息大于可用余额,无法进行定价");
|
||||
}
|
||||
if (optPoolBalance.getManagerBalance().compareTo(BigDecimal.ZERO) > 0 && interestAmt.compareTo(BigDecimal.ZERO) > 0){
|
||||
finalRateAdjustVO.setApprovalStatus("0");
|
||||
finalRateAdjustVO.setApprovalLevel("0");
|
||||
BigDecimal managerUsage = interestAmt.compareTo(optPoolBalance.getManagerBalance()) > 0 ? optPoolBalance.getManagerBalance() : interestAmt;
|
||||
finalRateAdjustVO.setManagerUsage(managerUsage);
|
||||
interestAmt = interestAmt.subtract(managerUsage);
|
||||
}
|
||||
|
||||
if (optPoolBalance.getOutletBalance().compareTo(BigDecimal.ZERO) > 0 && interestAmt.compareTo(BigDecimal.ZERO) > 0){
|
||||
finalRateAdjustVO.setApprovalStatus("1");
|
||||
finalRateAdjustVO.setApprovalLevel("1");
|
||||
BigDecimal outletUsage = interestAmt.compareTo(optPoolBalance.getOutletBalance()) > 0 ? optPoolBalance.getOutletBalance() : interestAmt;
|
||||
finalRateAdjustVO.setOutletUsage(outletUsage);
|
||||
interestAmt = interestAmt.subtract(outletUsage);
|
||||
}else{
|
||||
finalRateAdjustVO.setOutletUsage(BigDecimal.ZERO);
|
||||
}
|
||||
if (optPoolBalance.getBranchBalance().compareTo(BigDecimal.ZERO) > 0 && interestAmt.compareTo(BigDecimal.ZERO) > 0){
|
||||
finalRateAdjustVO.setApprovalStatus("1");
|
||||
finalRateAdjustVO.setApprovalLevel("2");
|
||||
BigDecimal branchUsage = interestAmt.compareTo(optPoolBalance.getBranchBalance()) > 0 ? optPoolBalance.getBranchBalance() : interestAmt;
|
||||
finalRateAdjustVO.setBranchUsage(branchUsage);
|
||||
interestAmt = interestAmt.subtract(branchUsage);
|
||||
}else{
|
||||
finalRateAdjustVO.setBranchUsage(BigDecimal.ZERO);
|
||||
}
|
||||
finalRateAdjustVO.setProfitAmt(BigDecimal.ZERO);
|
||||
}else {
|
||||
finalRateAdjustVO.setApprovalStatus("0");
|
||||
finalRateAdjustVO.setApprovalLevel("0");
|
||||
finalRateAdjustVO.setManagerUsage(BigDecimal.ZERO);
|
||||
finalRateAdjustVO.setOutletUsage(BigDecimal.ZERO);
|
||||
finalRateAdjustVO.setBranchUsage(BigDecimal.ZERO);
|
||||
finalRateAdjustVO.setProfitAmt(new BigDecimal(loanPricingApply.getApplyAmt()).multiply(finalRateAdjustDTO.getFinalRate().subtract(minRateBranch)).divide(BigDecimal.valueOf(6), 4, RoundingMode.HALF_UP));
|
||||
}
|
||||
return finalRateAdjustVO;
|
||||
}
|
||||
|
||||
public String submitFinalRate(FinalRateSubmitDTO finalRateSubmitDTO){
|
||||
LoanPricingApply loanPricingApply;
|
||||
if (redisCache.hasKey(APPLICATION_ID_PREFIX + finalRateSubmitDTO.getApplicationId())){
|
||||
loanPricingApply = redisCache.getCacheObject(APPLICATION_ID_PREFIX + finalRateSubmitDTO.getApplicationId());
|
||||
}else{
|
||||
loanPricingApply = optService.queryLoanPricingApply(finalRateSubmitDTO.getApplicationId());
|
||||
}
|
||||
if (Objects.isNull(loanPricingApply.getMinRateBranch())){
|
||||
throw new ServiceException("利率定价模型信息不存在,请重新发起");
|
||||
}
|
||||
BeanUtils.copyProperties(finalRateSubmitDTO, loanPricingApply);
|
||||
|
||||
redisCache.setCacheObject(APPLICATION_ID_PREFIX + loanPricingApply.getApplicationId(), loanPricingApply);
|
||||
|
||||
return optService.submitRatePricingResult(loanPricingApply);
|
||||
}
|
||||
|
||||
public LoanPricingApply queryLoanPricingApplyFromRedis(Long applicationId){
|
||||
if (redisCache.hasKey(APPLICATION_ID_PREFIX + applicationId)){
|
||||
return redisCache.getCacheObject(APPLICATION_ID_PREFIX + applicationId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
94
loan-rate-pricing/src/main/resources/data/model_output.json
Normal file
94
loan-rate-pricing/src/main/resources/data/model_output.json
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"traceId": "350626558347246735E7F4722CUZRWOMNRR53O0",
|
||||
"cost": 2267,
|
||||
"tokenId": "17364055486305E7F4722M8IPFWNL8TOBEB",
|
||||
"mappingOutputFields": {
|
||||
"custIsn": "C20250109001",
|
||||
"custType": "企业",
|
||||
"guarType": "省农担担保",
|
||||
"custName": "浙江绿源农业发展有限公司",
|
||||
"idType": "统一社会信用代码",
|
||||
"loanRate": "4.35",
|
||||
"isKeyClient": "1",
|
||||
"isFirstLoan": "0",
|
||||
"faithDay": "210",
|
||||
"custAge": "6",
|
||||
"bpFirstLoan": "12",
|
||||
"bpAgeLoan": "6",
|
||||
"bpAge": "4",
|
||||
"totalBpLoyalty": "22",
|
||||
"perAvg": "80000",
|
||||
"perSp": "15000",
|
||||
"perFmyAvg": "45000",
|
||||
"perFmySp": "8000",
|
||||
"perEntAvg": "250000",
|
||||
"perEntSp": "60000",
|
||||
"entAvg": "1200000",
|
||||
"entSp": "250000",
|
||||
"entPerAvg": "90000",
|
||||
"entPerSp": "20000",
|
||||
"entPerFmyAvg": "50000",
|
||||
"entPerFmySp": "10000",
|
||||
"entFinAvg": "70000",
|
||||
"entFinSp": "15000",
|
||||
"balanceAvg": "1843000",
|
||||
"loanAvg": "950000",
|
||||
"derivationRate": "1.94",
|
||||
"totalBpContribution": "28",
|
||||
"midPerCard": "1",
|
||||
"midPerPass": "1",
|
||||
"midPerHarvest": "1",
|
||||
"midPerEffect": "0",
|
||||
"midPerQuickPay": "0",
|
||||
"midPerEleDdc": "0",
|
||||
"midPerCitizencard": "1",
|
||||
"midEntConnect": "1",
|
||||
"midEntEffect": "1",
|
||||
"midEntPublicFund": "0",
|
||||
"midEntEleDdc": "0",
|
||||
"midEntWaterDdc": "0",
|
||||
"midEntTax": "1",
|
||||
"bpMid": "15",
|
||||
"payroll": "200",
|
||||
"invLoanAmount": "6000000",
|
||||
"bpPayroll": "10",
|
||||
"isCleanEnt": "1",
|
||||
"hasSettleAcct": "0",
|
||||
"isManufacturing": "1",
|
||||
"isAgriGuar": "0",
|
||||
"isTaxA": "0",
|
||||
"isAgriLeading": "0",
|
||||
"bpEntType": "18",
|
||||
"totalBpRelevance": "28",
|
||||
"applyAmt": "1500000",
|
||||
"bpLoanAmount": "8",
|
||||
"loanPurpose": "农产品种植与加工",
|
||||
"bizProof": "有",
|
||||
"bpLoanUse": "10",
|
||||
"collType": "农业设施抵押",
|
||||
"collThirdParty": "1",
|
||||
"bpCollateral": "12",
|
||||
"greyCust": "1",
|
||||
"prinOverdue": "无",
|
||||
"interestOverdue": "无",
|
||||
"cardOverdue": "无",
|
||||
"bpGreyOverdue": "0",
|
||||
"totalBpRisk": "0",
|
||||
"totalBp": "50",
|
||||
"calculateRate": "4.28",
|
||||
"baseLoanRate": "3.85",
|
||||
"minRateBranch": "3.78",
|
||||
"minRateProduct": "3.70"
|
||||
},
|
||||
"extensionMap": {},
|
||||
"reasonMessage": "Running successfully",
|
||||
"bizTime": 1736405548630,
|
||||
"outputFields": {},
|
||||
"workflowCode": "TBKH",
|
||||
"orgCode": "802000",
|
||||
"bizId": "2025010914345",
|
||||
"reasonCode": 200,
|
||||
"workflowVersion": 14,
|
||||
"callTime": 1736405548630,
|
||||
"status": 1
|
||||
}
|
||||
47
loan-rate-pricing/src/main/resources/data/pool_balance.json
Normal file
47
loan-rate-pricing/src/main/resources/data/pool_balance.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"branch": {
|
||||
"poolNo": "BR2025Q4001",
|
||||
"poolType": "PUBLIC",
|
||||
"ownerId": "B0010001",
|
||||
"ownerName": "北京市朝阳区支行",
|
||||
"ownerType": "BRANCH",
|
||||
"year": "2025",
|
||||
"quarter": "4",
|
||||
"status": "1",
|
||||
"totalQuota": "10000000.00",
|
||||
"usedQuota": "6528000.50",
|
||||
"availableQuota": "3471999.50",
|
||||
"frozenQuota": "0.00",
|
||||
"usageRate": "65.28"
|
||||
},
|
||||
"network": {
|
||||
"poolNo": "NW2025Q4008",
|
||||
"poolType": "PRIVATE",
|
||||
"ownerId": "N0080012",
|
||||
"ownerName": "北京市朝阳区建国路网点",
|
||||
"ownerType": "NETWORK",
|
||||
"year": "2025",
|
||||
"quarter": "4",
|
||||
"status": "1",
|
||||
"totalQuota": "2000000.00",
|
||||
"usedQuota": "1268900.30",
|
||||
"availableQuota": "731099.70",
|
||||
"frozenQuota": "0.00",
|
||||
"usageRate": "63.45"
|
||||
},
|
||||
"manager": {
|
||||
"poolNo": "MG2025Q4099",
|
||||
"poolType": "PRIVATE",
|
||||
"ownerId": "M0990045",
|
||||
"ownerName": "张三",
|
||||
"ownerType": "MANAGER",
|
||||
"year": "2025",
|
||||
"quarter": "4",
|
||||
"status": "1",
|
||||
"totalQuota": "500000.00",
|
||||
"usedQuota": "389600.80",
|
||||
"availableQuota": "110399.20",
|
||||
"frozenQuota": "0.00",
|
||||
"usageRate": "77.92"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user