新增流程列表编辑功能

This commit is contained in:
wkc
2026-05-25 16:04:23 +08:00
parent cc6836804e
commit 998f0b3c48
14 changed files with 746 additions and 50 deletions

View File

@@ -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。

View File

@@ -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。

View File

@@ -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));
}
/**
* 查询个人客户号映射
*/

View File

@@ -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);
/**
* 查询利率定价流程列表
*

View File

@@ -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()));
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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({

View File

@@ -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 => {

View File

@@ -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 => {

View File

@@ -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
}
}
}

View File

@@ -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"') &&

View File

@@ -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')