From 8c467328f1a7b1858d7d5ce816c87a9dd1464bef Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Wed, 20 May 2026 08:56:48 +0800 Subject: [PATCH] commit --- .../transact/ProposalSecondedController.java | 56 ++++++++++- .../service/ProposalSecondedService.java | 3 +- .../impl/ProposalSecondedServiceImpl.java | 96 ++++++++++++++++++- .../proposal/transact/seconded/index.html | 46 ++++++--- .../proposal/transact/seconded/index.html | 54 +++++++---- 5 files changed, 219 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java index a2d1d7dd..89b5e9fd 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java @@ -22,6 +22,7 @@ import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.Static; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -92,14 +93,23 @@ public class ProposalSecondedController { ta.actorId taskActorUserId, ta.actorName taskActorName, IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, - IF(rt.id IS NOT NULL, 1, 0) AS canRevoke + IF(rt.id IS NOT NULL, 1, 0) AS canRevoke, + CASE + WHEN t.taskState = 10 THEN 1 + WHEN t.taskState = 99 AND dt.id IS NOT NULL AND ft.id IS NULL AND ps.isAgree IS NULL THEN 1 + ELSE 0 + END AS canAudit, + IF(t.taskState = 99, 1, 0) AS abandonedSecondTask FROM wf_process_task t LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10 LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 + LEFT JOIN wf_process_task dt ON dt.processInstanceId = ins.id AND dt.taskName = 'delegation' AND dt.taskState = 10 + LEFT JOIN wf_process_task ft ON ft.processInstanceId = ins.id AND ft.taskName = 'delegation' AND ft.taskState = 20 LEFT JOIN proposal_info info ON info.id = ins.businessNo + LEFT JOIN proposal_second ps ON ps.proposalId = info.id AND ps.seconderId = ta.actorId AND ps.mode = 1 LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId @@ -116,7 +126,20 @@ public class ProposalSecondedController { if (approval) { cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode())); } else { - cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); + // 未附议列表包含两类数据: + // 1. second 仍在进行中的正常受邀附议待办; + // 2. second 已因人数达标被系统废弃,但 delegation 团长审核还未办理,此时允许受邀人补审并写入流程记录。 + cnd.and(new Static(""" + ( + t.taskState = 10 + OR ( + t.taskState = 99 + AND dt.id IS NOT NULL + AND ft.id IS NULL + AND ps.isAgree IS NULL + ) + ) + """)); } ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); @@ -145,11 +168,25 @@ public class ProposalSecondedController { LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id LEFT JOIN proposal_info info ON info.id = ins.businessNo + LEFT JOIN proposal_second ps ON ps.proposalId = info.id AND ps.seconderId = ta.actorId AND ps.mode = 1 + LEFT JOIN wf_process_task dt ON dt.processInstanceId = ins.id AND dt.taskName = 'delegation' AND dt.taskState = 10 + LEFT JOIN wf_process_task ft ON ft.processInstanceId = ins.id AND ft.taskName = 'delegation' AND ft.taskState = 20 $condition """); Cnd cnd = Cnd.NEW(); cnd.and("t.taskName", "=", "second"); - cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); + // 待办数量与列表口径保持一致:正常待办 + delegation 未审前可补审的废弃 second 任务。 + cnd.and(new Static(""" + ( + t.taskState = 10 + OR ( + t.taskState = 99 + AND dt.id IS NOT NULL + AND ft.id IS NULL + AND ps.isAgree IS NULL + ) + ) + """)); cnd.andEX("info.sessionId", "=", sessionId); /* if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { @@ -167,10 +204,21 @@ public class ProposalSecondedController { @ApiOperation("修改提案附议信息") @SLog(tag = "提案管理系统-附议提案", msg = "修改提案附议信息") public Result updateInfo(@Param("proposalId") String proposalId, + @Param("taskId") Long taskId, @Param("taskActorUserId") String taskActorUserId, @Param("opinion") String opinion, @Param("submitType") Integer submitType) { - return proposalSecondedService.updateInviteSecondInfo(proposalId, taskActorUserId, opinion, submitType); + /* + * 参数说明: + * proposalId:提案ID,用于定位 proposal_second 业务附议记录。 + * taskId:second 流程任务ID。正常待办先由前端执行流程任务,再调用本接口同步业务记录; + * 如果 second 已被系统废弃但 delegation 还未审核,本接口会用该 taskId 补写流程审批记录。 + * taskActorUserId:受邀附议人用户ID,必须与 proposal_second.seconderId 和任务参与人一致。 + * opinion:受邀附议意见,写入业务表;废弃任务补审时也写入流程任务变量。 + * submitType:提交类型,1 表示同意,20 表示不同意。 + * 返回值:Result,code=0 表示保存成功;废弃任务补审成功时,流程记录和业务附议记录都会同步完成。 + */ + return proposalSecondedService.updateInviteSecondInfo(proposalId, taskId, taskActorUserId, opinion, submitType); } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java index 6a05049b..65cdb120 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java @@ -10,12 +10,13 @@ public interface ProposalSecondedService extends BaseService { * 保存受邀附议办理结果,并在受邀附议同意人数达到配置数量时自动推进到团长审核。 * * @param proposalId 提案ID + * @param taskId second 流程任务ID;正常待办提交后用于业务记录同步,废弃任务补审时用于写入流程审批记录 * @param taskActorUserId 受邀附议办理人ID * @param opinion 附议意见 * @param submitType 提交类型 * @return 处理结果 */ - Result updateInviteSecondInfo(String proposalId, String taskActorUserId, String opinion, Integer submitType); + Result updateInviteSecondInfo(String proposalId, Long taskId, String taskActorUserId, String opinion, Integer submitType); /** * 保存主动附议结果。 diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalSecondedServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalSecondedServiceImpl.java index f8c17722..33f59cf5 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalSecondedServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalSecondedServiceImpl.java @@ -3,13 +3,17 @@ package com.budwk.app.zhgh.democratic.proposal.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.engine.FlowEngine; +import com.budwk.app.flow.engine.util.FlowUtil; import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.entity.ProcessTaskActor; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; +import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.sys.views.View_user; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; @@ -40,7 +44,11 @@ public class ProposalSecondedServiceImpl extends BaseServiceImpl @Override @Aop(TransAop.READ_COMMITTED) - public Result updateInviteSecondInfo(String proposalId, String taskActorUserId, String opinion, Integer submitType) { + public Result updateInviteSecondInfo(String proposalId, Long taskId, String taskActorUserId, String opinion, Integer submitType) { + if (!ObjectUtil.equals(taskActorUserId, SecurityUtil.getUserId())) { + return Result.error("只能办理当前登录人的受邀附议任务"); + } + ProposalSecond proposalSecond = dao().fetch( ProposalSecond.class, Cnd.where(ProposalSecond::getProposalId, "=", proposalId) @@ -50,6 +58,18 @@ public class ProposalSecondedServiceImpl extends BaseServiceImpl if (proposalSecond == null) { return Result.error("未查询到附议记录"); } + if (proposalSecond.getIsAgree() != null) { + return Result.error("该受邀附议已办理,请勿重复提交"); + } + + ProcessTask secondTask = taskId == null ? null : dao().fetch(ProcessTask.class, taskId); + if (secondTask != null && ProcessTaskStateEnum.ABANDON.getCode().equals(secondTask.getTaskState())) { + Result supplementResult = supplementAbandonedSecondTask(proposalId, secondTask, taskActorUserId, opinion, submitType); + if (supplementResult.getCode() != 0) { + return supplementResult; + } + } + proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode())); proposalSecond.setOpinion(opinion); proposalSecond.setSecondedTime(DateUtil.date()); @@ -119,6 +139,80 @@ public class ProposalSecondedServiceImpl extends BaseServiceImpl return Result.success(); } + /** + * delegation 团长审核未办理前,允许受邀人补审已经被系统废弃的 second 会签任务。 + * 这里不能再调用流程引擎执行任务,因为引擎只允许执行 DOING 状态任务;补审只负责把废弃任务补写成 + * FINISHED 审批记录,并保存本次附议意见,不再触发流程继续流转。 + */ + private Result supplementAbandonedSecondTask(String proposalId, ProcessTask secondTask, String taskActorUserId, String opinion, Integer submitType) { + if (!"second".equals(secondTask.getTaskName())) { + return Result.error("当前任务不是受邀附议任务"); + } + + ProcessInstance processInstance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", proposalId)); + if (processInstance == null || !ObjectUtil.equals(processInstance.getId(), secondTask.getProcessInstanceId())) { + return Result.error("未查询到提案流程信息"); + } + + int taskActorCount = dao().count( + ProcessTaskActor.class, + Cnd.where(ProcessTaskActor::getProcessTaskId, "=", secondTask.getId()) + .and(ProcessTaskActor::getActorId, "=", taskActorUserId) + ); + if (taskActorCount == 0) { + return Result.error("当前用户不是该受邀附议任务办理人"); + } + + int delegationDoneCount = dao().count( + ProcessTask.class, + Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) + .and(ProcessTask::getTaskName, "=", "delegation") + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) + ); + if (delegationDoneCount > 0) { + return Result.error("团长已审核,不能再补充受邀附议"); + } + + int delegationDoingCount = dao().count( + ProcessTask.class, + Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) + .and(ProcessTask::getTaskName, "=", "delegation") + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()) + ); + if (delegationDoingCount == 0) { + return Result.error("当前提案不在团长审核待办阶段,不能补充受邀附议"); + } + + Dict taskVariable = FlowUtil.variableToDict(secondTask.getVariable()); + taskVariable.put(FlowConst.SUBMIT_TYPE, submitType); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "opinion", opinion); + fillTaskOperatorInfo(taskActorUserId, taskVariable); + FlowUtil.addUserInfoToArgs(taskActorUserId, taskVariable); + + secondTask.setTaskState(ProcessTaskStateEnum.FINISHED.getCode()); + secondTask.setOperator(taskActorUserId); + secondTask.setFinishTime(DateUtil.date()); + secondTask.setUpdatedAt(System.currentTimeMillis()); + secondTask.setUpdatedBy(taskActorUserId); + secondTask.setVariable(JSONUtil.toJsonStr(taskVariable)); + dao().update(secondTask, "taskState|operator|finishTime|updatedAt|updatedBy|variable"); + return Result.success(); + } + + /** + * 补写流程记录时,补齐与 /flow/common/executeTask 正常审批一致的任务表单用户信息。 + * 这些字段用于审批记录展示和后续导出模板读取,避免补审记录只有意见、没有办理人组织信息。 + */ + private void fillTaskOperatorInfo(String taskActorUserId, Dict taskVariable) { + View_user user = dao().fetch(View_user.class, Cnd.where("id", "=", taskActorUserId)); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", user == null ? "" : user.getUsername()); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", user == null ? "" : user.getLoginname()); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", user == null ? "" : user.getUnitId()); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", user == null ? "" : user.getUnitName()); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", user == null ? "" : user.getUnionId()); + taskVariable.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionName", user == null ? "" : user.getUnionName()); + } + /** * 只按受邀附议记录判断是否达到配置的附议人数。 * 一旦满足配置数量,就用系统自动执行一个仍处于办理中的 second 任务来触发流程引擎会签合并, diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html index de39ffe6..a212b3af 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html @@ -275,7 +275,7 @@ layout("/layouts/platform.html"){ showAuditButton: function (row) { if (this.activeTab === "inviteSeconded") { - return row.taskState === 10 + return row.canAudit === 1 } return row.count === 0 }, @@ -287,13 +287,19 @@ layout("/layouts/platform.html"){ }) }, - openAudit: async function (row) { + openAudit: function (row) { if (this.activeTab === "positiveSeconded") { - var validatePass = await this.validateCanSeconded(row.id) - if (!validatePass) { - return - } + this.validateCanSeconded(row.id).then((validatePass) => { + if (validatePass) { + this.openAuditForm(row) + } + }) + return } + this.openAuditForm(row) + }, + + openAuditForm: function (row) { this.$set(this, "row", row) this.$refs.guava.edit(() => { this.$set(this, "showApprovalForm", true) @@ -314,7 +320,6 @@ layout("/layouts/platform.html"){ } this.$refs.proposalInfoRef.onOpen(row) }) - }, handleTaskAction: function (val) { @@ -335,6 +340,17 @@ layout("/layouts/platform.html"){ }, handleInviteSeconded: function (val) { + if (this.row.abandonedSecondTask === 1) { + this.updateSecond(val).then((res) => { + if (res.code === 0) { + this.$refs.guava.index() + this.$message.success(res.msg) + this.doSearch() + this.listInviteSecondedTodoCount() + } + }) + return + } this.$axios.post("/flow/common/executeTask", { data: JSON.stringify({ ...this.formData, @@ -342,18 +358,22 @@ layout("/layouts/platform.html"){ }) }).then((res) => { if (res.code === 0) { - this.updateSecond(val) - this.$refs.guava.index() - this.$message.success(res.msg) - this.doSearch() - this.listInviteSecondedTodoCount() + this.updateSecond(val).then((updateRes) => { + if (updateRes.code === 0) { + this.$refs.guava.index() + this.$message.success(res.msg) + this.doSearch() + this.listInviteSecondedTodoCount() + } + }) } }) }, updateSecond: function (submitType) { - this.$axios.post("/platform/proposal/seconded/updateInfo", { + return this.$axios.post("/platform/proposal/seconded/updateInfo", { proposalId: this.row.id, + taskId: this.row.taskId, taskActorUserId: this.row.taskActorUserId, opinion: this.formData.tf_opinion, submitType: submitType, diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/seconded/index.html b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/seconded/index.html index 6d61d091..89d8c386 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/seconded/index.html +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/seconded/index.html @@ -245,7 +245,7 @@ layout("/layouts/platform_h5.html"){ }, showApprovalButton(row) { if (this.activeTab === "inviteSeconded") { - return row.taskState === 10 + return row.canAudit === 1 } return row.count === 0 }, @@ -255,13 +255,19 @@ layout("/layouts/platform_h5.html"){ this.$refs.proposalInfoRef.onOpen(row) }, - async onApproval(row) { + onApproval(row) { if (this.activeTab === "positiveSeconded") { - const validatePass = await this.validateCanSeconded(row.id) - if (!validatePass) { - return - } + this.validateCanSeconded(row.id).then((validatePass) => { + if (validatePass) { + this.openApprovalForm(row) + } + }) + return } + this.openApprovalForm(row) + }, + + openApprovalForm(row) { this.$set(this, "row", row) this.$set(this, "showApprovalForm", true) this.$refs.proposalInfoRef.onOpen(row) @@ -294,9 +300,8 @@ layout("/layouts/platform_h5.html"){ }) }, - async handleTaskAction(val) { - try { - await this.$refs.formRef.validate() + handleTaskAction(val) { + this.$refs.formRef.validate().then(() => { this.$dialog.confirm({ title: "提示", message: "您确定要提交吗?" @@ -309,12 +314,23 @@ layout("/layouts/platform_h5.html"){ }).catch(() => { }) - } catch (error) { + }).catch(() => { - } + }) }, handleInviteSeconded(val) { + if (this.row.abandonedSecondTask === 1) { + this.updateSecond(val).then((res) => { + if (res.code === 0) { + this.$refs.proposalInfoRef.onClose() + this.$toast.success(res.msg) + this.doSearch() + this.listInviteSecondedTodoCount() + } + }) + return + } this.$axios.post("/flow/common/executeTask", { data: JSON.stringify({ ...this.formData, @@ -322,11 +338,14 @@ layout("/layouts/platform_h5.html"){ }) }).then((res) => { if (res.code === 0) { - this.$refs.proposalInfoRef.onClose() - this.$toast.success(res.msg) - this.doSearch() - this.updateSecond(val) - this.listInviteSecondedTodoCount() + this.updateSecond(val).then((updateRes) => { + if (updateRes.code === 0) { + this.$refs.proposalInfoRef.onClose() + this.$toast.success(res.msg) + this.doSearch() + this.listInviteSecondedTodoCount() + } + }) } }) }, @@ -346,8 +365,9 @@ layout("/layouts/platform_h5.html"){ }, updateSecond(submitType) { - this.$axios.post("/platform/proposal/seconded/updateInfo", { + return this.$axios.post("/platform/proposal/seconded/updateInfo", { proposalId: this.row.id, + taskId: this.row.taskId, taskActorUserId: this.row.taskActorUserId, opinion: this.formData.tf_opinion, submitType: submitType,