实现项目打标状态联动并执行前后端适配
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ruoyi.ccdi.project.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目状态常量
|
||||||
|
*/
|
||||||
|
public final class CcdiProjectStatusConstants {
|
||||||
|
|
||||||
|
public static final String PROCESSING = "0";
|
||||||
|
public static final String COMPLETED = "1";
|
||||||
|
public static final String ARCHIVED = "2";
|
||||||
|
public static final String TAGGING = "3";
|
||||||
|
|
||||||
|
private CcdiProjectStatusConstants() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ public class CcdiProject implements Serializable {
|
|||||||
/** 配置方式:default-全局默认,custom-自定义 */
|
/** 配置方式:default-全局默认,custom-自定义 */
|
||||||
private String configType;
|
private String configType;
|
||||||
|
|
||||||
/** 项目状态:0-进行中,1-已完成,2-已归档 */
|
/** 项目状态:0-进行中,1-已完成,2-已归档,3-打标中 */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
/** 是否归档:0-未归档,1-已归档 */
|
/** 是否归档:0-未归档,1-已归档 */
|
||||||
|
|||||||
@@ -20,4 +20,7 @@ public class CcdiProjectStatusCountsVO {
|
|||||||
|
|
||||||
/** 已归档项目数(状态2) */
|
/** 已归档项目数(状态2) */
|
||||||
private Long status2;
|
private Long status2;
|
||||||
|
|
||||||
|
/** 打标中项目数(状态3) */
|
||||||
|
private Long status3;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,4 +59,28 @@ public interface ICcdiProjectService {
|
|||||||
* @return 状态统计
|
* @return 状态统计
|
||||||
*/
|
*/
|
||||||
CcdiProjectStatusCountsVO getStatusCounts();
|
CcdiProjectStatusCountsVO getStatusCounts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新项目状态
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @param status 状态编码
|
||||||
|
* @param operator 操作人
|
||||||
|
*/
|
||||||
|
void updateProjectStatus(Long projectId, String status, String operator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验项目是否允许进入打标流程
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID
|
||||||
|
*/
|
||||||
|
void ensureProjectCanStartTagging(Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验项目是否允许写入
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @param message 拒绝文案
|
||||||
|
*/
|
||||||
|
void ensureProjectWritable(Long projectId, String message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ruoyi.ccdi.project.service.impl;
|
package com.ruoyi.ccdi.project.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagResult;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagResult;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
|
||||||
@@ -13,6 +14,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
|||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -54,6 +56,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
@Resource
|
@Resource
|
||||||
private BankTagRuleConfigResolver configResolver;
|
private BankTagRuleConfigResolver configResolver;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Qualifier("tagRuleExecutor")
|
@Qualifier("tagRuleExecutor")
|
||||||
private Executor tagRuleExecutor;
|
private Executor tagRuleExecutor;
|
||||||
@@ -88,6 +93,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
*/
|
*/
|
||||||
public Long rebuildProject(Long projectId, String modelCode, String operator, TriggerType triggerType) {
|
public Long rebuildProject(Long projectId, String modelCode, String operator, TriggerType triggerType) {
|
||||||
long taskStartTime = System.currentTimeMillis();
|
long taskStartTime = System.currentTimeMillis();
|
||||||
|
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.TAGGING, operator);
|
||||||
CcdiBankTagTask task = buildRunningTask(projectId, modelCode, operator, triggerType);
|
CcdiBankTagTask task = buildRunningTask(projectId, modelCode, operator, triggerType);
|
||||||
taskMapper.insertTask(task);
|
taskMapper.insertTask(task);
|
||||||
log.info("【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}",
|
log.info("【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}",
|
||||||
@@ -128,6 +134,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
task.setUpdateBy(operator);
|
task.setUpdateBy(operator);
|
||||||
task.setUpdateTime(new Date());
|
task.setUpdateTime(new Date());
|
||||||
taskMapper.updateTask(task);
|
taskMapper.updateTask(task);
|
||||||
|
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.COMPLETED, operator);
|
||||||
log.info("【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}",
|
log.info("【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}",
|
||||||
task.getId(), projectId, modelCode, triggerType, rules.size(), allResults.size(),
|
task.getId(), projectId, modelCode, triggerType, rules.size(), allResults.size(),
|
||||||
System.currentTimeMillis() - taskStartTime);
|
System.currentTimeMillis() - taskStartTime);
|
||||||
@@ -140,6 +147,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
task.setUpdateBy(operator);
|
task.setUpdateBy(operator);
|
||||||
task.setUpdateTime(new Date());
|
task.setUpdateTime(new Date());
|
||||||
taskMapper.updateTask(task);
|
taskMapper.updateTask(task);
|
||||||
|
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.PROCESSING, operator);
|
||||||
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
|
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
|
||||||
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
|
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
|
|||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiFileUploadService;
|
import com.ruoyi.ccdi.project.service.ICcdiFileUploadService;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||||
import com.ruoyi.lsfx.constants.LsfxConstants;
|
import com.ruoyi.lsfx.constants.LsfxConstants;
|
||||||
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
|
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
|
||||||
@@ -96,6 +97,9 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiBankTagService bankTagService;
|
private ICcdiBankTagService bankTagService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取临时文件存储目录
|
* 获取临时文件存储目录
|
||||||
*/
|
*/
|
||||||
@@ -165,6 +169,8 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
throw new IllegalArgumentException("开始日期不能晚于结束日期");
|
throw new IllegalArgumentException("开始日期不能晚于结束日期");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
|
||||||
|
|
||||||
CcdiProject project = projectMapper.selectById(projectId);
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
if (project == null) {
|
if (project == null) {
|
||||||
throw new IllegalArgumentException("项目不存在: projectId=" + projectId);
|
throw new IllegalArgumentException("项目不存在: projectId=" + projectId);
|
||||||
@@ -311,6 +317,8 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
log.info("【文件上传】开始批量上传: projectId={}, 文件数量={}, username={}",
|
log.info("【文件上传】开始批量上传: projectId={}, 文件数量={}, username={}",
|
||||||
projectId, files.length, username);
|
projectId, files.length, username);
|
||||||
|
|
||||||
|
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
|
||||||
|
|
||||||
// 1. 生成批次ID
|
// 1. 生成批次ID
|
||||||
String batchId = UUID.randomUUID().toString().replace("-", "");
|
String batchId = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.ruoyi.ccdi.project.domain.vo.ModelGroupVO;
|
|||||||
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -46,6 +47,9 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectMapper projectMapper;
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ModelListVO> selectModelList(Long projectId) {
|
public List<ModelListVO> selectModelList(Long projectId) {
|
||||||
log.info("selectModelList 被调用,projectId={}", projectId);
|
log.info("selectModelList 被调用,projectId={}", projectId);
|
||||||
@@ -102,6 +106,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
|
|||||||
Long projectId = saveDTO.getProjectId();
|
Long projectId = saveDTO.getProjectId();
|
||||||
|
|
||||||
if (projectId > 0) {
|
if (projectId > 0) {
|
||||||
|
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
|
||||||
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
|
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +183,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
|
|||||||
Long projectId = saveAllDTO.getProjectId();
|
Long projectId = saveAllDTO.getProjectId();
|
||||||
|
|
||||||
if (projectId > 0) {
|
if (projectId > 0) {
|
||||||
|
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
|
||||||
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
|
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.ruoyi.ccdi.project.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
||||||
@@ -18,6 +19,8 @@ import org.springframework.beans.BeanUtils;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目Service实现类
|
* 项目Service实现类
|
||||||
*
|
*
|
||||||
@@ -43,7 +46,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
BeanUtils.copyProperties(dto, project);
|
BeanUtils.copyProperties(dto, project);
|
||||||
|
|
||||||
// 3. 设置默认值和流水分析平台ID
|
// 3. 设置默认值和流水分析平台ID
|
||||||
project.setStatus("0"); // 进行中
|
project.setStatus(CcdiProjectStatusConstants.PROCESSING);
|
||||||
project.setIsArchived(0); // 未归档
|
project.setIsArchived(0); // 未归档
|
||||||
project.setTargetCount(0);
|
project.setTargetCount(0);
|
||||||
project.setHighRiskCount(0);
|
project.setHighRiskCount(0);
|
||||||
@@ -115,27 +118,70 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
// 统计进行中项目(状态0)
|
// 统计进行中项目(状态0)
|
||||||
Long status0Count = projectMapper.selectCount(
|
Long status0Count = projectMapper.selectCount(
|
||||||
new LambdaQueryWrapper<CcdiProject>()
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
.eq(CcdiProject::getStatus, "0")
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING)
|
||||||
);
|
);
|
||||||
vo.setStatus0(status0Count);
|
vo.setStatus0(status0Count);
|
||||||
|
|
||||||
// 统计已完成项目(状态1)
|
// 统计已完成项目(状态1)
|
||||||
Long status1Count = projectMapper.selectCount(
|
Long status1Count = projectMapper.selectCount(
|
||||||
new LambdaQueryWrapper<CcdiProject>()
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
.eq(CcdiProject::getStatus, "1")
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED)
|
||||||
);
|
);
|
||||||
vo.setStatus1(status1Count);
|
vo.setStatus1(status1Count);
|
||||||
|
|
||||||
// 统计已归档项目(状态2)
|
// 统计已归档项目(状态2)
|
||||||
Long status2Count = projectMapper.selectCount(
|
Long status2Count = projectMapper.selectCount(
|
||||||
new LambdaQueryWrapper<CcdiProject>()
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
.eq(CcdiProject::getStatus, "2")
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED)
|
||||||
);
|
);
|
||||||
vo.setStatus2(status2Count);
|
vo.setStatus2(status2Count);
|
||||||
|
|
||||||
|
Long status3Count = projectMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING)
|
||||||
|
);
|
||||||
|
vo.setStatus3(status3Count);
|
||||||
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProjectStatus(Long projectId, String status, String operator) {
|
||||||
|
CcdiProject project = getRequiredProject(projectId);
|
||||||
|
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())
|
||||||
|
&& !CcdiProjectStatusConstants.ARCHIVED.equals(status)) {
|
||||||
|
throw new ServiceException("已归档项目不允许重新进入打标流程");
|
||||||
|
}
|
||||||
|
project.setStatus(status);
|
||||||
|
project.setUpdateBy(operator);
|
||||||
|
project.setUpdateTime(new Date());
|
||||||
|
projectMapper.updateById(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ensureProjectCanStartTagging(Long projectId) {
|
||||||
|
CcdiProject project = getRequiredProject(projectId);
|
||||||
|
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
|
||||||
|
throw new ServiceException("已归档项目不允许重新进入打标流程");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ensureProjectWritable(Long projectId, String message) {
|
||||||
|
CcdiProject project = getRequiredProject(projectId);
|
||||||
|
if (CcdiProjectStatusConstants.TAGGING.equals(project.getStatus())) {
|
||||||
|
throw new ServiceException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CcdiProject getRequiredProject(Long projectId) {
|
||||||
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
|
if (project == null) {
|
||||||
|
throw new ServiceException("项目不存在");
|
||||||
|
}
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用流水分析平台获取projectId
|
* 调用流水分析平台获取projectId
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.service.impl;
|
|||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||||
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -27,6 +28,9 @@ public class ProjectBankTagRebuildCoordinator {
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiBankTagServiceImpl bankTagService;
|
private CcdiBankTagServiceImpl bankTagService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交手动重算
|
* 提交手动重算
|
||||||
*
|
*
|
||||||
@@ -43,6 +47,7 @@ public class ProjectBankTagRebuildCoordinator {
|
|||||||
throw new ServiceException("当前项目标签正在重算中,请稍后再试");
|
throw new ServiceException("当前项目标签正在重算中,请稍后再试");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectService.ensureProjectCanStartTagging(projectId);
|
||||||
executeWithLock(projectId, () -> bankTagService.rebuildProject(projectId, modelCode, operator, TriggerType.MANUAL));
|
executeWithLock(projectId, () -> bankTagService.rebuildProject(projectId, modelCode, operator, TriggerType.MANUAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +67,7 @@ public class ProjectBankTagRebuildCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
executeWithLock(projectId, () -> {
|
executeWithLock(projectId, () -> {
|
||||||
|
projectService.ensureProjectCanStartTagging(projectId);
|
||||||
boolean needRerun;
|
boolean needRerun;
|
||||||
do {
|
do {
|
||||||
Long taskId = bankTagService.rebuildProject(projectId, null, "system", triggerType);
|
Long taskId = bankTagService.rebuildProject(projectId, null, "system", triggerType);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
|
|||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
@@ -56,6 +57,9 @@ class CcdiBankTagServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BankTagRuleConfigResolver configResolver;
|
private BankTagRuleConfigResolver configResolver;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks() {
|
void rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks() {
|
||||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||||
@@ -218,6 +222,42 @@ class CcdiBankTagServiceImplTest {
|
|||||||
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getFailedRuleCount() == 0));
|
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getFailedRuleCount() == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMarkProjectTaggingBeforeExecutingAndCompletedAfterSuccess() {
|
||||||
|
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||||
|
|
||||||
|
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
|
||||||
|
"HOUSE_OR_CAR_EXPENSE", "房车消费支出交易", "STATEMENT");
|
||||||
|
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
|
||||||
|
|
||||||
|
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
|
||||||
|
when(configResolver.resolve(40L, rule)).thenReturn(config);
|
||||||
|
when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of());
|
||||||
|
|
||||||
|
service.rebuildProject(40L, null, "tester", TriggerType.MANUAL);
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(projectService, taskMapper);
|
||||||
|
inOrder.verify(projectService).updateProjectStatus(40L, "3", "tester");
|
||||||
|
inOrder.verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus())));
|
||||||
|
inOrder.verify(projectService).updateProjectStatus(40L, "1", "tester");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRollbackProjectStatusToProcessingWhenRebuildFails() {
|
||||||
|
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||||
|
|
||||||
|
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
|
||||||
|
"HOUSE_OR_CAR_EXPENSE", "房车消费支出交易", "STATEMENT");
|
||||||
|
|
||||||
|
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
|
||||||
|
when(configResolver.resolve(40L, rule)).thenThrow(new RuntimeException("threshold missing"));
|
||||||
|
|
||||||
|
assertThrows(RuntimeException.class,
|
||||||
|
() -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));
|
||||||
|
|
||||||
|
verify(projectService).updateProjectStatus(40L, "0", "tester");
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiBankTagRule buildRule(String modelCode, String modelName, String ruleCode, String ruleName, String resultType) {
|
private CcdiBankTagRule buildRule(String modelCode, String modelName, String ruleCode, String ruleName, String resultType) {
|
||||||
CcdiBankTagRule rule = new CcdiBankTagRule();
|
CcdiBankTagRule rule = new CcdiBankTagRule();
|
||||||
rule.setModelCode(modelCode);
|
rule.setModelCode(modelCode);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
|
|||||||
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||||
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
|
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
|
||||||
import com.ruoyi.lsfx.domain.response.CheckParseStatusResponse;
|
import com.ruoyi.lsfx.domain.response.CheckParseStatusResponse;
|
||||||
@@ -89,6 +91,9 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private ICcdiBankTagService bankTagService;
|
private ICcdiBankTagService bankTagService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@TempDir
|
@TempDir
|
||||||
Path tempDir;
|
Path tempDir;
|
||||||
|
|
||||||
@@ -154,6 +159,38 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectPullBankInfoWhenProjectIsTagging() {
|
||||||
|
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许上传或拉取数据"))
|
||||||
|
.when(projectService).ensureProjectWritable(PROJECT_ID, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class,
|
||||||
|
() -> service.submitPullBankInfo(
|
||||||
|
PROJECT_ID,
|
||||||
|
List.of("3301"),
|
||||||
|
"2026-01-01",
|
||||||
|
"2026-01-31",
|
||||||
|
1L,
|
||||||
|
"tester"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectBatchUploadWhenProjectIsTagging() {
|
||||||
|
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许上传或拉取数据"))
|
||||||
|
.when(projectService).ensureProjectWritable(PROJECT_ID, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
|
||||||
|
|
||||||
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"files",
|
||||||
|
"test.xlsx",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"content".getBytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class,
|
||||||
|
() -> service.batchUploadFiles(PROJECT_ID, new MultipartFile[]{file}, "tester"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void submitTasksAsync_shouldNotCreateLocalBatchLogFiles() throws Exception {
|
void submitTasksAsync_shouldNotCreateLocalBatchLogFiles() throws Exception {
|
||||||
setField("uploadPath", tempDir.toString());
|
setField("uploadPath", tempDir.toString());
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ package com.ruoyi.ccdi.project.service.impl;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
|
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
|
import com.ruoyi.ccdi.project.domain.dto.ModelParamGroupDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
|
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveAllDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.dto.ParamValueItem;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
|
import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@@ -18,6 +23,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.anyList;
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -37,6 +43,9 @@ class CcdiModelParamServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private CcdiProjectMapper projectMapper;
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void selectAllParams_shouldReadSystemDefaultsForDefaultProject() {
|
void selectAllParams_shouldReadSystemDefaultsForDefaultProject() {
|
||||||
CcdiProject project = new CcdiProject();
|
CcdiProject project = new CcdiProject();
|
||||||
@@ -95,6 +104,30 @@ class CcdiModelParamServiceImplTest {
|
|||||||
verify(modelParamMapper).updateParamValue(123L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2222", "admin");
|
verify(modelParamMapper).updateParamValue(123L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2222", "admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectSaveAllParamsWhenProjectIsTagging() {
|
||||||
|
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许修改参数"))
|
||||||
|
.when(projectService).ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数");
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class, () -> service.saveAllParams(buildSaveAllDto()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectSaveParamsWhenProjectIsTagging() {
|
||||||
|
ModelParamSaveDTO saveDTO = new ModelParamSaveDTO();
|
||||||
|
saveDTO.setProjectId(40L);
|
||||||
|
saveDTO.setModelCode("LARGE_TRANSACTION");
|
||||||
|
ModelParamSaveDTO.ParamValueItem item = new ModelParamSaveDTO.ParamValueItem();
|
||||||
|
item.setParamCode("SINGLE_TRANSACTION_AMOUNT");
|
||||||
|
item.setParamValue("2000");
|
||||||
|
saveDTO.setParams(List.of(item));
|
||||||
|
|
||||||
|
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许修改参数"))
|
||||||
|
.when(projectService).ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数");
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class, () -> service.saveParams(saveDTO));
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiModelParam buildParam(
|
private CcdiModelParam buildParam(
|
||||||
Long id,
|
Long id,
|
||||||
Long projectId,
|
Long projectId,
|
||||||
@@ -114,4 +147,19 @@ class CcdiModelParamServiceImplTest {
|
|||||||
param.setSortOrder(1);
|
param.setSortOrder(1);
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ModelParamSaveAllDTO buildSaveAllDto() {
|
||||||
|
ParamValueItem item = new ParamValueItem();
|
||||||
|
item.setParamCode("SINGLE_TRANSACTION_AMOUNT");
|
||||||
|
item.setParamValue("2000");
|
||||||
|
|
||||||
|
ModelParamGroupDTO group = new ModelParamGroupDTO();
|
||||||
|
group.setModelCode("LARGE_TRANSACTION");
|
||||||
|
group.setParams(List.of(item));
|
||||||
|
|
||||||
|
ModelParamSaveAllDTO dto = new ModelParamSaveAllDTO();
|
||||||
|
dto.setProjectId(40L);
|
||||||
|
dto.setModels(List.of(group));
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.ruoyi.ccdi.project.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
|
||||||
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CcdiProjectServiceImplTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private CcdiProjectServiceImpl service;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCountTaggingProjectsSeparately() {
|
||||||
|
when(projectMapper.selectCount(any())).thenReturn(10L, 3L, 4L, 2L, 1L);
|
||||||
|
|
||||||
|
CcdiProjectStatusCountsVO counts = service.getStatusCounts();
|
||||||
|
|
||||||
|
assertEquals(1L, counts.getStatus3());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectUpdatingArchivedProjectToTagging() {
|
||||||
|
CcdiProject archived = new CcdiProject();
|
||||||
|
archived.setProjectId(99L);
|
||||||
|
archived.setStatus("2");
|
||||||
|
when(projectMapper.selectById(99L)).thenReturn(archived);
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class,
|
||||||
|
() -> service.updateProjectStatus(99L, "3", "system"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectWritingWhenProjectIsTagging() {
|
||||||
|
CcdiProject tagging = new CcdiProject();
|
||||||
|
tagging.setProjectId(40L);
|
||||||
|
tagging.setStatus("3");
|
||||||
|
when(projectMapper.selectById(40L)).thenReturn(tagging);
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class,
|
||||||
|
() -> service.ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import ch.qos.logback.core.read.ListAppender;
|
|||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||||
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||||
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@@ -33,6 +34,9 @@ class ProjectBankTagRebuildCoordinatorTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private CcdiBankTagServiceImpl bankTagService;
|
private CcdiBankTagServiceImpl bankTagService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void submitManualRebuild_shouldRejectWhenProjectAlreadyRunning() {
|
void submitManualRebuild_shouldRejectWhenProjectAlreadyRunning() {
|
||||||
CcdiBankTagTask runningTask = new CcdiBankTagTask();
|
CcdiBankTagTask runningTask = new CcdiBankTagTask();
|
||||||
@@ -109,4 +113,13 @@ class ProjectBankTagRebuildCoordinatorTest {
|
|||||||
logger.detachAppender(logAppender);
|
logger.detachAppender(logAppender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectSubmittingRebuildForArchivedProject() {
|
||||||
|
org.mockito.Mockito.doThrow(new ServiceException("已归档项目不允许重新进入打标流程"))
|
||||||
|
.when(projectService).ensureProjectCanStartTagging(40L);
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class,
|
||||||
|
() -> coordinator.submitManual(40L, null, "tester"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ruoyi.ccdi.project.sql;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class CcdiProjectStatusSqlTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldContainTaggingStatusInInitAndMigrationSql() throws IOException {
|
||||||
|
Path repoRoot = Path.of("..");
|
||||||
|
String initSql = Files.readString(repoRoot.resolve("sql/ccdi_project.sql"));
|
||||||
|
String migrationSql = Files.readString(repoRoot.resolve("sql/migration/2026-03-18-add-project-tagging-status.sql"));
|
||||||
|
|
||||||
|
assertTrue(initSql.contains("打标中"));
|
||||||
|
assertTrue(initSql.contains("'3'"));
|
||||||
|
assertTrue(migrationSql.contains("ccdi_project_status"));
|
||||||
|
assertTrue(migrationSql.contains("打标中"));
|
||||||
|
assertTrue(migrationSql.contains("'3'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# 项目打标状态联动后端实施记录
|
||||||
|
|
||||||
|
## 本次改动
|
||||||
|
|
||||||
|
- 新增项目状态常量 `3-打标中`,并补齐初始化 SQL 与增量 SQL。
|
||||||
|
- 在项目服务中新增统一状态更新、打标准入校验、打标中写保护能力,并把状态统计扩展到 `status3`。
|
||||||
|
- 在银行流水打标主链路中接入项目状态流转:
|
||||||
|
- 开始打标前置为 `3`
|
||||||
|
- 打标成功后置为 `1`
|
||||||
|
- 打标失败后回退为 `0`
|
||||||
|
- 在上传流水、拉取本行信息、参数保存链路中统一接入项目写保护,打标中直接拒绝写操作。
|
||||||
|
- 增补 SQL 基线测试、项目状态服务测试、打标状态流转测试、上传/参数写保护测试。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- `sql/ccdi_project.sql`
|
||||||
|
- `sql/migration/2026-03-18-add-project-tagging-status.sql`
|
||||||
|
- `ccdi-project` 模块中的项目服务、打标服务、协调器、文件上传服务、模型参数服务及对应测试
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 后端聚焦回归命令执行通过:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-project -Dtest=CcdiProjectServiceImplTest,CcdiBankTagServiceImplTest,ProjectBankTagRebuildCoordinatorTest,CcdiFileUploadServiceImplTest,CcdiModelParamServiceImplTest,CcdiProjectStatusSqlTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
- 测试结果:`Tests run: 44, Failures: 0, Errors: 0, Skipped: 0`
|
||||||
|
|
||||||
|
## SQL 执行方式
|
||||||
|
|
||||||
|
- 联调或生产前执行状态增量脚本时,必须使用仓库脚本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/mysql_utf8_exec.sh sql/migration/2026-03-18-add-project-tagging-status.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
- 这样可以保证中文字典值“打标中”在 MySQL 会话中按 `utf8mb4` 正确写入,避免乱码。
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# 项目打标状态联动前端实施记录
|
||||||
|
|
||||||
|
## 本次改动
|
||||||
|
|
||||||
|
- 在项目列表、详情页和顶部状态统计中补充 `3-打标中` 展示。
|
||||||
|
- 在上传数据页增加统一受限态:
|
||||||
|
- “拉取本行信息”按钮禁用
|
||||||
|
- “上传流水”入口禁用
|
||||||
|
- 页面顶部展示受限提示文案
|
||||||
|
- 在参数模型页增加只读态:
|
||||||
|
- 参数输入框禁用
|
||||||
|
- “保存所有修改”按钮禁用
|
||||||
|
- 页面顶部展示只读提示文案
|
||||||
|
- 在项目详情页接入 `refresh-project` 事件,上传页在以下时机触发父组件刷新项目状态:
|
||||||
|
- 批量上传提交成功后
|
||||||
|
- 拉取本行信息提交成功后
|
||||||
|
- 文件轮询检测到状态变化后
|
||||||
|
- 手工刷新文件列表后
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/index.vue`
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue`
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/components/SearchBar.vue`
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||||
|
- `docs/tests/records/2026-03-18-project-bank-tag-status-lock-frontend-verification.md`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 基线构建通过:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ruoyi-ui
|
||||||
|
npm run build:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
- 改动后构建通过:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ruoyi-ui
|
||||||
|
npm run build:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
- 当前已完成构建验证,尚未在本次记录中执行真实页面联调。
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# 项目打标状态联动后端验证记录
|
||||||
|
|
||||||
|
## 验证项
|
||||||
|
|
||||||
|
- [x] 状态 `3-打标中` SQL 已同步
|
||||||
|
- [x] 打标成功后状态为 `1`
|
||||||
|
- [x] 打标失败后状态回退为 `0`
|
||||||
|
- [x] 打标中拒绝上传/拉取本行信息
|
||||||
|
- [x] 打标中拒绝参数保存
|
||||||
|
|
||||||
|
## 自动化验证
|
||||||
|
|
||||||
|
- 执行时间:2026-03-18 14:56:22 +08:00
|
||||||
|
- 执行命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-project -Dtest=CcdiProjectServiceImplTest,CcdiBankTagServiceImplTest,ProjectBankTagRebuildCoordinatorTest,CcdiFileUploadServiceImplTest,CcdiModelParamServiceImplTest,CcdiProjectStatusSqlTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
- 结果:`PASS`
|
||||||
|
- 统计:`Tests run: 44, Failures: 0, Errors: 0, Skipped: 0`
|
||||||
|
|
||||||
|
## SQL 执行说明
|
||||||
|
|
||||||
|
- 联调环境执行增量脚本时,必须使用以下命令,确保中文内容以 `utf8mb4` 写入:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/mysql_utf8_exec.sh sql/migration/2026-03-18-add-project-tagging-status.sql
|
||||||
|
```
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# 项目打标状态联动前端验证记录
|
||||||
|
|
||||||
|
## 验证范围
|
||||||
|
|
||||||
|
- 列表页状态与统计显示
|
||||||
|
- 详情页状态标签
|
||||||
|
- 上传数据页禁用
|
||||||
|
- 参数模型页只读
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- [ ] 列表页出现“打标中”
|
||||||
|
- [ ] 顶部统计出现“打标中”
|
||||||
|
- [ ] 详情页出现“打标中”
|
||||||
|
- [ ] 打标中时“拉取本行信息”按钮禁用
|
||||||
|
- [ ] 打标中时“上传流水”按钮禁用
|
||||||
|
- [ ] 打标中时仍可查看上传记录列表
|
||||||
|
- [ ] 页面有明确提示文案
|
||||||
|
- [ ] 打标中时参数输入框禁用
|
||||||
|
- [ ] 打标中时“保存所有修改”按钮禁用
|
||||||
|
- [ ] 参数页有只读提示文案
|
||||||
|
- [ ] 提交上传或拉取任务后,详情页能重新获取项目状态
|
||||||
|
- [ ] 文件轮询期间如后端状态切为“打标中”,页面会同步受限
|
||||||
|
|
||||||
|
## 构建验证
|
||||||
|
|
||||||
|
- [x] `npm run build:prod` 基线构建通过(2026-03-18 14:56 +08:00)
|
||||||
|
- [x] `npm run build:prod` 改动后构建通过(2026-03-18 15:00 +08:00)
|
||||||
|
|
||||||
|
## 手工联调说明
|
||||||
|
|
||||||
|
- [ ] 已启动前后端与依赖服务进行联调
|
||||||
|
- [ ] 联调完成后已关闭测试过程中启动的进程
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
- 本次已完成前端静态实现与两轮生产构建验证。
|
||||||
|
- 真实页面联调尚未在本记录中勾选,如需补齐,请按计划启动前后端与依赖服务后继续验证。
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="scope.row.status === '0'"
|
v-if="scope.row.status === '0' || scope.row.status === '3'"
|
||||||
size="mini"
|
size="mini"
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-right"
|
icon="el-icon-right"
|
||||||
@@ -192,6 +192,7 @@ export default {
|
|||||||
0: "#1890ff",
|
0: "#1890ff",
|
||||||
1: "#52c41a",
|
1: "#52c41a",
|
||||||
2: "#8c8c8c",
|
2: "#8c8c8c",
|
||||||
|
3: "#fa8c16",
|
||||||
};
|
};
|
||||||
return colorMap[status] || "#8c8c8c";
|
return colorMap[status] || "#8c8c8c";
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export default {
|
|||||||
all: 0,
|
all: 0,
|
||||||
'0': 0,
|
'0': 0,
|
||||||
'1': 0,
|
'1': 0,
|
||||||
'2': 0
|
'2': 0,
|
||||||
|
'3': 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -51,7 +52,8 @@ export default {
|
|||||||
{ label: '全部项目', value: 'all', count: 0 },
|
{ label: '全部项目', value: 'all', count: 0 },
|
||||||
{ label: '进行中', value: '0', count: 0 },
|
{ label: '进行中', value: '0', count: 0 },
|
||||||
{ label: '已完成', value: '1', count: 0 },
|
{ label: '已完成', value: '1', count: 0 },
|
||||||
{ label: '已归档', value: '2', count: 0 }
|
{ label: '已归档', value: '2', count: 0 },
|
||||||
|
{ label: '打标中', value: '3', count: 0 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="param-config-container" v-loading="loading" element-loading-text="加载中...">
|
<div class="param-config-container" v-loading="loading" element-loading-text="加载中...">
|
||||||
|
<div v-if="isProjectTagging" class="readonly-tip">
|
||||||
|
项目正在进行银行流水打标,参数暂不可修改。
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 模型参数卡片组(垂直堆叠) -->
|
<!-- 模型参数卡片组(垂直堆叠) -->
|
||||||
<div class="model-cards-container" v-if="!loading && modelGroups.length > 0">
|
<div class="model-cards-container" v-if="!loading && modelGroups.length > 0">
|
||||||
<div
|
<div
|
||||||
@@ -20,6 +24,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="row.paramValue"
|
v-model="row.paramValue"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
placeholder="请输入阈值"
|
placeholder="请输入阈值"
|
||||||
@input="markAsModified(model.modelCode, row)"
|
@input="markAsModified(model.modelCode, row)"
|
||||||
/>
|
/>
|
||||||
@@ -37,7 +42,7 @@
|
|||||||
|
|
||||||
<!-- 统一保存按钮 -->
|
<!-- 统一保存按钮 -->
|
||||||
<div class="button-section" v-if="!loading && modelGroups.length > 0">
|
<div class="button-section" v-if="!loading && modelGroups.length > 0">
|
||||||
<el-button type="primary" @click="handleSaveAll" :loading="saving">
|
<el-button type="primary" :disabled="isProjectTagging || saving" @click="handleSaveAll" :loading="saving">
|
||||||
保存所有修改
|
保存所有修改
|
||||||
</el-button>
|
</el-button>
|
||||||
<span v-if="modifiedCount > 0" class="modified-tip">
|
<span v-if="modifiedCount > 0" class="modified-tip">
|
||||||
@@ -74,6 +79,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
modifiedCount() {
|
modifiedCount() {
|
||||||
return Object.keys(this.modifiedParams).length;
|
return Object.keys(this.modifiedParams).length;
|
||||||
|
},
|
||||||
|
isProjectTagging() {
|
||||||
|
return String(this.projectInfo.projectStatus) === "3";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -151,6 +159,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async handleSaveAll() {
|
async handleSaveAll() {
|
||||||
|
if (this.isProjectTagging) {
|
||||||
|
this.$message.warning("项目正在进行银行流水打标,参数暂不可修改");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.modifiedCount === 0) {
|
if (this.modifiedCount === 0) {
|
||||||
this.$message.info('没有需要保存的修改');
|
this.$message.info('没有需要保存的修改');
|
||||||
return;
|
return;
|
||||||
@@ -201,6 +213,15 @@ export default {
|
|||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.readonly-tip {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
color: #ad6800;
|
||||||
|
background: #fff7e6;
|
||||||
|
border: 1px solid #ffd591;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.model-cards-container {
|
.model-cards-container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
size="small"
|
size="small"
|
||||||
icon="el-icon-download"
|
icon="el-icon-download"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
@click="handleFetchBankInfo"
|
@click="handleFetchBankInfo"
|
||||||
>
|
>
|
||||||
拉取本行信息
|
拉取本行信息
|
||||||
@@ -24,6 +25,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isProjectTagging" class="tagging-lock-tip">
|
||||||
|
项目正在进行银行流水打标,暂不可上传或拉取数据。
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 上传模块 -->
|
<!-- 上传模块 -->
|
||||||
<div class="upload-section">
|
<div class="upload-section">
|
||||||
<div class="upload-cards">
|
<div class="upload-cards">
|
||||||
@@ -165,6 +170,7 @@
|
|||||||
class="upload-area"
|
class="upload-area"
|
||||||
drag
|
drag
|
||||||
action="#"
|
action="#"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:on-change="handleFileChange"
|
:on-change="handleFileChange"
|
||||||
:file-list="fileList"
|
:file-list="fileList"
|
||||||
@@ -179,6 +185,7 @@
|
|||||||
<el-button @click="showUploadDialog = false">取消</el-button>
|
<el-button @click="showUploadDialog = false">取消</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
@click="handleConfirmUpload"
|
@click="handleConfirmUpload"
|
||||||
:loading="uploading"
|
:loading="uploading"
|
||||||
>确定</el-button
|
>确定</el-button
|
||||||
@@ -231,6 +238,7 @@
|
|||||||
v-model="pullBankInfoForm.idCardText"
|
v-model="pullBankInfoForm.idCardText"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
placeholder="支持逗号、中文逗号、换行分隔"
|
placeholder="支持逗号、中文逗号、换行分隔"
|
||||||
/>
|
/>
|
||||||
<div class="pull-bank-field-tip">
|
<div class="pull-bank-field-tip">
|
||||||
@@ -248,6 +256,7 @@
|
|||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:file-list="idCardFileList"
|
:file-list="idCardFileList"
|
||||||
accept=".xls,.xlsx"
|
accept=".xls,.xlsx"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
:on-change="handleIdCardFileChange"
|
:on-change="handleIdCardFileChange"
|
||||||
:on-remove="handleIdCardFileRemove"
|
:on-remove="handleIdCardFileRemove"
|
||||||
>
|
>
|
||||||
@@ -279,6 +288,7 @@
|
|||||||
<el-date-picker
|
<el-date-picker
|
||||||
class="pull-bank-range-picker"
|
class="pull-bank-range-picker"
|
||||||
v-model="pullBankInfoForm.dateRange"
|
v-model="pullBankInfoForm.dateRange"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
type="daterange"
|
type="daterange"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="yyyy-MM-dd"
|
||||||
:picker-options="pullBankInfoDatePickerOptions"
|
:picker-options="pullBankInfoDatePickerOptions"
|
||||||
@@ -292,6 +302,7 @@
|
|||||||
<el-button @click="pullBankInfoDialogVisible = false">取消</el-button>
|
<el-button @click="pullBankInfoDialogVisible = false">取消</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:disabled="isProjectTagging"
|
||||||
:loading="pullBankInfoLoading"
|
:loading="pullBankInfoLoading"
|
||||||
@click="handleConfirmPullBankInfo"
|
@click="handleConfirmPullBankInfo"
|
||||||
>
|
>
|
||||||
@@ -312,6 +323,7 @@
|
|||||||
drag
|
drag
|
||||||
action="#"
|
action="#"
|
||||||
multiple
|
multiple
|
||||||
|
:disabled="isProjectTagging"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:on-change="handleBatchFileChange"
|
:on-change="handleBatchFileChange"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -350,8 +362,8 @@
|
|||||||
<el-button @click="batchUploadDialogVisible = false">取消</el-button>
|
<el-button @click="batchUploadDialogVisible = false">取消</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:disabled="isProjectTagging || selectedFiles.length === 0"
|
||||||
:loading="uploadLoading"
|
:loading="uploadLoading"
|
||||||
:disabled="selectedFiles.length === 0"
|
|
||||||
@click="handleBatchUpload"
|
@click="handleBatchUpload"
|
||||||
>开始上传</el-button
|
>开始上传</el-button
|
||||||
>
|
>
|
||||||
@@ -518,6 +530,7 @@ export default {
|
|||||||
pollingTimer: null,
|
pollingTimer: null,
|
||||||
pollingEnabled: false,
|
pollingEnabled: false,
|
||||||
pollingInterval: 5000,
|
pollingInterval: 5000,
|
||||||
|
lastFileStatusSignature: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -526,12 +539,21 @@ export default {
|
|||||||
disabledDate: (time) => this.isPullBankInfoDateDisabled(time),
|
disabledDate: (time) => this.isPullBankInfoDateDisabled(time),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
isProjectTagging() {
|
||||||
|
return String(this.projectInfo.projectStatus) === "3";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
"projectInfo.projectStatus"() {
|
||||||
|
this.syncUploadCardDisabledState();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
// 加载初始数据
|
// 加载初始数据
|
||||||
// this.loadInitialData();
|
// this.loadInitialData();
|
||||||
// 监听路由变化更新选中菜单
|
// 监听路由变化更新选中菜单
|
||||||
this.updateActiveMenu();
|
this.updateActiveMenu();
|
||||||
|
this.syncUploadCardDisabledState();
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// 组件挂载后监听项目ID变化
|
// 组件挂载后监听项目ID变化
|
||||||
@@ -597,6 +619,19 @@ export default {
|
|||||||
: card.btnText;
|
: card.btnText;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.syncUploadCardDisabledState();
|
||||||
|
},
|
||||||
|
syncUploadCardDisabledState() {
|
||||||
|
this.uploadCards = this.uploadCards.map((card) => {
|
||||||
|
if (card.key === "transaction") {
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
disabled: this.isProjectTagging,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 更新质量指标 */
|
/** 更新质量指标 */
|
||||||
@@ -660,6 +695,10 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 确认上传 */
|
/** 确认上传 */
|
||||||
async handleConfirmUpload() {
|
async handleConfirmUpload() {
|
||||||
|
if (this.isProjectTagging) {
|
||||||
|
this.$message.warning("项目正在进行银行流水打标,暂不可上传或拉取数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.fileList.length === 0) {
|
if (this.fileList.length === 0) {
|
||||||
this.$message.warning("请选择要上传的文件");
|
this.$message.warning("请选择要上传的文件");
|
||||||
return;
|
return;
|
||||||
@@ -919,6 +958,10 @@ export default {
|
|||||||
return this.parseIdCardText(this.pullBankInfoForm.idCardText);
|
return this.parseIdCardText(this.pullBankInfoForm.idCardText);
|
||||||
},
|
},
|
||||||
async handleConfirmPullBankInfo() {
|
async handleConfirmPullBankInfo() {
|
||||||
|
if (this.isProjectTagging) {
|
||||||
|
this.$message.warning("项目正在进行银行流水打标,暂不可上传或拉取数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const idCards = this.buildFinalIdCardList();
|
const idCards = this.buildFinalIdCardList();
|
||||||
const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
|
const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
|
||||||
|
|
||||||
@@ -953,6 +996,8 @@ export default {
|
|||||||
this.$message.success((res && res.msg) || "拉取任务已提交");
|
this.$message.success((res && res.msg) || "拉取任务已提交");
|
||||||
|
|
||||||
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
||||||
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
||||||
|
this.$emit("refresh-project");
|
||||||
|
|
||||||
const hasPollingRecords =
|
const hasPollingRecords =
|
||||||
this.statistics.uploading > 0 ||
|
this.statistics.uploading > 0 ||
|
||||||
@@ -973,6 +1018,10 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 拉取本行信息 */
|
/** 拉取本行信息 */
|
||||||
handleFetchBankInfo() {
|
handleFetchBankInfo() {
|
||||||
|
if (this.isProjectTagging) {
|
||||||
|
this.$message.warning("项目正在进行银行流水打标,暂不可上传或拉取数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.resetPullBankInfoForm();
|
this.resetPullBankInfoForm();
|
||||||
this.openPullBankInfoDialog();
|
this.openPullBankInfoDialog();
|
||||||
},
|
},
|
||||||
@@ -1009,6 +1058,7 @@ export default {
|
|||||||
0: "processing",
|
0: "processing",
|
||||||
1: "success",
|
1: "success",
|
||||||
2: "archived",
|
2: "archived",
|
||||||
|
3: "tagging",
|
||||||
};
|
};
|
||||||
return statusMap[status] || "processing";
|
return statusMap[status] || "processing";
|
||||||
},
|
},
|
||||||
@@ -1019,6 +1069,7 @@ export default {
|
|||||||
0: "进行中",
|
0: "进行中",
|
||||||
1: "已完成",
|
1: "已完成",
|
||||||
2: "已归档",
|
2: "已归档",
|
||||||
|
3: "打标中",
|
||||||
};
|
};
|
||||||
return statusMap[status] || "未知";
|
return statusMap[status] || "未知";
|
||||||
},
|
},
|
||||||
@@ -1059,6 +1110,10 @@ export default {
|
|||||||
|
|
||||||
/** 开始批量上传 */
|
/** 开始批量上传 */
|
||||||
async handleBatchUpload() {
|
async handleBatchUpload() {
|
||||||
|
if (this.isProjectTagging) {
|
||||||
|
this.$message.warning("项目正在进行银行流水打标,暂不可上传或拉取数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.selectedFiles.length === 0) {
|
if (this.selectedFiles.length === 0) {
|
||||||
this.$message.warning("请选择要上传的文件");
|
this.$message.warning("请选择要上传的文件");
|
||||||
return;
|
return;
|
||||||
@@ -1079,6 +1134,8 @@ export default {
|
|||||||
|
|
||||||
// 刷新数据并启动轮询
|
// 刷新数据并启动轮询
|
||||||
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
||||||
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
||||||
|
this.$emit("refresh-project");
|
||||||
|
|
||||||
this.startPolling();
|
this.startPolling();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1118,6 +1175,9 @@ export default {
|
|||||||
const res = await getFileUploadList(params);
|
const res = await getFileUploadList(params);
|
||||||
this.fileUploadList = res.rows || [];
|
this.fileUploadList = res.rows || [];
|
||||||
this.total = res.total || 0;
|
this.total = res.total || 0;
|
||||||
|
if (!this.lastFileStatusSignature) {
|
||||||
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$message.error("加载文件列表失败");
|
this.$message.error("加载文件列表失败");
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -1138,6 +1198,11 @@ export default {
|
|||||||
|
|
||||||
Promise.all([this.loadStatistics(), this.loadFileList()])
|
Promise.all([this.loadStatistics(), this.loadFileList()])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
const currentSignature = this.buildFileStatusSignature();
|
||||||
|
if (currentSignature !== this.lastFileStatusSignature) {
|
||||||
|
this.lastFileStatusSignature = currentSignature;
|
||||||
|
this.$emit("refresh-project");
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.statistics.uploading === 0 &&
|
this.statistics.uploading === 0 &&
|
||||||
this.statistics.parsing === 0
|
this.statistics.parsing === 0
|
||||||
@@ -1168,6 +1233,8 @@ export default {
|
|||||||
/** 手动刷新 */
|
/** 手动刷新 */
|
||||||
async handleManualRefresh() {
|
async handleManualRefresh() {
|
||||||
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
||||||
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
||||||
|
this.$emit("refresh-project");
|
||||||
|
|
||||||
this.$message.success("刷新成功");
|
this.$message.success("刷新成功");
|
||||||
|
|
||||||
@@ -1183,6 +1250,11 @@ export default {
|
|||||||
this.queryParams.pageNum = pageNum;
|
this.queryParams.pageNum = pageNum;
|
||||||
this.loadFileList();
|
this.loadFileList();
|
||||||
},
|
},
|
||||||
|
buildFileStatusSignature() {
|
||||||
|
return (this.fileUploadList || [])
|
||||||
|
.map((item) => `${item.id || ""}:${item.fileStatus || ""}`)
|
||||||
|
.join("|");
|
||||||
|
},
|
||||||
|
|
||||||
getRowAction(row) {
|
getRowAction(row) {
|
||||||
return getUploadFileAction(row.fileStatus);
|
return getUploadFileAction(row.fileStatus);
|
||||||
@@ -1401,6 +1473,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tagging-lock-tip {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
color: #ad6800;
|
||||||
|
background: #fff7e6;
|
||||||
|
border: 1px solid #ffd591;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
// 上传模块
|
// 上传模块
|
||||||
.upload-section {
|
.upload-section {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
:project-id="projectId"
|
:project-id="projectId"
|
||||||
:project-info="projectInfo"
|
:project-info="projectInfo"
|
||||||
@menu-change="handleMenuChange"
|
@menu-change="handleMenuChange"
|
||||||
|
@refresh-project="handleRefreshProject"
|
||||||
@data-uploaded="handleDataUploaded"
|
@data-uploaded="handleDataUploaded"
|
||||||
@name-selected="handleNameSelected"
|
@name-selected="handleNameSelected"
|
||||||
@generate-report="handleGenerateReport"
|
@generate-report="handleGenerateReport"
|
||||||
@@ -215,6 +216,7 @@ export default {
|
|||||||
0: "primary", // 进行中
|
0: "primary", // 进行中
|
||||||
1: "success", // 已完成
|
1: "success", // 已完成
|
||||||
2: "info", // 已归档
|
2: "info", // 已归档
|
||||||
|
3: "warning", // 打标中
|
||||||
};
|
};
|
||||||
return statusMap[status] || "info";
|
return statusMap[status] || "info";
|
||||||
},
|
},
|
||||||
@@ -224,6 +226,7 @@ export default {
|
|||||||
0: "进行中",
|
0: "进行中",
|
||||||
1: "已完成",
|
1: "已完成",
|
||||||
2: "已归档",
|
2: "已归档",
|
||||||
|
3: "打标中",
|
||||||
};
|
};
|
||||||
return statusMap[status] || "未知";
|
return statusMap[status] || "未知";
|
||||||
},
|
},
|
||||||
@@ -292,6 +295,9 @@ export default {
|
|||||||
this.initPageData();
|
this.initPageData();
|
||||||
this.$message.success("刷新成功");
|
this.$message.success("刷新成功");
|
||||||
},
|
},
|
||||||
|
handleRefreshProject() {
|
||||||
|
this.initPageData();
|
||||||
|
},
|
||||||
/** 导出报告 */
|
/** 导出报告 */
|
||||||
handleExport() {
|
handleExport() {
|
||||||
console.log("导出报告");
|
console.log("导出报告");
|
||||||
|
|||||||
@@ -101,7 +101,8 @@ export default {
|
|||||||
all: 0,
|
all: 0,
|
||||||
'0': 0,
|
'0': 0,
|
||||||
'1': 0,
|
'1': 0,
|
||||||
'2': 0
|
'2': 0,
|
||||||
|
'3': 0
|
||||||
},
|
},
|
||||||
// 新增/编辑弹窗
|
// 新增/编辑弹窗
|
||||||
addDialogVisible: false,
|
addDialogVisible: false,
|
||||||
@@ -137,7 +138,8 @@ export default {
|
|||||||
all: counts.all || 0,
|
all: counts.all || 0,
|
||||||
'0': counts.status0 || 0,
|
'0': counts.status0 || 0,
|
||||||
'1': counts.status1 || 0,
|
'1': counts.status1 || 0,
|
||||||
'2': counts.status2 || 0
|
'2': counts.status2 || 0,
|
||||||
|
'3': counts.status3 || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ CREATE TABLE `ccdi_project` (
|
|||||||
`project_name` VARCHAR(200) NOT NULL COMMENT '项目名称',
|
`project_name` VARCHAR(200) NOT NULL COMMENT '项目名称',
|
||||||
`description` VARCHAR(500) DEFAULT NULL COMMENT '项目描述',
|
`description` VARCHAR(500) DEFAULT NULL COMMENT '项目描述',
|
||||||
`config_type` VARCHAR(20) NOT NULL DEFAULT 'default' COMMENT '配置方式:default-全局默认,custom-自定义',
|
`config_type` VARCHAR(20) NOT NULL DEFAULT 'default' COMMENT '配置方式:default-全局默认,custom-自定义',
|
||||||
`status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '项目状态:0-进行中,1-已完成,2-已归档',
|
`status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '项目状态:0-进行中,1-已完成,2-已归档,3-打标中',
|
||||||
`is_archived` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否归档:0-未归档,1-已归档',
|
`is_archived` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否归档:0-未归档,1-已归档',
|
||||||
`target_count` INT NOT NULL DEFAULT 0 COMMENT '目标人数',
|
`target_count` INT NOT NULL DEFAULT 0 COMMENT '目标人数',
|
||||||
`high_risk_count` INT NOT NULL DEFAULT 0 COMMENT '高风险人数',
|
`high_risk_count` INT NOT NULL DEFAULT 0 COMMENT '高风险人数',
|
||||||
@@ -41,7 +41,8 @@ INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_cla
|
|||||||
VALUES
|
VALUES
|
||||||
(1, '进行中', '0', 'ccdi_project_status', '', 'primary', 'Y', '0', 'admin', NOW()),
|
(1, '进行中', '0', 'ccdi_project_status', '', 'primary', 'Y', '0', 'admin', NOW()),
|
||||||
(2, '已完成', '1', 'ccdi_project_status', '', 'success', 'N', '0', 'admin', NOW()),
|
(2, '已完成', '1', 'ccdi_project_status', '', 'success', 'N', '0', 'admin', NOW()),
|
||||||
(3, '已归档', '2', 'ccdi_project_status', '', 'info', 'N', '0', 'admin', NOW());
|
(3, '已归档', '2', 'ccdi_project_status', '', 'info', 'N', '0', 'admin', NOW()),
|
||||||
|
(4, '打标中', '3', 'ccdi_project_status', '', 'warning', 'N', '0', 'admin', NOW());
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- 4. 插入配置方式字典
|
-- 4. 插入配置方式字典
|
||||||
|
|||||||
32
sql/migration/2026-03-18-add-project-tagging-status.sql
Normal file
32
sql/migration/2026-03-18-add-project-tagging-status.sql
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
ALTER TABLE ccdi_project
|
||||||
|
MODIFY COLUMN status CHAR(1) NOT NULL DEFAULT '0' COMMENT '项目状态:0-进行中,1-已完成,2-已归档,3-打标中';
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (
|
||||||
|
dict_sort,
|
||||||
|
dict_label,
|
||||||
|
dict_value,
|
||||||
|
dict_type,
|
||||||
|
css_class,
|
||||||
|
list_class,
|
||||||
|
is_default,
|
||||||
|
status,
|
||||||
|
create_by,
|
||||||
|
create_time
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
4,
|
||||||
|
'打标中',
|
||||||
|
'3',
|
||||||
|
'ccdi_project_status',
|
||||||
|
'',
|
||||||
|
'warning',
|
||||||
|
'N',
|
||||||
|
'0',
|
||||||
|
'admin',
|
||||||
|
NOW()
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_dict_data
|
||||||
|
WHERE dict_type = 'ccdi_project_status'
|
||||||
|
AND dict_value = '3'
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user