Merge branch 'refs/heads/master' into wsq-20260727
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
-- ============================================================
|
||||
-- 数据库全量清理脚本
|
||||
-- 目标:清除所有业务数据与流程运行时数据,
|
||||
-- 保留用户/部门/角色/菜单/权限/字典等系统配置
|
||||
-- 保留已设计的流程定义(可重新发布)
|
||||
-- 效果:登录后可看到菜单,点进去是空的业务界面
|
||||
--
|
||||
-- 表来源:代码 @TableName 注解 + 数据库已存在
|
||||
-- 部分表可能在某些环境中尚未创建,执行时若"doesn't exist"
|
||||
-- 错误可忽略(该表当前环境无数据需清理)
|
||||
--
|
||||
-- ============================================================
|
||||
-- 执行后必须执行的恢复操作:
|
||||
-- 1. 同步用户到 Flowable 引擎:
|
||||
-- PUT /act/process/extActProcess/doSyncUser
|
||||
-- (act_id_* 表已清空,不执行此步则按角色/部门分配任务会失败)
|
||||
-- 2. 重新发布流程定义(如有需要):
|
||||
-- 进入流程设计器 → 发布已设计好的流程
|
||||
-- (ext_act_process.process_status 已重置为 0)
|
||||
-- ============================================================
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. Flowable 引擎运行时表(当前运行中的流程实例数据)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE act_ru_actinst;
|
||||
TRUNCATE TABLE act_ru_deadletter_job;
|
||||
TRUNCATE TABLE act_ru_entitylink;
|
||||
TRUNCATE TABLE act_ru_event_subscr;
|
||||
TRUNCATE TABLE act_ru_execution;
|
||||
TRUNCATE TABLE act_ru_external_job;
|
||||
TRUNCATE TABLE act_ru_history_job;
|
||||
TRUNCATE TABLE act_ru_identitylink;
|
||||
TRUNCATE TABLE act_ru_job;
|
||||
TRUNCATE TABLE act_ru_suspended_job;
|
||||
TRUNCATE TABLE act_ru_task;
|
||||
TRUNCATE TABLE act_ru_timer_job;
|
||||
TRUNCATE TABLE act_ru_variable;
|
||||
|
||||
-- ============================================================
|
||||
-- 2. Flowable 引擎历史表(已完成的流程实例归档数据)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE act_hi_actinst;
|
||||
TRUNCATE TABLE act_hi_attachment;
|
||||
TRUNCATE TABLE act_hi_comment;
|
||||
TRUNCATE TABLE act_hi_detail;
|
||||
TRUNCATE TABLE act_hi_entitylink;
|
||||
TRUNCATE TABLE act_hi_identitylink;
|
||||
TRUNCATE TABLE act_hi_procinst;
|
||||
TRUNCATE TABLE act_hi_taskinst;
|
||||
TRUNCATE TABLE act_hi_tsk_log;
|
||||
TRUNCATE TABLE act_hi_varinst;
|
||||
TRUNCATE TABLE act_evt_log;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Flowable 引擎部署表(已发布的流程定义快照,重新发布会自动重建)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE act_re_procdef;
|
||||
TRUNCATE TABLE act_re_deployment;
|
||||
TRUNCATE TABLE act_re_model;
|
||||
TRUNCATE TABLE act_ge_bytearray;
|
||||
TRUNCATE TABLE act_procdef_info;
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Flowable 事件注册表运行时数据
|
||||
-- flw_channel_definition / flw_event_* / flw_ev_* 保留
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE flw_ru_batch;
|
||||
TRUNCATE TABLE flw_ru_batch_part;
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Flowable 身份表(从 sys_user/sys_role 同步的缓存,清空后需重新同步)
|
||||
-- act_id_property 保留(引擎配置元数据)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE act_id_user;
|
||||
TRUNCATE TABLE act_id_group;
|
||||
TRUNCATE TABLE act_id_membership;
|
||||
TRUNCATE TABLE act_id_info;
|
||||
TRUNCATE TABLE act_id_bytearray;
|
||||
TRUNCATE TABLE act_id_priv;
|
||||
TRUNCATE TABLE act_id_priv_mapping;
|
||||
TRUNCATE TABLE act_id_token;
|
||||
|
||||
-- ============================================================
|
||||
-- 6. 自定义 BPM 运行时数据
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE ext_act_bpm_log;
|
||||
TRUNCATE TABLE ext_act_bpm_file;
|
||||
TRUNCATE TABLE ext_act_flow_data;
|
||||
TRUNCATE TABLE ext_act_task_cc;
|
||||
TRUNCATE TABLE ext_act_task_notification;
|
||||
TRUNCATE TABLE ext_act_mutil_flow_data;
|
||||
TRUNCATE TABLE task_approval_opinion;
|
||||
|
||||
-- ============================================================
|
||||
-- 7. 流程部署快照(与 act_re_* 对应,重新发布流程时自动重建)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE ext_act_process_node_deploy;
|
||||
|
||||
-- ============================================================
|
||||
-- 8. 表单设计器运行时数据
|
||||
-- design_form 及其他 design_form_* 表保留(表单定义)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE ext_act_design_flow_data;
|
||||
TRUNCATE TABLE design_form_data;
|
||||
|
||||
-- ============================================================
|
||||
-- 9. 流程定义状态重置为"未发布"
|
||||
-- 以下流程定义配置表保留不动:
|
||||
-- ext_act_process / ext_act_process_node / ext_act_process_form
|
||||
-- ext_act_process_node_auth / ext_act_process_node_reject_target
|
||||
-- ext_act_listener / ext_act_expression
|
||||
-- ============================================================
|
||||
UPDATE ext_act_process SET process_status = 0;
|
||||
|
||||
-- ============================================================
|
||||
-- 10. 监督模块业务表(代码中有 @TableName 定义)
|
||||
-- ============================================================
|
||||
-- 习语讲话
|
||||
TRUNCATE TABLE bg_xi_speak;
|
||||
TRUNCATE TABLE bg_xi_speak_feedback;
|
||||
-- 把脉问诊
|
||||
TRUNCATE TABLE bg_takepulse;
|
||||
TRUNCATE TABLE bg_takepulse_feedback;
|
||||
-- 党务管理
|
||||
TRUNCATE TABLE bg_partymatter;
|
||||
TRUNCATE TABLE bg_partymatter_feedback;
|
||||
-- 党建检查与整改
|
||||
TRUNCATE TABLE dj_inspect_improve;
|
||||
TRUNCATE TABLE dj_inspection_rectify_list;
|
||||
TRUNCATE TABLE dj_check;
|
||||
TRUNCATE TABLE dj_meeting_record;
|
||||
TRUNCATE TABLE dj_demo_life_meeting;
|
||||
TRUNCATE TABLE dj_people_service;
|
||||
TRUNCATE TABLE dj_officialdoc_distribute;
|
||||
-- 纪律检查
|
||||
TRUNCATE TABLE dq_inspect_task;
|
||||
TRUNCATE TABLE dq_inspect_progress;
|
||||
TRUNCATE TABLE dq_inspect_problem;
|
||||
-- 改善提案
|
||||
TRUNCATE TABLE improvement_proposal;
|
||||
TRUNCATE TABLE improvement_institute_vote;
|
||||
TRUNCATE TABLE improvement_dept_score;
|
||||
-- 任务清单
|
||||
TRUNCATE TABLE task_task;
|
||||
TRUNCATE TABLE task_list;
|
||||
TRUNCATE TABLE task_list_detial;
|
||||
TRUNCATE TABLE task_list_favorite;
|
||||
TRUNCATE TABLE task_list_permission;
|
||||
-- 监督测试
|
||||
TRUNCATE TABLE supervision_test;
|
||||
|
||||
-- ============================================================
|
||||
-- 11. JOA 流程业务演示表
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE ext_biz_leave;
|
||||
TRUNCATE TABLE joa_busines_strip;
|
||||
TRUNCATE TABLE joa_doc_receiving;
|
||||
TRUNCATE TABLE joa_doc_sending;
|
||||
TRUNCATE TABLE joa_employee_leave;
|
||||
TRUNCATE TABLE joa_loan;
|
||||
TRUNCATE TABLE joa_overtime;
|
||||
TRUNCATE TABLE joa_overtime_detail;
|
||||
TRUNCATE TABLE joa_overtime_leave;
|
||||
TRUNCATE TABLE joa_demo;
|
||||
|
||||
-- ============================================================
|
||||
-- 12. Demo 模块测试表
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE demo;
|
||||
TRUNCATE TABLE demo_staff;
|
||||
TRUNCATE TABLE demo_staff_group;
|
||||
TRUNCATE TABLE demo_field_def_val_main;
|
||||
TRUNCATE TABLE demo_field_def_val_sub;
|
||||
TRUNCATE TABLE demo_sub_flow_hq;
|
||||
TRUNCATE TABLE jeecg_order_main;
|
||||
TRUNCATE TABLE jeecg_order_customer;
|
||||
TRUNCATE TABLE jeecg_order_ticket;
|
||||
TRUNCATE TABLE jeecg_tree_demo;
|
||||
|
||||
-- ============================================================
|
||||
-- 13. OA 办公业务表
|
||||
-- ============================================================
|
||||
-- WPS
|
||||
TRUNCATE TABLE oa_wps_file;
|
||||
TRUNCATE TABLE oa_wps_file_version;
|
||||
TRUNCATE TABLE oa_wps_file_watermark;
|
||||
TRUNCATE TABLE oa_wps_user_acl;
|
||||
-- 人事
|
||||
TRUNCATE TABLE oa_become_regular;
|
||||
TRUNCATE TABLE oa_dimission;
|
||||
TRUNCATE TABLE oa_employee_award;
|
||||
TRUNCATE TABLE oa_employee_certificate;
|
||||
TRUNCATE TABLE oa_employee_education;
|
||||
TRUNCATE TABLE oa_employee_experience;
|
||||
TRUNCATE TABLE oa_employee_family;
|
||||
TRUNCATE TABLE oa_employee_info;
|
||||
TRUNCATE TABLE oa_employee_relations;
|
||||
TRUNCATE TABLE oa_employment;
|
||||
TRUNCATE TABLE oa_employment_apply;
|
||||
TRUNCATE TABLE oa_interview;
|
||||
TRUNCATE TABLE oa_promotion;
|
||||
-- 出差/用车/请假/加班
|
||||
TRUNCATE TABLE oa_business_travel;
|
||||
TRUNCATE TABLE oa_business_travel_accompanied;
|
||||
TRUNCATE TABLE oa_car_info;
|
||||
TRUNCATE TABLE oa_car_use;
|
||||
TRUNCATE TABLE oa_leave;
|
||||
TRUNCATE TABLE oa_overtime;
|
||||
TRUNCATE TABLE oa_security_vehicle;
|
||||
-- 签到
|
||||
TRUNCATE TABLE oa_sign_info;
|
||||
TRUNCATE TABLE oa_sign_out;
|
||||
TRUNCATE TABLE oa_sign_patch;
|
||||
TRUNCATE TABLE oa_sign_rule;
|
||||
-- 用品
|
||||
TRUNCATE TABLE oa_supplies_detail;
|
||||
TRUNCATE TABLE oa_supplies_use;
|
||||
-- 公文
|
||||
TRUNCATE TABLE oa_officialdoc_depart_user;
|
||||
TRUNCATE TABLE oa_officialdoc_distribute;
|
||||
TRUNCATE TABLE oa_officialdoc_issued;
|
||||
TRUNCATE TABLE oa_officialdoc_organcode;
|
||||
TRUNCATE TABLE oa_officialdoc_received;
|
||||
TRUNCATE TABLE oa_officialdoc_seal;
|
||||
TRUNCATE TABLE oa_officialdoc_temp;
|
||||
|
||||
-- ============================================================
|
||||
-- 14. EOA 在线办公表
|
||||
-- ============================================================
|
||||
-- 聊天
|
||||
TRUNCATE TABLE eoa_chat_group;
|
||||
TRUNCATE TABLE eoa_chat_off_message;
|
||||
TRUNCATE TABLE eoa_chat_on_message;
|
||||
TRUNCATE TABLE eoa_chat_on_message_his;
|
||||
TRUNCATE TABLE eoa_chat_session;
|
||||
TRUNCATE TABLE eoa_chat_user;
|
||||
TRUNCATE TABLE eoa_chat_user_group;
|
||||
-- CMS
|
||||
TRUNCATE TABLE eoa_cms_article;
|
||||
TRUNCATE TABLE eoa_cms_article_read;
|
||||
TRUNCATE TABLE eoa_cms_menu;
|
||||
TRUNCATE TABLE eoa_cms_site;
|
||||
-- 文件
|
||||
TRUNCATE TABLE eoa_file;
|
||||
TRUNCATE TABLE eoa_file_log;
|
||||
-- 邮箱
|
||||
TRUNCATE TABLE eoa_mailbox_attach;
|
||||
TRUNCATE TABLE eoa_mailbox_category;
|
||||
TRUNCATE TABLE eoa_mailbox_info;
|
||||
TRUNCATE TABLE eoa_mailbox_receiver;
|
||||
-- 会议
|
||||
TRUNCATE TABLE eoa_metting;
|
||||
TRUNCATE TABLE eoa_metting_device;
|
||||
TRUNCATE TABLE eoa_metting_room;
|
||||
TRUNCATE TABLE eoa_metting_sign;
|
||||
-- 计划/门户
|
||||
TRUNCATE TABLE eoa_plan;
|
||||
TRUNCATE TABLE eoa_portal_site;
|
||||
TRUNCATE TABLE eoa_portal_site_auth;
|
||||
|
||||
-- ============================================================
|
||||
-- 15. 测试表(代码 @TableName + DB 中存在)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE ceshi_des_qingjia;
|
||||
TRUNCATE TABLE ceshi_nongmin;
|
||||
TRUNCATE TABLE ceshi_qingjia_dan;
|
||||
TRUNCATE TABLE ceshi_ruzhi;
|
||||
TRUNCATE TABLE ceshi_subqt;
|
||||
TRUNCATE TABLE ce_fenzhi_zx;
|
||||
TRUNCATE TABLE ce_paita_wangguan;
|
||||
TRUNCATE TABLE joa_onl_leave;
|
||||
TRUNCATE TABLE joa_onl_bthuiq;
|
||||
TRUNCATE TABLE joa_onl_sxhuiq;
|
||||
TRUNCATE TABLE oa_demo_bhan_wguan;
|
||||
TRUNCATE TABLE rep_demo_dxtj;
|
||||
TRUNCATE TABLE rep_demo_employee;
|
||||
TRUNCATE TABLE rep_demo_gongsi;
|
||||
TRUNCATE TABLE rep_demo_huizong;
|
||||
TRUNCATE TABLE rep_demo_jianpiao;
|
||||
TRUNCATE TABLE rep_demo_order_main;
|
||||
TRUNCATE TABLE rep_demo_order_product;
|
||||
TRUNCATE TABLE test_demo;
|
||||
TRUNCATE TABLE test_enhance_select;
|
||||
TRUNCATE TABLE test_main_table;
|
||||
TRUNCATE TABLE test_note;
|
||||
TRUNCATE TABLE test_online_link;
|
||||
TRUNCATE TABLE test_order_customer;
|
||||
TRUNCATE TABLE test_order_main;
|
||||
TRUNCATE TABLE test_order_product;
|
||||
TRUNCATE TABLE test_order_user;
|
||||
TRUNCATE TABLE test_person;
|
||||
TRUNCATE TABLE test_shoptype_tree;
|
||||
TRUNCATE TABLE test_son_table;
|
||||
TRUNCATE TABLE test_third_link;
|
||||
TRUNCATE TABLE test_tree;
|
||||
|
||||
-- ============================================================
|
||||
-- 16. 系统运行日志 / 消息 / API 调用记录
|
||||
-- sys_files / sys_file_attachment 保留(头像、系统图标等文件)
|
||||
-- jimu_report_export_log 保留(可选清除,见下方)
|
||||
-- ============================================================
|
||||
TRUNCATE TABLE sys_log;
|
||||
TRUNCATE TABLE sys_data_log;
|
||||
TRUNCATE TABLE sys_announcement;
|
||||
TRUNCATE TABLE sys_announcement_send;
|
||||
TRUNCATE TABLE sys_sms;
|
||||
TRUNCATE TABLE sys_comment;
|
||||
TRUNCATE TABLE sys_files_operate;
|
||||
TRUNCATE TABLE open_api_log;
|
||||
|
||||
-- ============================================================
|
||||
-- 17. (可选)取消注释以清除更多内容
|
||||
-- ============================================================
|
||||
-- TRUNCATE TABLE jimu_report_export_log;
|
||||
-- TRUNCATE TABLE sys_file_attachment;
|
||||
-- TRUNCATE TABLE sys_files;
|
||||
-- TRUNCATE TABLE flyway_schema_history_1;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
+1
-3
@@ -105,9 +105,7 @@ public class KsimTestController {
|
||||
if (msgRespBody != null) {
|
||||
int code = msgRespBody.getIntValue("code");
|
||||
result.put("success", code == 0 || code == 200);
|
||||
if (msgRespBody.getJSONObject("dataBody") != null) {
|
||||
result.put("messageId", msgRespBody.getJSONObject("dataBody").getString("messageId"));
|
||||
}
|
||||
result.put("requestId", msgRespBody.getString("requestId"));
|
||||
result.put("responseCode", code);
|
||||
} else {
|
||||
result.put("success", false);
|
||||
|
||||
+3
-4
@@ -66,10 +66,9 @@ public class KsimMessageServiceImpl implements IKsimMessageService {
|
||||
return Result.error(apiResult.getCode(), "即时通发送失败: " + apiResult.getMessage());
|
||||
}
|
||||
|
||||
String messageId = apiResult.getDataBody() != null
|
||||
? apiResult.getDataBody().getString("messageId") : "";
|
||||
log.info("【即时通推送-sendBatch】成功, messageId: {}, toUserCount: {}, title: {}", messageId,
|
||||
String requestId = apiResult.getRequestId() != null ? apiResult.getRequestId() : "";
|
||||
log.info("【即时通推送-sendBatch】成功, requestId: {}, toUserCount: {}, title: {}", requestId,
|
||||
toUserValueList != null ? toUserValueList.size() : 0, title);
|
||||
return Result.OK(messageId);
|
||||
return Result.OK(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import java.util.Map;
|
||||
public class TaskCreatedAutoSubmitListener implements TaskListener {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DEFAULT_START_APPROVAL_OPINION = "发起督办流程。";
|
||||
private static final String DEFAULT_START_APPROVAL_OPINION = "发起流程。";
|
||||
private static ISysBaseAPI sysBaseAPI;
|
||||
private static IExtActBpmLogService extActBpmLogService;
|
||||
protected static RuntimeService runtimeService;
|
||||
|
||||
+23
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.Execution;
|
||||
@@ -276,6 +277,28 @@ public class FlowNodeExpression {
|
||||
return sysbase.getUserByRoleIdLocalApi(roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required usernames for a Flowable multi-instance task.
|
||||
* An empty collection would otherwise make Flowable skip the task, so fail fast instead.
|
||||
* Example: ${flowNodeExpression.getRequiredUsersListByRole('roleIdValue')}
|
||||
*
|
||||
* @param roleId system role id
|
||||
* @return non-empty username list
|
||||
*/
|
||||
public List<String> getRequiredUsersListByRole(String roleId) {
|
||||
if (StringUtils.isBlank(roleId)) {
|
||||
throw new JeecgBootException("流程角色ID不能为空");
|
||||
}
|
||||
|
||||
List<String> usernames = ISysBaseAPI.getUsernamesByRoleIdLocalApi(roleId);
|
||||
log.info("流程多实例按角色取人,roleId={}, usernames={}, count={}", roleId, usernames,
|
||||
usernames == null ? 0 : usernames.size());
|
||||
if (CollectionUtils.isEmpty(usernames)) {
|
||||
throw new JeecgBootException("角色[" + roleId + "]未配置有效用户,流程无法继续");
|
||||
}
|
||||
return usernames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subprocess variable.
|
||||
* Example: ${flowNodeExpression.getSonProcessVariable(execution, 'subApproveUser')}
|
||||
|
||||
+4
@@ -37,6 +37,8 @@ import org.jeecg.modules.extbpm.util.JSONHelper;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -60,6 +62,7 @@ import java.util.stream.Collectors;
|
||||
* @date: 2019-03-15
|
||||
* @version: V1.0
|
||||
*/
|
||||
@Tag(name = "流程定义")
|
||||
@RestController("extActProcessController")
|
||||
@RequestMapping("/act/process/extActProcess")
|
||||
@Slf4j
|
||||
@@ -570,6 +573,7 @@ public class ExtActProcessController {
|
||||
* @param extActProcess
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "同步用户到Flowable引擎")
|
||||
@PutMapping(value = "/doSyncUser")
|
||||
public Result<ExtActProcess> doSyncUser(HttpServletRequest request) {
|
||||
Result<ExtActProcess> result = new Result<ExtActProcess>();
|
||||
|
||||
+56
@@ -27,6 +27,8 @@ import org.jeecg.common.util.oConvertUtils;
|
||||
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;
|
||||
@@ -157,12 +159,60 @@ public class ImprovementProposalController {
|
||||
return handleBpmAction(() -> improvementProposalService.saveInitialReview(action, currentUser()));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/bpm/myDeptScore")
|
||||
@Operation(summary = "查询当前部门评委个人评分")
|
||||
public Result<ImprovementDeptScore> getMyDeptScore(@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.getMyDeptScore(action, currentUser()));
|
||||
} catch (IllegalStateException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(value = "/bpm/deptScore")
|
||||
@Operation(summary = "保存当前部门评委个人评分草稿")
|
||||
public Result<String> saveDeptScore(@RequestBody ImprovementDeptScoreBpmDTO score) {
|
||||
return handleBpmAction(() -> improvementProposalService.saveDeptScore(score, currentUser()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/bpm/submitDeptScore")
|
||||
@Operation(summary = "锁定当前部门评委个人评分")
|
||||
public Result<String> submitDeptScore(@RequestBody ImprovementProposalBpmActionDTO action) {
|
||||
return handleBpmAction(() -> improvementProposalService.submitDeptScore(action, currentUser()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/bpm/leaderAward")
|
||||
@Operation(summary = "保存部门负责人确认奖项")
|
||||
public Result<String> saveLeaderAward(@RequestBody ImprovementProposalBpmActionDTO action) {
|
||||
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) {
|
||||
@@ -175,6 +225,12 @@ public class ImprovementProposalController {
|
||||
return handleBpmAction(() -> improvementProposalService.castInstituteVote(action, currentUser()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/bpm/submitInstituteVote")
|
||||
@Operation(summary = "确认所级评委投票并直接完成流程任务")
|
||||
public Result<String> submitInstituteVote(@RequestBody ImprovementProposalBpmActionDTO action, HttpServletRequest request) {
|
||||
return handleBpmAction(() -> improvementProposalService.submitInstituteVoteAndComplete(action, currentUser(), request));
|
||||
}
|
||||
|
||||
private LoginUser currentUser() {
|
||||
return (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.jeecg.modules.improvementproposal.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部门评议评委提交个人评分时使用的流程节点参数。
|
||||
*/
|
||||
@Data
|
||||
public class ImprovementDeptScoreBpmDTO {
|
||||
|
||||
@Schema(description = "改善提案主表ID")
|
||||
private String mainId;
|
||||
|
||||
@Schema(description = "流程实例ID")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "当前流程任务ID")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "安全得分,0-100")
|
||||
private BigDecimal safetyScore;
|
||||
|
||||
@Schema(description = "质量得分,0-100")
|
||||
private BigDecimal qualityScore;
|
||||
|
||||
@Schema(description = "效率得分,0-100")
|
||||
private BigDecimal efficiencyScore;
|
||||
|
||||
@Schema(description = "成本得分,0-100")
|
||||
private BigDecimal costScore;
|
||||
|
||||
@Schema(description = "可推广度得分,0-100")
|
||||
private BigDecimal promotionScore;
|
||||
|
||||
@Schema(description = "独创性得分,0-100")
|
||||
private BigDecimal originalityScore;
|
||||
|
||||
@Schema(description = "努力度得分,0-100")
|
||||
private BigDecimal effortScore;
|
||||
|
||||
@Schema(description = "评分意见")
|
||||
private String scoreOpinion;
|
||||
}
|
||||
+25
@@ -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;
|
||||
}
|
||||
+8
@@ -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;
|
||||
}
|
||||
|
||||
+16
@@ -50,6 +50,22 @@ public class ImprovementDeptScore implements Serializable {
|
||||
@Excel(name = "评分状态:pending/completed", width = 15)
|
||||
@Schema(description = "评分状态:pending/completed")
|
||||
private String scoreStatus;
|
||||
/**安全得分*/
|
||||
@Excel(name = "安全得分", width = 15)
|
||||
@Schema(description = "安全得分")
|
||||
private java.math.BigDecimal safetyScore;
|
||||
/**质量得分*/
|
||||
@Excel(name = "质量得分", width = 15)
|
||||
@Schema(description = "质量得分")
|
||||
private java.math.BigDecimal qualityScore;
|
||||
/**效率得分*/
|
||||
@Excel(name = "效率得分", width = 15)
|
||||
@Schema(description = "效率得分")
|
||||
private java.math.BigDecimal efficiencyScore;
|
||||
/**成本得分*/
|
||||
@Excel(name = "成本得分", width = 15)
|
||||
@Schema(description = "成本得分")
|
||||
private java.math.BigDecimal costScore;
|
||||
/**改善效果得分*/
|
||||
@Excel(name = "改善效果得分", width = 15)
|
||||
@Schema(description = "改善效果得分")
|
||||
|
||||
+7
-3
@@ -125,6 +125,10 @@ public class ImprovementProposal implements Serializable {
|
||||
@Schema(description = "最终奖项")
|
||||
@Dict(dicCode = "proposal_prize")
|
||||
private String finalAward;
|
||||
/**所级评议领导名单*/
|
||||
@Excel(name = "所级评议领导名单", width = 15)
|
||||
@Schema(description = "所级评议领导名单")
|
||||
private Integer instituteVoters;
|
||||
/**所级评议应投票人数*/
|
||||
@Excel(name = "所级评议应投票人数", width = 15)
|
||||
@Schema(description = "所级评议应投票人数")
|
||||
@@ -137,9 +141,9 @@ public class ImprovementProposal implements Serializable {
|
||||
@Excel(name = "所级评议不同意票数", width = 15)
|
||||
@Schema(description = "所级评议不同意票数")
|
||||
private Integer instituteDisagreeCount;
|
||||
/**所级评议结果:passed/failed*/
|
||||
@Excel(name = "所级评议结果:passed/failed", width = 15)
|
||||
@Schema(description = "所级评议结果:passed/failed")
|
||||
/**所级评议结果:通过/不通过*/
|
||||
@Excel(name = "所级评议结果:通过/不通过", width = 15)
|
||||
@Schema(description = "所级评议结果:通过/不通过")
|
||||
private String instituteVoteResult;
|
||||
/**金奖未通过后的经办人手动确认结果*/
|
||||
@Excel(name = "金奖未通过后的经办人手动确认结果", width = 15)
|
||||
|
||||
+3
@@ -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);
|
||||
}
|
||||
|
||||
+5
@@ -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
-1
@@ -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>
|
||||
|
||||
+13
@@ -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>
|
||||
|
||||
+24
@@ -3,9 +3,12 @@ package org.jeecg.modules.improvementproposal.service;
|
||||
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;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -62,15 +65,36 @@ public interface IImprovementProposalService extends IService<ImprovementProposa
|
||||
/** 保存部门精益专员初审结果,并写入部门评分网关变量。 */
|
||||
void saveInitialReview(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
|
||||
|
||||
/** 获取当前部门评委在当前流程任务下的个人评分,不返回其他评委数据。 */
|
||||
ImprovementDeptScore getMyDeptScore(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
|
||||
|
||||
/** 保存当前部门评委的四维度评分草稿,流程任务提交前允许修改。 */
|
||||
void saveDeptScore(ImprovementDeptScoreBpmDTO score, LoginUser loginUser);
|
||||
|
||||
/** 锁定当前评委的已保存评分,并在全部评委完成时汇总部门评议结果。 */
|
||||
void submitDeptScore(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
|
||||
|
||||
/** 保存部门负责人确认奖项,并写入所级投票网关变量。 */
|
||||
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);
|
||||
|
||||
/** 记录当前所级评委的投票结果。 */
|
||||
void castInstituteVote(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
|
||||
|
||||
/** 锁定当前所级评委的投票并在全员完成后汇总投票结果。 */
|
||||
void submitInstituteVote(ImprovementProposalBpmActionDTO action, LoginUser loginUser);
|
||||
|
||||
/** 锁定投票后,使用通用流程办理能力直接完成当前所级评委任务。 */
|
||||
void submitInstituteVoteAndComplete(ImprovementProposalBpmActionDTO action, LoginUser loginUser, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 获取指定年度最新提案编号中的XXXX部分
|
||||
*
|
||||
|
||||
+439
-9
@@ -3,6 +3,8 @@ package org.jeecg.modules.improvementproposal.service.impl;
|
||||
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;
|
||||
@@ -12,22 +14,31 @@ import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.modules.extbpm.process.service.impl.BpmBaseExtApiImpl;
|
||||
import org.jeecg.modules.extbpm.process.service.IExtActTaskCcService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
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;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @Description: improvement_proposal
|
||||
@@ -43,13 +54,18 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
private static final String FLOW_NAME = "改善提案审批";
|
||||
private static final String BPM_FORM_URL = "gh/improvementProposal/components/ImprovementProposalBPMForm";
|
||||
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";
|
||||
private static final String AWARD_PARTICIPATION = "3";
|
||||
private static final String INSTITUTE_VOTE_RESULT_PASSED = "通过";
|
||||
private static final String INSTITUTE_VOTE_RESULT_FAILED = "不通过";
|
||||
|
||||
@Autowired
|
||||
private ImprovementProposalMapper improvementProposalMapper;
|
||||
@@ -62,7 +78,13 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
@Autowired
|
||||
private RuntimeService runtimeService;
|
||||
@Autowired
|
||||
private HistoryService historyService;
|
||||
@Autowired
|
||||
private TaskService taskService;
|
||||
@Autowired
|
||||
private ISysRoleService sysRoleService;
|
||||
@Autowired
|
||||
private IExtActTaskCcService extActTaskCcService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -201,11 +223,104 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
runtimeService.setVariable(action.getProcessInstanceId(), "need_dept_score", needDeptScore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImprovementDeptScore getMyDeptScore(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
|
||||
ImprovementProposal proposal = validateDeptScoreTask(action, loginUser);
|
||||
return findDeptScore(proposal.getId(), loginUser.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveDeptScore(ImprovementDeptScoreBpmDTO scoreAction, LoginUser loginUser) {
|
||||
validateScoreValues(scoreAction);
|
||||
ImprovementProposalBpmActionDTO action = toBpmAction(scoreAction);
|
||||
ImprovementProposal proposal = validateDeptScoreTask(action, loginUser);
|
||||
|
||||
// 锁定主表后再查询个人记录,未设置数据库唯一约束时也能阻止同一评委并发生成重复评分。
|
||||
ImprovementProposal lockedProposal = improvementProposalMapper.selectByIdForUpdate(proposal.getId());
|
||||
if (lockedProposal == null || !scoreAction.getProcessInstanceId().equals(lockedProposal.getProcessInstanceId())) {
|
||||
throw new IllegalStateException("提案流程状态已变化,请刷新后重试");
|
||||
}
|
||||
ImprovementDeptScore existingScore = findDeptScore(lockedProposal.getId(), loginUser.getId());
|
||||
if (existingScore != null && "completed".equals(existingScore.getScoreStatus())) {
|
||||
throw new IllegalStateException("评分已锁定,请重新提交流程办理");
|
||||
}
|
||||
|
||||
// 改善效果由四个子项在服务端计算,避免前端篡改汇总得分或加权总分。
|
||||
BigDecimal effectScore = calculateEffectScore(scoreAction);
|
||||
BigDecimal totalScore = calculateTotalScore(scoreAction, effectScore);
|
||||
if (existingScore == null) {
|
||||
ImprovementDeptScore newScore = new ImprovementDeptScore();
|
||||
newScore.setMainId(lockedProposal.getId());
|
||||
newScore.setProcessTaskId(scoreAction.getTaskId());
|
||||
newScore.setScorerId(loginUser.getId());
|
||||
newScore.setScorerName(loginUser.getRealname());
|
||||
fillDeptScoreDraft(newScore, scoreAction, effectScore, totalScore);
|
||||
// 草稿阶段记录最近保存时间,供评委在当前节点实时查看。
|
||||
newScore.setScoreTime(new Date());
|
||||
improvementDeptScoreMapper.insert(newScore);
|
||||
} else {
|
||||
// 草稿保存只能更新 pending 记录,避免流程已提交后通过接口修改最终评分。
|
||||
int updated = improvementDeptScoreMapper.update(null, new LambdaUpdateWrapper<ImprovementDeptScore>()
|
||||
.eq(ImprovementDeptScore::getId, existingScore.getId())
|
||||
.eq(ImprovementDeptScore::getScoreStatus, "pending")
|
||||
.set(ImprovementDeptScore::getProcessTaskId, scoreAction.getTaskId())
|
||||
.set(ImprovementDeptScore::getSafetyScore, scoreAction.getSafetyScore())
|
||||
.set(ImprovementDeptScore::getQualityScore, scoreAction.getQualityScore())
|
||||
.set(ImprovementDeptScore::getEfficiencyScore, scoreAction.getEfficiencyScore())
|
||||
.set(ImprovementDeptScore::getCostScore, scoreAction.getCostScore())
|
||||
.set(ImprovementDeptScore::getEffectScore, effectScore)
|
||||
.set(ImprovementDeptScore::getPromotionScore, scoreAction.getPromotionScore())
|
||||
.set(ImprovementDeptScore::getOriginalityScore, scoreAction.getOriginalityScore())
|
||||
.set(ImprovementDeptScore::getEffortScore, scoreAction.getEffortScore())
|
||||
.set(ImprovementDeptScore::getTotalScore, totalScore)
|
||||
.set(ImprovementDeptScore::getScoreOpinion, scoreAction.getScoreOpinion())
|
||||
.set(ImprovementDeptScore::getScoreStatus, "pending")
|
||||
.set(ImprovementDeptScore::getScoreTime, new Date()));
|
||||
if (updated != 1) {
|
||||
throw new IllegalStateException("评分状态已变化,请刷新后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitDeptScore(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
|
||||
ImprovementProposal proposal = validateDeptScoreTask(action, loginUser);
|
||||
ImprovementProposal lockedProposal = improvementProposalMapper.selectByIdForUpdate(proposal.getId());
|
||||
if (lockedProposal == null || !action.getProcessInstanceId().equals(lockedProposal.getProcessInstanceId())) {
|
||||
throw new IllegalStateException("提案流程状态已变化,请刷新后重试");
|
||||
}
|
||||
ImprovementDeptScore score = findDeptScore(lockedProposal.getId(), loginUser.getId());
|
||||
if (score == null) {
|
||||
throw new IllegalStateException("请先保存个人评分");
|
||||
}
|
||||
validateSavedScore(score);
|
||||
|
||||
// 通用流程办理可能因网络重试再次调用 beforeHandle;已锁定时保持幂等,让任务能够再次提交完成。
|
||||
if (!"completed".equals(score.getScoreStatus())) {
|
||||
int updated = improvementDeptScoreMapper.update(null, new LambdaUpdateWrapper<ImprovementDeptScore>()
|
||||
.eq(ImprovementDeptScore::getId, score.getId())
|
||||
.eq(ImprovementDeptScore::getScoreStatus, "pending")
|
||||
.set(ImprovementDeptScore::getScoreStatus, "completed")
|
||||
.set(ImprovementDeptScore::getScoreTime, new Date())
|
||||
.set(ImprovementDeptScore::getProcessTaskId, action.getTaskId()));
|
||||
if (updated != 1) {
|
||||
throw new IllegalStateException("评分状态已变化,请刷新后重试");
|
||||
}
|
||||
}
|
||||
updateDeptScoreSummaryIfComplete(lockedProposal, action.getProcessInstanceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveLeaderAward(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
|
||||
validateAward(action.getLeaderFinalAward());
|
||||
ImprovementProposal proposal = validateRunningTask(action, loginUser, TASK_LEADER_AWARD);
|
||||
String awardBase = resolveLeaderAwardBase(proposal);
|
||||
if (awardRank(action.getLeaderFinalAward()) < awardRank(awardBase)) {
|
||||
throw new IllegalStateException("负责人确认奖项只能选择当前档位或降档奖项");
|
||||
}
|
||||
String needInstituteVote = AWARD_GOLD.equals(action.getLeaderFinalAward()) ? "1" : "0";
|
||||
ImprovementProposal update = new ImprovementProposal();
|
||||
update.setId(proposal.getId());
|
||||
@@ -215,6 +330,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) {
|
||||
@@ -225,7 +409,7 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
update.setFinalAward(action.getFinalAward());
|
||||
update.setArchiveBy(loginUser.getId());
|
||||
update.setArchiveTime(new Date());
|
||||
if (AWARD_GOLD.equals(proposal.getLeaderFinalAward()) && "failed".equals(proposal.getInstituteVoteResult())) {
|
||||
if (AWARD_GOLD.equals(proposal.getLeaderFinalAward()) && INSTITUTE_VOTE_RESULT_FAILED.equals(proposal.getInstituteVoteResult())) {
|
||||
update.setManualConfirmResult("confirmed");
|
||||
}
|
||||
improvementProposalMapper.updateById(update);
|
||||
@@ -238,25 +422,252 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
throw new IllegalStateException("投票结果只能是同意或不同意");
|
||||
}
|
||||
ImprovementProposal proposal = validateRunningTask(action, loginUser, TASK_INSTITUTE_VOTE);
|
||||
ImprovementInstituteVote vote = improvementInstituteVoteMapper.selectByMainId(proposal.getId()).stream()
|
||||
.filter(item -> loginUser.getId().equals(item.getVoterId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("当前用户不在所级评议投票名单中"));
|
||||
ImprovementInstituteVote vote = findInstituteVote(proposal.getId(), loginUser.getId());
|
||||
if ("voted".equals(vote.getVoteStatus())) {
|
||||
throw new IllegalStateException("您已完成投票,不能重复修改");
|
||||
throw new IllegalStateException("投票已锁定,请重新提交流程办理");
|
||||
}
|
||||
int updated = improvementInstituteVoteMapper.update(null, new LambdaUpdateWrapper<ImprovementInstituteVote>()
|
||||
.eq(ImprovementInstituteVote::getId, vote.getId())
|
||||
.eq(ImprovementInstituteVote::getVoteStatus, "pending")
|
||||
.set(ImprovementInstituteVote::getVoteResult, action.getVoteResult())
|
||||
.set(ImprovementInstituteVote::getVoteStatus, "voted")
|
||||
.set(ImprovementInstituteVote::getVoteTime, new Date())
|
||||
.set(ImprovementInstituteVote::getVoteTime, null)
|
||||
.set(ImprovementInstituteVote::getProcessTaskId, action.getTaskId()));
|
||||
if (updated != 1) {
|
||||
throw new IllegalStateException("投票状态已变化,请刷新后重试");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitInstituteVote(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
|
||||
if (!"agree".equals(action.getVoteResult()) && !"disagree".equals(action.getVoteResult())) {
|
||||
throw new IllegalStateException("投票结果只能是同意或不同意");
|
||||
}
|
||||
ImprovementProposal proposal = validateRunningTask(action, loginUser, TASK_INSTITUTE_VOTE);
|
||||
ImprovementProposal lockedProposal = improvementProposalMapper.selectByIdForUpdate(proposal.getId());
|
||||
if (lockedProposal == null || !action.getProcessInstanceId().equals(lockedProposal.getProcessInstanceId())) {
|
||||
throw new IllegalStateException("提案流程状态已变化,请刷新后重试");
|
||||
}
|
||||
ImprovementInstituteVote vote = findInstituteVote(lockedProposal.getId(), loginUser.getId());
|
||||
if ("voted".equals(vote.getVoteStatus())) {
|
||||
// 网络异常导致流程任务未完成时,允许使用已确认的同一结果重试办理;不允许改票。
|
||||
if (!action.getVoteResult().equals(vote.getVoteResult())) {
|
||||
throw new IllegalStateException("投票已确认,不能修改投票结果");
|
||||
}
|
||||
} else {
|
||||
int updated = improvementInstituteVoteMapper.update(null, new LambdaUpdateWrapper<ImprovementInstituteVote>()
|
||||
.eq(ImprovementInstituteVote::getId, vote.getId())
|
||||
.eq(ImprovementInstituteVote::getVoteStatus, "pending")
|
||||
.set(ImprovementInstituteVote::getVoteResult, action.getVoteResult())
|
||||
.set(ImprovementInstituteVote::getVoteStatus, "voted")
|
||||
.set(ImprovementInstituteVote::getVoteTime, new Date())
|
||||
.set(ImprovementInstituteVote::getProcessTaskId, action.getTaskId()));
|
||||
if (updated != 1) {
|
||||
throw new IllegalStateException("投票状态已变化,请刷新后重试");
|
||||
}
|
||||
}
|
||||
updateInstituteVoteSummaryIfComplete(lockedProposal, action.getProcessInstanceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitInstituteVoteAndComplete(ImprovementProposalBpmActionDTO action, LoginUser loginUser, HttpServletRequest request) {
|
||||
submitInstituteVote(action, loginUser);
|
||||
|
||||
// 复用通用办理服务,确保审批日志、流程状态及多实例收敛逻辑与普通办理保持一致。
|
||||
HashMap<String, String> processParams = new HashMap<>(4);
|
||||
processParams.put("taskId", action.getTaskId());
|
||||
processParams.put("processModel", "1");
|
||||
processParams.put("nextCodeCount", "1");
|
||||
// Task5 只有正常流转分支;通用办理服务以 end + 单分支执行 taskService.complete。
|
||||
processParams.put("nextnode", "end");
|
||||
Result<Object> result = extActTaskCcService.processComplete(processParams, request);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
throw new IllegalStateException(result == null || oConvertUtils.isEmpty(result.getMessage())
|
||||
? "投票已确认,但流程办理失败"
|
||||
: result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private ImprovementInstituteVote findInstituteVote(String mainId, String voterId) {
|
||||
return improvementInstituteVoteMapper.selectByMainId(mainId).stream()
|
||||
.filter(item -> voterId.equals(item.getVoterId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("当前用户不在所级评议投票名单中"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 所级评议以名单确定时写入的应投票人数为分母。赞成票满足 agree * 3 >= total * 2 即通过,避免小数取整歧义。
|
||||
*/
|
||||
private void updateInstituteVoteSummaryIfComplete(ImprovementProposal proposal, String processInstanceId) {
|
||||
Integer totalVoters = proposal.getInstituteTotalVoters();
|
||||
if (totalVoters == null || totalVoters <= 0) {
|
||||
throw new IllegalStateException("所级评议应投票人数未初始化");
|
||||
}
|
||||
List<ImprovementInstituteVote> votes = improvementInstituteVoteMapper.selectByMainId(proposal.getId());
|
||||
if (votes.size() != totalVoters) {
|
||||
throw new IllegalStateException("所级评议投票名单与应投票人数不一致");
|
||||
}
|
||||
if (votes.stream().anyMatch(item -> !"voted".equals(item.getVoteStatus()))) {
|
||||
return;
|
||||
}
|
||||
long agreeCount = votes.stream().filter(item -> "agree".equals(item.getVoteResult())).count();
|
||||
long disagreeCount = votes.stream().filter(item -> "disagree".equals(item.getVoteResult())).count();
|
||||
if (agreeCount + disagreeCount != totalVoters) {
|
||||
throw new IllegalStateException("所级评议投票结果不完整");
|
||||
}
|
||||
String voteResult = agreeCount * 3 >= (long) totalVoters * 2
|
||||
? INSTITUTE_VOTE_RESULT_PASSED
|
||||
: INSTITUTE_VOTE_RESULT_FAILED;
|
||||
ImprovementProposal update = new ImprovementProposal();
|
||||
update.setId(proposal.getId());
|
||||
update.setInstituteAgreeCount((int) agreeCount);
|
||||
update.setInstituteDisagreeCount((int) disagreeCount);
|
||||
update.setInstituteVoteResult(voteResult);
|
||||
improvementProposalMapper.updateById(update);
|
||||
runtimeService.setVariable(processInstanceId, "institute_vote_result", voteResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 固定按角色编码读取名单,保存时再次查询以防前端伪造用户 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());
|
||||
action.setProcessInstanceId(scoreAction.getProcessInstanceId());
|
||||
action.setTaskId(scoreAction.getTaskId());
|
||||
return action;
|
||||
}
|
||||
|
||||
private ImprovementProposal validateDeptScoreTask(ImprovementProposalBpmActionDTO action, LoginUser loginUser) {
|
||||
ImprovementProposal proposal = validateRunningTask(action, loginUser, TASK_DEPT_SCORE);
|
||||
Task task = taskService.createTaskQuery().taskId(action.getTaskId()).singleResult();
|
||||
// 部门评议必须使用并行多实例的直接办理人任务,避免候选组任务被未分配人员越权评分。
|
||||
if (task == null || !loginUser.getUsername().equals(task.getAssignee())) {
|
||||
throw new IllegalStateException("当前用户不是本评分任务的办理人");
|
||||
}
|
||||
return proposal;
|
||||
}
|
||||
|
||||
private ImprovementDeptScore findDeptScore(String mainId, String scorerId) {
|
||||
return improvementDeptScoreMapper.selectByMainId(mainId).stream()
|
||||
.filter(item -> scorerId.equals(item.getScorerId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private void validateScoreValues(ImprovementDeptScoreBpmDTO scoreAction) {
|
||||
if (scoreAction == null) {
|
||||
throw new IllegalStateException("评分参数不能为空");
|
||||
}
|
||||
validateScoreValue(scoreAction.getSafetyScore(), "安全");
|
||||
validateScoreValue(scoreAction.getQualityScore(), "质量");
|
||||
validateScoreValue(scoreAction.getEfficiencyScore(), "效率");
|
||||
validateScoreValue(scoreAction.getCostScore(), "成本");
|
||||
validateScoreValue(scoreAction.getPromotionScore(), "可推广度");
|
||||
validateScoreValue(scoreAction.getOriginalityScore(), "独创性");
|
||||
validateScoreValue(scoreAction.getEffortScore(), "努力度");
|
||||
}
|
||||
|
||||
private void validateScoreValue(BigDecimal score, String dimensionName) {
|
||||
if (score == null || score.scale() > 2 || score.compareTo(BigDecimal.ZERO) < 0 || score.compareTo(BigDecimal.valueOf(100)) > 0) {
|
||||
throw new IllegalStateException(dimensionName + "得分必须是 0 到 100 之间且最多两位小数的数值");
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal calculateEffectScore(ImprovementDeptScoreBpmDTO scoreAction) {
|
||||
return calculateEffectScore(scoreAction.getSafetyScore(), scoreAction.getQualityScore(), scoreAction.getEfficiencyScore(), scoreAction.getCostScore());
|
||||
}
|
||||
|
||||
private BigDecimal calculateEffectScore(BigDecimal safetyScore, BigDecimal qualityScore, BigDecimal efficiencyScore, BigDecimal costScore) {
|
||||
return safetyScore.add(qualityScore).add(efficiencyScore).add(costScore).divide(BigDecimal.valueOf(4), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal calculateTotalScore(ImprovementDeptScoreBpmDTO scoreAction, BigDecimal effectScore) {
|
||||
return effectScore.multiply(new BigDecimal("0.50"))
|
||||
.add(scoreAction.getPromotionScore().multiply(new BigDecimal("0.20")))
|
||||
.add(scoreAction.getOriginalityScore().multiply(new BigDecimal("0.20")))
|
||||
.add(scoreAction.getEffortScore().multiply(new BigDecimal("0.10")))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private void fillDeptScoreDraft(ImprovementDeptScore target, ImprovementDeptScoreBpmDTO source, BigDecimal effectScore, BigDecimal totalScore) {
|
||||
target.setSafetyScore(source.getSafetyScore());
|
||||
target.setQualityScore(source.getQualityScore());
|
||||
target.setEfficiencyScore(source.getEfficiencyScore());
|
||||
target.setCostScore(source.getCostScore());
|
||||
target.setEffectScore(effectScore);
|
||||
target.setPromotionScore(source.getPromotionScore());
|
||||
target.setOriginalityScore(source.getOriginalityScore());
|
||||
target.setEffortScore(source.getEffortScore());
|
||||
target.setTotalScore(totalScore);
|
||||
target.setScoreOpinion(source.getScoreOpinion());
|
||||
target.setScoreStatus("pending");
|
||||
target.setScoreTime(null);
|
||||
}
|
||||
|
||||
private void validateSavedScore(ImprovementDeptScore score) {
|
||||
validateScoreValue(score.getSafetyScore(), "安全");
|
||||
validateScoreValue(score.getQualityScore(), "质量");
|
||||
validateScoreValue(score.getEfficiencyScore(), "效率");
|
||||
validateScoreValue(score.getCostScore(), "成本");
|
||||
validateScoreValue(score.getPromotionScore(), "可推广度");
|
||||
validateScoreValue(score.getOriginalityScore(), "独创性");
|
||||
validateScoreValue(score.getEffortScore(), "努力度");
|
||||
BigDecimal calculatedEffectScore = calculateEffectScore(score.getSafetyScore(), score.getQualityScore(), score.getEfficiencyScore(), score.getCostScore());
|
||||
if (score.getEffectScore() == null || score.getEffectScore().compareTo(calculatedEffectScore) != 0 || score.getTotalScore() == null) {
|
||||
throw new IllegalStateException("请先保存完整个人评分");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDeptScoreSummaryIfComplete(ImprovementProposal proposal, String processInstanceId) {
|
||||
long expectedScorerCount = historyService.createHistoricTaskInstanceQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.taskDefinitionKey(TASK_DEPT_SCORE)
|
||||
.count();
|
||||
List<ImprovementDeptScore> scoreList = improvementDeptScoreMapper.selectByMainId(proposal.getId());
|
||||
boolean allCompleted = expectedScorerCount > 0 && scoreList.size() == expectedScorerCount
|
||||
&& scoreList.stream().allMatch(item -> "completed".equals(item.getScoreStatus()) && item.getTotalScore() != null);
|
||||
if (!allCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal totalScore = scoreList.stream()
|
||||
.map(ImprovementDeptScore::getTotalScore)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal averageScore = totalScore.divide(BigDecimal.valueOf(expectedScorerCount), 2, RoundingMode.HALF_UP);
|
||||
ImprovementProposal summary = new ImprovementProposal();
|
||||
summary.setId(proposal.getId());
|
||||
summary.setDeptAvgScore(averageScore);
|
||||
summary.setSuggestedAward(resolveSuggestedAward(averageScore));
|
||||
improvementProposalMapper.updateById(summary);
|
||||
}
|
||||
|
||||
private String resolveSuggestedAward(BigDecimal averageScore) {
|
||||
if (averageScore.compareTo(BigDecimal.valueOf(80)) >= 0) {
|
||||
return AWARD_GOLD;
|
||||
}
|
||||
if (averageScore.compareTo(BigDecimal.valueOf(60)) >= 0) {
|
||||
return AWARD_SILVER;
|
||||
}
|
||||
if (averageScore.compareTo(BigDecimal.valueOf(40)) >= 0) {
|
||||
return AWARD_BRONZE;
|
||||
}
|
||||
return AWARD_PARTICIPATION;
|
||||
}
|
||||
|
||||
private ImprovementProposal validateRunningTask(ImprovementProposalBpmActionDTO action, LoginUser loginUser, String expectedTaskDefKey) {
|
||||
if (action == null || oConvertUtils.isEmpty(action.getMainId()) || oConvertUtils.isEmpty(action.getProcessInstanceId())
|
||||
|| oConvertUtils.isEmpty(action.getTaskId())) {
|
||||
@@ -278,11 +689,30 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
}
|
||||
|
||||
private void validateAward(String award) {
|
||||
if (!AWARD_GOLD.equals(award) && !AWARD_SILVER.equals(award) && !AWARD_BRONZE.equals(award) && !AWARD_PARTICIPATION.equals(award)) {
|
||||
if (!isValidAward(award)) {
|
||||
throw new IllegalStateException("请选择有效的奖项");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidAward(String award) {
|
||||
return AWARD_GOLD.equals(award) || AWARD_SILVER.equals(award) || AWARD_BRONZE.equals(award) || AWARD_PARTICIPATION.equals(award);
|
||||
}
|
||||
|
||||
/**
|
||||
* 负责人只能在当前评定档位内确认或降档:经过评分时以系统建议奖项为准,未评分时以初评奖项为准。
|
||||
*/
|
||||
private String resolveLeaderAwardBase(ImprovementProposal proposal) {
|
||||
String awardBase = "1".equals(proposal.getNeedDeptScore()) ? proposal.getSuggestedAward() : proposal.getInitialAward();
|
||||
if (!isValidAward(awardBase)) {
|
||||
throw new IllegalStateException("部门评议建议奖项尚未生成,不能确认负责人奖项");
|
||||
}
|
||||
return awardBase;
|
||||
}
|
||||
|
||||
private int awardRank(String award) {
|
||||
return Integer.parseInt(award);
|
||||
}
|
||||
|
||||
private boolean needsDeptScore(String award) {
|
||||
return AWARD_GOLD.equals(award) || AWARD_SILVER.equals(award);
|
||||
}
|
||||
|
||||
+3
-3
@@ -128,9 +128,9 @@ public class ImprovementProposalPage {
|
||||
@Excel(name = "所级评议不同意票数", width = 15)
|
||||
@Schema(description = "所级评议不同意票数")
|
||||
private Integer instituteDisagreeCount;
|
||||
/**所级评议结果:passed/failed*/
|
||||
@Excel(name = "所级评议结果:passed/failed", width = 15)
|
||||
@Schema(description = "所级评议结果:passed/failed")
|
||||
/**所级评议结果:通过/不通过*/
|
||||
@Excel(name = "所级评议结果:通过/不通过", width = 15)
|
||||
@Schema(description = "所级评议结果:通过/不通过")
|
||||
private String instituteVoteResult;
|
||||
/**金奖未通过后的经办人手动确认结果*/
|
||||
@Excel(name = "金奖未通过后的经办人手动确认结果", width = 15)
|
||||
|
||||
+9
@@ -78,6 +78,15 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
@GetMapping("/sys/api/getUserById")
|
||||
LoginUser getUserById(@RequestParam("id") String id);
|
||||
|
||||
/**
|
||||
* 查询角色下未逻辑删除用户的登录用户名,供流程多实例任务分配使用。
|
||||
*
|
||||
* @param roleId 系统角色 ID
|
||||
* @return 用户名列表
|
||||
*/
|
||||
@GetMapping("/sys/api/getUsernamesByRoleId")
|
||||
List<String> getUsernamesByRoleIdLocalApi(@RequestParam("roleId") String roleId);
|
||||
|
||||
/**
|
||||
* 7通过用户账号查询角色集合
|
||||
* @param username
|
||||
|
||||
+7
@@ -10,6 +10,7 @@ import org.jeecg.common.constant.enums.EmailTemplateEnum;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -24,6 +25,12 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
|
||||
@Setter
|
||||
private Throwable cause;
|
||||
|
||||
@Override
|
||||
public List<String> getUsernamesByRoleIdLocalApi(String roleId) {
|
||||
log.error("Query usernames by role failed, roleId={}", roleId, cause);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSysAnnouncement(MessageDTO message) {
|
||||
log.error("发送消息失败 {}", cause);
|
||||
|
||||
+10
-1
@@ -575,4 +575,13 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
List<String> getUsersListByDeptIdAndRoleIdLocalApi(String deptId, String roleId);
|
||||
|
||||
List<String> getUserByRoleIdLocalApi(String roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query active usernames by role for Flowable multi-instance tasks.
|
||||
* This method intentionally uses an independent query path so existing role queries remain unchanged.
|
||||
*
|
||||
* @param roleId system role id
|
||||
* @return active username list
|
||||
*/
|
||||
List<String> getUsernamesByRoleIdLocalApi(String roleId);
|
||||
}
|
||||
|
||||
+8
@@ -131,6 +131,14 @@ public class SystemApiController {
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程多实例节点只接受 username 作为办理人标识,统一由系统服务按角色查询。
|
||||
*/
|
||||
@GetMapping("/getUsernamesByRoleId")
|
||||
List<String> getUsernamesByRoleId(@RequestParam("roleId") String roleId) {
|
||||
return sysBaseApi.getUsernamesByRoleIdLocalApi(roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户账号查询角色集合
|
||||
* @param username
|
||||
|
||||
+9
@@ -251,4 +251,13 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
* @return
|
||||
*/
|
||||
List<SysUser> getUserByRoleId(String roleId);
|
||||
|
||||
/**
|
||||
* Query active usernames by role through a dedicated statement.
|
||||
* It must not reuse the overloaded getUserByRoleId statement used by the user management page.
|
||||
*
|
||||
* @param roleId role id
|
||||
* @return username list
|
||||
*/
|
||||
List<String> getUsernamesByRoleId(@Param("roleId") String roleId);
|
||||
}
|
||||
|
||||
+11
@@ -59,6 +59,17 @@
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- Multi-instance flow task query. Keep it separate from the overloaded getUserByRoleId statement. -->
|
||||
<select id="getUsernamesByRoleId" resultType="java.lang.String">
|
||||
select distinct u.username
|
||||
from sys_user u
|
||||
inner join sys_user_role ur on ur.user_id = u.id
|
||||
where u.del_flag = 0
|
||||
and ur.role_id = #{roleId}
|
||||
and u.username is not null
|
||||
and u.username != ''
|
||||
</select>
|
||||
|
||||
<select id="getUserByRoleIdWithDeptIds" resultType="org.jeecg.modules.system.vo.SysUserRoleDeptsVo">
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
|
||||
+9
@@ -498,4 +498,13 @@ public interface ISysUserService extends IService<SysUser> {
|
||||
List<String> queryUserByDeptAndROle(@NotBlank(message = "deptId不为空") String deptId, @NotBlank(message = "角色id不为空") String roleId, @NotBlank(message = "secretLevel不为空") Integer secretLevel);
|
||||
|
||||
List<String> getUserByRoleId(String roleId);
|
||||
|
||||
/**
|
||||
* Query active usernames by role for Flowable multi-instance task assignment.
|
||||
* This is separate from the legacy role-user query to avoid changing its behavior.
|
||||
*
|
||||
* @param roleId system role id
|
||||
* @return active username list
|
||||
*/
|
||||
List<String> getUsernamesByRoleId(String roleId);
|
||||
}
|
||||
|
||||
+12
-1
@@ -2161,4 +2161,15 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
.stream()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUsernamesByRoleIdLocalApi(String roleId) {
|
||||
List<String> usernames = sysUserService.getUsernamesByRoleId(roleId);
|
||||
return Optional.ofNullable(usernames)
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -2424,4 +2424,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
public List<String> getUserByRoleId(String roleId){
|
||||
return sysUserMapper.getUserByRoleId(roleId).stream().map(SysUser::getUsername).collect(Collectors.toList()) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUsernamesByRoleId(String roleId) {
|
||||
if (StringUtils.isBlank(roleId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return sysUserMapper.getUsernamesByRoleId(roleId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user