diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..9de0f16 --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,16 @@ +# CodeGraph data files +# These are local to each machine and should not be committed + +# Database +*.db +*.db-wal +*.db-shm + +# Cache +cache/ + +# Logs +*.log + +# Hook markers +.dirty diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutConstant.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutConstant.java new file mode 100644 index 0000000..8426bb7 --- /dev/null +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutConstant.java @@ -0,0 +1,10 @@ +package org.jeecg.inspectcloseout; + +public class InspectCloseoutConstant { + public static final String JJ_DEPT_ID = "2054466800692432898"; + public static final String JJ_DEPT_LEADER_ROLE_ID = "2044680793306591234"; + public static final String JJ_DEPT_WORKER_ROLE_ID = "2047511967494213633"; + public static final String SLD_DEPT_ID = "2044677562460508161"; + public static final String SLD_JWSJ_ROLE_ID = "2063902766943363073"; + public static final int ZERO_INDEX = 0; +} diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutFlow.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutFlow.java index 8ca5d22..1dfc91e 100644 --- a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutFlow.java +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectcloseout/InspectCloseoutFlow.java @@ -1,12 +1,16 @@ package org.jeecg.inspectcloseout; import com.alibaba.fastjson.JSONObject; +import com.jeecg.weibo.exception.BusinessException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.jeecg.common.system.api.ISysBaseAPI; +import org.jeecg.inspectimprove.InspectImproveConfig; import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.util.*; import java.util.stream.Collectors; @@ -18,6 +22,7 @@ public class InspectCloseoutFlow { private final InspectCloseoutConfig inspectCloseoutConfig; private final FlowNodeExpression flowNodeExpression; + private final ISysBaseAPI iSysBaseAPI; // =================================================================== // 通用工具方法 @@ -47,8 +52,77 @@ public class InspectCloseoutFlow { } } + //流程表达式内用法 ${inspectImproveFlow.getJJDeptLdUserIdList()} + public List getJJDeptLdUserIdList() { + + log.info("【巡视整改销号-流程表达式】查询部门角色用户列表, deptId: {}, roleId: {}", InspectCloseoutConstant.JJ_DEPT_ID, InspectCloseoutConstant.JJ_DEPT_LEADER_ROLE_ID); + List userList = iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi( + InspectCloseoutConstant.JJ_DEPT_ID, + InspectCloseoutConstant.JJ_DEPT_LEADER_ROLE_ID + ); + log.info("【巡视整改销号-流程表达式】查询到用户列表, userList: {}", userList); + return userList; + } + + //流程表达式内用法 ${inspectCloseoutFlow.getJJDeptLdUserIdListLength()} + public int getJJDeptLdUserIdListLength() { + List jjDeptLdUserIdList = this.getJJDeptLdUserIdList(); + if (jjDeptLdUserIdList != null && !jjDeptLdUserIdList.isEmpty()) { + return jjDeptLdUserIdList.size(); + } + log.error("【巡视整改销号-流程表达式】JJ部门LD角色没有对应用户"); + return 0; + } + + //流程表达式内用法 ${inspectCloseoutFlow.getJJDeptLdWorkerIdList()} + public List getJJDeptLdWorkerIdList() { + + log.info("【巡视整改销号-流程表达式】查询部门角色用户列表, deptId: {}, roleId: {}", InspectCloseoutConstant.JJ_DEPT_ID, InspectCloseoutConstant.JJ_DEPT_LEADER_ROLE_ID); + List userList = iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi( + InspectCloseoutConstant.JJ_DEPT_ID, + InspectCloseoutConstant.JJ_DEPT_WORKER_ROLE_ID + ); + log.info("【巡视整改销号-流程表达式】查询到用户列表, userList: {}", userList); + return userList; + } + + //流程表达式内用法 ${inspectCloseoutFlow.getJWSJUser()} + public String getJWSJUser() { + String deptId = InspectCloseoutConstant.SLD_DEPT_ID; + String roleId = InspectCloseoutConstant.SLD_JWSJ_ROLE_ID; + + log.info("【巡视整改销号-流程表达式】查询纪委书记用户, deptId: {}, roleId: {}", deptId, roleId); + + List userList = iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi(deptId, roleId); + + if (CollectionUtils.isEmpty(userList)) { + log.error("【巡视整改销号-流程表达式】未查询到纪委书记用户, deptId: {}, roleId: {}", deptId, roleId); + throw new BusinessException("流程流转失败:未配置纪委书记用户,请联系管理员!"); + } + + if (userList.size() > 1) { + log.error("【巡视整改销号-流程表达式】查询到多个纪委书记用户, 数量: {}, 用户列表: {}", userList.size(), userList); + throw new BusinessException("流程流转失败:存在多个纪委书记用户,数据不唯一,请检查角色配置!"); + } + + String targetUser = userList.get(InspectCloseoutConstant.ZERO_INDEX); + log.info("【巡视整改销号-流程表达式】匹配到纪委书记用户: {}", targetUser); + return targetUser; + } + + //流程表达式内用法 ${inspectCloseoutFlow.getJJDeptLdWorkerIdListLength()} + public int getJJDeptLdWorkerIdListLength() { + List jjDeptLdUserIdList = this.getJJDeptLdUserIdList(); + if (jjDeptLdUserIdList != null && !jjDeptLdUserIdList.isEmpty()) { + return jjDeptLdUserIdList.size(); + } + log.error("【巡视整改销号-流程表达式】JJ部门WORKER角色没有对应用户"); + return 0; + } + private List getListFromData(Object jsonData, String codeName, String logLabel) { if (jsonData == null || StringUtils.isBlank(codeName)) { + log.warn("【巡视整改销号-流程表达式】{}解析参数为空, codeName: {}", logLabel, codeName); return Collections.emptyList(); } try { @@ -57,23 +131,27 @@ public class InspectCloseoutFlow { data = (JSONObject) jsonData; } else if (jsonData instanceof String) { String jsonStr = (String) jsonData; - if (StringUtils.isBlank(jsonStr)) return Collections.emptyList(); + if (StringUtils.isBlank(jsonStr)) { + log.warn("【巡视整改销号-流程表达式】{}JSON字符串为空", logLabel); + return Collections.emptyList(); + } data = JSONObject.parseObject(jsonStr); } else { data = JSONObject.parseObject(JSONObject.toJSONString(jsonData)); } String rawValue = data.getString(codeName); if (StringUtils.isBlank(rawValue)) { + log.warn("【巡视整改销号-流程表达式】{}字段值为空, codeName: {}", logLabel, codeName); return Collections.emptyList(); } List result = Arrays.stream(rawValue.split(",")) .map(String::trim) .filter(StringUtils::isNotBlank) .collect(Collectors.toList()); - log.info("{} 解析结果: {}", logLabel, result); + log.info("【巡视整改销号-流程表达式】{}解析结果: {}", logLabel, result); return result; } catch (Exception e) { - log.warn("JSON解析失败: label={}, key={}", logLabel, codeName, e); + log.error("【巡视整改销号-流程表达式】{}JSON解析失败, codeName: {}", logLabel, codeName, e); return Collections.emptyList(); } } diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/InspectImproveFlow.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/InspectImproveFlow.java index 173f7bd..97cd0a4 100644 --- a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/InspectImproveFlow.java +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/InspectImproveFlow.java @@ -111,6 +111,26 @@ public class InspectImproveFlow { return InspectConstant.NEED_APPROVE_STR; } + // 流程表达式内用法 ${inspectImproveFlow.getMeasureResLeader(execution)} + public String getMeasureResLeader(DelegateExecution execution) { + DqInspectTask temDqInspectTask = this.getDqInspectTask(execution); + + if (temDqInspectTask == null) { + log.error("【巡视整改-流程表达式】获取措施责任领导时未找到业务表单数据, processInstanceId: {}", execution.getProcessInstanceId()); + throw new IllegalStateException("无法获取措施责任领导:未找到对应的业务表单数据,流程暂停。"); + } + + String measureResLeader = temDqInspectTask.getMeasureResLeader(); + if (StringUtils.isBlank(measureResLeader)) { + log.warn("【巡视整改-流程表达式】措施责任领导为空, businessKey: {}, processInstanceId: {}", + temDqInspectTask.getId(), execution.getProcessInstanceId()); + return null; + } + + log.info("【巡视整改-流程表达式】获取到措施责任领导: {}, businessKey: {}", measureResLeader, temDqInspectTask.getId()); + return measureResLeader; + } + // 流程表达式内用法:${inspectImproveFlow.getImplDeptLeaderList(execution)} public List getImplDeptLeaderList(DelegateExecution execution) { diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/listener/AfterMeasureResLeaderStoreListener.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/listener/AfterMeasureResLeaderStoreListener.java new file mode 100644 index 0000000..ad09dbf --- /dev/null +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/inspectimprove/listener/AfterMeasureResLeaderStoreListener.java @@ -0,0 +1,56 @@ +package org.jeecg.inspectimprove.listener; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.task.service.delegate.DelegateTask; +import org.jeecg.inspectimprove.InspectImproveConfig; +import org.jeecg.modules.demo.dqinspecttask.entity.DqInspectTask; +import org.jeecg.modules.demo.dqinspecttask.service.IDqInspectTaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 巡视整改-措施责任领导监听器:任务完成后将当前审批人写入 DqInspectTask.measureResLeader + */ +@Slf4j +@Component("AfterMeasureResLeaderStoreListener") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class AfterMeasureResLeaderStoreListener implements TaskListener { + + private static final long serialVersionUID = 1L; + + private final InspectImproveConfig inspectImproveConfig; + private final IDqInspectTaskService dqInspectTaskService; + + @Override + public void notify(DelegateTask delegateTask) { + String assignee = delegateTask.getAssignee(); + String taskId = delegateTask.getId(); + + if (StringUtils.isEmpty(assignee)) { + log.error("【巡视整改-措施责任领导存储监听器】未获取到审批人, taskId: {}", taskId); + return; + } + + Object businessKeyObj = delegateTask.getVariable(inspectImproveConfig.getInspectImprove().getBusinessKey()); + if (businessKeyObj == null || StringUtils.isBlank(businessKeyObj.toString())) { + log.error("【巡视整改-措施责任领导存储监听器】未获取到业务表单ID, taskId: {}", taskId); + return; + } + + String businessKey = businessKeyObj.toString(); + DqInspectTask task = dqInspectTaskService.getById(businessKey); + if (task == null) { + log.error("【巡视整改-措施责任领导存储监听器】未查询到业务表单记录, businessKey: {}", businessKey); + return; + } + + task.setMeasureResLeader(assignee); + dqInspectTaskService.updateById(task); + + log.info("【巡视整改-措施责任领导存储监听器】已将审批人 {} 写入 DqInspectTask.measureResLeader, businessKey: {}, taskId: {}", + assignee, businessKey, taskId); + } +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java new file mode 100644 index 0000000..b8d7622 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java @@ -0,0 +1,34 @@ +package org.jeecg.modules.dashboard.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; +import org.jeecg.modules.dashboard.service.IProcessPortalService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Tag(name = "流程门户") +@RestController +@RequestMapping("/dashboard/processPortal") +@Slf4j +public class ProcessPortalController { + + @Resource + private IProcessPortalService processPortalService; + + @Operation(summary = "流程门户-获取统计卡片数据") + @GetMapping("/stats") + public Result getStats() { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + String userId = sysUser.getUsername(); + ProcessPortalStatsDTO stats = processPortalService.getStats(userId); + return Result.OK(stats); + } +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java new file mode 100644 index 0000000..8987030 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java @@ -0,0 +1,22 @@ +package org.jeecg.modules.dashboard.dto; + +import lombok.Data; + +/** + * 流程门户 - 统计卡片 DTO + */ +@Data +public class ProcessPortalStatsDTO { + + /** 待办事项数 */ + private Long todoCount; + + /** 本月已办数 */ + private Long doneThisMonth; + + /** 平均时长 */ + private String avgDuration; + + /** 超时预警数 */ + private Long overdueCount; +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java new file mode 100644 index 0000000..c53b4ac --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java @@ -0,0 +1,57 @@ +package org.jeecg.modules.dashboard.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface ProcessPortalMapper { + + /** + * 当前用户待办任务数(直接分配 + 候选人/候选组) + * 与 ActivitiServiceImpl#findBaseTodoTasks 逻辑一致 + */ + @Select("SELECT COUNT(*) FROM (" + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "INNER JOIN ACT_RU_IDENTITYLINK I ON I.TASK_ID_ = RES.ID_ " + + "WHERE RES.ASSIGNEE_ IS NULL AND I.TYPE_ = 'candidate' " + + "AND (I.USER_ID_ = #{userId} OR I.GROUP_ID_ IN (SELECT g.GROUP_ID_ FROM ACT_ID_MEMBERSHIP g WHERE g.USER_ID_ = #{userId})) " + + "AND RES.SUSPENSION_STATE_ = 1) " + + "UNION " + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "WHERE RES.ASSIGNEE_ = #{userId} AND RES.SUSPENSION_STATE_ = 1)" + + ") T") + Long countTodo(@Param("userId") String userId); + + /** + * 当前用户本月已办任务数 + * 与 ActivitiMapper#getHistoryTasks 逻辑一致:用 DURATION_ > 0 判定已完成 + */ + @Select("SELECT COUNT(*) FROM ACT_HI_TASKINST " + + "WHERE ASSIGNEE_ = #{userId} AND DURATION_ > 0 AND END_TIME_ >= #{startOfMonth}") + Long countDoneThisMonth(@Param("userId") String userId, @Param("startOfMonth") String startOfMonth); + + /** + * 当前用户任务平均处理时长(毫秒) + */ + @Select("SELECT COALESCE(AVG(DURATION_), 0) FROM ACT_HI_TASKINST " + + "WHERE ASSIGNEE_ = #{userId} AND DURATION_ > 0") + Long avgDurationMillis(@Param("userId") String userId); + + /** + * 当前用户超时未完成任务数(直接分配 + 候选人/候选组) + * deadline 变量类型为 localdatetime,存储在 LONG_ 列(epoch millis) + */ + @Select("SELECT COUNT(DISTINCT T.ID_) FROM ACT_RU_TASK T " + + "INNER JOIN ACT_RU_VARIABLE V ON V.PROC_INST_ID_ = T.PROC_INST_ID_ AND V.NAME_ = 'deadline' " + + "WHERE T.SUSPENSION_STATE_ = 1 " + + "AND (T.ASSIGNEE_ = #{userId} " + + " OR T.ID_ IN (" + + " SELECT I.TASK_ID_ FROM ACT_RU_IDENTITYLINK I " + + " WHERE I.TYPE_ = 'candidate' " + + " AND (I.USER_ID_ = #{userId} OR I.GROUP_ID_ IN (SELECT g.GROUP_ID_ FROM ACT_ID_MEMBERSHIP g WHERE g.USER_ID_ = #{userId}))" + + " )) " + + "AND V.LONG_ IS NOT NULL " + + "AND FROM_UNIXTIME(V.LONG_ / 1000) < CURDATE()") + Long countOverdue(@Param("userId") String userId); +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java new file mode 100644 index 0000000..07040b0 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java @@ -0,0 +1,8 @@ +package org.jeecg.modules.dashboard.service; + +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; + +public interface IProcessPortalService { + + ProcessPortalStatsDTO getStats(String userId); +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java new file mode 100644 index 0000000..df2b4e5 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java @@ -0,0 +1,52 @@ +package org.jeecg.modules.dashboard.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; +import org.jeecg.modules.dashboard.mapper.ProcessPortalMapper; +import org.jeecg.modules.dashboard.service.IProcessPortalService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@Service +@Slf4j +public class ProcessPortalServiceImpl implements IProcessPortalService { + + @Resource + private ProcessPortalMapper processPortalMapper; + + @Override + public ProcessPortalStatsDTO getStats(String userId) { + ProcessPortalStatsDTO dto = new ProcessPortalStatsDTO(); + + String startOfMonth = LocalDate.now().withDayOfMonth(1).format(DateTimeFormatter.ISO_LOCAL_DATE) + " 00:00:00"; + + dto.setTodoCount(processPortalMapper.countTodo(userId)); + dto.setDoneThisMonth(processPortalMapper.countDoneThisMonth(userId, startOfMonth)); + + Long avgMillis = processPortalMapper.avgDurationMillis(userId); + dto.setAvgDuration(formatDuration(avgMillis)); + + dto.setOverdueCount(processPortalMapper.countOverdue(userId)); + + log.info("【流程门户-统计】查询统计完成, userId: {}, 待办: {}, 已办: {}, 平均时长: {}, 超时: {}", + userId, dto.getTodoCount(), dto.getDoneThisMonth(), dto.getAvgDuration(), dto.getOverdueCount()); + return dto; + } + + private String formatDuration(Long millis) { + if (millis == null || millis == 0) { + return "0时"; + } + double hours = millis / (1000.0 * 60 * 60); + if (hours < 1) { + return String.format("%.0f分", millis / (1000.0 * 60)); + } + if (hours < 24) { + return String.format("%.1f时", hours); + } + return String.format("%.1f天", hours / 24); + } +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/dqinspecttask/entity/DqInspectProgress.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/dqinspecttask/entity/DqInspectProgress.java index df6e7a5..37b85e8 100644 --- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/dqinspecttask/entity/DqInspectProgress.java +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/dqinspecttask/entity/DqInspectProgress.java @@ -84,7 +84,7 @@ public class DqInspectProgress implements Serializable { @Excel(name = "删除标识", width = 15) @Schema(description = "删除标识") @TableLogic - private java.lang.String delFlag; + private java.lang.String delFlag = "0"; /**整改措施任务ID*/ @Schema(description = "整改措施任务ID") private java.lang.String mainId; diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java index c9271a4..cba65da 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java @@ -80,26 +80,25 @@ public class LoginController { return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!"); } - // step.1 验证码check - String captcha = sysLoginModel.getCaptcha(); - if(captcha==null){ - result.error500("验证码无效"); - return result; - } - String lowerCaseCaptcha = captcha.toLowerCase(); - // 加入密钥作为混淆,避免简单的拼接,被外部利用,用户自定义该密钥即可 - //update-begin---author:chenrui ---date:20250107 for:[QQYUN-10775]验证码可以复用 #7674------------ - String keyPrefix = Md5Util.md5Encode(sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret(), "utf-8"); - String realKey = keyPrefix + lowerCaseCaptcha; - //update-end---author:chenrui ---date:20250107 for:[QQYUN-10775]验证码可以复用 #7674------------ - Object checkCode = redisUtil.get(realKey); - //当进入登录页时,有一定几率出现验证码错误 #1714 - if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) { - log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode); - result.error500("验证码错误"); - // 改成特殊的code 便于前端判断 - result.setCode(HttpStatus.PRECONDITION_FAILED.value()); - return result; + // step.1 验证码check(前端已隐藏验证码,后台兼容跳过校验) + String captcha = sysLoginModel.getCaptcha(); + String realKey = null; + if (oConvertUtils.isNotEmpty(captcha)) { + String lowerCaseCaptcha = captcha.toLowerCase(); + // 加入密钥作为混淆,避免简单的拼接,被外部利用,用户自定义该密钥即可 + //update-begin---author:chenrui ---date:20250107 for:[QQYUN-10775]验证码可以复用 #7674------------ + String keyPrefix = Md5Util.md5Encode(sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret(), "utf-8"); + realKey = keyPrefix + lowerCaseCaptcha; + //update-end---author:chenrui ---date:20250107 for:[QQYUN-10775]验证码可以复用 #7674------------ + Object checkCode = redisUtil.get(realKey); + //当进入登录页时,有一定几率出现验证码错误 #1714 + if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) { + log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode); + result.error500("验证码错误"); + // 改成特殊的code 便于前端判断 + result.setCode(HttpStatus.PRECONDITION_FAILED.value()); + return result; + } } // step.2 校验用户是否存在且有效 @@ -124,7 +123,9 @@ public class LoginController { userInfo(sysUser, result, request); // step.5 登录成功删除验证码 - redisUtil.del(realKey); + if (oConvertUtils.isNotEmpty(realKey)) { + redisUtil.del(realKey); + } redisUtil.del(CommonConstant.LOGIN_FAIL + username); // step.6 记录用户登录日志