yxk-20260729

改善提案
在 Task_improve004 增加所级评议人员选择。经办人选择 improve_suoleader 角色中的一人或多人后,通过通用“提交/办理”保存名单、初始化待投票记录,并写入流程变量 suoleadervotelist,供 Task_improve005 多实例任务使用。
This commit is contained in:
ye1023
2026-07-29 10:18:06 +08:00
parent 3343d66093
commit eb021835f7
9 changed files with 188 additions and 1 deletions
@@ -28,6 +28,7 @@ import org.jeecg.modules.improvementproposal.entity.ImprovementDeptScore;
import org.jeecg.modules.improvementproposal.entity.ImprovementInstituteVote;
import org.jeecg.modules.improvementproposal.entity.ImprovementProposal;
import org.jeecg.modules.improvementproposal.dto.ImprovementDeptScoreBpmDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementProposalBpmActionDTO;
import org.jeecg.modules.improvementproposal.vo.ImprovementProposalPage;
import org.jeecg.modules.improvementproposal.service.IImprovementProposalService;
@@ -191,6 +192,27 @@ public class ImprovementProposalController {
return handleBpmAction(() -> improvementProposalService.saveLeaderAward(action, currentUser()));
}
@GetMapping(value = "/bpm/instituteVoteCandidates")
@Operation(summary = "查询所级评议投票候选人")
public Result<List<ImprovementInstituteVoteCandidateDTO>> getInstituteVoteCandidates(@RequestParam String mainId,
@RequestParam String processInstanceId, @RequestParam String taskId) {
ImprovementProposalBpmActionDTO action = new ImprovementProposalBpmActionDTO();
action.setMainId(mainId);
action.setProcessInstanceId(processInstanceId);
action.setTaskId(taskId);
try {
return Result.OK(improvementProposalService.getInstituteVoteCandidates(action, currentUser()));
} catch (IllegalStateException e) {
return Result.error(e.getMessage());
}
}
@PostMapping(value = "/bpm/instituteVoteMembers")
@Operation(summary = "保存所级评议投票人员")
public Result<String> saveInstituteVoteMembers(@RequestBody ImprovementProposalBpmActionDTO action) {
return handleBpmAction(() -> improvementProposalService.saveInstituteVoteMembers(action, currentUser()));
}
@PostMapping(value = "/bpm/archiveAward")
@Operation(summary = "保存归档最终奖项")
public Result<String> saveArchiveAward(@RequestBody ImprovementProposalBpmActionDTO action) {
@@ -0,0 +1,25 @@
package org.jeecg.modules.improvementproposal.dto;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 所级评议投票候选人。
* 仅暴露名单选择和 Flowable 多实例所需的最小用户信息。
*/
@Data
@Schema(description = "所级评议投票候选人")
public class ImprovementInstituteVoteCandidateDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID")
private String id;
@Schema(description = "登录用户名")
private String username;
@Schema(description = "真实姓名")
private String realname;
}
@@ -1,6 +1,7 @@
package org.jeecg.modules.improvementproposal.dto;
import java.io.Serializable;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -35,4 +36,11 @@ public class ImprovementProposalBpmActionDTO implements Serializable {
@Schema(description = "投票结果:agree/disagree")
private String voteResult;
/**
* 精益管理部门经办人在所级评议节点选择的投票人 ID 列表。
* 服务端会按固定角色重新校验该列表,不能将其作为投票人身份的信任来源。
*/
@Schema(description = "所级评议投票人ID列表")
private List<String> voterIds;
}
@@ -28,4 +28,7 @@ public interface ImprovementInstituteVoteMapper extends BaseMapper<ImprovementIn
* @return List<ImprovementInstituteVote>
*/
public List<ImprovementInstituteVote> selectByMainId(@Param("mainId") String mainId);
/** 任务尚未进入投票节点时,仅失效本轮待投票记录,保留已投票历史。 */
int logicalDeletePendingByMainId(@Param("mainId") String mainId);
}
@@ -2,8 +2,10 @@ package org.jeecg.modules.improvementproposal.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO;
import org.jeecg.modules.improvementproposal.entity.ImprovementProposal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* @Description: improvement_proposal
@@ -20,4 +22,7 @@ public interface ImprovementProposalMapper extends BaseMapper<ImprovementProposa
@Select("SELECT * FROM improvement_proposal WHERE id = #{id} AND del_flag = '0' FOR UPDATE")
ImprovementProposal selectByIdForUpdate(@Param("id") String id);
/** 查询指定角色下可供所级评议选择的有效用户。 */
List<ImprovementInstituteVoteCandidateDTO> selectInstituteVoteCandidates(@Param("roleCode") String roleCode);
}
@@ -12,5 +12,16 @@
SELECT *
FROM improvement_institute_vote
WHERE
main_id = #{mainId} </select>
main_id = #{mainId}
AND del_flag = '0'
</select>
<!-- Task_improve004 重试保存名单时,只替换尚未投票的有效记录。 -->
<update id="logicalDeletePendingByMainId" parameterType="java.lang.String">
UPDATE improvement_institute_vote
SET del_flag = '1'
WHERE main_id = #{mainId}
AND del_flag = '0'
AND vote_status = 'pending'
</update>
</mapper>
@@ -10,4 +10,17 @@
LIMIT 1
</select>
<!-- 角色名单由服务端查询,前端只能从该受控集合中选择。 -->
<select id="selectInstituteVoteCandidates" resultType="org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO">
SELECT DISTINCT u.id, u.username, u.realname
FROM sys_role r
INNER JOIN sys_user_role ur ON ur.role_id = r.id
INNER JOIN sys_user u ON u.id = ur.user_id
WHERE r.role_code = #{roleCode}
AND u.del_flag = 0
AND u.username IS NOT NULL
AND u.username != ''
ORDER BY u.realname, u.username
</select>
</mapper>
@@ -4,6 +4,7 @@ import org.jeecg.modules.improvementproposal.entity.ImprovementDeptScore;
import org.jeecg.modules.improvementproposal.entity.ImprovementInstituteVote;
import org.jeecg.modules.improvementproposal.entity.ImprovementProposal;
import org.jeecg.modules.improvementproposal.dto.ImprovementDeptScoreBpmDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementProposalBpmActionDTO;
import org.jeecg.common.system.vo.LoginUser;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -75,6 +76,12 @@ public interface IImprovementProposalService extends IService<ImprovementProposa
/** 保存部门负责人确认奖项,并写入所级投票网关变量。 */
void saveLeaderAward(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
/** 查询所级评议节点可选择的角色成员。 */
List<ImprovementInstituteVoteCandidateDTO> getInstituteVoteCandidates(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
/** 保存所级评议名单、待投票记录及多实例流程变量。 */
void saveInstituteVoteMembers(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
/** 保存经办人归档确认的最终奖项。 */
void saveArchiveAward(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
@@ -4,6 +4,7 @@ import org.jeecg.modules.improvementproposal.entity.ImprovementProposal;
import org.jeecg.modules.improvementproposal.entity.ImprovementDeptScore;
import org.jeecg.modules.improvementproposal.entity.ImprovementInstituteVote;
import org.jeecg.modules.improvementproposal.dto.ImprovementDeptScoreBpmDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO;
import org.jeecg.modules.improvementproposal.dto.ImprovementProposalBpmActionDTO;
import org.jeecg.modules.improvementproposal.mapper.ImprovementDeptScoreMapper;
import org.jeecg.modules.improvementproposal.mapper.ImprovementInstituteVoteMapper;
@@ -29,9 +30,13 @@ import org.flowable.engine.RuntimeService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.jeecg.modules.system.entity.SysRole;
import org.jeecg.modules.system.service.ISysRoleService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
/**
* @Description: improvement_proposal
@@ -49,8 +54,10 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
private static final String TASK_INITIAL_REVIEW = "Task_improve001";
private static final String TASK_DEPT_SCORE = "Task_improve002";
private static final String TASK_LEADER_AWARD = "Task_improve003";
private static final String TASK_INSTITUTE_REVIEW = "Task_improve004";
private static final String TASK_INSTITUTE_VOTE = "Task_improve005";
private static final String TASK_ARCHIVE = "Task_improve006";
private static final String INSTITUTE_VOTE_ROLE_CODE = "improve_suoleader";
private static final String AWARD_GOLD = "0";
private static final String AWARD_SILVER = "1";
private static final String AWARD_BRONZE = "2";
@@ -70,6 +77,8 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
private HistoryService historyService;
@Autowired
private TaskService taskService;
@Autowired
private ISysRoleService sysRoleService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -313,6 +322,75 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
runtimeService.setVariable(action.getProcessInstanceId(), "isneedvote", needInstituteVote);
}
@Override
public List<ImprovementInstituteVoteCandidateDTO> getInstituteVoteCandidates(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
validateRunningTask(action, loginUser, TASK_INSTITUTE_REVIEW);
return loadInstituteVoteCandidates();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveInstituteVoteMembers(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
ImprovementProposal proposal = validateRunningTask(action, loginUser, TASK_INSTITUTE_REVIEW);
ImprovementProposal lockedProposal = improvementProposalMapper.selectByIdForUpdate(proposal.getId());
if (lockedProposal == null || !action.getProcessInstanceId().equals(lockedProposal.getProcessInstanceId())) {
throw new IllegalStateException("提案流程状态已变化,请刷新后重试");
}
List<ImprovementInstituteVoteCandidateDTO> candidates = loadInstituteVoteCandidates();
Map<String, ImprovementInstituteVoteCandidateDTO> candidateById = new LinkedHashMap<>();
for (ImprovementInstituteVoteCandidateDTO candidate : candidates) {
candidateById.put(candidate.getId(), candidate);
}
if (action.getVoterIds() == null || action.getVoterIds().isEmpty()) {
throw new IllegalStateException("请至少选择一名所级评议人员");
}
LinkedHashSet<String> selectedUserIds = new LinkedHashSet<>();
for (String voterId : action.getVoterIds()) {
if (oConvertUtils.isEmpty(voterId) || !selectedUserIds.add(voterId)) {
throw new IllegalStateException("所级评议人员选择数据不合法");
}
}
LinkedHashSet<String> usernames = new LinkedHashSet<>();
for (String voterId : selectedUserIds) {
ImprovementInstituteVoteCandidateDTO candidate = candidateById.get(voterId);
if (candidate == null) {
throw new IllegalStateException("所选人员不属于所级评议小组");
}
if (!usernames.add(candidate.getUsername())) {
throw new IllegalStateException("所级评议人员用户名重复");
}
}
List<ImprovementInstituteVote> activeVotes = improvementInstituteVoteMapper.selectByMainId(lockedProposal.getId());
if (activeVotes.stream().anyMatch(item -> !"pending".equals(item.getVoteStatus()))) {
throw new IllegalStateException("所级评议已开始投票,不能重新选择人员");
}
// beforeHandle 可能因网络重试重复调用;先失效未投票记录再重建,确保有效名单始终和流程变量一致。
improvementInstituteVoteMapper.logicalDeletePendingByMainId(lockedProposal.getId());
for (String voterId : selectedUserIds) {
ImprovementInstituteVoteCandidateDTO candidate = candidateById.get(voterId);
ImprovementInstituteVote vote = new ImprovementInstituteVote();
vote.setMainId(lockedProposal.getId());
vote.setVoterId(candidate.getId());
vote.setVoterName(oConvertUtils.isEmpty(candidate.getRealname()) ? candidate.getUsername() : candidate.getRealname());
vote.setVoteStatus("pending");
vote.setSysOrgCode(lockedProposal.getSysOrgCode());
vote.setDelFlag(NORMAL_DEL_FLAG);
improvementInstituteVoteMapper.insert(vote);
}
ImprovementProposal update = new ImprovementProposal();
update.setId(lockedProposal.getId());
update.setInstituteTotalVoters(selectedUserIds.size());
update.setInstituteAgreeCount(null);
update.setInstituteDisagreeCount(null);
update.setInstituteVoteResult(null);
improvementProposalMapper.updateById(update);
runtimeService.setVariable(action.getProcessInstanceId(), "suoleadervotelist", String.join(",", usernames));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveArchiveAward(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
@@ -355,6 +433,21 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
}
}
/**
* 固定按角色编码读取名单,保存时再次查询以防前端伪造用户 ID 或角色配置已变更。
*/
private List<ImprovementInstituteVoteCandidateDTO> loadInstituteVoteCandidates() {
SysRole role = sysRoleService.getRoleNoTenant(INSTITUTE_VOTE_ROLE_CODE);
if (role == null) {
throw new IllegalStateException("未配置所级评议小组角色:" + INSTITUTE_VOTE_ROLE_CODE);
}
List<ImprovementInstituteVoteCandidateDTO> candidates = improvementProposalMapper.selectInstituteVoteCandidates(INSTITUTE_VOTE_ROLE_CODE);
if (candidates == null || candidates.isEmpty()) {
throw new IllegalStateException("所级评议小组角色未配置有效用户");
}
return candidates;
}
private ImprovementProposalBpmActionDTO toBpmAction(ImprovementDeptScoreBpmDTO scoreAction) {
ImprovementProposalBpmActionDTO action = new ImprovementProposalBpmActionDTO();
action.setMainId(scoreAction.getMainId());