新增流程列表编辑功能
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# 流程列表编辑功能后端实施记录
|
||||
|
||||
## 修改内容
|
||||
- 在利率定价流程接口新增编辑查询接口:`GET /loanPricing/workflow/{serialNum}/edit`。
|
||||
- 新增个人流程编辑接口:`PUT /loanPricing/workflow/{serialNum}/personal`。
|
||||
- 新增企业流程编辑接口:`PUT /loanPricing/workflow/{serialNum}/corporate`。
|
||||
- 编辑接口按当前登录用户的 `昵称-柜员号` 校验创建者,只允许流程创建者编辑。
|
||||
- 编辑时保持原业务方流水号、客户类型、创建者、创建时间和创建人部门,只覆盖表单字段。
|
||||
- 编辑保存后重新调用模型服务;已有模型输出记录时覆盖原模型输出,并保持流程关联。
|
||||
|
||||
## 验证记录
|
||||
- `mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
- 结果:通过。
|
||||
- 覆盖:创建者编辑、非创建者拒绝、客户类型不匹配拒绝、编辑数据解密返回、重新测算覆盖模型结果。
|
||||
- `mvn -pl ruoyi-loan-pricing -am test`
|
||||
- 结果:通过。
|
||||
- 覆盖:利率定价模块现有单测和本次新增单测。
|
||||
- `mvn -pl ruoyi-admin -am -DskipTests package`
|
||||
- 结果:通过,重新打包 `ruoyi-admin/target/ruoyi-admin.jar` 用于真实接口验证。
|
||||
- 真实接口验证:
|
||||
- 创建临时个人流程 `20260525110739953`,创建者为 `若依-admin`。
|
||||
- 创建者调用 `GET /loanPricing/workflow/20260525110739953/edit` 成功返回原始客户名称 `编辑测试客户` 和原始证件号 `330103199901019999`。
|
||||
- 创建者通过页面编辑提交后,编辑详情接口返回 `applyAmt=120000`,并保持原 `serialNum`、`custType`、`createBy`、`createTime`、`deptId`。
|
||||
- 非创建者 `8929999` 调用编辑详情接口返回 `只有创建者可以编辑该流程`。
|
||||
- 非创建者 `8929999` 调用个人更新接口返回 `只有创建者可以编辑该流程`。
|
||||
- 验证完成后已按精确流水号删除临时流程和关联 `model_retail_output_fields` 记录,清理后计数均为 0。
|
||||
@@ -0,0 +1,24 @@
|
||||
# 流程列表编辑功能前端实施记录
|
||||
|
||||
## 修改内容
|
||||
- 在流程列表操作列新增“编辑”按钮。
|
||||
- 编辑按钮只在 `row.createBy` 等于当前登录用户 `nickName-name` 时展示。
|
||||
- 点击编辑后直接查询流程编辑数据,并按客户类型打开个人或企业弹窗,不再进入客户类型选择和客户号选择流程。
|
||||
- 个人和企业新增弹窗复用为新增/编辑双模式:
|
||||
- 新增模式继续调用原新增接口。
|
||||
- 编辑模式显示编辑标题、回显原始数据,并调用对应更新接口。
|
||||
- 编辑回显时跳过担保方式和抵质押类型监听中的清空逻辑,避免抵质押字段被初始化过程误清除。
|
||||
|
||||
## 验证记录
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node tests/workflow-index-refresh.test.js && node tests/personal-create-input-params.test.js && node tests/corporate-create-input-params.test.js`
|
||||
- 结果:通过。
|
||||
- 覆盖:操作列编辑按钮、创建者展示控制、编辑数据查询、个人/企业弹窗编辑模式和更新接口调用。
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && npm run build:prod`
|
||||
- 结果:通过;仅保留项目既有的 webpack 体积提示。
|
||||
- 真实页面验证:
|
||||
- 前端地址:`http://localhost:1024/loanPricing/workflow`。
|
||||
- 创建者 `admin` 登录后,临时流水 `20260525110739953` 操作列显示“查看”和“编辑”。
|
||||
- 点击“编辑”直接打开 `编辑个人利率定价流程` 弹窗,回显客户内码 `EDITTEST20260525`、客户名称 `编辑测试客户`、证件号 `330103199901019999`、担保方式 `信用`、申请金额 `100000`、借款期限 `3`、业务种类 `新增`。
|
||||
- 将申请金额改为 `120000` 后提交,页面提示“编辑成功”,列表刷新后该流水申请金额变为 `120000`。
|
||||
- 非创建者 `8929999` 登录后,同一流水仍可查看,但操作列只显示“查看”,不显示“编辑”。
|
||||
- 浏览器控制台无相关 error;仅出现登录和表单过程中的 `async-validator` 校验 warning。
|
||||
@@ -69,6 +69,46 @@ public class LoanPricingWorkflowController extends BaseController
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询利率定价流程编辑数据
|
||||
*/
|
||||
@Operation(summary = "查询利率定价流程编辑数据")
|
||||
@GetMapping("/{serialNum}/edit")
|
||||
public AjaxResult getEditInfo(
|
||||
@Parameter(description = "业务方流水号")
|
||||
@PathVariable("serialNum") String serialNum)
|
||||
{
|
||||
return success(loanPricingWorkflowService.selectEditableLoanPricingBySerialNum(serialNum));
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑个人客户利率定价流程
|
||||
*/
|
||||
@Operation(summary = "编辑个人客户利率定价流程")
|
||||
@Log(title = "个人客户利率定价流程", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{serialNum}/personal")
|
||||
public AjaxResult updatePersonal(
|
||||
@Parameter(description = "业务方流水号")
|
||||
@PathVariable("serialNum") String serialNum,
|
||||
@Validated @RequestBody PersonalLoanPricingCreateDTO dto)
|
||||
{
|
||||
return success(loanPricingWorkflowService.updatePersonalLoanPricing(serialNum, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑企业客户利率定价流程
|
||||
*/
|
||||
@Operation(summary = "编辑企业客户利率定价流程")
|
||||
@Log(title = "企业客户利率定价流程", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{serialNum}/corporate")
|
||||
public AjaxResult updateCorporate(
|
||||
@Parameter(description = "业务方流水号")
|
||||
@PathVariable("serialNum") String serialNum,
|
||||
@Validated @RequestBody CorporateLoanPricingCreateDTO dto)
|
||||
{
|
||||
return success(loanPricingWorkflowService.updateCorporateLoanPricing(serialNum, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询个人客户号映射
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,32 @@ public interface ILoanPricingWorkflowService
|
||||
*/
|
||||
public LoanPricingWorkflow createCorporateLoanPricing(CorporateLoanPricingCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 查询利率定价流程编辑数据
|
||||
*
|
||||
* @param serialNum 业务方流水号
|
||||
* @return 编辑用流程数据
|
||||
*/
|
||||
public LoanPricingWorkflow selectEditableLoanPricingBySerialNum(String serialNum);
|
||||
|
||||
/**
|
||||
* 编辑个人客户利率定价流程
|
||||
*
|
||||
* @param serialNum 业务方流水号
|
||||
* @param dto 个人客户编辑DTO
|
||||
* @return 更新后的流程数据
|
||||
*/
|
||||
public LoanPricingWorkflow updatePersonalLoanPricing(String serialNum, PersonalLoanPricingCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 编辑企业客户利率定价流程
|
||||
*
|
||||
* @param serialNum 业务方流水号
|
||||
* @param dto 企业客户编辑DTO
|
||||
* @return 更新后的流程数据
|
||||
*/
|
||||
public LoanPricingWorkflow updateCorporateLoanPricing(String serialNum, CorporateLoanPricingCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 查询利率定价流程列表
|
||||
*
|
||||
|
||||
@@ -40,6 +40,14 @@ public class LoanPricingModelService {
|
||||
private SensitiveFieldCryptoService sensitiveFieldCryptoService;
|
||||
|
||||
public void invokeModelAsync(Long workflowId) {
|
||||
invokeModelAndSave(workflowId, false);
|
||||
}
|
||||
|
||||
public void reinvokeModelAndOverwrite(Long workflowId) {
|
||||
invokeModelAndSave(workflowId, true);
|
||||
}
|
||||
|
||||
private void invokeModelAndSave(Long workflowId, boolean overwriteModelOutput) {
|
||||
LoanPricingWorkflow loanPricingWorkflow = loanPricingWorkflowMapper.selectById(workflowId);
|
||||
if (Objects.isNull(loanPricingWorkflow)){
|
||||
log.error("未找到对应的流程信息,未调用模型服务");
|
||||
@@ -68,26 +76,43 @@ public class LoanPricingModelService {
|
||||
if (loanPricingWorkflow.getCustType().equals("个人")){
|
||||
// 个人模型
|
||||
ModelRetailOutputFields modelRetailOutputFields = modelService.invokePersonalModel(modelInvokeDTO);
|
||||
modelRetailOutputFieldsMapper.insert(modelRetailOutputFields);
|
||||
if (overwriteModelOutput && Objects.nonNull(loanPricingWorkflow.getModelOutputId()))
|
||||
{
|
||||
modelRetailOutputFields.setId(loanPricingWorkflow.getModelOutputId());
|
||||
modelRetailOutputFieldsMapper.updateById(modelRetailOutputFields);
|
||||
}
|
||||
else
|
||||
{
|
||||
modelRetailOutputFieldsMapper.insert(modelRetailOutputFields);
|
||||
}
|
||||
log.info("个人模型调用成功");
|
||||
LoanPricingWorkflow workflowToUpdate = new LoanPricingWorkflow();
|
||||
workflowToUpdate.setId(loanPricingWorkflow.getId());
|
||||
workflowToUpdate.setModelOutputId(modelRetailOutputFields.getId());
|
||||
loanPricingWorkflowMapper.updateById(workflowToUpdate);
|
||||
log.info("更新流程信息成功");
|
||||
updateWorkflowModelOutputId(loanPricingWorkflow, modelRetailOutputFields.getId());
|
||||
}else if (loanPricingWorkflow.getCustType().equals("企业")){
|
||||
// 企业模型
|
||||
ModelCorpOutputFields modelCorpOutputFields = modelService.invokeCorporateModel(modelInvokeDTO);
|
||||
modelCorpOutputFieldsMapper.insert(modelCorpOutputFields);
|
||||
if (overwriteModelOutput && Objects.nonNull(loanPricingWorkflow.getModelOutputId()))
|
||||
{
|
||||
modelCorpOutputFields.setId(loanPricingWorkflow.getModelOutputId());
|
||||
modelCorpOutputFieldsMapper.updateById(modelCorpOutputFields);
|
||||
}
|
||||
else
|
||||
{
|
||||
modelCorpOutputFieldsMapper.insert(modelCorpOutputFields);
|
||||
}
|
||||
log.info("企业模型调用成功");
|
||||
LoanPricingWorkflow workflowToUpdate = new LoanPricingWorkflow();
|
||||
workflowToUpdate.setId(loanPricingWorkflow.getId());
|
||||
workflowToUpdate.setModelOutputId(modelCorpOutputFields.getId());
|
||||
loanPricingWorkflowMapper.updateById(workflowToUpdate);
|
||||
log.info("更新流程信息成功");
|
||||
updateWorkflowModelOutputId(loanPricingWorkflow, modelCorpOutputFields.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWorkflowModelOutputId(LoanPricingWorkflow loanPricingWorkflow, Long modelOutputId)
|
||||
{
|
||||
LoanPricingWorkflow workflowToUpdate = new LoanPricingWorkflow();
|
||||
workflowToUpdate.setId(loanPricingWorkflow.getId());
|
||||
workflowToUpdate.setModelOutputId(modelOutputId);
|
||||
loanPricingWorkflowMapper.updateById(workflowToUpdate);
|
||||
log.info("更新流程信息成功");
|
||||
}
|
||||
|
||||
private void normalizePersonalModelInvokeDTO(ModelInvokeDTO modelInvokeDTO)
|
||||
{
|
||||
modelInvokeDTO.setBizProof(toZeroOne(modelInvokeDTO.getBizProof()));
|
||||
@@ -98,6 +123,7 @@ public class LoanPricingModelService {
|
||||
private void normalizeCorporateModelInvokeDTO(ModelInvokeDTO modelInvokeDTO)
|
||||
{
|
||||
modelInvokeDTO.setCollThirdParty(toZeroOne(modelInvokeDTO.getCollThirdParty()));
|
||||
modelInvokeDTO.setResCover(toZeroOne(modelInvokeDTO.getResCover()));
|
||||
modelInvokeDTO.setIsGreenLoan(toZeroOne(modelInvokeDTO.getIsGreenLoan()));
|
||||
modelInvokeDTO.setIsTradeBuildEnt(toZeroOne(modelInvokeDTO.getIsTradeBuildEnt()));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.loanpricing.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
@@ -47,6 +48,10 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
|
||||
private static final String WORKFLOW_ADMIN_ROLE_KEY = "headAdmin";
|
||||
|
||||
private static final String WORKFLOW_BRANCH_ADMIN_ROLE_NAME = "支行管理员";
|
||||
|
||||
private static final String WORKFLOW_BRANCH_ADMIN_ROLE_KEY = "branchAdmin";
|
||||
|
||||
@Resource
|
||||
private LoanPricingWorkflowMapper loanPricingWorkflowMapper;
|
||||
|
||||
@@ -93,6 +98,7 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
loanPricingWorkflow.setRunType("1");
|
||||
}
|
||||
|
||||
loanPricingWorkflow.setDeptId(SecurityUtils.getDeptId());
|
||||
loanPricingWorkflow.setCustName(sensitiveFieldCryptoService.encrypt(loanPricingWorkflow.getCustName()));
|
||||
loanPricingWorkflow.setIdNum(sensitiveFieldCryptoService.encrypt(loanPricingWorkflow.getIdNum()));
|
||||
loanPricingWorkflowMapper.insert(loanPricingWorkflow);
|
||||
@@ -193,6 +199,97 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
return createLoanPricing(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanPricingWorkflow selectEditableLoanPricingBySerialNum(String serialNum)
|
||||
{
|
||||
LoanPricingWorkflow workflow = selectWorkflowBySerialNum(serialNum);
|
||||
assertCurrentUserIsCreator(workflow);
|
||||
workflow.setCustName(sensitiveFieldCryptoService.decrypt(workflow.getCustName()));
|
||||
workflow.setIdNum(sensitiveFieldCryptoService.decrypt(workflow.getIdNum()));
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public LoanPricingWorkflow updatePersonalLoanPricing(String serialNum, PersonalLoanPricingCreateDTO dto)
|
||||
{
|
||||
LoanPricingWorkflow editingWorkflow = LoanPricingConverter.toEntity(dto);
|
||||
return updateLoanPricing(serialNum, editingWorkflow, "个人");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public LoanPricingWorkflow updateCorporateLoanPricing(String serialNum, CorporateLoanPricingCreateDTO dto)
|
||||
{
|
||||
LoanPricingWorkflow editingWorkflow = LoanPricingConverter.toEntity(dto);
|
||||
return updateLoanPricing(serialNum, editingWorkflow, "企业");
|
||||
}
|
||||
|
||||
private LoanPricingWorkflow updateLoanPricing(String serialNum, LoanPricingWorkflow editingWorkflow, String expectedCustType)
|
||||
{
|
||||
LoanPricingWorkflow existingWorkflow = selectWorkflowBySerialNum(serialNum);
|
||||
assertCurrentUserIsCreator(existingWorkflow);
|
||||
if (!expectedCustType.equals(existingWorkflow.getCustType()))
|
||||
{
|
||||
throw new ServiceException("客户类型不匹配,不能编辑该流程");
|
||||
}
|
||||
|
||||
editingWorkflow.setId(existingWorkflow.getId());
|
||||
editingWorkflow.setSerialNum(existingWorkflow.getSerialNum());
|
||||
editingWorkflow.setCustType(existingWorkflow.getCustType());
|
||||
editingWorkflow.setModelOutputId(existingWorkflow.getModelOutputId());
|
||||
validateBusinessTypeAndHistoryRate(editingWorkflow);
|
||||
validateCollateralTypeAndCouponRate(editingWorkflow);
|
||||
|
||||
String encryptedCustName = sensitiveFieldCryptoService.encrypt(editingWorkflow.getCustName());
|
||||
String encryptedIdNum = sensitiveFieldCryptoService.encrypt(editingWorkflow.getIdNum());
|
||||
LambdaUpdateWrapper<LoanPricingWorkflow> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(LoanPricingWorkflow::getId, existingWorkflow.getId())
|
||||
.set(LoanPricingWorkflow::getCustIsn, editingWorkflow.getCustIsn())
|
||||
.set(LoanPricingWorkflow::getCustName, encryptedCustName)
|
||||
.set(LoanPricingWorkflow::getIdType, editingWorkflow.getIdType())
|
||||
.set(LoanPricingWorkflow::getIdNum, encryptedIdNum)
|
||||
.set(LoanPricingWorkflow::getGuarType, editingWorkflow.getGuarType())
|
||||
.set(LoanPricingWorkflow::getApplyAmt, editingWorkflow.getApplyAmt())
|
||||
.set(LoanPricingWorkflow::getBusinessType, editingWorkflow.getBusinessType())
|
||||
.set(LoanPricingWorkflow::getLoanRateHistory, editingWorkflow.getLoanRateHistory())
|
||||
.set(LoanPricingWorkflow::getCouponRate, editingWorkflow.getCouponRate())
|
||||
.set(LoanPricingWorkflow::getLoanTerm, editingWorkflow.getLoanTerm())
|
||||
.set(LoanPricingWorkflow::getCollType, editingWorkflow.getCollType())
|
||||
.set(LoanPricingWorkflow::getCollThirdParty, editingWorkflow.getCollThirdParty())
|
||||
.set(LoanPricingWorkflow::getLoanLoop, editingWorkflow.getLoanLoop())
|
||||
.set(LoanPricingWorkflow::getResCover, editingWorkflow.getResCover())
|
||||
.set(LoanPricingWorkflow::getRepayMethod, editingWorkflow.getRepayMethod())
|
||||
.set(LoanPricingWorkflow::getIsGreenLoan, editingWorkflow.getIsGreenLoan())
|
||||
.set(LoanPricingWorkflow::getIsTradeBuildEnt, editingWorkflow.getIsTradeBuildEnt())
|
||||
.set(LoanPricingWorkflow::getUpdateBy, buildCurrentCreateBy(SecurityUtils.getLoginUser()))
|
||||
.set(LoanPricingWorkflow::getUpdateTime, new Date());
|
||||
loanPricingWorkflowMapper.update(null, updateWrapper);
|
||||
loanPricingModelService.reinvokeModelAndOverwrite(existingWorkflow.getId());
|
||||
return selectEditableLoanPricingBySerialNum(serialNum);
|
||||
}
|
||||
|
||||
private LoanPricingWorkflow selectWorkflowBySerialNum(String serialNum)
|
||||
{
|
||||
LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(LoanPricingWorkflow::getSerialNum, serialNum);
|
||||
LoanPricingWorkflow workflow = loanPricingWorkflowMapper.selectOne(wrapper);
|
||||
if (workflow == null)
|
||||
{
|
||||
throw new ServiceException("记录不存在");
|
||||
}
|
||||
return workflow;
|
||||
}
|
||||
|
||||
private void assertCurrentUserIsCreator(LoanPricingWorkflow workflow)
|
||||
{
|
||||
String currentCreateBy = buildCurrentCreateBy(SecurityUtils.getLoginUser());
|
||||
if (!currentCreateBy.equals(workflow.getCreateBy()))
|
||||
{
|
||||
throw new ServiceException("只有创建者可以编辑该流程");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询利率定价流程列表
|
||||
*
|
||||
@@ -306,7 +403,15 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
{
|
||||
LoanPricingWorkflow scopedQuery = query == null ? new LoanPricingWorkflow() : query;
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (!canViewAllWorkflows(loginUser))
|
||||
if (canViewAllWorkflows(loginUser))
|
||||
{
|
||||
return scopedQuery;
|
||||
}
|
||||
if (canViewBranchWorkflows(loginUser))
|
||||
{
|
||||
scopedQuery.setDataScopeDeptId(loginUser.getDeptId() == null ? -1L : loginUser.getDeptId());
|
||||
}
|
||||
else
|
||||
{
|
||||
scopedQuery.setDataScopeCreateBy(buildCurrentCreateBy(loginUser));
|
||||
}
|
||||
@@ -331,6 +436,19 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
|| WORKFLOW_ADMIN_ROLE_KEY.equals(role.getRoleKey())));
|
||||
}
|
||||
|
||||
private boolean canViewBranchWorkflows(LoginUser loginUser)
|
||||
{
|
||||
List<SysRole> roles = loginUser.getUser().getRoles();
|
||||
if (roles == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return roles.stream().anyMatch(role -> role != null
|
||||
&& UserConstants.ROLE_NORMAL.equals(role.getStatus())
|
||||
&& (WORKFLOW_BRANCH_ADMIN_ROLE_NAME.equals(role.getRoleName())
|
||||
|| WORKFLOW_BRANCH_ADMIN_ROLE_KEY.equals(role.getRoleKey())));
|
||||
}
|
||||
|
||||
private String buildCurrentCreateBy(LoginUser loginUser)
|
||||
{
|
||||
SysUser user = loginUser.getUser();
|
||||
|
||||
@@ -57,6 +57,7 @@ class LoanPricingModelServiceTest
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdNum("cipher-id");
|
||||
workflow.setRepayMethod("分期");
|
||||
workflow.setResCover("true");
|
||||
workflow.setIsGreenLoan("true");
|
||||
workflow.setIsTradeBuildEnt("false");
|
||||
workflow.setCollThirdParty("true");
|
||||
@@ -67,6 +68,7 @@ class LoanPricingModelServiceTest
|
||||
|
||||
ModelInvokeDTO request = context.modelService.corporateRequest;
|
||||
assertEquals("分期", request.getRepayMethod());
|
||||
assertEquals("1", request.getResCover());
|
||||
assertEquals("1", request.getIsGreenLoan());
|
||||
assertEquals("0", request.getIsTradeBuildEnt());
|
||||
assertEquals("1", request.getCollThirdParty());
|
||||
@@ -77,6 +79,24 @@ class LoanPricingModelServiceTest
|
||||
assertEquals(1, context.corpInsertCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOverwriteExistingCorporateModelOutputWhenReinvokingModel() throws Exception
|
||||
{
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
workflow.setId(4L);
|
||||
workflow.setModelOutputId(88L);
|
||||
workflow.setCustType("企业");
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdNum("cipher-id");
|
||||
TestContext context = createContext(workflow);
|
||||
|
||||
context.service.reinvokeModelAndOverwrite(4L);
|
||||
|
||||
assertEquals(1, context.modelService.corporateCalls.get());
|
||||
assertEquals(0, context.corpInsertCount.get());
|
||||
assertEquals(1, context.corpUpdateCount.get());
|
||||
}
|
||||
|
||||
private static LoanPricingWorkflow personalWorkflow(Long id)
|
||||
{
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
@@ -96,15 +116,17 @@ class LoanPricingModelServiceTest
|
||||
context.workflow = workflow;
|
||||
context.updatedWorkflow = new AtomicReference<>();
|
||||
context.retailInsertCount = new AtomicInteger();
|
||||
context.retailUpdateCount = new AtomicInteger();
|
||||
context.corpInsertCount = new AtomicInteger();
|
||||
context.corpUpdateCount = new AtomicInteger();
|
||||
|
||||
setField(context.service, "modelService", context.modelService);
|
||||
setField(context.service, "loanPricingWorkflowMapper",
|
||||
workflowMapper(context.workflow, context.updatedWorkflow));
|
||||
setField(context.service, "modelRetailOutputFieldsMapper",
|
||||
insertCountingMapper(ModelRetailOutputFieldsMapper.class, context.retailInsertCount));
|
||||
savingMapper(ModelRetailOutputFieldsMapper.class, context.retailInsertCount, context.retailUpdateCount));
|
||||
setField(context.service, "modelCorpOutputFieldsMapper",
|
||||
insertCountingMapper(ModelCorpOutputFieldsMapper.class, context.corpInsertCount));
|
||||
savingMapper(ModelCorpOutputFieldsMapper.class, context.corpInsertCount, context.corpUpdateCount));
|
||||
setField(context.service, "sensitiveFieldCryptoService", new TestSensitiveFieldCryptoService());
|
||||
return context;
|
||||
}
|
||||
@@ -126,7 +148,7 @@ class LoanPricingModelServiceTest
|
||||
});
|
||||
}
|
||||
|
||||
private static <T> T insertCountingMapper(Class<T> mapperClass, AtomicInteger insertCount)
|
||||
private static <T> T savingMapper(Class<T> mapperClass, AtomicInteger insertCount, AtomicInteger updateCount)
|
||||
{
|
||||
return proxy(mapperClass, (proxy, method, args) -> {
|
||||
if ("insert".equals(method.getName()))
|
||||
@@ -134,6 +156,11 @@ class LoanPricingModelServiceTest
|
||||
insertCount.incrementAndGet();
|
||||
return 1;
|
||||
}
|
||||
if ("updateById".equals(method.getName()))
|
||||
{
|
||||
updateCount.incrementAndGet();
|
||||
return 1;
|
||||
}
|
||||
return defaultValue(method);
|
||||
});
|
||||
}
|
||||
@@ -172,7 +199,9 @@ class LoanPricingModelServiceTest
|
||||
private LoanPricingWorkflow workflow;
|
||||
private AtomicReference<LoanPricingWorkflow> updatedWorkflow;
|
||||
private AtomicInteger retailInsertCount;
|
||||
private AtomicInteger retailUpdateCount;
|
||||
private AtomicInteger corpInsertCount;
|
||||
private AtomicInteger corpUpdateCount;
|
||||
}
|
||||
|
||||
private static class CapturingModelService extends ModelService
|
||||
|
||||
@@ -11,6 +11,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -100,6 +101,7 @@ class LoanPricingWorkflowServiceImplTest
|
||||
verify(loanPricingWorkflowMapper).insert(argThat((LoanPricingWorkflow entity) ->
|
||||
Objects.equals("cipher-name", entity.getCustName())
|
||||
&& Objects.equals("cipher-id", entity.getIdNum())
|
||||
&& Objects.equals(100L, entity.getDeptId())
|
||||
&& Objects.equals("CUST001", entity.getCustIsn())));
|
||||
}
|
||||
|
||||
@@ -148,6 +150,7 @@ class LoanPricingWorkflowServiceImplTest
|
||||
verify(loanPricingWorkflowMapper).selectWorkflowPageWithRates(any(), queryCaptor.capture());
|
||||
|
||||
assertNull(queryCaptor.getValue().getDataScopeCreateBy());
|
||||
assertNull(queryCaptor.getValue().getDataScopeDeptId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,6 +165,40 @@ class LoanPricingWorkflowServiceImplTest
|
||||
verify(loanPricingWorkflowMapper).selectWorkflowPageWithRates(any(), queryCaptor.capture());
|
||||
|
||||
assertNull(queryCaptor.getValue().getDataScopeCreateBy());
|
||||
assertNull(queryCaptor.getValue().getDataScopeDeptId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetCurrentDeptForBranchAdminWhenReturningPagedWorkflowList()
|
||||
{
|
||||
setLoginUser(102L, 101L, "8920100", "测试支行管理员", role(102L, "支行管理员", "branchAdmin"));
|
||||
when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(emptyPageResult());
|
||||
|
||||
loanPricingWorkflowService.selectLoanPricingPage(new Page<>(1, 10), new LoanPricingWorkflow());
|
||||
|
||||
ArgumentCaptor<LoanPricingWorkflow> queryCaptor = ArgumentCaptor.forClass(LoanPricingWorkflow.class);
|
||||
verify(loanPricingWorkflowMapper).selectWorkflowPageWithRates(any(), queryCaptor.capture());
|
||||
|
||||
assertEquals(101L, queryCaptor.getValue().getDataScopeDeptId());
|
||||
assertNull(queryCaptor.getValue().getDataScopeCreateBy());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepBranchAdminWithinDeptDataScopeWhenCreateByQueryIsProvided()
|
||||
{
|
||||
setLoginUser(102L, 101L, "8920100", "测试支行管理员", role(102L, "支行管理员", "branchAdmin"));
|
||||
LoanPricingWorkflow query = new LoanPricingWorkflow();
|
||||
query.setCreateBy("8920001");
|
||||
when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(emptyPageResult());
|
||||
|
||||
loanPricingWorkflowService.selectLoanPricingPage(new Page<>(1, 10), query);
|
||||
|
||||
ArgumentCaptor<LoanPricingWorkflow> queryCaptor = ArgumentCaptor.forClass(LoanPricingWorkflow.class);
|
||||
verify(loanPricingWorkflowMapper).selectWorkflowPageWithRates(any(), queryCaptor.capture());
|
||||
|
||||
assertEquals("8920001", queryCaptor.getValue().getCreateBy());
|
||||
assertEquals(101L, queryCaptor.getValue().getDataScopeDeptId());
|
||||
assertNull(queryCaptor.getValue().getDataScopeCreateBy());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,6 +230,7 @@ class LoanPricingWorkflowServiceImplTest
|
||||
|
||||
assertEquals("若依-admin", queryCaptor.getValue().getCreateBy());
|
||||
assertEquals("测试客户经理-8920001", queryCaptor.getValue().getDataScopeCreateBy());
|
||||
assertNull(queryCaptor.getValue().getDataScopeDeptId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -336,15 +374,151 @@ class LoanPricingWorkflowServiceImplTest
|
||||
dto.setLoanTerm("3");
|
||||
dto.setCollType("存单质押");
|
||||
dto.setCouponRate("2.35");
|
||||
dto.setResCover("1");
|
||||
|
||||
loanPricingWorkflowService.createCorporateLoanPricing(dto);
|
||||
|
||||
verify(loanPricingWorkflowMapper).insert(argThat((LoanPricingWorkflow entity) ->
|
||||
Objects.equals("企业", entity.getCustType())
|
||||
&& Objects.equals("新增", entity.getBusinessType())
|
||||
&& Objects.equals("1", entity.getResCover())
|
||||
&& Objects.equals("2.35", entity.getCouponRate())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEditableWorkflowWithPlainSensitiveFieldsForCreator()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("个人", "若依-admin");
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdNum("cipher-id");
|
||||
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("110101199001011234");
|
||||
|
||||
LoanPricingWorkflow result = loanPricingWorkflowService.selectEditableLoanPricingBySerialNum("P20260525001");
|
||||
|
||||
assertEquals("张三", result.getCustName());
|
||||
assertEquals("110101199001011234", result.getIdNum());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectEditableWorkflowWhenCurrentUserIsNotCreator()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("个人", "其他用户-8920001");
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> loanPricingWorkflowService.selectEditableLoanPricingBySerialNum("P20260525001"));
|
||||
|
||||
assertEquals("只有创建者可以编辑该流程", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdatePersonalWorkflowAndReinvokeModelForCreator()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("个人", "若依-admin");
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
when(sensitiveFieldCryptoService.encrypt("张三")).thenReturn("cipher-name-new");
|
||||
when(sensitiveFieldCryptoService.encrypt("110101199001019999")).thenReturn("cipher-id-new");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("110101199001019999");
|
||||
|
||||
PersonalLoanPricingCreateDTO dto = new PersonalLoanPricingCreateDTO();
|
||||
dto.setCustIsn("CUST001");
|
||||
dto.setCustName("张三");
|
||||
dto.setIdType("身份证");
|
||||
dto.setIdNum("110101199001019999");
|
||||
dto.setGuarType("信用");
|
||||
dto.setApplyAmt("200000");
|
||||
dto.setBusinessType("新增");
|
||||
dto.setLoanTerm("3");
|
||||
dto.setLoanLoop("1");
|
||||
|
||||
loanPricingWorkflowService.updatePersonalLoanPricing("P20260525001", dto);
|
||||
|
||||
ArgumentCaptor<LambdaUpdateWrapper> updateWrapperCaptor = ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
|
||||
verify(loanPricingWorkflowMapper).update(any(), updateWrapperCaptor.capture());
|
||||
assertTrue(updateWrapperCaptor.getValue().getSqlSet().contains("apply_amt"));
|
||||
verify(loanPricingModelService).reinvokeModelAndOverwrite(101L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateCorporateWorkflowAndReinvokeModelForCreator()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("企业", "若依-admin");
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
when(sensitiveFieldCryptoService.encrypt("测试科技有限公司")).thenReturn("cipher-corp-name");
|
||||
when(sensitiveFieldCryptoService.encrypt("91110000100000000X")).thenReturn("cipher-corp-id");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("测试科技有限公司");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("91110000100000000X");
|
||||
|
||||
CorporateLoanPricingCreateDTO dto = new CorporateLoanPricingCreateDTO();
|
||||
dto.setCustIsn("CORP001");
|
||||
dto.setCustName("测试科技有限公司");
|
||||
dto.setIdType("统一社会信用代码");
|
||||
dto.setIdNum("91110000100000000X");
|
||||
dto.setGuarType("质押");
|
||||
dto.setApplyAmt("300000");
|
||||
dto.setBusinessType("新增");
|
||||
dto.setLoanTerm("5");
|
||||
dto.setCollType("存单质押");
|
||||
dto.setCollThirdParty("1");
|
||||
dto.setCouponRate("2.35");
|
||||
dto.setResCover("1");
|
||||
dto.setIsGreenLoan("1");
|
||||
dto.setIsTradeBuildEnt("0");
|
||||
|
||||
loanPricingWorkflowService.updateCorporateLoanPricing("C20260525001", dto);
|
||||
|
||||
ArgumentCaptor<LambdaUpdateWrapper> updateWrapperCaptor = ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
|
||||
verify(loanPricingWorkflowMapper).update(any(), updateWrapperCaptor.capture());
|
||||
String sqlSet = updateWrapperCaptor.getValue().getSqlSet();
|
||||
assertTrue(sqlSet.contains("res_cover"), sqlSet);
|
||||
assertTrue(sqlSet.contains("is_green_loan"), sqlSet);
|
||||
assertTrue(sqlSet.contains("is_trade_construction"), sqlSet);
|
||||
assertTrue(sqlSet.contains("coll_third_party"), sqlSet);
|
||||
verify(loanPricingModelService).reinvokeModelAndOverwrite(101L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUpdateWhenCustomerTypeDoesNotMatch()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("个人", "若依-admin");
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
|
||||
CorporateLoanPricingCreateDTO dto = new CorporateLoanPricingCreateDTO();
|
||||
dto.setCustIsn("CORP001");
|
||||
dto.setGuarType("信用");
|
||||
dto.setApplyAmt("300000");
|
||||
dto.setBusinessType("新增");
|
||||
dto.setLoanTerm("5");
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> loanPricingWorkflowService.updateCorporateLoanPricing("P20260525001", dto));
|
||||
|
||||
assertEquals("客户类型不匹配,不能编辑该流程", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUpdateWhenCurrentUserIsNotCreator()
|
||||
{
|
||||
LoanPricingWorkflow workflow = editableWorkflow("个人", "其他用户-8920001");
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
|
||||
PersonalLoanPricingCreateDTO dto = new PersonalLoanPricingCreateDTO();
|
||||
dto.setCustIsn("CUST001");
|
||||
dto.setGuarType("信用");
|
||||
dto.setApplyAmt("200000");
|
||||
dto.setBusinessType("新增");
|
||||
dto.setLoanTerm("3");
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> loanPricingWorkflowService.updatePersonalLoanPricing("P20260525001", dto));
|
||||
|
||||
assertEquals("只有创建者可以编辑该流程", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseRetailModelOutputFinalCalculateRateForWorkflowDetail()
|
||||
{
|
||||
@@ -479,6 +653,25 @@ class LoanPricingWorkflowServiceImplTest
|
||||
return workflow;
|
||||
}
|
||||
|
||||
private LoanPricingWorkflow editableWorkflow(String custType, String createBy)
|
||||
{
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
workflow.setId(101L);
|
||||
workflow.setSerialNum("个人".equals(custType) ? "P20260525001" : "C20260525001");
|
||||
workflow.setModelOutputId(201L);
|
||||
workflow.setCustIsn("CUST001");
|
||||
workflow.setCustType(custType);
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdType("身份证");
|
||||
workflow.setIdNum("cipher-id");
|
||||
workflow.setGuarType("信用");
|
||||
workflow.setApplyAmt("100000");
|
||||
workflow.setBusinessType("新增");
|
||||
workflow.setLoanTerm("3");
|
||||
workflow.setCreateBy(createBy);
|
||||
return workflow;
|
||||
}
|
||||
|
||||
private Page<LoanPricingWorkflowListVO> emptyPageResult()
|
||||
{
|
||||
Page<LoanPricingWorkflowListVO> pageResult = new Page<>(1, 10);
|
||||
@@ -487,13 +680,19 @@ class LoanPricingWorkflowServiceImplTest
|
||||
}
|
||||
|
||||
private void setLoginUser(Long userId, String username, String nickName, SysRole... roles)
|
||||
{
|
||||
setLoginUser(userId, 100L, username, nickName, roles);
|
||||
}
|
||||
|
||||
private void setLoginUser(Long userId, Long deptId, String username, String nickName, SysRole... roles)
|
||||
{
|
||||
SysUser user = new SysUser();
|
||||
user.setUserId(userId);
|
||||
user.setDeptId(deptId);
|
||||
user.setUserName(username);
|
||||
user.setNickName(nickName);
|
||||
user.setRoles(java.util.Arrays.asList(roles));
|
||||
LoginUser loginUser = new LoginUser(userId, 100L, user, Collections.emptySet());
|
||||
LoginUser loginUser = new LoginUser(userId, deptId, user, Collections.emptySet());
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
@@ -17,6 +17,14 @@ export function getWorkflow(serialNum) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询利率定价流程编辑数据
|
||||
export function getWorkflowEdit(serialNum) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/' + serialNum + '/edit',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 创建个人客户利率定价流程
|
||||
export function createPersonalWorkflow(data) {
|
||||
return request({
|
||||
@@ -26,6 +34,15 @@ export function createPersonalWorkflow(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑个人客户利率定价流程
|
||||
export function updatePersonalWorkflow(serialNum, data) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/' + serialNum + '/personal',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 创建企业客户利率定价流程
|
||||
export function createCorporateWorkflow(data) {
|
||||
return request({
|
||||
@@ -35,6 +52,15 @@ export function createCorporateWorkflow(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑企业客户利率定价流程
|
||||
export function updateCorporateWorkflow(serialNum, data) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/' + serialNum + '/corporate',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询个人客户号映射
|
||||
export function queryPersonalCustomerMap(custId) {
|
||||
return request({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog title="新增企业利率定价流程" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
@close="handleClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px" class="workflow-create-form">
|
||||
<!-- 基本信息 -->
|
||||
@@ -59,6 +59,11 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="余值覆盖" prop="resCover">
|
||||
<el-switch v-model="form.resCover"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
@@ -128,7 +133,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createCorporateWorkflow, queryHistoryContracts} from "@/api/loanPricing/workflow"
|
||||
import {createCorporateWorkflow, queryHistoryContracts, updateCorporateWorkflow} from "@/api/loanPricing/workflow"
|
||||
import HistoryContractSelector from "./HistoryContractSelector"
|
||||
import {formatRate} from "@/utils/rate"
|
||||
|
||||
@@ -145,6 +150,10 @@ export default {
|
||||
customerMap: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
editData: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -191,6 +200,7 @@ export default {
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10'
|
||||
],
|
||||
submitting: false,
|
||||
resettingForm: false,
|
||||
showHistorySelector: false,
|
||||
historyLoading: false,
|
||||
historyContracts: [],
|
||||
@@ -207,6 +217,7 @@ export default {
|
||||
loanTerm: undefined,
|
||||
businessType: undefined,
|
||||
loanRateHistory: undefined,
|
||||
resCover: false,
|
||||
isGreenLoan: false,
|
||||
isTradeBuildEnt: false,
|
||||
collType: undefined,
|
||||
@@ -270,6 +281,12 @@ export default {
|
||||
isCertificatePledge() {
|
||||
return this.form.guarType === '质押' && this.form.collType === '存单质押'
|
||||
},
|
||||
isEdit() {
|
||||
return !!(this.editData && this.editData.serialNum)
|
||||
},
|
||||
dialogTitle() {
|
||||
return this.isEdit ? '编辑企业利率定价流程' : '新增企业利率定价流程'
|
||||
},
|
||||
collateralTypeOptions() {
|
||||
if (this.form.guarType === '抵押') {
|
||||
return ['一类', '二类', '三类', '四类', '排污权抵押', '设备等其他不动产抵押']
|
||||
@@ -287,11 +304,17 @@ export default {
|
||||
}
|
||||
},
|
||||
'form.guarType'(val, oldVal) {
|
||||
if (this.resettingForm) {
|
||||
return
|
||||
}
|
||||
if (val !== oldVal) {
|
||||
this.resetCollateralFields()
|
||||
}
|
||||
},
|
||||
'form.collType'() {
|
||||
if (this.resettingForm) {
|
||||
return
|
||||
}
|
||||
this.resetCouponRateIfNotCertificatePledge()
|
||||
}
|
||||
},
|
||||
@@ -299,7 +322,43 @@ export default {
|
||||
formatRate,
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
this.resettingForm = true
|
||||
this.form = this.buildForm()
|
||||
this.submitting = false
|
||||
this.showHistorySelector = false
|
||||
this.historyLoading = false
|
||||
this.historyContracts = []
|
||||
this.selectedHistoryContract = null
|
||||
this.$nextTick(() => {
|
||||
this.resettingForm = false
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
buildForm() {
|
||||
if (this.isEdit) {
|
||||
return {
|
||||
orgCode: this.editData.orgCode || '892000',
|
||||
runType: this.editData.runType || '1',
|
||||
custIsn: this.editData.custIsn,
|
||||
custName: this.editData.custName,
|
||||
idType: this.editData.idType,
|
||||
idNum: this.editData.idNum,
|
||||
guarType: this.editData.guarType,
|
||||
applyAmt: this.editData.applyAmt,
|
||||
loanTerm: this.editData.loanTerm,
|
||||
businessType: this.editData.businessType,
|
||||
loanRateHistory: this.editData.loanRateHistory,
|
||||
resCover: this.isOne(this.editData.resCover),
|
||||
isGreenLoan: this.isOne(this.editData.isGreenLoan),
|
||||
isTradeBuildEnt: this.isOne(this.editData.isTradeBuildEnt),
|
||||
collType: this.editData.collType,
|
||||
collThirdParty: this.isOne(this.editData.collThirdParty),
|
||||
couponRate: this.editData.couponRate
|
||||
}
|
||||
}
|
||||
return {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: this.customerMap ? this.customerMap.cust_isn : undefined,
|
||||
@@ -311,22 +370,16 @@ export default {
|
||||
loanTerm: undefined,
|
||||
businessType: undefined,
|
||||
loanRateHistory: undefined,
|
||||
resCover: false,
|
||||
isGreenLoan: false,
|
||||
isTradeBuildEnt: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false,
|
||||
couponRate: undefined
|
||||
}
|
||||
this.submitting = false
|
||||
this.showHistorySelector = false
|
||||
this.historyLoading = false
|
||||
this.historyContracts = []
|
||||
this.selectedHistoryContract = null
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
isOne(value) {
|
||||
return value === true || value === 'true' || value === '1'
|
||||
},
|
||||
/** 对话框关闭处理 */
|
||||
handleClose() {
|
||||
@@ -415,6 +468,7 @@ export default {
|
||||
// 转换开关值为字符串
|
||||
const data = {
|
||||
...this.form,
|
||||
resCover: this.form.resCover ? '1' : '0',
|
||||
isGreenLoan: this.form.isGreenLoan ? '1' : '0',
|
||||
isTradeBuildEnt: this.form.isTradeBuildEnt ? '1' : '0'
|
||||
}
|
||||
@@ -431,8 +485,11 @@ export default {
|
||||
delete data.couponRate
|
||||
}
|
||||
|
||||
createCorporateWorkflow(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
const submitRequest = this.isEdit
|
||||
? updateCorporateWorkflow(this.editData.serialNum, data)
|
||||
: createCorporateWorkflow(data)
|
||||
submitRequest.then(response => {
|
||||
this.$modal.msgSuccess(this.isEdit ? "编辑成功" : "新增成功")
|
||||
this.dialogVisible = false
|
||||
this.$emit('success')
|
||||
}).catch(error => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog title="新增个人利率定价流程" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
@close="handleClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px" class="workflow-create-form">
|
||||
<!-- 基本信息 -->
|
||||
@@ -120,7 +120,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createPersonalWorkflow, queryHistoryContracts} from "@/api/loanPricing/workflow"
|
||||
import {createPersonalWorkflow, queryHistoryContracts, updatePersonalWorkflow} from "@/api/loanPricing/workflow"
|
||||
import HistoryContractSelector from "./HistoryContractSelector"
|
||||
import {formatRate} from "@/utils/rate"
|
||||
|
||||
@@ -137,6 +137,10 @@ export default {
|
||||
customerMap: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
editData: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -169,6 +173,7 @@ export default {
|
||||
'1', '2', '3', '4', '5', '6'
|
||||
],
|
||||
submitting: false,
|
||||
resettingForm: false,
|
||||
showHistorySelector: false,
|
||||
historyLoading: false,
|
||||
historyContracts: [],
|
||||
@@ -247,6 +252,12 @@ export default {
|
||||
isCertificatePledge() {
|
||||
return this.form.guarType === '质押' && this.form.collType === '存单质押'
|
||||
},
|
||||
isEdit() {
|
||||
return !!(this.editData && this.editData.serialNum)
|
||||
},
|
||||
dialogTitle() {
|
||||
return this.isEdit ? '编辑个人利率定价流程' : '新增个人利率定价流程'
|
||||
},
|
||||
collateralTypeOptions() {
|
||||
if (this.form.guarType === '抵押') {
|
||||
return ['一线', '一类', '二类', '三类']
|
||||
@@ -264,11 +275,17 @@ export default {
|
||||
}
|
||||
},
|
||||
'form.guarType'(val, oldVal) {
|
||||
if (this.resettingForm) {
|
||||
return
|
||||
}
|
||||
if (val !== oldVal) {
|
||||
this.resetCollateralFields()
|
||||
}
|
||||
},
|
||||
'form.collType'() {
|
||||
if (this.resettingForm) {
|
||||
return
|
||||
}
|
||||
this.resetCouponRateIfNotCertificatePledge()
|
||||
}
|
||||
},
|
||||
@@ -276,7 +293,41 @@ export default {
|
||||
formatRate,
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
this.resettingForm = true
|
||||
this.form = this.buildForm()
|
||||
this.submitting = false
|
||||
this.showHistorySelector = false
|
||||
this.historyLoading = false
|
||||
this.historyContracts = []
|
||||
this.selectedHistoryContract = null
|
||||
this.$nextTick(() => {
|
||||
this.resettingForm = false
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
buildForm() {
|
||||
if (this.isEdit) {
|
||||
return {
|
||||
orgCode: this.editData.orgCode || '892000',
|
||||
runType: this.editData.runType || '1',
|
||||
custIsn: this.editData.custIsn,
|
||||
custName: this.editData.custName,
|
||||
idType: this.editData.idType,
|
||||
idNum: this.editData.idNum,
|
||||
guarType: this.editData.guarType,
|
||||
applyAmt: this.editData.applyAmt,
|
||||
loanTerm: this.editData.loanTerm,
|
||||
businessType: this.editData.businessType,
|
||||
loanRateHistory: this.editData.loanRateHistory,
|
||||
loanLoop: this.isOne(this.editData.loanLoop),
|
||||
collType: this.editData.collType,
|
||||
collThirdParty: this.isOne(this.editData.collThirdParty),
|
||||
couponRate: this.editData.couponRate
|
||||
}
|
||||
}
|
||||
return {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: this.customerMap ? this.customerMap.cust_isn : undefined,
|
||||
@@ -293,16 +344,9 @@ export default {
|
||||
collThirdParty: false,
|
||||
couponRate: undefined
|
||||
}
|
||||
this.submitting = false
|
||||
this.showHistorySelector = false
|
||||
this.historyLoading = false
|
||||
this.historyContracts = []
|
||||
this.selectedHistoryContract = null
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
isOne(value) {
|
||||
return value === true || value === 'true' || value === '1'
|
||||
},
|
||||
/** 对话框关闭处理 */
|
||||
handleClose() {
|
||||
@@ -406,8 +450,11 @@ export default {
|
||||
delete data.couponRate
|
||||
}
|
||||
|
||||
createPersonalWorkflow(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
const submitRequest = this.isEdit
|
||||
? updatePersonalWorkflow(this.editData.serialNum, data)
|
||||
: createPersonalWorkflow(data)
|
||||
submitRequest.then(response => {
|
||||
this.$modal.msgSuccess(this.isEdit ? "编辑成功" : "新增成功")
|
||||
this.dialogVisible = false
|
||||
this.$emit('success')
|
||||
}).catch(error => {
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建者" align="center" prop="createBy" min-width="220" class-name="workflow-important-column" />
|
||||
<el-table-column label="操作" align="center" fixed="right" min-width="110" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" align="center" fixed="right" min-width="150" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
@@ -75,6 +75,13 @@
|
||||
icon="el-icon-view"
|
||||
@click="handleView(scope.row)"
|
||||
>查看</el-button>
|
||||
<el-button
|
||||
v-if="canEdit(scope.row)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(scope.row)"
|
||||
>编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -101,6 +108,7 @@
|
||||
<personal-create-dialog
|
||||
:visible.sync="showPersonalDialog"
|
||||
:customer-map="selectedCustomerMap"
|
||||
:edit-data="editWorkflow"
|
||||
@success="handleCreateSuccess"
|
||||
/>
|
||||
|
||||
@@ -108,13 +116,15 @@
|
||||
<corporate-create-dialog
|
||||
:visible.sync="showCorporateDialog"
|
||||
:customer-map="selectedCustomerMap"
|
||||
:edit-data="editWorkflow"
|
||||
@success="handleCreateSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listWorkflow} from "@/api/loanPricing/workflow"
|
||||
import {getWorkflowEdit, listWorkflow} from "@/api/loanPricing/workflow"
|
||||
import {mapGetters} from "vuex"
|
||||
import CustomerTypeSelector from "./components/CustomerTypeSelector"
|
||||
import CustomerMapSelector from "./components/CustomerMapSelector"
|
||||
import PersonalCreateDialog from "./components/PersonalCreateDialog"
|
||||
@@ -147,6 +157,8 @@ export default {
|
||||
selectedCustomerType: undefined,
|
||||
// 当前选择的客户号映射记录
|
||||
selectedCustomerMap: null,
|
||||
// 当前编辑的流程记录
|
||||
editWorkflow: null,
|
||||
// 是否显示个人客户创建弹出层
|
||||
showPersonalDialog: false,
|
||||
// 是否显示企业客户创建弹出层
|
||||
@@ -164,6 +176,15 @@ export default {
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'nickName'
|
||||
]),
|
||||
currentCreateBy() {
|
||||
return `${this.nickName}-${this.name}`
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
this.getList()
|
||||
},
|
||||
@@ -207,8 +228,26 @@ export default {
|
||||
params: { serialNum: row.serialNum }
|
||||
})
|
||||
},
|
||||
/** 是否允许编辑 */
|
||||
canEdit(row) {
|
||||
return row && row.createBy === this.currentCreateBy
|
||||
},
|
||||
/** 编辑操作 */
|
||||
handleEdit(row) {
|
||||
getWorkflowEdit(row.serialNum).then(response => {
|
||||
this.editWorkflow = response.data
|
||||
this.selectedCustomerMap = null
|
||||
this.selectedCustomerType = undefined
|
||||
if (this.editWorkflow.custType === '个人') {
|
||||
this.showPersonalDialog = true
|
||||
} else if (this.editWorkflow.custType === '企业') {
|
||||
this.showCorporateDialog = true
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.editWorkflow = null
|
||||
this.showTypeSelector = true
|
||||
},
|
||||
/** 选择客户类型回调 */
|
||||
@@ -219,6 +258,7 @@ export default {
|
||||
},
|
||||
/** 选择客户号映射记录回调 */
|
||||
handleCustomerMapSelect(row) {
|
||||
this.editWorkflow = null
|
||||
this.selectedCustomerMap = row
|
||||
if (this.selectedCustomerType === 'personal') {
|
||||
this.showPersonalDialog = true
|
||||
@@ -235,6 +275,7 @@ export default {
|
||||
clearSelectedCustomer() {
|
||||
this.selectedCustomerMap = null
|
||||
this.selectedCustomerType = undefined
|
||||
this.editWorkflow = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,15 @@ function read(relativePath) {
|
||||
const personalCreateDialog = read('src/views/loanPricing/workflow/components/PersonalCreateDialog.vue')
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('updatePersonalWorkflow') &&
|
||||
personalCreateDialog.includes("this.isEdit ? '编辑个人利率定价流程' : '新增个人利率定价流程'") &&
|
||||
personalCreateDialog.includes('editData.serialNum') &&
|
||||
personalCreateDialog.includes('updatePersonalWorkflow(this.editData.serialNum, data)') &&
|
||||
personalCreateDialog.includes(': createPersonalWorkflow(data)'),
|
||||
'个人弹窗应支持编辑模式回显并调用个人更新接口'
|
||||
)
|
||||
|
||||
assert(
|
||||
!personalCreateDialog.includes('label="贷款用途"') &&
|
||||
!personalCreateDialog.includes('prop="loanPurpose"') &&
|
||||
|
||||
@@ -20,7 +20,18 @@ function loadComponentOptions(filePath) {
|
||||
}
|
||||
|
||||
const stubImports = importNames.map(name => `const ${name} = {};`).join('\n')
|
||||
const transformed = `${stubImports}\n${scriptMatch[1]}`
|
||||
const namedImportStubs = `
|
||||
const listWorkflow = function() {};
|
||||
const getWorkflowEdit = function() {};
|
||||
const formatRate = function(value) { return value; };
|
||||
const mapGetters = function(names) {
|
||||
const result = {};
|
||||
names.forEach((name) => {
|
||||
result[name] = function() { return ''; };
|
||||
});
|
||||
return result;
|
||||
};`
|
||||
const transformed = `${stubImports}\n${namedImportStubs}\n${scriptMatch[1]}`
|
||||
.replace(/^import .*$/gm, '')
|
||||
.replace(/export default/, 'module.exports =')
|
||||
|
||||
@@ -37,8 +48,13 @@ function loadComponentOptions(filePath) {
|
||||
|
||||
const filePath = path.resolve(__dirname, '../src/views/loanPricing/workflow/index.vue')
|
||||
const component = loadComponentOptions(filePath)
|
||||
const source = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
assert.strictEqual(typeof component.activated, 'function', '流程列表页应在激活时刷新数据')
|
||||
assert(source.includes('icon="el-icon-edit"') && source.includes('>编辑</el-button>'), '流程列表操作列应包含编辑按钮')
|
||||
assert(source.includes('v-if="canEdit(scope.row)"'), '流程列表编辑按钮应只对可编辑行显示')
|
||||
assert(source.includes('row.createBy === this.currentCreateBy'), '流程列表应按创建者判断编辑按钮权限')
|
||||
assert(source.includes('getWorkflowEdit(row.serialNum)'), '编辑操作应先查询编辑数据')
|
||||
|
||||
let refreshCount = 0
|
||||
component.activated.call({
|
||||
@@ -49,4 +65,16 @@ component.activated.call({
|
||||
|
||||
assert.strictEqual(refreshCount, 1, '流程列表页激活时应调用一次 getList')
|
||||
|
||||
assert.strictEqual(component.methods.canEdit.call({
|
||||
currentCreateBy: '张三-8920001'
|
||||
}, {
|
||||
createBy: '张三-8920001'
|
||||
}), true, '创建者应允许编辑')
|
||||
|
||||
assert.strictEqual(component.methods.canEdit.call({
|
||||
currentCreateBy: '张三-8920001'
|
||||
}, {
|
||||
createBy: '李四-8920002'
|
||||
}), false, '非创建者不应允许编辑')
|
||||
|
||||
console.log('workflow-index-refresh test passed')
|
||||
|
||||
Reference in New Issue
Block a user