This commit is contained in:
=
2026-04-11 16:42:03 +08:00
32 changed files with 1560 additions and 892 deletions
@@ -27,10 +27,10 @@ import java.util.Map;
@IocBean @IocBean
public class SmsJshvcServiceImpl implements SmsService { public class SmsJshvcServiceImpl implements SmsService {
private static final String APPID = "1430576717768122368"; private static final String APPID = "";
private static final String APP_SECRET = "19A0ACB03FBQHL4R4XQD"; private static final String APP_SECRET = "";
private static final String TOKEN_URL = "https://gateway.jshvc.edu.cn/token/gateway/accessToken"; private static final String TOKEN_URL = "";
private static final String MSG_URL = "https://gateway.jshvc.edu.cn/mp/restful/v2/message/send"; private static final String MSG_URL = "";
private static final String REDIS_KEY_MSG_ACCESS_TOKEN = "msg:token:"; private static final String REDIS_KEY_MSG_ACCESS_TOKEN = "msg:token:";
@Inject @Inject
@@ -9,6 +9,7 @@ import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCaseCheckService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -39,6 +40,8 @@ public class ProposalCaseCheckController {
@Inject @Inject
private BaseService baseService; private BaseService baseService;
@Inject
private ProposalCaseCheckService proposalCaseCheckService;
@At("") @At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/caseCheck/index.html") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/caseCheck/index.html")
@@ -103,4 +106,11 @@ public class ProposalCaseCheckController {
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(),pageForm.getPageSize(), sql); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(),pageForm.getPageSize(), sql);
return Result.success(pagination); return Result.success(pagination);
} }
@At
@SaCheckPermission("proposal.caseCheck")
@ApiOperation("查询当前提案承办单位")
public Result listReplyUnits(String proposalId) {
return Result.success(proposalCaseCheckService.listReplyUnits(proposalId));
}
} }
@@ -5,12 +5,9 @@ import cn.hutool.core.lang.Dict;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalMerge;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService; import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@@ -25,7 +22,6 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json; import org.nutz.json.Json;
import org.nutz.lang.random.R;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
@@ -44,9 +40,6 @@ public class ProposalCommitteeFilingController {
private Dao dao; private Dao dao;
@Inject @Inject
private ProposalCommitteeFilingService proposalCommitteeFilingService; private ProposalCommitteeFilingService proposalCommitteeFilingService;
@Inject
private FlowCommonService flowCommonService;
@At("") @At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html")
@@ -118,27 +111,16 @@ public class ProposalCommitteeFilingController {
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public Result merge(@Param("data") String data) { public Result merge(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data); Dict args = Json.fromJson(Dict.class, data);
proposalCommitteeFilingService.merge(args);
return Result.success();
}
List<Integer> taskIds = (List<Integer>) args.get("processTaskIds"); @At
List<String> proposalIds = (List<String>) args.get("proposalIds"); @SaCheckPermission("proposal.committeeFiling")
@ApiOperation("撤回任务")
// 记录提案并案 @Aop(TransAop.READ_COMMITTED)
dao.clear(ProposalMerge.class, Cnd.where(ProposalMerge::getProposalId, "in", proposalIds)); public Result revokeTask(@Param("taskId") Long taskId, @Param("proposalId") String proposalId) {
String groupId = R.UU32(); proposalCommitteeFilingService.revokeTask(taskId, proposalId);
List<ProposalMerge> merges = proposalIds.stream().map(proposalId -> {
ProposalMerge merge = new ProposalMerge();
merge.setProposalId(proposalId);
merge.setGroupId(groupId);
return merge;
}).toList();
dao.insert(merges);
for (Integer taskId : taskIds) {
Dict taskArgs = args.clone();
taskArgs.put(FlowConst.PROCESS_TASK_ID_KEY, taskId);
flowCommonService.executeTask(taskArgs);
}
return Result.success(); return Result.success();
} }
@@ -8,7 +8,6 @@ import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.entity.ProcessTaskActor; import com.budwk.app.flow.entity.ProcessTaskActor;
@@ -128,28 +127,7 @@ public class ProposalCommitteeFilingUnitController {
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public Result executeTask(@Param("data") String data) { public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data); Dict args = Json.fromJson(Dict.class, data);
String proposalId = args.getStr("proposalId"); proposalCommitteeFilingUnitService.executeFlowTask(args);
// 查询是否提案
// 单条审核
List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId);
if (ObjectUtil.isEmpty(mergeProposalIds)) {
flowCommonService.executeTask(args);
return Result.success();
}
// 并案审核
ProcessTask thisTask = baseService.dao().fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY));
List<ProcessTask> mergeTasks = processTaskService.getDoingTaskByBizIdTaskName(mergeProposalIds, thisTask.getTaskName());
for (ProcessTask mergeTask : mergeTasks) {
Dict cloneArgs = args.clone();
cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, mergeTask.getId());
flowCommonService.executeTask(cloneArgs);
}
ProposalInfo info = proposalCommonService.fetch(proposalId);
info.setCaseFilingResult(args.getStr("caseFilingResult"));
return Result.success(); return Result.success();
} }
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.Dict; import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
@@ -23,10 +24,12 @@ import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonServi
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json; import org.nutz.json.Json;
@@ -35,6 +38,7 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@IocBean @IocBean
@@ -95,6 +99,8 @@ public class ProposalSchoolLeaderApprovalController {
t.finishTime, t.finishTime,
t.taskParentId, t.taskParentId,
t.variable taskVariable, t.variable taskVariable,
GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 1 THEN pru.unitName END) AS masterUnitName,
GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 0 THEN pru.unitName END) AS slaveUnitNames,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, 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,
GROUP_CONCAT(DISTINCT ta.actorName, '(', ta.actorAccount, ')' ) AS auditUser GROUP_CONCAT(DISTINCT ta.actorName, '(', ta.actorAccount, ')' ) AS auditUser
@@ -107,6 +113,7 @@ public class ProposalSchoolLeaderApprovalController {
LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN proposal_merge mer ON mer.proposalId = info.id LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition $condition
@@ -142,6 +149,7 @@ public class ProposalSchoolLeaderApprovalController {
@At @At
@SaCheckPermission("proposal.schoolLeaderApproval") @SaCheckPermission("proposal.schoolLeaderApproval")
@ApiOperation("执行任务") @ApiOperation("执行任务")
@Aop(TransAop.READ_COMMITTED)
public Result executeTask(@Param("data") String data) { public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data); Dict args = Json.fromJson(Dict.class, data);
@@ -156,18 +164,34 @@ public class ProposalSchoolLeaderApprovalController {
ProcessTask thisTask = dao.fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY)); ProcessTask thisTask = dao.fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY));
ProcessTask parentTask = dao.fetch(ProcessTask.class, Cnd.where("id", "=", thisTask.getTaskParentId())); ProcessTask parentTask = dao.fetch(ProcessTask.class, Cnd.where("id", "=", thisTask.getTaskParentId()));
Dict dict = Json.fromJson(Dict.class, parentTask.getVariable()); Dict dict = ObjectUtil.defaultIfNull(Json.fromJson(Dict.class, parentTask.getVariable()), Dict.create());
args.put("tf_caseFilingResult", dict.getStr("tf_caseFilingResult"));
args.put("tf_helpunitreply", dict.getStr("tf_helpunitreply"));
args.put("tf_masterUnitId", dict.getStr("tf_masterUnitId"));
args.put("tf_slaveUnitIds", dict.getObj("tf_slaveUnitIds"));
ProcessInstance instance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", thisTask.getProcessInstanceId())); ProcessInstance instance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", thisTask.getProcessInstanceId()));
Dict instanceDict = Json.fromJson(Dict.class, instance.getVariable()); Dict instanceDict = ObjectUtil.defaultIfNull(Json.fromJson(Dict.class, instance.getVariable()), Dict.create());
instanceDict.put("tf_helpunitreply", dict.getStr("tf_helpunitreply"));
instanceDict.put("tf_masterUnitId", dict.getStr("tf_masterUnitId")); // 分管领导审批需要把提委会立案环节的承办单位信息继续透传,兼容主办单位由单个改为多个的场景。
instanceDict.put("tf_slaveUnitIds", dict.getObj("tf_slaveUnitIds")); Object masterUnitIds = getProcessVariable(dict, instanceDict, "tf_masterUnitIds");
String masterUnitId = getProcessVariableStr(dict, instanceDict, "tf_masterUnitId");
if (ObjectUtil.isEmpty(masterUnitIds) && StrUtil.isNotBlank(masterUnitId)) {
masterUnitIds = List.of(masterUnitId);
}
if (StrUtil.isBlank(masterUnitId)) {
List<String> masterUnitIdList = toStringList(masterUnitIds);
if (ObjectUtil.isNotEmpty(masterUnitIdList)) {
masterUnitId = masterUnitIdList.get(0);
}
}
args.put("tf_caseFilingResult", getProcessVariableStr(dict, instanceDict, "tf_caseFilingResult"));
args.put("tf_helpunitreply", getProcessVariableStr(dict, instanceDict, "tf_helpunitreply"));
args.put("tf_masterUnitId", masterUnitId);
args.put("tf_masterUnitIds", masterUnitIds);
args.put("tf_slaveUnitIds", getProcessVariable(dict, instanceDict, "tf_slaveUnitIds"));
instanceDict.put("tf_helpunitreply", getProcessVariableStr(dict, instanceDict, "tf_helpunitreply"));
instanceDict.put("tf_masterUnitId", masterUnitId);
instanceDict.put("tf_masterUnitIds", masterUnitIds);
instanceDict.put("tf_slaveUnitIds", getProcessVariable(dict, instanceDict, "tf_slaveUnitIds"));
instance.setVariable(JSONUtil.toJsonStr(instanceDict)); instance.setVariable(JSONUtil.toJsonStr(instanceDict));
dao.update(instance); dao.update(instance);
@@ -190,4 +214,55 @@ public class ProposalSchoolLeaderApprovalController {
} }
return Result.success(); return Result.success();
} }
/**
* 优先取当前父任务变量,没有时回退到流程实例变量,避免审批流转时丢失承办单位信息。
*/
private Object getProcessVariable(Dict taskDict, Dict instanceDict, String key) {
Object taskValue = taskDict.getObj(key);
if (ObjectUtil.isNotEmpty(taskValue)) {
return taskValue;
}
return instanceDict.getObj(key);
}
/**
* 字符串类型流程变量统一做空值兼容,便于兼容老流程里单主办字段的读取。
*/
private String getProcessVariableStr(Dict taskDict, Dict instanceDict, String key) {
Object value = getProcessVariable(taskDict, instanceDict, key);
return ObjectUtil.isEmpty(value) ? "" : String.valueOf(value);
}
/**
* 主办单位可能以集合、数组、JSON 字符串或逗号分隔字符串保存,这里统一转换为字符串列表。
*/
private List<String> toStringList(Object value) {
if (ObjectUtil.isEmpty(value)) {
return List.of();
}
if (value instanceof List<?> list) {
return list.stream().filter(ObjectUtil::isNotEmpty).map(String::valueOf).toList();
}
if (value instanceof Object[] array) {
List<String> result = new ArrayList<>();
for (Object item : array) {
if (ObjectUtil.isNotEmpty(item)) {
result.add(String.valueOf(item));
}
}
return result;
}
if (value instanceof String strValue) {
if (StrUtil.isBlank(strValue)) {
return List.of();
}
if (JSONUtil.isTypeJSONArray(strValue)) {
return JSONUtil.parseArray(strValue).toList(String.class);
}
return StrUtil.splitTrim(strValue, ",");
}
return List.of(String.valueOf(value));
}
} }
@@ -6,10 +6,8 @@ import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService; import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.sys.models.Sys_role; import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.models.Sys_user_role;
@@ -18,6 +16,7 @@ import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalUnderTakeReplyService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@@ -38,6 +37,8 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -53,7 +54,7 @@ public class ProposalUnderTakeReplyController {
@Inject @Inject
private ProposalCommonService proposalCommonService; private ProposalCommonService proposalCommonService;
@Inject @Inject
private FlowCommonService flowCommonService; private ProposalUnderTakeReplyService proposalUnderTakeReplyService;
@Inject @Inject
private ProcessTaskService processTaskService; private ProcessTaskService processTaskService;
@Inject @Inject
@@ -77,6 +78,11 @@ public class ProposalUnderTakeReplyController {
@SaCheckPermission("proposal.unitReply") @SaCheckPermission("proposal.unitReply")
@ApiOperation("分页列表") @ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval, boolean transfer) { public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval, boolean transfer) {
/*
* 承办单位答复列表需要按“承办单位”维度展示。
* 同一个任务可能挂了多个承办单位负责人,若只按 taskId 分组会把多条待审核合并成一条,
* 因此这里优先使用任务参与人的承办单位信息拆行,历史数据缺失时再回退到流程变量。
*/
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
info.*, info.*,
@@ -121,10 +127,11 @@ public class ProposalUnderTakeReplyController {
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply")); // cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply"));
cnd.and("t.taskName", "in", List.of("unit_reply", "two_unit_reply", "opinion_unit_reply"));
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { if (!SecurityUtil.getUserLoginname().equals("superadmin")) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
} }
if (approval) { if (approval) {
@@ -140,45 +147,85 @@ public class ProposalUnderTakeReplyController {
List<NutMap> list = pagination.getList(); List<NutMap> list = pagination.getList();
for (NutMap row : list) { for (NutMap row : list) {
if (row.getInt("taskIsMaster") == 1) { /*
boolean b = Stream.of("master_reply", "slave_reply", "school_leader") * 承办单位答复撤回必须按“当前整组待办节点”统一判断。
.anyMatch(a -> row.getString("curTaskKey").contains(a)); * 这里不能再用 contains,只要出现过 unit_reply/two_unit_reply 就会误判可撤回;
row.setv("canRevoke", b && row.getInt("taskState") == ProcessTaskStateEnum.FINISHED.getCode()); * 正确规则是:curTaskKey 必须精确等于某一个允许撤回的节点编码,
} else { * 像 unit_reply,two_unit_reply 这种混合状态一律不允许撤回。
boolean b = Stream.of("master_reply", "slave_reply").anyMatch(a -> row.getString("curTaskKey").contains(a)); */
row.setv("canRevoke", b && row.getInt("taskState") == ProcessTaskStateEnum.FINISHED.getCode()); row.setv("canRevoke", isReplyTaskGroupRevocable(row));
}
} }
pagination.setList(list); pagination.setList(list);
return Result.success(pagination); return Result.success(pagination);
} }
/**
* 承办单位答复节点撤回需要基于整组当前节点判断。
* 只有“当前流程正在办理的节点”和“这条列表记录所属节点”完全一致时,才允许撤回;
* 这样流程已经从 unit_reply 走到 two_unit_reply 后,历史 unit_reply 记录会自动失去撤回资格。
* 主办只能撤回 unit_reply、two_unit_reply、school_leader
* 协办只能撤回 unit_reply、two_unit_reply。
*/
private boolean isReplyTaskGroupRevocable(NutMap row) {
if (!ObjectUtil.equal(row.getInt("taskState"), ProcessTaskStateEnum.FINISHED.getCode())) {
return false;
}
String taskKey = row.getString("taskKey");
String curTaskKey = row.getString("curTaskKey");
if (!ObjectUtil.equal(taskKey, curTaskKey)) {
return false;
}
if (row.getInt("underTakeIsMaster") == 1) {
return Stream.of("unit_reply", "two_unit_reply", "school_leader")
.anyMatch(a -> ObjectUtil.equal(a, taskKey));
}
return Stream.of("unit_reply", "two_unit_reply")
.anyMatch(a -> ObjectUtil.equal(a, taskKey));
}
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("proposal.unitReply") @SaCheckPermission("proposal.unitReply")
@ApiOperation("执行任务") @ApiOperation("执行任务")
public Result executeTask(@Param("data") String data) { public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data); Dict args = Json.fromJson(Dict.class, data);
String proposalId = args.getStr("proposalId"); proposalUnderTakeReplyService.executeFlowTask(args);
return Result.success();
}
// 查询是否提案 @At
@SaCheckPermission("proposal.unitReply")
// 单条审核 @ApiOperation("查询历史答复内容")
List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId); public Result getReplyOpinion(@Param("taskId") @NotNull(message = "任务ID不能为空") Long taskId) {
if (ObjectUtil.isEmpty(mergeProposalIds)) { /*
flowCommonService.executeTask(args); * 页面上的“编辑答复内容”入口只对超级管理员开放。
return Result.success(); * 这里继续在服务端做一次角色兜底,避免前端绕过按钮显隐后越权修改历史答复。
*/
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
return Result.error("只有超级管理员可以编辑答复内容");
} }
return Result.success(proposalUnderTakeReplyService.getReplyOpinion(taskId));
}
// 并案审核 @At
ProcessTask thisTask = dao.fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY)); @SaCheckPermission("proposal.unitReply")
List<ProcessTask> mergeTasks = processTaskService.getDoingTaskByBizIdTaskName(mergeProposalIds, thisTask.getTaskName()); @ApiOperation("更新历史答复内容")
for (ProcessTask mergeTask : mergeTasks) { @Aop(TransAop.READ_COMMITTED)
Dict cloneArgs = args.clone(); public Result updateReplyOpinion(@Param("taskId") @NotNull(message = "任务ID不能为空") Long taskId,
cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, mergeTask.getId()); @Param("opinion") @NotBlank(message = "答复内容不能为空") String opinion) {
flowCommonService.executeTask(cloneArgs); /*
* 这里只允许超级管理员改历史答复内容,且只改 tf_opinion 字段。
* 具体的任务范围和状态校验放在 service 层统一处理,避免控制器承载业务逻辑。
*/
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
return Result.error("只有超级管理员可以编辑答复内容");
} }
proposalUnderTakeReplyService.updateReplyOpinion(taskId, opinion);
return Result.success(); return Result.success();
} }
@@ -0,0 +1,172 @@
package com.budwk.app.zhgh.democratic.proposal.handler;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* 提案承办单位处理人
*/
public class ProposalUnitAssignmentHandler implements AssignmentHandler {
@Override
@Aop(TransAop.READ_COMMITTED)
public List<String> assign(TaskModel model, Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
String proposalId = execution.getProcessInstance().getBusinessNo();
// 当前节点需要同时处理主办和协办负责人。
// 这里不能按单位ID去重,必须保留每个承办单位对应的一条分派记录,
// 否则同一用户负责多个单位时,会被错误合并成更少的待办。
List<String> unitIds = new ArrayList<>();
unitIds.addAll(resolveMasterUnitIds(execution, dao, proposalId));
unitIds.addAll(resolveSlaveUnitIds(execution, dao, proposalId));
if (unitIds.isEmpty()) {
throw new BaseException("提案没有配置承办单位");
}
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER);
if (sysRole == null) {
throw new BaseException("提案承办单位领导角色不存在");
}
List<String> assignee = new ArrayList<>();
for (String unitId : unitIds) {
ProposalUndertake undertake = dao.fetch(ProposalUndertake.class, unitId);
if (undertake == null) {
throw new BaseException("承办单位不存在");
}
// 只要存在单位 ID,就必须找到该单位负责人,否则流程无法继续往下分派。
List<Sys_user_role> sysUserRoles = dao.query(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId())
.and(Sys_user_role::getUnderTakeId, "=", undertake.getId()));
if (ObjectUtil.isEmpty(sysUserRoles)) {
throw new BaseException(undertake.getName() + "单位没有设置负责人!");
}
/*
* 会签需要按“承办单位”创建实例。
* 同一个用户若同时负责主办、协办多个承办单位,不能在这里跨单位去重;
* 否则流程会把多个承办单位合并成一个会签实例,导致“全部承办单位答复完”判断失真。
*/
assignee.addAll(sysUserRoles.stream().map(Sys_user_role::getUserId).toList());
}
return assignee;
}
/**
* 优先从当前流程变量读取主办单位,读不到时回落到承办单位记录表,兼容重新流转场景。
*/
private List<String> resolveMasterUnitIds(Execution execution, Dao dao, String proposalId) {
List<String> masterUnitIds = new ArrayList<>();
/*
* 页面会同时提交 tf_masterUnitId 和 tf_masterUnitIds
* 前者是给旧流程兼容的“第一个主办单位”,后者才是当前页面真实的主办单位列表。
* 这里必须优先使用列表字段;只有列表为空时,才回退到单值字段,
* 否则单主办场景会把同一个单位重复加入两次,最终多生成一条会签任务。
*/
List<String> masterUnitIdList = getStringList(execution.getArgs(), "tf_masterUnitIds");
if (ObjectUtil.isNotEmpty(masterUnitIdList)) {
masterUnitIds.addAll(masterUnitIdList);
} else {
String masterUnitId = execution.getArgs().getStr("tf_masterUnitId");
if (StrUtil.isNotBlank(masterUnitId)) {
masterUnitIds.add(masterUnitId);
}
}
if (masterUnitIds.isEmpty()) {
List<ProposalReplyUnit> masterUnits = dao.query(ProposalReplyUnit.class,
Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId)
.and(ProposalReplyUnit::getIsMaster, "=", true));
if (ObjectUtil.isNotEmpty(masterUnits)) {
masterUnitIds.addAll(masterUnits.stream().map(ProposalReplyUnit::getUnitId).toList());
}
}
return masterUnitIds;
}
/**
* 协办单位不区分是否需要答复,只要流程变量或记录表里存在协办单位 ID,就分派给对应负责人。
*/
private List<String> resolveSlaveUnitIds(Execution execution, Dao dao, String proposalId) {
List<String> slaveUnitIds = new ArrayList<>();
List<String> slaveUnitIdList = getStringList(execution.getArgs(), "tf_slaveUnitIds");
if (ObjectUtil.isNotEmpty(slaveUnitIdList)) {
slaveUnitIds.addAll(slaveUnitIdList);
}
if (slaveUnitIds.isEmpty()) {
List<ProposalReplyUnit> slaveUnits = dao.query(ProposalReplyUnit.class,
Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId)
.and(ProposalReplyUnit::getIsMaster, "=", false));
if (ObjectUtil.isNotEmpty(slaveUnits)) {
slaveUnitIds.addAll(slaveUnits.stream().map(ProposalReplyUnit::getUnitId).toList());
}
}
return slaveUnitIds;
}
/**
* 流程变量里的单位ID可能是 List、数组、JSON 字符串或逗号分隔字符串,这里统一转成字符串列表。
*/
private List<String> getStringList(Dict args, String key) {
Object value = args.getObj(key);
if (ObjectUtil.isEmpty(value)) {
return List.of();
}
if (value instanceof Collection<?> collection) {
return collection.stream().filter(ObjectUtil::isNotEmpty).map(String::valueOf).toList();
}
if (value instanceof Object[] array) {
return Arrays.stream(array).filter(ObjectUtil::isNotEmpty).map(String::valueOf).toList();
}
if (value instanceof String strValue) {
if (StrUtil.isBlank(strValue)) {
return List.of();
}
if (JSONUtil.isTypeJSONArray(strValue)) {
return JSONUtil.parseArray(strValue).toList(String.class);
}
return Arrays.stream(strValue.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.toList();
}
return List.of(String.valueOf(value));
}
@Override
public String getMessage() {
return "提案单位处理人";
}
@Override
public int getOrder() {
return 210;
}
}
@@ -26,6 +26,9 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.json.Json; import org.nutz.json.Json;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@@ -36,62 +39,26 @@ public class ProposalCaseFilingInterceptor implements FlowInterceptor {
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void intercept(Execution execution) { public void intercept(Execution execution) {
if ("0".equals(execution.getArgs().getStr("tf_caseCheckNeedSecondReply"))){
return;
}
Dao dao = ServiceContext.find(Dao.class); Dao dao = ServiceContext.find(Dao.class);
// 提案ID // 提案ID
String proposalId = execution.getProcessInstance().getBusinessNo(); String proposalId = execution.getProcessInstance().getBusinessNo();
// 立案结果
String caseFilingResult = execution.getArgs().getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult");
// 储存承办单位
if (StrUtil.isNotBlank(caseFilingResult) && List.of("CONFIRM_FILING", "SUGGESTION").contains(caseFilingResult)) {
// 删除原来的记录
dao.clear(ProposalReplyUnit.class,Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId));
// 主办单位
String masterUnitId = execution.getArgs().getStr(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId");
ProposalUndertake masterUnit = dao.fetch(ProposalUndertake.class, masterUnitId);
ProposalReplyUnit masterReplyUnit = new ProposalReplyUnit();
masterReplyUnit.setProposalId(proposalId);
masterReplyUnit.setUnitId(masterUnit.getId());
masterReplyUnit.setUnitCode(masterUnit.getCode());
masterReplyUnit.setUnitName(masterUnit.getName());
masterReplyUnit.setIsMaster(true);
dao.insert(masterReplyUnit);
// 协办单位
List<String> helpUnitIds = (List<String>) execution.getArgs().getObj(FlowConst.TASK_FORM_DATA_PREFIX + "slaveUnitIds");
if(ObjectUtil.isNotEmpty(helpUnitIds)){
for (String helpUnitId : helpUnitIds) {
ProposalUndertake helpUnit = dao.fetch(ProposalUndertake.class, helpUnitId);
ProposalReplyUnit helpReplyUnit = new ProposalReplyUnit();
helpReplyUnit.setProposalId(proposalId);
helpReplyUnit.setUnitId(helpUnit.getId());
helpReplyUnit.setUnitCode(helpUnit.getCode());
helpReplyUnit.setUnitName(helpUnit.getName());
helpReplyUnit.setIsMaster(false);
dao.insert(helpReplyUnit);
}
}
}
// 更新立案结果、立案类型
ProcessInstance processInstance = execution.getProcessInstance();
String businessNo = processInstance.getBusinessNo();
Chain chain = Chain.make("caseFilingResult", execution.getArgs().getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult"));
chain.add("caseFilingType", execution.getArgs().getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingType"));
dao.update(ProposalInfo.class, chain, Cnd.where(ProposalInfo::getId, "=", businessNo));
// 当前任务 // 当前任务
List<ProcessTask> processTaskList = execution.getProcessTaskList(); List<ProcessTask> processTaskList = execution.getProcessTaskList();
for (ProcessTask task : processTaskList) { for (ProcessTask task : processTaskList) {
// 办理人 // 办理人
ProcessTaskActor taskActor = dao.fetch(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", task.getId())); ProcessTaskActor taskActor = dao.fetch(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", task.getId()));
if (taskActor == null) {
continue;
}
String actorUnitId = taskActor.getActorUnitId(); String actorUnitId = taskActor.getActorUnitId();
ProposalReplyUnit replyUnit = dao.fetch(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId).and(ProposalReplyUnit::getUnitId, "=", actorUnitId)); ProposalReplyUnit replyUnit = dao.fetch(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId).and(ProposalReplyUnit::getUnitId, "=", actorUnitId));
if (replyUnit == null) {
continue;
}
Dict variable = FlowUtil.variableToDict(task.getVariable()); Dict variable = FlowUtil.variableToDict(task.getVariable());
variable.set("underTakeName", replyUnit.getUnitName()); variable.set("underTakeName", replyUnit.getUnitName());
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.democratic.proposal.interceptor; package com.budwk.app.zhgh.democratic.proposal.interceptor;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowInterceptor; import com.budwk.app.flow.engine.FlowInterceptor;
@@ -13,7 +14,7 @@ import org.nutz.dao.Dao;
/** /**
* 提案分管领导审批前缀拦截器 * 提案分管领导审批前缀拦截器
* 用于设置主办单位变量 tf_masterUnitId * 用于设置主办单位变量,兼容主办单位由单个改为多个的场景
*/ */
public class ProposalFgxldPrefixInterceptor implements FlowInterceptor { public class ProposalFgxldPrefixInterceptor implements FlowInterceptor {
@Override @Override
@@ -27,8 +28,12 @@ public class ProposalFgxldPrefixInterceptor implements FlowInterceptor {
.and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode())
.desc(ProcessTask::getCreatedAt) .desc(ProcessTask::getCreatedAt)
); );
String masterUnitId = FlowUtil.variableToDict(task.getVariable()).getStr(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId"); Dict variable = FlowUtil.variableToDict(task.getVariable());
String masterUnitId = variable.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId");
execution.getArgs().set(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId", masterUnitId); execution.getArgs().set(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId", masterUnitId);
// 退回提委会时保留主办单位集合,避免多主办场景下页面无法完整回显。
execution.getArgs().set(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitIds",
variable.getObj(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitIds"));
} }
} }
} }
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.democratic.proposal.service;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface ProposalCaseCheckService {
/**
* 查询当前提案已经配置的承办单位。
* 二次答复只能从当前提案已有的主办/协办单位里选择,避免页面误选到无关单位。
*
* @param proposalId 提案ID
* @return 承办单位列表
*/
List<NutMap> listReplyUnits(String proposalId);
}
@@ -1,12 +1,24 @@
package com.budwk.app.zhgh.democratic.proposal.service; package com.budwk.app.zhgh.democratic.proposal.service;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import cn.hutool.core.lang.Dict;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingApprovalParam;
public interface ProposalCommitteeFilingService extends BaseService<ProposalInfo> { public interface ProposalCommitteeFilingService extends BaseService<ProposalInfo> {
void approval(ProposalCommitteeFilingApprovalParam approvalParam); /**
* 提案委员会并案审核。
*
* @param args 页面提交的流程参数
*/
void merge(Dict args);
void revoke(String taskId); /**
* 提案委员会立案撤回。
* 单条提案只撤回当前任务;并案提案需要整组一起撤回,确保流程状态一致。
*
* @param taskId 当前已办任务ID
* @param proposalId 当前提案ID
*/
void revokeTask(Long taskId, String proposalId);
} }
@@ -1,12 +1,19 @@
package com.budwk.app.zhgh.democratic.proposal.service; package com.budwk.app.zhgh.democratic.proposal.service;
import cn.hutool.core.lang.Dict;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingUnitApprovalParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingUnitApprovalParam;
public interface ProposalCommitteeFilingUnitService extends BaseService<ProposalInfo> { public interface ProposalCommitteeFilingUnitService extends BaseService<ProposalInfo> {
void approval(ProposalCommitteeFilingUnitApprovalParam approvalParam);
void revoke(String taskId); /**
* old-flow 提案委员会确认承办单位执行。
* 执行流程任务后,同步落库立案结果以及主协办单位配置。
*
* @param args 页面提交的流程参数
*/
void executeFlowTask(Dict args);
} }
@@ -1,10 +1,10 @@
package com.budwk.app.zhgh.democratic.proposal.service; package com.budwk.app.zhgh.democratic.proposal.service;
import cn.hutool.core.lang.Dict;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalUnderTakeReplyApprovalParam;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalUnderTakeReplyTransferParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalUnderTakeReplyTransferParam;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
@@ -15,7 +15,15 @@ public interface ProposalUnderTakeReplyService extends BaseService<ProposalInfo>
Pagination pageData(ProposalSearchParam param); Pagination pageData(ProposalSearchParam param);
void approval(ProposalUnderTakeReplyApprovalParam param);
/**
* old-flow 提案承办单位答复执行。
* 同一批次存在多个 wf_process_task 时,前面的任务只完结当前待办,
* 最后一条待办才真正驱动流程流转到下一节点。
*
* @param args 页面提交的流程参数
*/
void executeFlowTask(Dict args);
/** /**
* 查询本单位可转办用户 * 查询本单位可转办用户
@@ -24,26 +32,21 @@ public interface ProposalUnderTakeReplyService extends BaseService<ProposalInfo>
List<NutMap> queryTransferUser(String keyword); List<NutMap> queryTransferUser(String keyword);
/** /**
* 转办 * 查询历史承办单位答复任务中的答复内容。
* @param param * 该能力仅用于超级管理员在页面上回显“编辑答复内容”弹窗,
* 因此只返回当前编辑场景所需的最小字段,避免误改其他流程数据。
*
* @param taskId 历史承办单位答复任务ID
* @return 包含任务ID和 tf_opinion 的回显数据
*/ */
void transfer(ProposalUnderTakeReplyTransferParam param); NutMap getReplyOpinion(Long taskId);
/** /**
* 撤回 * 更新历史承办单位答复任务中的答复内容。
* @param taskId * 这里只允许修改流程任务变量里的 tf_opinion,其他字段如落实情况、附件、流程状态均保持不变。
*
* @param taskId 历史承办单位答复任务ID
* @param opinion 最新答复内容
*/ */
void revokeTask(@Valid String taskId); void updateReplyOpinion(Long taskId, String opinion);
/**
* 检查是否可以办理
* @param taskId
*/
String checkCanReply(String taskId);
/**
* 撤回转办
* @param taskId
*/
void revokeTransfer(String taskId);
} }
@@ -0,0 +1,41 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCaseCheckService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class ProposalCaseCheckServiceImpl implements ProposalCaseCheckService {
private final Dao dao;
public ProposalCaseCheckServiceImpl(Dao dao) {
this.dao = dao;
}
@Override
public List<NutMap> listReplyUnits(String proposalId) {
if (proposalId == null || proposalId.isBlank()) {
throw new BaseException("提案ID不能为空");
}
/*
* 二次答复的承办单位来源于当前提案已落库的主办/协办配置。
* 这里按主办优先、再按单位名称排序,便于页面选择时直观看到主协办顺序。
*/
List<ProposalReplyUnit> replyUnits = dao.query(ProposalReplyUnit.class,
Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId)
.desc(ProposalReplyUnit::getIsMaster)
.asc(ProposalReplyUnit::getUnitName));
return replyUnits.stream().map(replyUnit -> NutMap.NEW()
.setv("unitId", replyUnit.getUnitId())
.setv("unitName", replyUnit.getUnitName())
.setv("isMaster", replyUnit.getIsMaster())
).toList();
}
}
@@ -1,40 +1,26 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl; package com.budwk.app.zhgh.democratic.proposal.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.bpm.dto.BpmAssignmentDto; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.bpm.enums.BpmTaskApprovalTypeEnum; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.bpm.models.BpmProcessInstance; import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.bpm.models.BpmProcessTask; import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysMsgService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.web.controllers.open.commons.service.CommonService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConsolidation; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConsolidation;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; import com.budwk.app.zhgh.democratic.proposal.models.ProposalMerge;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingApprovalParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService; import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.aop.Aop; import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.random.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
@IocBean(args = {"refer:dao"}) @IocBean(args = {"refer:dao"})
public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<ProposalInfo> implements ProposalCommitteeFilingService { public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<ProposalInfo> implements ProposalCommitteeFilingService {
@@ -42,155 +28,61 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<Proposal
@Inject @Inject
private ProposalCommonService proposalCommonService; private ProposalCommonService proposalCommonService;
@Inject @Inject
private BpmService bpmService; private FlowCommonService flowCommonService;
@Inject @Inject
private CommonService commonService; private ProcessTaskService processTaskService;
@Inject
private SysMsgService sysMsgService;
public ProposalCommitteeFilingServiceImpl(Dao dao) { public ProposalCommitteeFilingServiceImpl(Dao dao) {
super(dao); super(dao);
} }
@Override @Override
public void approval(ProposalCommitteeFilingApprovalParam approvalParam) { @Aop(TransAop.READ_COMMITTED)
approvalParam.getBpmTaskApprovalTypeEnum(); public void merge(Dict args) {
Map<String, Object> variables = BeanUtil.beanToMap(approvalParam); List<Integer> taskIds = (List<Integer>) args.get("processTaskIds");
variables.put("proposalIds", approvalParam.getProposalIds()); List<String> proposalIds = (List<String>) args.get("proposalIds");
variables.put("hostUnitId", approvalParam.getHostUnitId());
variables.put("helpUnitIds", approvalParam.getHelpUnitIds());
variables.put("caseFilingResult", approvalParam.getCaseFilingResult());
variables.put("consolidationIds", approvalParam.getProposalIds());
ProposalUndertake hostUnit = null; // 记录提案并案,后续撤回、查询等场景都依赖这组并案关系。
List<ProposalUndertake> helpUnits = null; dao().clear(ProposalMerge.class, Cnd.where(ProposalMerge::getProposalId, "in", proposalIds));
//承办单位答复人初步阅览 ProposalConsolidation proposalConsolidation = new ProposalConsolidation();
List<BpmAssignmentDto> nextApprovals = new ArrayList<>(); proposalConsolidation.setConsolidationIds(proposalIds);
dao().insert(proposalConsolidation);
if (approvalParam.getBpmTaskApprovalTypeEnum().equals(BpmTaskApprovalTypeEnum.DYNAMIC)) { String groupId = R.UU32();
if (List.of("CONFIRM_FILING", "SUGGESTION").contains(approvalParam.getCaseFilingResult())) { List<ProposalMerge> merges = proposalIds.stream().map(proposalId -> {
hostUnit = dao().fetch(ProposalUndertake.class, approvalParam.getHostUnitId()); ProposalMerge merge = new ProposalMerge();
helpUnits = dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "in", approvalParam.getHelpUnitIds())); merge.setProposalId(proposalId);
variables.put("hostUnit", hostUnit.getName()); merge.setGroupId(groupId);
variables.put("helpUnits", helpUnits.stream().map(ProposalUndertake::getName).toArray(String[]::new)); return merge;
}).toList();
dao().insert(merges);
List<JSONObject> units = new ArrayList<>(); // 并案审核需要把同一批提案的当前节点一起提交,保持整组流程同步往下走。
units.add(new JSONObject() for (Integer taskId : taskIds) {
.set("isMaster",true) Dict taskArgs = args.clone();
.set("underTakeId", hostUnit.getId()) taskArgs.put(FlowConst.PROCESS_TASK_ID_KEY, taskId);
.set("underTakeName", hostUnit.getName()) flowCommonService.executeTask(taskArgs);
.set("underTakeCode", hostUnit.getCode())
);
if(ObjectUtil.isNotEmpty(helpUnits)){
for (ProposalUndertake helpUnit : helpUnits) {
units.add(new JSONObject()
.set("isMaster",false)
.set("underTakeId", helpUnit.getId())
.set("underTakeName", helpUnit.getName())
.set("underTakeCode", helpUnit.getCode())
);
}
}
variables.put("units",units);
}
//并案审核
if (approvalParam.getProposalIds().length > 1) {
ProposalConsolidation proposalConsolidation = new ProposalConsolidation();
proposalConsolidation.setConsolidationIds(Arrays.asList(approvalParam.getProposalIds()));
dao().insert(proposalConsolidation);
}
//立案和作为意见会有承办单位
if (approvalParam.getCaseFilingResult().equals("CONFIRM_FILING") || approvalParam.getCaseFilingResult().equals("SUGGESTION")) {
List<ProposalUndertake> units = new ArrayList<>();
units.add(hostUnit);
if (ObjectUtil.isNotNull(helpUnits)) {
units.addAll(helpUnits);
}
for (ProposalUndertake unit : units) {
String approvalLoginName = commonService.findUserLoginNameByRoleCode(RoleConstant.PROPOSAL_UNIT_LEADER, Cnd.where(Sys_user_role::getUnderTakeId, "=", unit.getId()));
if (StrUtil.isBlank(approvalLoginName)) {
throw new BaseException("{}答复人没有配置,请到承办单位管理页面设置!", unit.getName());
}
assert hostUnit != null;
nextApprovals.add(new BpmAssignmentDto()
.setLoginName(approvalLoginName)
.setJsonObject(new JSONObject()
.set("isMaster", hostUnit.getId().equals(unit.getId()))
.set("underTakeId", unit.getId())
.set("underTakeName", unit.getName())
.set("underTakeCode", unit.getCode())
)
);
}
}
//下一个审批人
String nextApprovalLoginName = proposalCommonService.getCommitteeDirectorByProposalId(approvalParam.getProcessInstanceBusinessId());
//提交并案
for (String consolidationProposalId : approvalParam.getProposalIds()) {
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationProposalId));
// bpmService.completeTaskWithNextTaskVariable(instance.getProcessInstanceTaskIds().get(0), approvalParam.getBpmTaskApprovalTypeEnum(), variables, nextApprovals);
bpmService.completeTask(instance.getProcessInstanceTaskIds().get(0), approvalParam.getBpmTaskApprovalTypeEnum(), variables, List.of(nextApprovalLoginName));
}
//修改提案立案结果
dao().update(ProposalInfo.class, Chain.make("caseFilingResult", approvalParam.getCaseFilingResult()), Cnd.where(ProposalInfo::getId, "in", approvalParam.getProposalIds()));
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class, Cnd.where(ProposalInfo::getId, "in", approvalParam.getProposalIds()));
for (ProposalInfo proposalInfo : proposalInfos) {
//给代表发送消息
if("CONFIRM_FILING".equals(approvalParam.getCaseFilingResult())){
String template = "{}代表您好!您的提案《{}》提案委员会已立案审核,审核结果为准予立案,您可登陆智慧校园下的暖心工会查看详情。";
String content = StrUtil.format(template, proposalInfo.getCreateUserName(), proposalInfo.getName());
sysMsgService.sendMsg(proposalInfo.getCreateUserLoginName(), "提案委员会立案审核", content, SecurityUtil.getUserId());
}else if("SUGGESTION".equals(approvalParam.getCaseFilingResult())){
}else if("NOT".equals(approvalParam.getCaseFilingResult())){
String template = "{}代表您好!您的提案《{}》提案委员会已立案审核,审核结果为不予立案,您可登陆智慧校园下的暖心工会查看详情。";
String content = StrUtil.format(template, proposalInfo.getCreateUserName(), proposalInfo.getName());
sysMsgService.sendMsg(proposalInfo.getCreateUserLoginName(), "提案委员会立案审核", content, SecurityUtil.getUserId());
}
//给承办单位发送消息
if("CONFIRM_FILING".equals(approvalParam.getCaseFilingResult())){
for (BpmAssignmentDto nextApproval : nextApprovals) {
String template = "经提案委员会立案审核,提案《{}》由{}作为{}单位,您可登陆智慧校园下的暖心工会查看详情,感谢您对教代会提案工作的大力支持!。";
String underTakeName = nextApproval.getJsonObject().getStr("underTakeName");
Boolean isMaster = nextApproval.getJsonObject().getBool("isMaster", false);
String content = StrUtil.format(template, proposalInfo.getName(), underTakeName,isMaster?"主办":"协办");
sysMsgService.sendMsg(nextApproval.getLoginName(), "承办单位初步意见", content, SecurityUtil.getUserId());
}
}
}
} }
} }
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void revoke(String taskId) { public void revokeTask(Long taskId, String proposalId) {
BpmProcessTask task = dao().fetch(BpmProcessTask.class, taskId); /*
String instanceId = task.getProcessInstanceId(); * 提委会立案撤回统一走 flowCommonService。
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, instanceId); * 非并案时直接撤回当前任务;并案时按当前节点找出整组已办任务,逐条撤回,避免只回退一部分提案。
String instanceBusinessId = instance.getProcessInstanceBusinessId(); */
List<String> consolidationIds = proposalCommonService.getConsolidation(instanceBusinessId); List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId);
for (String consolidationId : consolidationIds) { if (ObjectUtil.isEmpty(mergeProposalIds)) {
BpmProcessInstance consolidationProposalInstance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationId)); flowCommonService.revokeTask(taskId);
if (ObjectUtil.isNotEmpty(consolidationProposalInstance.getProcessInstanceTaskIds())) { return;
//还有待处理的任务 那么走的就是确定立案 }
String activeTaskId = consolidationProposalInstance.getProcessInstanceTaskIds().get(0);
BpmProcessTask activeTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getId, "=", activeTaskId)); ProcessTask thisTask = dao().fetch(ProcessTask.class, taskId);
bpmService.revokeTask(activeTask.getPrevTaskId()); List<ProcessTask> mergeTasks = processTaskService.getDoneTaskByBizIdTaskName(mergeProposalIds, thisTask.getTaskName());
}else{ for (ProcessTask mergeTask : mergeTasks) {
//不予立案后撤回 flowCommonService.revokeTask(mergeTask.getId());
bpmService.revokeTask(taskId);
}
} }
dao().update(ProposalInfo.class, Chain.make("caseFilingResult", null), Cnd.where(ProposalInfo::getId, "in", consolidationIds));
dao().clear(ProposalConsolidation.class, Cnd.where(new Static("JSON_CONTAINS(consolidationIds,'\"" + instanceBusinessId + "\"')")));
} }
} }
@@ -1,8 +1,10 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl; package com.budwk.app.zhgh.democratic.proposal.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.exception.BaseException;
@@ -12,6 +14,10 @@ import com.budwk.app.bpm.enums.BpmProcessTaskStatusEnum;
import com.budwk.app.bpm.models.BpmProcessInstance; import com.budwk.app.bpm.models.BpmProcessInstance;
import com.budwk.app.bpm.models.BpmProcessTask; import com.budwk.app.bpm.models.BpmProcessTask;
import com.budwk.app.bpm.service.BpmService; import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysMsgService; import com.budwk.app.sys.services.SysMsgService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -19,6 +25,7 @@ import com.budwk.app.web.controllers.open.commons.service.CommonService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConsolidation; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConsolidation;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingUnitApprovalParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingUnitApprovalParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService; import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService;
@@ -33,9 +40,12 @@ import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"}) @IocBean(args = {"refer:dao"})
public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<ProposalInfo> implements ProposalCommitteeFilingUnitService { public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<ProposalInfo> implements ProposalCommitteeFilingUnitService {
@@ -48,6 +58,10 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
private BpmService bpmService; private BpmService bpmService;
@Inject @Inject
private SysMsgService sysMsgService; private SysMsgService sysMsgService;
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProcessTaskService processTaskService;
public ProposalCommitteeFilingUnitServiceImpl(Dao dao) { public ProposalCommitteeFilingUnitServiceImpl(Dao dao) {
super(dao); super(dao);
@@ -55,129 +69,134 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void approval(ProposalCommitteeFilingUnitApprovalParam approvalParam) { public void executeFlowTask(Dict args) {
approvalParam.getBpmTaskApprovalTypeEnum(); Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
Map<String, Object> variables = BeanUtil.beanToMap(approvalParam); ProcessTask currentTask = dao().fetch(ProcessTask.class, processTaskId);
if (currentTask == null) {
//审批变量 throw new BaseException("没有进行中的流程任务");
variables.put("proposalIds", approvalParam.getProposalIds());
variables.put("hostUnitId", approvalParam.getHostUnitId());
variables.put("helpUnitIds", approvalParam.getHelpUnitIds());
variables.put("caseFilingResult", approvalParam.getCaseFilingResult());
//承办单位
ProposalUndertake hostUnit = dao().fetch(ProposalUndertake.class, approvalParam.getHostUnitId());
List<ProposalUndertake> helpUnits = dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "in", approvalParam.getHelpUnitIds()));
variables.put("hostUnit", hostUnit.getName());
variables.put("helpUnits", helpUnits.stream().map(ProposalUndertake::getName).toArray(String[]::new));
//查询提案配置协办单位是否需要答复
ProposalConfig proposalConfig = dao().fetch(ProposalConfig.class, Cnd.NEW());
Boolean slaveUnitNeedReply = proposalConfig.getSlaveUnitNeedReply();
if (ObjectUtil.isNull(slaveUnitNeedReply)) {
throw new BaseException("协办是否需要答复提案配置缺失,请联系校工会!");
} }
//重新并案审核 String proposalId = args.getStr("proposalId");
if (approvalParam.getProposalIds().length > 1) { List<String> proposalIds = proposalCommonService.mergeProposal(proposalId);
for (String proposalId : approvalParam.getProposalIds()) { if (ObjectUtil.isEmpty(proposalIds)) {
dao().clear(ProposalConsolidation.class, Cnd.where(new Static("JSON_CONTAINS(consolidationIds,'\"%s\"')".formatted(proposalId)))); proposalIds = List.of(proposalId);
}
ProposalConsolidation proposalConsolidation = new ProposalConsolidation();
proposalConsolidation.setConsolidationIds(Arrays.asList(approvalParam.getProposalIds()));
dao().insert(proposalConsolidation);
} }
List<ProposalUndertake> proposalUndertakes = new ArrayList<>(); /*
proposalUndertakes.add(hostUnit); * 并案场景下,同一个节点会在每条提案实例上各有一条待办。
if (ObjectUtil.isNotEmpty(helpUnits) && slaveUnitNeedReply) { * 这里统一按 taskName 找出整组正在办理中的任务,逐条执行,确保并案提案的流程状态保持一致。
proposalUndertakes.addAll(helpUnits); */
List<ProcessTask> executeTasks = processTaskService.getDoingTaskByBizIdTaskName(proposalIds, currentTask.getTaskName());
if (ObjectUtil.isEmpty(executeTasks)) {
executeTasks = List.of(currentTask);
}
for (ProcessTask executeTask : executeTasks) {
Dict cloneArgs = args.clone();
cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, executeTask.getId());
flowCommonService.executeTask(cloneArgs);
} }
//承办单位答复人 syncCommitteeFilingUnitData(proposalIds, args);
List<BpmAssignmentDto> nextApprovals = new ArrayList<>(); }
// if (approvalParam.getCaseFilingResult().equals("CONFIRM_FILING") || approvalParam.getCaseFilingResult().equals("SUGGESTION")) { /**
//南邮 只有立案需要答复 * old-flow 页面提交后,需要把立案结果以及主协办单位同步落业务表。
if (approvalParam.getCaseFilingResult().equals("CONFIRM_FILING")) { * 若这次改成“不予立案”等无需承办单位的结果,也要先清掉旧的承办单位记录,避免脏数据残留。
for (ProposalUndertake undertake : proposalUndertakes) { */
String approvalLoginName = commonService.findUserLoginNameByRoleCode(RoleConstant.PROPOSAL_UNIT_LEADER, Cnd.where(Sys_user_role::getUnderTakeId, "=", undertake.getId())); private void syncCommitteeFilingUnitData(List<String> proposalIds, Dict args) {
if (StrUtil.isBlank(approvalLoginName)) { String caseFilingResult = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult");
throw new BaseException("{}答复人没有配置,请到承办单位管理页面进行配置。", undertake.getName()); String caseFilingType = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingType");
}
nextApprovals.add(new BpmAssignmentDto() dao().update(ProposalInfo.class,
.setLoginName(approvalLoginName) Chain.make("caseFilingResult", caseFilingResult).add("caseFilingType", caseFilingType),
.setJsonObject(new JSONObject() Cnd.where(ProposalInfo::getId, "in", proposalIds));
.set("isMaster", hostUnit.getId().equals(undertake.getId()))
.set("underTakeId", undertake.getId()) dao().clear(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "in", proposalIds));
.set("underTakeName", undertake.getName()) if (!List.of("CONFIRM_FILING", "SUGGESTION").contains(caseFilingResult)) {
.set("underTakeCode", undertake.getCode()) return;
) }
);
List<String> masterUnitIds = getUnitIdList(args, FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitIds");
if (ObjectUtil.isEmpty(masterUnitIds)) {
String masterUnitId = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "masterUnitId");
if (StrUtil.isNotBlank(masterUnitId)) {
masterUnitIds = List.of(masterUnitId);
} }
} }
List<String> slaveUnitIds = getUnitIdList(args, FlowConst.TASK_FORM_DATA_PREFIX + "slaveUnitIds");
for (String consolidationProposalId : approvalParam.getProposalIds()) { List<String> allUnitIds = new ArrayList<>();
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationProposalId)); allUnitIds.addAll(masterUnitIds);
BpmProcessTask caseUnitTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getProcessInstanceId, "=", instance.getId()) allUnitIds.addAll(slaveUnitIds);
.and(BpmProcessTask::getProcessTaskNodeCode, "=", 70) if (allUnitIds.isEmpty()) {
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.ACTIVE.name()) return;
.and(BpmProcessTask::getDelFlag, "=", 0)
.desc(BpmProcessTask::getCreatedOn)
);
bpmService.completeTaskWithNextTaskVariable(caseUnitTask.getId(), approvalParam.getBpmTaskApprovalTypeEnum(), variables, nextApprovals);
} }
//修改提案立案结果 Map<String, ProposalUndertake> undertakeMap = dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "in", allUnitIds))
dao().update(ProposalInfo.class, Chain.make("caseFilingResult", approvalParam.getCaseFilingResult()) .stream()
, Cnd.where(ProposalInfo::getId, "in", approvalParam.getProposalIds())); .collect(Collectors.toMap(ProposalUndertake::getId, v -> v));
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class, Cnd.where(ProposalInfo::getId, "in", approvalParam.getProposalIds())); List<ProposalReplyUnit> replyUnits = new ArrayList<>();
for (ProposalInfo proposalInfo : proposalInfos) { for (String proposalId : proposalIds) {
//给代表发送消息 replyUnits.addAll(buildReplyUnits(proposalId, masterUnitIds, undertakeMap, true));
// if("CONFIRM_FILING".equals(approvalParam.getCaseFilingResult())){ replyUnits.addAll(buildReplyUnits(proposalId, slaveUnitIds, undertakeMap, false));
// String template = "{}代表您好!您的提案《{}》提案委员会已立案审核,审核结果为准予立案,您可登陆智慧校园下的暖心工会查看详情。"; }
// String content = StrUtil.format(template, proposalInfo.getCreateUserName(), proposalInfo.getName()); if (!replyUnits.isEmpty()) {
// sysMsgService.sendMsg(proposalInfo.getCreateUserLoginName(), "提案委员会立案审核", content, SecurityUtil.getUserId()); dao().insert(replyUnits);
// }else if("SUGGESTION".equals(approvalParam.getCaseFilingResult())){
//
// }else if("NOT".equals(approvalParam.getCaseFilingResult())){
// String template = "{}代表您好!您的提案《{}》提案委员会已立案审核,审核结果为不予立案,您可登陆智慧校园下的暖心工会查看详情。";
// String content = StrUtil.format(template, proposalInfo.getCreateUserName(), proposalInfo.getName());
// sysMsgService.sendMsg(proposalInfo.getCreateUserLoginName(), "提案委员会立案审核", content, SecurityUtil.getUserId());
// }
//给承办单位发送消息
if("CONFIRM_FILING".equals(approvalParam.getCaseFilingResult())) {
for (BpmAssignmentDto nextApproval : nextApprovals) {
String template = "经提案委员会立案审核,提案《{}》最终由{}作为{}单位,您可登陆智慧校园下的暖心工会进行答复,感谢您对教代会提案工作的大力支持!。";
String underTakeName = nextApproval.getJsonObject().getStr("underTakeName");
Boolean isMaster = nextApproval.getJsonObject().getBool("isMaster", false);
String content = StrUtil.format(template, proposalInfo.getName(), underTakeName, isMaster ? "主办" : "协办");
sysMsgService.sendMsg(nextApproval.getLoginName(), "承办单位答复", content, SecurityUtil.getUserId());
}
}
} }
} }
@Override /**
@Aop(TransAop.READ_COMMITTED) * 一个提案可以配置多个主办单位和多个协办单位。
public void revoke(String taskId) { * 这里按页面提交顺序逐条生成承办单位记录,兼容多主办的业务场景。
BpmProcessTask task = dao().fetch(BpmProcessTask.class, taskId); */
String instanceId = task.getProcessInstanceId(); private List<ProposalReplyUnit> buildReplyUnits(String proposalId, List<String> unitIds, Map<String, ProposalUndertake> undertakeMap, boolean isMaster) {
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, instanceId); List<ProposalReplyUnit> replyUnits = new ArrayList<>();
String instanceBusinessId = instance.getProcessInstanceBusinessId(); for (String unitId : unitIds) {
List<String> consolidationIds = proposalCommonService.getConsolidation(instanceBusinessId); ProposalUndertake undertake = undertakeMap.get(unitId);
for (String consolidationId : consolidationIds) { if (undertake == null) {
BpmProcessInstance consolidationProposalInstance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationId)); continue;
String activeTaskId = consolidationProposalInstance.getProcessInstanceTaskIds().get(0); }
BpmProcessTask activeTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getId, "=", activeTaskId)); ProposalReplyUnit replyUnit = new ProposalReplyUnit();
bpmService.revokeTask(activeTask.getPrevTaskId()); replyUnit.setProposalId(proposalId);
replyUnit.setUnitId(undertake.getId());
replyUnit.setUnitCode(undertake.getCode());
replyUnit.setUnitName(undertake.getName());
replyUnit.setIsMaster(isMaster);
replyUnits.add(replyUnit);
} }
dao().update(ProposalInfo.class, Chain.make("caseFilingResult", null), Cnd.where(ProposalInfo::getId, "in", consolidationIds)); return replyUnits;
dao().clear(ProposalConsolidation.class, Cnd.where(new Static("JSON_CONTAINS(consolidationIds,'\"" + instanceBusinessId + "\"')"))); }
/**
* 页面里的单位字段可能是数组、JSON 字符串或逗号分隔字符串,这里统一转成去空去重后的列表。
*/
private List<String> getUnitIdList(Dict args, String key) {
Object value = args.get(key);
if (ObjectUtil.isEmpty(value)) {
return List.of();
}
List<String> unitIds = new ArrayList<>();
if (value instanceof Collection<?> collection) {
collection.forEach(item -> {
if (ObjectUtil.isNotEmpty(item)) {
unitIds.add(String.valueOf(item));
}
});
} else if (value instanceof String strValue) {
if (StrUtil.isBlank(strValue)) {
return List.of();
}
if (JSONUtil.isTypeJSONArray(strValue)) {
unitIds.addAll(JSONUtil.parseArray(strValue).toList(String.class));
} else {
Arrays.stream(strValue.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.forEach(unitIds::add);
}
} else {
unitIds.add(String.valueOf(value));
}
return new ArrayList<>(new LinkedHashSet<>(unitIds));
} }
} }
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl; package com.budwk.app.zhgh.democratic.proposal.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.budwk.app.base.constant.BpmProcessConstant; import com.budwk.app.base.constant.BpmProcessConstant;
@@ -16,6 +17,16 @@ import com.budwk.app.bpm.models.BpmProcessInstance;
import com.budwk.app.bpm.models.BpmProcessNodeDefine; import com.budwk.app.bpm.models.BpmProcessNodeDefine;
import com.budwk.app.bpm.models.BpmProcessTask; import com.budwk.app.bpm.models.BpmProcessTask;
import com.budwk.app.bpm.service.BpmService; import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.engine.util.FlowUtil;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessInstanceService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.sys.models.Sys_role; import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user; import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.models.Sys_user_role;
@@ -43,6 +54,7 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
import org.nutz.json.Json;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -65,6 +77,12 @@ public class ProposalUnderTakeReplyServiceImpl extends BaseServiceImpl<ProposalI
private SysUserService sysUserService; private SysUserService sysUserService;
@Inject @Inject
private SysMsgService sysMsgService; private SysMsgService sysMsgService;
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProcessTaskService processTaskService;
@Inject
private ProcessInstanceService processInstanceService;
public ProposalUnderTakeReplyServiceImpl(Dao dao) { public ProposalUnderTakeReplyServiceImpl(Dao dao) {
super(dao); super(dao);
@@ -77,115 +95,25 @@ public class ProposalUnderTakeReplyServiceImpl extends BaseServiceImpl<ProposalI
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void approval(ProposalUnderTakeReplyApprovalParam approvalParam) { public void executeFlowTask(Dict args) {
approvalParam.getBpmTaskApprovalTypeEnum(); String proposalId = args.getStr("proposalId");
Map<String, Object> variables = BeanUtil.beanToMap(approvalParam); List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId);
if (mergeProposalIds == null || mergeProposalIds.isEmpty()) {
String processInstanceBusinessId = approvalParam.getProcessInstanceBusinessId(); executeSingleFlowTask(args);
ProposalInfo proposalInfo = dao().fetch(ProposalInfo.class, processInstanceBusinessId); return;
//查询是否都答复完成
boolean ALL_REPLY = false;
BpmProcessTask task = dao().fetch(BpmProcessTask.class, approvalParam.getProcessInstanceTaskId());
String prevTaskId = task.getPrevTaskId();
//查询一同分配的承办单位的答复记录
int otherUnderTakeTasks = dao().count(BpmProcessTask.class, Cnd.where("prevTaskId", "=", prevTaskId)
.and(BpmProcessTask::getProcessInstanceId, "=", approvalParam.getProcessInstanceId())
.and(BpmProcessTask::getId, "!=", task.getId())
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.ACTIVE.name())
.and(BpmProcessTask::getDelFlag, "=", 0)
);
if (otherUnderTakeTasks == 0) {
//说明其他单位都答复完成了
ALL_REPLY = true;
}
variables.put("ALL_REPLY", ALL_REPLY);
variables.put("caseFilingResult", proposalInfo.getCaseFilingResult());
//查询提案配置
ProposalConfig proposalConfig = dao().fetch(ProposalConfig.class, Cnd.NEW());
Boolean slaveUnitLeaderNeedApprove = proposalConfig.getSlaveUnitLeaderNeedApprove();
//查询出分配的承办单位
BpmProcessDefine proposalProcessDefine = dao().fetch(BpmProcessDefine.class, Cnd.where(BpmProcessDefine::getCode, "=", BpmProcessConstant.PROPOSAL.name()));
BpmProcessNodeDefine caseUnitNode = dao().fetch(BpmProcessNodeDefine.class, Cnd.where(BpmProcessNodeDefine::getProcessDefineId, "=", proposalProcessDefine.getId())
.and(BpmProcessNodeDefine::getNodeCode, "=", 70));
BpmProcessTask caseUnitTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getProcessInstanceId, "=", approvalParam.getProcessInstanceId())
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.COMPLETE.name())
.and(BpmProcessTask::getDelFlag, "=", 0)
.and(BpmProcessTask::getProcessTaskNodeId, "=", caseUnitNode.getId())
);
JSONObject caseUnitExtVariable = caseUnitTask.getExtVariable();
List<String> helpUnitIds = caseUnitExtVariable.getBeanList("helpUnitIds", String.class);
String hostUnitId = caseUnitExtVariable.getStr("hostUnitId");
//任务接收人
List<BpmAssignmentDto> assignmentDTOS = new ArrayList<>();
if (ALL_REPLY) {
String masterApprovalLoginName = commonService.findUserLoginNameByRoleCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER, Cnd.where(Sys_user_role::getUnderTakeId, "=", hostUnitId));
if (StrUtil.isBlank(masterApprovalLoginName)) {
throw new BaseException("主办单位分管校领导没有配置!");
}
ProposalUndertake masterUndertake = dao().fetch(ProposalUndertake.class, hostUnitId);
assignmentDTOS.add(new BpmAssignmentDto()
.setLoginName(masterApprovalLoginName)
.setJsonObject(new JSONObject()
.set("isMaster", true)
.set("underTakeId", masterUndertake.getId())
.set("underTakeName", masterUndertake.getName())
.set("underTakeCode", masterUndertake.getCode())
)
);
//如果需要协办领导审批
if (slaveUnitLeaderNeedApprove) {
for (String helpUnitId : helpUnitIds) {
String slaveApprovalLoginName = commonService.findUserLoginNameByRoleCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER, Cnd.where(Sys_user_role::getUnderTakeId, "=", helpUnitId));
if (StrUtil.isBlank(slaveApprovalLoginName)) {
throw new BaseException("协办单位分管校领导没有配置!");
}
ProposalUndertake slaveUndertake = dao().fetch(ProposalUndertake.class, helpUnitId);
assignmentDTOS.add(new BpmAssignmentDto()
.setLoginName(slaveApprovalLoginName)
.setJsonObject(new JSONObject()
.set("isMaster", false)
.set("underTakeId", slaveUndertake.getId())
.set("underTakeName", slaveUndertake.getName())
.set("underTakeCode", slaveUndertake.getCode())
)
);
}
}
} }
//查询并案的提案 ProcessTask currentTask = dao().fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY));
List<String> consolidationProposalIds = proposalCommonService.getConsolidation(proposalInfo.getId()); if (currentTask == null) {
//完成本单位的任务 throw new BaseException("没有进行中的流程任务");
for (String consolidationProposalId : consolidationProposalIds) {
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationProposalId));
BpmProcessTask otherTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getProcessInstanceId, "=", instance.getId())
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.ACTIVE.name())
.and(BpmProcessTask::getProcessTaskNodeCode, "=", task.getProcessTaskNodeCode())
.and("JSON_EXTRACT(createVariable, '$.underTakeId')", "=", approvalParam.getUnderTakeId())
.and(BpmProcessTask::getDelFlag, "=", 0)
);
bpmService.completeTaskWithNextTaskVariable(otherTask.getId(), BpmTaskApprovalTypeEnum.DYNAMIC, variables, assignmentDTOS);
} }
//全部答复完成发送消息到校领导 List<ProcessTask> mergeTasks = processTaskService.getDoingTaskByBizIdTaskName(mergeProposalIds, currentTask.getTaskName());
if(ALL_REPLY){ for (ProcessTask mergeTask : mergeTasks) {
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class, Cnd.where(ProposalInfo::getId, "in", consolidationProposalIds)); Dict cloneArgs = args.clone();
for (ProposalInfo info : proposalInfos) { cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, mergeTask.getId());
for (BpmAssignmentDto assignmentDTO : assignmentDTOS) { executeSingleFlowTask(cloneArgs);
String template = "尊敬的校领导您好!提案《{}》承办单位已经全部答复完毕,请您登陆智慧校园下的暖心工会进行审批,感谢您对教代会提案工作的大力支持!。";
String content = StrUtil.format(template, info.getName());
sysMsgService.sendMsg(assignmentDTO.getLoginName(), "分管校领导审批", content, SecurityUtil.getUserId());
}
}
} }
} }
@Override @Override
@@ -203,154 +131,218 @@ public class ProposalUnderTakeReplyServiceImpl extends BaseServiceImpl<ProposalI
return pagination.getList(); return pagination.getList();
} }
@Override
public NutMap getReplyOpinion(Long taskId) {
ProcessTask replyTask = getEditableReplyTask(taskId);
Dict variable = FlowUtil.variableToDict(replyTask.getVariable());
String opinion = variable.getStr("tf_opinion");
return NutMap.NEW()
.addv("taskId", replyTask.getId())
.addv("tf_opinion", opinion == null ? "" : opinion);
}
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void transfer(com.budwk.app.zhgh.democratic.proposal.param.ProposalUnderTakeReplyTransferParam param) { public void updateReplyOpinion(Long taskId, String opinion) {
BpmProcessTask task = dao().fetch(BpmProcessTask.class, param.getProcessInstanceTaskId()); ProcessTask replyTask = getEditableReplyTask(taskId);
Dict variable = FlowUtil.variableToDict(replyTask.getVariable());
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_PROXY); /*
* 超级管理员编辑入口只允许修改答复内容。
List<String> consolidation = proposalCommonService.getConsolidation(param.getProposalId()); * 因此这里仅覆盖 tf_opinion,避免把落实情况、附件或办理人信息一并改掉。
for (String proposalId : consolidation) { */
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", proposalId)); variable.put("tf_opinion", opinion);
//查询出其他并案提案的任务ID replyTask.setVariable(Json.toJson(variable));
BpmProcessTask otherTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getProcessInstanceId, "=", instance.getId()) dao().update(replyTask, "variable");
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.ACTIVE.name())
.and(BpmProcessTask::getProcessTaskNodeId, "=", task.getProcessTaskNodeId())
.and("JSON_EXTRACT(createVariable, '$.underTakeId')", "=", param.getUnderTakeId())
.and(BpmProcessTask::getDelFlag, "=", 0)
);
bpmService.transferTask(otherTask.getId(), param.getTransferUserLoginName(), BeanUtil.beanToMap(param));
//分配权限
FieldFilter fieldFilter = FieldFilter.create(Sys_user.class, "^id$");
Sys_user proxyUser = Daos.ext(dao(), fieldFilter).fetch(Sys_user.class, Cnd.where(Sys_user::getLoginname, "=", param.getTransferUserLoginName()));
Sys_user_role sysUserRole = new Sys_user_role();
sysUserRole.setUserId(proxyUser.getId());
sysUserRole.setRoleId(sysRole.getId());
dao().insert(sysUserRole);
sysRoleService.clearCache();
sysUserService.deleteCache(proxyUser.getId());
}
//发送消息
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class, Cnd.where(ProposalInfo::getId, "in", consolidation));
for (ProposalInfo info : proposalInfos) {
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", info.getId()));
//查询出其他并案提案的任务ID
BpmProcessTask otherTask = dao().fetch(BpmProcessTask.class, Cnd.where(BpmProcessTask::getProcessInstanceId, "=", instance.getId())
.and(BpmProcessTask::getProcessTaskNodeId, "=", task.getProcessTaskNodeId())
.and("JSON_EXTRACT(createVariable, '$.underTakeId')", "=", param.getUnderTakeId())
.and(BpmProcessTask::getDelFlag, "=", 0)
);
String underTakeName = otherTask.getCreateVariable().getStr("underTakeName");
String template = "您好!提案《{}》{}单位的答复工作已由{}转交给您,请您登陆智慧校园下的暖心工会进行答复,感谢您对教代会提案工作的大力支持!。";
String content = StrUtil.format(template, info.getName(),underTakeName,SecurityUtil.getUserUsername());
sysMsgService.sendMsg(param.getTransferUserLoginName(), "承办单位答复", content, SecurityUtil.getUserId());
}
} }
@Override /**
@Aop({TransAop.READ_COMMITTED}) * 同一批次承办单位答复任务未全部完成时,只完结当前任务,不触发流程流转。
public void revokeTask(String taskId) { * 等最后一条 reply 任务提交时,再交给流程引擎继续往下走。
BpmProcessTask task = dao().fetch(BpmProcessTask.class, taskId); */
String instanceId = task.getProcessInstanceId(); private void executeSingleFlowTask(Dict args) {
BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, instanceId); Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
String instanceBusinessId = instance.getProcessInstanceBusinessId(); ProcessTask currentTask = dao().fetch(ProcessTask.class, processTaskId);
List<String> consolidationIds = proposalCommonService.getConsolidation(instanceBusinessId); if (currentTask == null || !ProcessTaskStateEnum.DOING.getCode().equals(currentTask.getTaskState())) {
for (String consolidationId : consolidationIds) { throw new BaseException("没有进行中的流程任务");
BpmProcessInstance consolidationProposalInstance = dao().fetch(BpmProcessInstance.class, Cnd.where(BpmProcessInstance::getProcessInstanceBusinessId, "=", consolidationId));
BpmProcessTask caseUnitTask = dao().fetch(BpmProcessTask.class,
Cnd.where(BpmProcessTask::getProcessInstanceId, "=", consolidationProposalInstance.getId())
.and(BpmProcessTask::getProcessTaskNodeCode, "=", 70)
.and(BpmProcessTask::getDelFlag, "=", 0)
.desc(BpmProcessTask::getCreatedOn)
);
BpmProcessTask replyTask = dao().fetch(BpmProcessTask.class,
Cnd.where(BpmProcessTask::getPrevTaskId, "=", caseUnitTask.getId())
.and("JSON_EXTRACT(createVariable, '$.underTakeId')", "=", task.getCreateVariable().getStr("underTakeId"))
.and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.COMPLETE.name())
.and(BpmProcessTask::getDelFlag, "=", 0)
);
Boolean isMaster = replyTask.getCreateVariable().getBool("isMaster", false);
if (isMaster) {
bpmService.revokeTask(replyTask.getId());
}else{
//判断主办是否已答复完
// int masterActiveTaskCount = dao().count(BpmProcessTask.class, Cnd.where(BpmProcessTask::getPrevTaskId, "=", caseUnitTask.getId())
// .and("JSON_EXTRACT(createVariable, '$.isMaster')", "=", task.getCreateVariable().getBool("isMaster") ? "true" : "false")
// .and(BpmProcessTask::getTaskStatus, "=", BpmProcessTaskStatusEnum.ACTIVE.name())
// .and(BpmProcessTask::getDelFlag, "=", 0)
// );
Sql sql = Sqls.create("""
SELECT
COUNT(*)\s
FROM
bpm_process_task\s
WHERE
prevTaskId = @prevTaskId
AND JSON_EXTRACT( createVariable, '$.isMaster' )= TRUE\s
AND taskStatus = 'ACTIVE'\s
AND processTaskNodeCode = 80\s
AND delFlag =0
""");
sql.setParam("prevTaskId",caseUnitTask.getId());
sql.setCallback(Sqls.callback.integer());
execute(sql);
int anInt = sql.getInt();
if(anInt == 0){
throw new BaseException("主办单位已经答复完成,无法撤回,如需修改答复内容请联系校工会");
}else{
bpmService.revokeTask(replyTask.getId());
}
}
} }
String operator = resolveFlowExecuteOperator(args);
if (!processTaskService.isAllowed(currentTask, operator)) {
throw new BaseException("当前参与者不能执行该流程任务");
}
Dict executeArgs = buildFlowExecuteArgs(args);
if (shouldOnlyFinishCurrentReplyTask(currentTask, executeArgs)) {
/*
* 主办单位可能先于协办单位提交,此时校领导候选人要先写入流程实例变量,
* 这样最后一条 reply 任务触发流转时,下一节点仍能拿到主办单位选择的办理人。
*/
persistSharedReplyArgs(currentTask.getProcessInstanceId(), executeArgs);
/*
* 会签节点如果配置了完成条件,最后一条任务真正走引擎时仍会读取流程实例里的计数器。
* 因此前面这些“只完结不流转”的任务,也要同步补齐会签统计,避免最后一条被误判退回。
*/
persistCountersignProgress(currentTask, executeArgs);
processTaskService.finishProcessTask(currentTask.getId(), operator, executeArgs);
return;
}
flowCommonService.executeTask(args);
} }
@Override /**
public String checkCanReply(String taskId) { * 仅在“正常提交”场景下拦截流程继续往后流转。
// 获取提案配置 * 只要同一批次还有别的承办单位 reply 待办,就让当前任务先单独完结。
ProposalConfig config = dao().fetch(ProposalConfig.class, Cnd.NEW()); */
// 如果不需要协办单位先答复,直接返回null private boolean shouldOnlyFinishCurrentReplyTask(ProcessTask currentTask, Dict executeArgs) {
if (!Boolean.TRUE.equals(config.getSlaveUnitFirstReply())) { Integer submitType = executeArgs.getInt(FlowConst.SUBMIT_TYPE);
return null; if (!ProcessSubmitTypeEnum.AGREE.getCode().equals(submitType)) {
return false;
} }
BpmProcessTask task = dao().fetch(BpmProcessTask.class, taskId); Long batchParentTaskId = resolveReplyBatchParentTaskId(currentTask, currentTask.getTaskName());
// 如果不是主办单位,直接返回null if (batchParentTaskId == null || batchParentTaskId <= 0) {
if (!task.getCreateVariable().getBool("isMaster", false)) { return false;
return null;
} }
List<ProcessTask> doingReplyTasks = dao().query(
// 查询同一批次的其他活动任务 ProcessTask.class,
List<BpmProcessTask> activeTasks = dao().query(BpmProcessTask.class, Cnd.where("processInstanceId", "=", currentTask.getProcessInstanceId())
Cnd.where("prevTaskId", "=", task.getPrevTaskId()) .and("taskState", "=", ProcessTaskStateEnum.DOING.getCode())
.and("id", "!=", task.getId()) .and("taskName", "=", currentTask.getTaskName())
.and("taskStatus", "=", BpmProcessTaskStatusEnum.ACTIVE.name())
.and("delFlag", "=", 0)
); );
return doingReplyTasks.stream()
// 如果还有未完成的协办任务,返回提示信息 .filter(task -> !task.getId().equals(currentTask.getId()))
return activeTasks.isEmpty() ? null : .anyMatch(task -> batchParentTaskId.equals(resolveReplyBatchParentTaskId(task, currentTask.getTaskName())));
activeTasks.stream()
.map(t -> t.getCreateVariable().getStr("underTakeName"))
.filter(Objects::nonNull)
.collect(Collectors.joining(",")) + "协办单位还未答复完成,暂时无法操作,您可以点击查看按钮关注协办单位办理进度。<br/> 如果您已经线下和协办单位交流沟通完成,请点击答复按钮进行答复。";
} }
@Override /**
public void revokeTransfer(String taskId) { * 转办会把新任务的 parentId 指向原 reply 任务,因此这里要一直向上追溯,
bpmService.revokeTask(taskId); * 找到本批次承办单位答复任务共同归属的上游节点ID,作为“同一批次”的判定依据。
*/
private Long resolveReplyBatchParentTaskId(ProcessTask task, String taskName) {
Long batchParentTaskId = task.getTaskParentId();
Long parentTaskId = task.getTaskParentId();
while (parentTaskId != null && parentTaskId > 0) {
ProcessTask parentTask = dao().fetch(ProcessTask.class, parentTaskId);
if (parentTask == null || !taskName.equals(parentTask.getTaskName())) {
break;
}
batchParentTaskId = parentTask.getTaskParentId();
parentTaskId = parentTask.getTaskParentId();
}
return batchParentTaskId;
} }
/**
* old-flow 的 executeTask 会补齐办理人信息并移除 processTaskId。
* 前置完结当前任务时,也要保持同样的数据结构,避免任务变量格式不一致。
*/
private Dict buildFlowExecuteArgs(Dict args) {
Dict executeArgs = args.clone();
executeArgs.remove(FlowConst.PROCESS_TASK_ID_KEY);
Integer submitType = executeArgs.getInt(FlowConst.SUBMIT_TYPE);
if (submitType == null) {
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
}
executeArgs.put(FlowConst.SUBMIT_TYPE, submitType);
executeArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", SecurityUtil.getUserUsername());
executeArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", SecurityUtil.getUserLoginname());
executeArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", SecurityUtil.getUnitId());
executeArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", SecurityUtil.getUnitId());
executeArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", SecurityUtil.getUnionId());
return executeArgs;
}
/**
* 承办单位答复环节真正往下流转时,下一节点可能依赖主办单位预先选择的候选人。
* 这里只把跨任务共享所需的字段写入流程实例,避免把每个单位自己的答复内容互相覆盖。
*/
private void persistSharedReplyArgs(Long processInstanceId, Dict executeArgs) {
Dict sharedArgs = Dict.create();
if (StrUtil.isNotBlank(executeArgs.getStr(FlowConst.NEXT_NODE_OPERATOR))) {
sharedArgs.put(FlowConst.NEXT_NODE_OPERATOR, executeArgs.getStr(FlowConst.NEXT_NODE_OPERATOR));
}
if (StrUtil.isNotBlank(executeArgs.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "nextNodeOperatorName"))) {
sharedArgs.put(FlowConst.TASK_FORM_DATA_PREFIX + "nextNodeOperatorName", executeArgs.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "nextNodeOperatorName"));
}
if (!sharedArgs.isEmpty()) {
processInstanceService.addVariable(processInstanceId, sharedArgs);
}
}
/**
* 前面的 reply 任务虽然不触发节点流转,但依然属于会签实例的一部分。
* 这里补齐流程实例上的会签计数,保证最后一条任务进入引擎时,计数与真实完结数一致。
*/
private void persistCountersignProgress(ProcessTask currentTask, Dict executeArgs) {
if (!ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode().equals(currentTask.getPerformType())) {
return;
}
String prefix = FlowConst.COUNTERSIGN_VARIABLE_PREFIX + currentTask.getTaskName() + "_";
List<ProcessTask> doingSameNameTasks = dao().query(
ProcessTask.class,
Cnd.where("processInstanceId", "=", currentTask.getProcessInstanceId())
.and("taskState", "=", ProcessTaskStateEnum.DOING.getCode())
.and("taskName", "=", currentTask.getTaskName())
);
Dict addVariable = Dict.create();
addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES,
getProcessInstanceIntValue(currentTask.getProcessInstanceId(), prefix + FlowConst.NR_OF_COMPLETED_INSTANCES) + 1);
if (!executeArgs.containsKey(FlowConst.COUNTERSIGN_DISAGREE_FLAG)) {
addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES,
getProcessInstanceIntValue(currentTask.getProcessInstanceId(), prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES) + 1);
}
addVariable.put(prefix + FlowConst.NR_OF_ACTIVATE_INSTANCES,
Math.max(doingSameNameTasks.size() - 1, 0));
processInstanceService.addVariable(currentTask.getProcessInstanceId(), addVariable);
}
/**
* old-flow 会签计数统一保存在流程实例变量里,这里读取当前值并转成整型,便于补写增量。
*/
private int getProcessInstanceIntValue(Long processInstanceId, String key) {
ProcessInstance processInstance = processInstanceService.getById(processInstanceId);
if (processInstance == null || StrUtil.isBlank(processInstance.getVariable())) {
return 0;
}
Dict variable = cn.hutool.json.JSONUtil.toBean(processInstance.getVariable(), Dict.class);
/*
* 当前项目使用的 Hutool Dict 不支持 getInt(key, defaultValue) 这种重载。
* 会签计数键在首次提交前可能还没写入流程实例变量,这里需要先显式判空,
* 避免 Integer 为 null 时自动拆箱触发空指针。
*/
Integer value = variable.getInt(key);
return value == null ? 0 : value;
}
/**
* 与 old-flow 公共执行逻辑保持一致,支持开启扩展执行人时以管理员身份提交。
*/
private String resolveFlowExecuteOperator(Dict args) {
String operator = SecurityUtil.getUserId();
if (args.containsKey(FlowConst.EXT_ENABLE_OPERATOR) && args.getBool(FlowConst.EXT_ENABLE_OPERATOR)) {
operator = FlowConst.ADMIN_ID;
}
return operator;
}
/**
* “编辑答复内容”只能针对已经完成的承办单位答复任务。
* 这里统一校验任务存在性、节点范围和任务状态,避免页面把其他流程节点误传进来。
*/
private ProcessTask getEditableReplyTask(Long taskId) {
ProcessTask replyTask = dao().fetch(ProcessTask.class, taskId);
if (replyTask == null) {
throw new BaseException("未找到答复任务");
}
if (!List.of("unit_reply", "two_unit_reply", "opinion_master_reply").contains(replyTask.getTaskName())) {
throw new BaseException("当前任务不支持编辑答复内容");
}
if (!Objects.equals(ProcessTaskStateEnum.FINISHED.getCode(), replyTask.getTaskState())) {
throw new BaseException("只有已完成的答复任务才能编辑内容");
}
return replyTask;
}
} }
@@ -21,10 +21,10 @@ const PROPOSAL_INFO = {
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="提案类别"> <el-descriptions-item label="提案类别">
{{viewData.typeName}} {{viewData.typeName}}
</el-descriptions-item> </el-descriptions-item><!--
<el-descriptions-item label="建议承办单位"> <el-descriptions-item label="建议承办单位">
{{viewData.suggestUnits}} {{viewData.suggestUnits}}
</el-descriptions-item> </el-descriptions-item>-->
<el-descriptions-item label="立案结果" :span="2"> <el-descriptions-item label="立案结果" :span="2">
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT" <dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
:value="viewData.caseFilingResult"></dict-tag> :value="viewData.caseFilingResult"></dict-tag>
@@ -144,7 +144,7 @@ const PROPOSAL_INFO = {
<!--提案委员会立案--> <!--提案委员会立案-->
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" <el-descriptions border class="flow-task-form" :column="3" :key="task.id"
v-else-if="task.taskName === 'committee'"> v-else-if="task.taskName === 'committee'||task.taskName === 'committeeFilingUnit'">
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName <el-descriptions-item label="办理用户">{{ task.taskFormData.userName
}}({{task.taskFormData.loginName}}) }}({{task.taskFormData.loginName}})
</el-descriptions-item> </el-descriptions-item>
@@ -162,10 +162,16 @@ const PROPOSAL_INFO = {
</template> </template>
</div> </div>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="主办单位" :span="3">{{task.ext.tf_masterUnitName}} <template v-if="['CONFIRM_FILING','SUGGESTION'].includes(task.ext.tf_caseFilingResult)">
</el-descriptions-item> <el-descriptions-item v-if="task.ext.tf_caseFilingResult === 'SUGGESTION'" label="承办单位" :span="3">{{task?.ext?.tf_masterUnitNameStr || task?.ext?.tf_slaveUnitNameStr}}
<el-descriptions-item label="协办单位" :span="3">{{task?.ext?.tf_slaveUnitNameStr}} </el-descriptions-item>
</el-descriptions-item> <template v-else>
<el-descriptions-item label="主办单位" :span="3">{{task?.ext?.tf_masterUnitNameStr}}
</el-descriptions-item>
<el-descriptions-item label="协办单位" :span="3">{{task?.ext?.tf_slaveUnitNameStr}}
</el-descriptions-item>
</template>
</template>
<el-descriptions-item label="办理意见" :span="3"> <el-descriptions-item label="办理意见" :span="3">
<div v-html="task.taskFormData.tf_opinion"></div> <div v-html="task.taskFormData.tf_opinion"></div>
</el-descriptions-item> </el-descriptions-item>
@@ -173,7 +179,7 @@ const PROPOSAL_INFO = {
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else> <el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
<el-descriptions-item label="承办单位" <el-descriptions-item label="承办单位"
v-if="['master_reply','slave_reply'].includes(task.taskName)" :span="3"> v-if="['opinion_unit_reply','unit_reply','two_unit_reply','master_reply','slave_reply'].includes(task.taskName)" :span="3">
{{task.ext.underTakeName}} {{task.ext.underTakeName}}
</el-descriptions-item> </el-descriptions-item>
@@ -244,6 +250,8 @@ const PROPOSAL_INFO = {
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => { this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.doneTasks = res.data this.doneTasks = res.data
// 通知外层页面已办节点数据已加载完成,便于当前环节做表单回显。
this.$emit("done-tasks", res.data)
} }
}) })
}, },
@@ -156,6 +156,9 @@ layout("/layouts/platform.html"){
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" <enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag> size="small"></enum-tag>
</template> </template>
<template v-else-if="column.prop === 'undertakeUnits'" scope="{row}">
{{ getUndertakeDisplay(row) }}
</template>
</el-table-column> </el-table-column>
<el-table-column label="操作" fixed="right" min-width="200px"> <el-table-column label="操作" fixed="right" min-width="200px">
<template scope="{row}"> <template scope="{row}">
@@ -211,14 +214,41 @@ layout("/layouts/platform.html"){
{label: "是否并案", prop: "merge"}, {label: "是否并案", prop: "merge"},
{label: "案由", prop: "brief", visible: false}, {label: "案由", prop: "brief", visible: false},
{label: "建议措施", prop: "measures", visible: false}, {label: "建议措施", prop: "measures", visible: false},
{label: "办单位", prop: "masterUnitName"}, {label: "办单位", prop: "undertakeUnits", width: "260px"},
{label: "协办单位", prop: "slaveUnitNames"},
{label: "当前节点", prop: "curTaskName"}, {label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"} {label: "流程状态", prop: "instanceState"}
] ]
} }
}, },
methods: { methods: {
/**
* 导出列表页沿用查询口径,确保“作为建议”展示为承办单位,
* “确定立案”则保留主办/协办信息。
*/
getUndertakeDisplay(row) {
var masterUnitNames = this.formatUnitNames(row.masterUnitName)
var slaveUnitNames = this.formatUnitNames(row.slaveUnitNames)
if (row.caseFilingResult === "SUGGESTION") {
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return item
}).join("")
}
return ""
},
formatUnitNames(unitNames) {
if (!unitNames) {
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return item
}).join("")
}
return unitNames
},
openView(row) { openView(row) {
this.$refs.guava.view(() => { this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row) this.$refs.infoRef.onOpen(row)
@@ -222,6 +222,9 @@ layout("/layouts/platform.html"){
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" <enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag> size="small"></enum-tag>
</template> </template>
<template v-else-if="column.prop === 'undertakeUnits'" scope="{row}">
{{ getUndertakeDisplay(row) }}
</template>
</el-table-column> </el-table-column>
<el-table-column label="操作" fixed="right" width="200px"> <el-table-column label="操作" fixed="right" width="200px">
<template scope="{row}"> <template scope="{row}">
@@ -260,8 +263,7 @@ layout("/layouts/platform.html"){
{label: "立案结果", prop: "caseFilingResult"}, {label: "立案结果", prop: "caseFilingResult"},
{label: "立案类型", prop: "caseFilingType"}, {label: "立案类型", prop: "caseFilingType"},
{label: "是否并案", prop: "merge"}, {label: "是否并案", prop: "merge"},
{label: "办单位", prop: "masterUnitName"}, {label: "办单位", prop: "undertakeUnits", width: "260px"},
{label: "协办单位", prop: "slaveUnitNames"},
{label: "当前节点", prop: "curTaskName"}, {label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"} {label: "流程状态", prop: "instanceState"}
], ],
@@ -301,6 +303,34 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* 查询列表按立案结果切换承办单位展示口径:
* 立案显示“主办/协办”,作为建议只显示“承办单位”。
*/
getUndertakeDisplay(row) {
var masterUnitNames = this.formatUnitNames(row.masterUnitName)
var slaveUnitNames = this.formatUnitNames(row.slaveUnitNames)
if (row.caseFilingResult === "SUGGESTION") {
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return item
}).join("")
}
return ""
},
formatUnitNames(unitNames) {
if (!unitNames) {
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return item
}).join("")
}
return unitNames
},
openView(row) { openView(row) {
this.$refs.guava.view(() => { this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row) this.$refs.infoRef.onOpen(row)
@@ -116,8 +116,11 @@ layout("/layouts/platform.html"){
<el-tag v-else type="warning" size="small"></el-tag> <el-tag v-else type="warning" size="small"></el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="办单位" prop="masterUnitName" show-overflow-tooltip></el-table-column> <el-table-column label="办单位" prop="undertakeUnits" show-overflow-tooltip>
<el-table-column label="协办单位" prop="slaveUnitNames" show-overflow-tooltip></el-table-column> <template slot-scope="{row}">
{{ getUndertakeDisplay(row) }}
</template>
</el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column> <el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态"> <el-table-column prop="instanceState" label="流程状态">
<template slot-scope="{row}"> <template slot-scope="{row}">
@@ -169,6 +172,34 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* 历史查询里统一把承办单位合并成一列展示,
* 便于“确定立案”和“作为建议”两种口径同时查看。
*/
getUndertakeDisplay(row) {
var masterUnitNames = this.formatUnitNames(row.masterUnitName)
var slaveUnitNames = this.formatUnitNames(row.slaveUnitNames)
if (row.caseFilingResult === "SUGGESTION") {
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return item
}).join("")
}
return ""
},
formatUnitNames(unitNames) {
if (!unitNames) {
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return item
}).join("")
}
return unitNames
},
openView(row) { openView(row) {
this.$refs.guava.view(() => { this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row) this.$refs.infoRef.onOpen(row)
@@ -112,8 +112,11 @@ layout("/layouts/platform.html"){
<el-tag size="mini" v-else type="danger"></el-tag> <el-tag size="mini" v-else type="danger"></el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="办单位" prop="masterUnitName" show-overflow-tooltip></el-table-column> <el-table-column label="办单位" prop="undertakeUnits" show-overflow-tooltip>
<el-table-column label="协办单位" prop="slaveUnitNames" show-overflow-tooltip></el-table-column> <template slot-scope="{row}">
{{ getUndertakeDisplay(row) }}
</template>
</el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column> <el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态"> <el-table-column prop="instanceState" label="流程状态">
<template slot-scope="{row}"> <template slot-scope="{row}">
@@ -163,6 +166,34 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* 年报查询列表里,把主办/协办与建议承办统一折叠成一个展示口径,
* 避免“作为建议”场景下仍显示主办/协办文案。
*/
getUndertakeDisplay(row) {
var masterUnitNames = this.formatUnitNames(row.masterUnitName)
var slaveUnitNames = this.formatUnitNames(row.slaveUnitNames)
if (row.caseFilingResult === "SUGGESTION") {
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return item
}).join("")
}
return ""
},
formatUnitNames(unitNames) {
if (!unitNames) {
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return item
}).join("")
}
return unitNames
},
openView(row) { openView(row) {
this.$refs.guava.view(() => { this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row) this.$refs.infoRef.onOpen(row)
@@ -2,106 +2,118 @@
layout("/layouts/platform.html"){ layout("/layouts/platform.html"){
#--> #-->
<div id="app" v-cloak> <div id="app" v-cloak>
<guava ref="guava"> <guava ref="guava">
<el-card shadow="never"> <el-card shadow="never">
<search @search="doSearch"> <search @search="doSearch">
<search-item label="教代会"> <search-item label="教代会">
<el-select @change="meetingChange" clearable filterable placeholder="所属教代会" <el-select @change="meetingChange" clearable filterable placeholder="所属教代会"
v-model="pageForm.sessionId"> v-model="pageForm.sessionId">
<el-option :key="item.id" :label="item.fullName" :value="item.id" <el-option :key="item.id" :label="item.fullName" :value="item.id"
v-for="item in sessionOptions"></el-option> v-for="item in sessionOptions"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="提案名称"> <search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable <el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input> style="width: 100%"></el-input>
</search-item> </search-item>
<search-item label="姓名/工号"> <search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.createUserKeyword"></el-input> <el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.createUserKeyword"></el-input>
</search-item> </search-item>
</search> </search>
</el-card> </el-card>
<el-card shadow="never"> <el-card shadow="never">
<table-tool :columns.sync="tableColumns"> <table-tool :columns.sync="tableColumns">
</table-tool> </table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%"> <el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column label="序号" width="50" type="index" :index="indexMethod" fixed="left"></el-table-column> <el-table-column label="序号" width="50" type="index" :index="indexMethod"
<el-table-column fixed="left"></el-table-column>
:label="column.label" <el-table-column
:prop="column.prop" :label="column.label"
:key="column.key" :prop="column.prop"
:min-width="column.width" :key="column.key"
:fixed="column.fixed" :min-width="column.width"
show-overflow-tooltip :fixed="column.fixed"
v-for="column in tableColumns" show-overflow-tooltip
v-if="column.visible !== false" v-for="column in tableColumns"
> v-if="column.visible !== false"
<template v-if="column.prop === 'instanceState'" scope="{row}"> >
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" <template v-if="column.prop === 'instanceState'" scope="{row}">
size="small"></enum-tag> <enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
</template> size="small"></enum-tag>
</el-table-column> </template>
<el-table-column label="操作" fixed="right" width="300px"> </el-table-column>
<template slot-scope="{row}"> <el-table-column label="操作" fixed="right" width="100px">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button> <template slot-scope="{row}">
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核 <el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
</el-button> </template>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回 </el-table-column>
</el-button> </el-table>
</template> <!--#include("/layouts/pagination.html"){}#-->
</el-table-column> </el-card>
</el-table> <template #edit>
<!--#include("/layouts/pagination.html"){}#--> <proposal-info ref="proposalInfoRef">
</el-card> </proposal-info>
</guava> </template>
</guava>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
<!--#include('../../common/info.js'){}#--> <!--#include('../../common/info.js'){}#-->
new Vue({ new Vue({
el: "#app", el: "#app",
store, store,
mixins: [initTableMixins], mixins: [initTableMixins],
components: { components: {
"proposal-info": PROPOSAL_INFO "proposal-info": PROPOSAL_INFO
}, },
data() { data() {
return { return {
tableColumns: [ tableColumns: [
{label: "提案编号", prop: "code"}, {label: "提案编号", prop: "code"},
{label: "提案名称", prop: "name", width: "200px"}, {label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName", width: "80px"}, {label: "提案人", prop: "createUserName", width: "80px"},
{label: "提案类别", prop: "typeName"}, {label: "提案类别", prop: "typeName"},
{label: "届次", prop: "sessionName"}, {label: "届次", prop: "sessionName"},
{label: "代表团", prop: "delegationName"}, {label: "代表团", prop: "delegationName"},
{label: "流程状态", prop: "instanceState"} {label: "流程状态", prop: "instanceState"}
], ],
pageForm: { pageForm: {
approval: false approval: false
}, },
showApprovalForm: false, showApprovalForm: false,
} sessionOptions:[]
}, }
created() { },
this.pageData() created() {
this.listOpenSession() this.pageData()
}, this.listOpenSession()
methods: { },
// 查询开启的教代会 methods: {
listOpenSession() { // 教代会
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => { async meetingChange(val) {
if (res.code === 0) { this.doSearch()
this.sessionOptions = res.data },
if (this.sessionOptions) { openView(row) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id) this.$refs.guava.edit(() => {
this.pageData() this.showApprovalForm = false
this.listDelegation() this.$refs.proposalInfoRef.onOpen(row)
} })
} },
}) // 查询开启的教代会
}, listOpenSession() {
} this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
}) if (res.code === 0) {
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.pageData()
this.listDelegation()
}
}
})
},
}
})
</script> </script>
<!--# <!--#
} }
@@ -45,7 +45,7 @@ layout("/layouts/platform.html"){
size="small"></enum-tag> size="small"></enum-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" fixed="right" width="300px"> <el-table-column label="操作" fixed="right" width="200px">
<template slot-scope="{row}"> <template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button> <el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核 <el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核
@@ -57,7 +57,7 @@ layout("/layouts/platform.html"){
</el-table> </el-table>
<!--#include("/layouts/pagination.html"){}#--> <!--#include("/layouts/pagination.html"){}#-->
</el-card> </el-card>
<template #edit> <template #edit>
<proposal-info ref="proposalInfoRef"> <proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm"> <div v-if="showApprovalForm">
@@ -65,7 +65,7 @@ layout("/layouts/platform.html"){
{{formData.taskName}} {{formData.taskName}}
</div> </div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="150px" label-suffix=""> <el-form :model="formData" ref="formRef" :rules="formRules" label-width="150px" label-suffix="">
<el-row gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-form-item label="审核人" proop="tf_username"> <el-form-item label="审核人" proop="tf_username">
<el-input readonly v-model="formData.tf_username"></el-input> <el-input readonly v-model="formData.tf_username"></el-input>
@@ -87,9 +87,24 @@ layout("/layouts/platform.html"){
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
<span class="text-primary ml10" v-if="formData.tf_caseCheckNeedSecondReply"> <span class="text-primary ml10" v-if="formData.tf_caseCheckNeedSecondReply">
(二次答复只需要主办单位答复) (请选择需要进入二次答复的承办单位,可多选)
</span> </span>
</el-form-item> </el-form-item>
<el-form-item
label="答复单位"
prop="tf_secondReplyUnitIds"
v-if="formData.tf_caseCheckNeedSecondReply"
:rules="[{required:formData.tf_caseCheckNeedSecondReply === 1,message:'请选择需要二次答复的承办单位',trigger:['change','blur']}]"
>
<el-select v-model="formData.tf_secondReplyUnitIds" clearable filterable multiple style="width: 100%">
<el-option
v-for="item in replyUnitOptions"
:key="item.unitId"
:label="item.unitName + '' + (item.isMaster ? '主办' : '协办') + ''"
:value="item.unitId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="审批意见" prop="tf_opinion" <el-form-item label="审批意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea> <user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
@@ -97,8 +112,8 @@ layout("/layouts/platform.html"){
</el-form> </el-form>
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button> <el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到提案人</el-button> <!-- <el-button @click="handleTaskAction(6)" size="small" type="info">退回到提案人</el-button>-->
<el-button @click="handleTaskAction(2)" size="small" type="danger">不同意</el-button> <!-- <el-button @click="handleTaskAction(2)" size="small" type="danger">不同意</el-button>-->
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意</el-button> <el-button @click="handleTaskAction(1)" size="small" type="primary">同意</el-button>
</el-row> </el-row>
</div> </div>
@@ -125,12 +140,15 @@ layout("/layouts/platform.html"){
{label: "届次", prop: "sessionName"}, {label: "届次", prop: "sessionName"},
{label: "代表团", prop: "delegationName"}, {label: "代表团", prop: "delegationName"},
{label: "当前节点", prop: "curTaskName"}, {label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"} {label: "流程状态", prop: "instanceState"},
{label: "办理时间", prop: "finishTime"}
], ],
pageForm: { pageForm: {
approval: false approval: false
}, },
showApprovalForm: false, showApprovalForm: false,
replyUnitOptions: [],
sessionOptions: [],
} }
}, },
created() { created() {
@@ -144,19 +162,27 @@ layout("/layouts/platform.html"){
this.$refs.proposalInfoRef.onOpen(row) this.$refs.proposalInfoRef.onOpen(row)
}) })
}, },
openAudit(row) { openAudit(row) {
this.loadReplyUnits(row.id)
this.$refs.guava.edit(() => { this.$refs.guava.edit(() => {
this.showApprovalForm = true this.showApprovalForm = true
this.formData = { this.formData = {
proposalId: row.id,
processTaskId: row.taskId, processTaskId: row.taskId,
taskName: row.curTaskName, taskName: row.curTaskName,
tf_auditTime: this.$moment().format('YYYY-MM-DD'), tf_auditTime: this.$moment().format('YYYY-MM-DD'),
tf_username: this.$store.state.user.username tf_username: this.$store.state.user.username,
tf_caseCheckNeedSecondReply: 0,
tf_secondReplyUnitIds: []
} }
this.$refs.proposalInfoRef.onOpen(row) this.$refs.proposalInfoRef.onOpen(row)
}) })
}, },
// 教代会
async meetingChange(val) {
this.doSearch()
},
// 查询开启的教代会 // 查询开启的教代会
listOpenSession() { listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => { this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
@@ -170,7 +196,16 @@ layout("/layouts/platform.html"){
} }
}) })
}, },
// 二次答复只能选择当前提案已配置的承办单位,避免误选到别的单位。
loadReplyUnits(proposalId) {
this.$axios.post("/platform/proposal/case/check/listReplyUnits", { proposalId: proposalId }).then((res) => {
if (res.code === 0) {
this.replyUnitOptions = res.data || []
}
})
},
handleTaskAction(val) { handleTaskAction(val) {
this.$refs.formRef.validate(valid => { this.$refs.formRef.validate(valid => {
if (!valid) return if (!valid) return
@@ -179,9 +214,35 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}).then(() => { }).then(() => {
let submitData = Object.assign({}, this.formData)
if (submitData.tf_caseCheckNeedSecondReply === 1) {
let secondReplyUnits = this.replyUnitOptions.filter(item => submitData.tf_secondReplyUnitIds.indexOf(item.unitId) > -1)
let masterUnitIds = secondReplyUnits.filter(item => item.isMaster).map(item => item.unitId)
let slaveUnitIds = secondReplyUnits.filter(item => !item.isMaster).map(item => item.unitId)
let masterUnitNames = secondReplyUnits.filter(item => item.isMaster).map(item => item.unitName)
let slaveUnitNames = secondReplyUnits.filter(item => !item.isMaster).map(item => item.unitName)
submitData.tf_masterUnitIds = masterUnitIds
submitData.tf_masterUnitId = masterUnitIds.length ? masterUnitIds[0] : ""
submitData.tf_masterUnitName = masterUnitNames.length ? masterUnitNames[0] : ""
submitData.tf_masterUnitNames = masterUnitNames
submitData.tf_masterUnitNameStr = masterUnitNames.join(",")
submitData.tf_slaveUnitIds = slaveUnitIds
submitData.tf_slaveUnitNames = slaveUnitNames
submitData.tf_slaveUnitNameStr = slaveUnitNames.join(",")
} else {
submitData.tf_secondReplyUnitIds = []
submitData.tf_masterUnitIds = []
submitData.tf_masterUnitId = ""
submitData.tf_masterUnitName = ""
submitData.tf_masterUnitNames = []
submitData.tf_masterUnitNameStr = ""
submitData.tf_slaveUnitIds = []
submitData.tf_slaveUnitNames = []
submitData.tf_slaveUnitNameStr = ""
}
this.$axios.post("/flow/common/executeTask", { this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({ data: JSON.stringify({
...this.formData, ...submitData,
submitType: val submitType: val
}) })
}).then((res) => { }).then((res) => {
@@ -193,7 +254,22 @@ layout("/layouts/platform.html"){
}) })
}) })
}) })
} },
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", { taskId: row.taskId }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
})
})
},
} }
}) })
</script> </script>
@@ -102,7 +102,7 @@ layout("/layouts/platform.html"){
<el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix=""> <el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix="">
<el-form-item label="立案结果" prop="tf_caseFilingResult" <el-form-item label="立案结果" prop="tf_caseFilingResult"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-radio-group v-model="formData.tf_caseFilingResult" size="small"> <el-radio-group v-model="formData.tf_caseFilingResult" size="small" @change="handleCaseFilingResultChange">
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code" <el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code"
border>{{item.label}} border>{{item.label}}
</el-radio> </el-radio>
@@ -118,12 +118,13 @@ layout("/layouts/platform.html"){
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="主办单位" :label="formData.tf_caseFilingResult === 'SUGGESTION' ? '承办单位' : '主办单位'"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:'必填',trigger:['change','blur']}]" :rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:formData.tf_caseFilingResult === 'SUGGESTION' ? '请选择承办单位' : '请选择主办单位',trigger:['change','blur']}]"
prop="tf_masterUnitId" prop="tf_masterUnitIds"
> >
<el-select v-model="formData.tf_masterUnitId" filterable clearable style="width: 100%"> <el-select v-model="formData.tf_masterUnitIds" filterable clearable multiple
style="width: 100%">
<el-option <el-option
v-for="item in underTakeOptions" v-for="item in underTakeOptions"
:label="item.name" :label="item.name"
@@ -134,7 +135,7 @@ layout("/layouts/platform.html"){
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="协办单位" <el-form-item label="协办单位"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'"
prop="tf_slaveUnitIds" prop="tf_slaveUnitIds"
> >
<el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple <el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple
@@ -144,7 +145,7 @@ layout("/layouts/platform.html"){
:label="item.name" :label="item.name"
:value="item.id" :value="item.id"
:key="item.id" :key="item.id"
:disabled="item.id===formData.tf_masterUnitId" :disabled="formData && formData.tf_masterUnitIds.includes(item.id)"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -197,13 +198,13 @@ layout("/layouts/platform.html"){
approval: false approval: false
}, },
formData: { formData: {
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
}, },
formRules: { formRules: {
tf_caseFilingResult: [{ required: true, message: "请选择立案结果", trigger: ["blur", "change"] }], tf_caseFilingResult: [{ required: true, message: "请选择立案结果", trigger: ["blur", "change"] }],
tf_caseFilingType: [{ required: true, message: "请选择立案类型", trigger: ["blur", "change"] }], tf_caseFilingType: [{ required: true, message: "请选择立案类型", trigger: ["blur", "change"] }],
tf_masterUnitId: [{ required: true, message: "请选择主办单位", trigger: "blur" }], tf_masterUnitIds: [{ required: true, message: "请选择主办单位", trigger: "blur" }],
approvalOpinion: [{ required: true, message: "请填写审核意见", trigger: "blur" }] approvalOpinion: [{ required: true, message: "请填写审核意见", trigger: "blur" }]
}, },
showApprovalForm: false, showApprovalForm: false,
@@ -213,6 +214,23 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* “作为建议”不再区分主办/协办,只保留承办单位;
* 切到“不予立案”等结果时,同时清空已选单位,避免旧值被误提交。
*/
handleCaseFilingResultChange(caseFilingResult) {
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_caseFilingType", "")
}
if (caseFilingResult === "SUGGESTION") {
this.$set(this.formData, "tf_slaveUnitIds", [])
return
}
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_masterUnitIds", [])
this.$set(this.formData, "tf_slaveUnitIds", [])
}
},
openView(row) { openView(row) {
this.$refs.guava.edit(() => { this.$refs.guava.edit(() => {
this.showApprovalForm = false this.showApprovalForm = false
@@ -225,7 +243,7 @@ layout("/layouts/platform.html"){
this.formData = { this.formData = {
processTaskId: row.taskId, processTaskId: row.taskId,
taskName: row.curTaskName, taskName: row.curTaskName,
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
} }
this.$refs.proposalInfoRef.onOpen(row) this.$refs.proposalInfoRef.onOpen(row)
@@ -240,9 +258,12 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}).then(() => { }).then(() => {
// “作为建议”统一按承办单位处理,提交时不再携带协办单位数据。
const masterUnitIds = this.formData.tf_masterUnitIds || []
const slaveUnitIds = this.formData.tf_caseFilingResult === "CONFIRM_FILING" ? (this.formData.tf_slaveUnitIds || []) : []
// 是否有协办单位 // 是否有协办单位
let tf_helpunitreply let tf_helpunitreply
if (this.formData.tf_slaveUnitIds && this.formData.tf_slaveUnitIds.length > 0) { if (slaveUnitIds.length > 0) {
tf_helpunitreply = "HAS_HELP_UNIT" tf_helpunitreply = "HAS_HELP_UNIT"
} else { } else {
tf_helpunitreply = "NO_HELP_UNIT" tf_helpunitreply = "NO_HELP_UNIT"
@@ -252,10 +273,16 @@ layout("/layouts/platform.html"){
this.$axios.post("/flow/common/executeTask", { this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({ data: JSON.stringify({
...this.formData, ...this.formData,
tf_masterUnitIds: masterUnitIds,
tf_slaveUnitIds: slaveUnitIds,
submitType: val, submitType: val,
tf_masterUnitName: this.formData.tf_masterUnitId ? this.underTakeOptions.find(v => v.id === this.formData.tf_masterUnitId)?.name : null, // 兼容旧流程仍按单个主办单位字段取值的场景,这里保留第一个主办单位ID和名称。
tf_slaveUnitNames: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name), tf_masterUnitId: masterUnitIds.length ? masterUnitIds[0] : "",
tf_slaveUnitNameStr: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(","), tf_masterUnitName: masterUnitIds.length ? this.underTakeOptions.find(v => v.id === masterUnitIds[0])?.name : "",
tf_masterUnitNames: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_masterUnitNameStr: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(","),
tf_slaveUnitNames: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_slaveUnitNameStr: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(","),
tf_helpunitreply: tf_helpunitreply tf_helpunitreply: tf_helpunitreply
}) })
}).then((res) => { }).then((res) => {
@@ -283,7 +310,7 @@ layout("/layouts/platform.html"){
this.$refs.mergeRef.onOpen(selection, { this.$refs.mergeRef.onOpen(selection, {
processTaskIds: selection.map(v => v.taskId), processTaskIds: selection.map(v => v.taskId),
taskName: selection[0].curTaskName, taskName: selection[0].curTaskName,
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
}) })
}) })
@@ -295,7 +322,8 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消", cancelButtonText: "取消",
type: "info" type: "info"
}).then(() => { }).then(() => {
this.$axios.post("/flow/common/revokeTask", { taskId: row.taskId }).then((res) => { // 提委会立案撤回需要走专用接口,确保并案提案会整组一起撤回。
this.$axios.post("/platform/proposal/committeeFiling/revokeTask", { taskId: row.taskId, proposalId: row.id }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.$message.success(res.msg) this.$message.success(res.msg)
this.doSearch() this.doSearch()
@@ -25,7 +25,7 @@ const merge = {
<el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix=""> <el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix="">
<el-form-item label="立案结果" prop="tf_caseFilingResult" <el-form-item label="立案结果" prop="tf_caseFilingResult"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-radio-group v-model="formData.tf_caseFilingResult" size="small"> <el-radio-group v-model="formData.tf_caseFilingResult" size="small" @change="handleCaseFilingResultChange">
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code" <el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code"
border>{{item.label}} border>{{item.label}}
</el-radio> </el-radio>
@@ -41,12 +41,12 @@ const merge = {
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="主办单位" :label="formData.tf_caseFilingResult === 'SUGGESTION' ? '承办单位' : '主办单位'"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:'必填',trigger:['change','blur']}]" :rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:formData.tf_caseFilingResult === 'SUGGESTION' ? '请选择承办单位' : '请选择主办单位',trigger:['change','blur']}]"
prop="tf_masterUnitId" prop="tf_masterUnitIds"
> >
<el-select v-model="formData.tf_masterUnitId" filterable clearable style="width: 100%"> <el-select v-model="formData.tf_masterUnitIds" filterable clearable multiple style="width: 100%">
<el-option <el-option
v-for="item in underTakeOptions" v-for="item in underTakeOptions"
:label="item.name" :label="item.name"
@@ -57,7 +57,7 @@ const merge = {
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="协办单位" <el-form-item label="协办单位"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'"
prop="tf_slaveUnitIds" prop="tf_slaveUnitIds"
> >
<el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple <el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple
@@ -67,13 +67,13 @@ const merge = {
:label="item.name" :label="item.name"
:value="item.id" :value="item.id"
:key="item.id" :key="item.id"
:disabled="item.id===formData.tf_masterUnitId" :disabled="formData && formData.tf_masterUnitIds.includes(item.id)"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="审核意见" prop="approvalOpinion" <el-form-item label="审核意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.approvalOpinion"></user-opinion-textarea> <user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
@@ -89,19 +89,36 @@ const merge = {
return { return {
selection: [], selection: [],
formData: { formData: {
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
}, },
formRules: { formRules: {
tf_caseFilingResult: [{ required: true, message: "请选择立案结果", trigger: ["blur", "change"] }], tf_caseFilingResult: [{ required: true, message: "请选择立案结果", trigger: ["blur", "change"] }],
tf_caseFilingType: [{ required: true, message: "请选择立案类型", trigger: ["blur", "change"] }], tf_caseFilingType: [{ required: true, message: "请选择立案类型", trigger: ["blur", "change"] }],
tf_masterUnitId: [{ required: true, message: "请选择主办单位", trigger: "blur" }], tf_masterUnitIds: [{ required: true, message: "请选择主办单位", trigger: "blur" }],
approvalOpinion: [{ required: true, message: "请填写审核意见", trigger: "blur" }], tf_opinion: [{ required: true, message: "请填写审核意见", trigger: "blur" }],
}, },
underTakeOptions: [] underTakeOptions: []
} }
}, },
methods: { methods: {
/**
* 并案审核与单案审核保持同一口径:
* “作为建议”只录入承办单位,不再区分协办单位。
*/
handleCaseFilingResultChange(caseFilingResult) {
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_caseFilingType", "")
}
if (caseFilingResult === "SUGGESTION") {
this.$set(this.formData, "tf_slaveUnitIds", [])
return
}
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_masterUnitIds", [])
this.$set(this.formData, "tf_slaveUnitIds", [])
}
},
onOpen(selection, formData) { onOpen(selection, formData) {
this.selection = selection this.selection = selection
this.formData = formData this.formData = formData
@@ -124,40 +141,52 @@ const merge = {
}) })
}, },
handleTaskAction(val) { handleTaskAction(val) {
this.$confirm("您确定要提交吗?", "提示", { this.$refs.formRef.validate(valid => {
confirmButtonText: "确定", if (valid) {
cancelButtonText: "取消", this.$confirm("您确定要提交吗?", "提示", {
type: "warning" confirmButtonText: "确定",
}).then(() => { cancelButtonText: "取消",
debugger type: "warning"
// 是否有协办单位 }).then(() => {
let tf_helpunitreply; // 并案审核提交参数与单案立案页保持一致,避免两个入口的流程变量不一致。
if (this.formData.tf_slaveUnitIds && this.formData.tf_slaveUnitIds.length > 0) { const masterUnitIds = this.formData.tf_masterUnitIds || []
tf_helpunitreply = 'HAS_HELP_UNIT' const slaveUnitIds = this.formData.tf_caseFilingResult === "CONFIRM_FILING" ? (this.formData.tf_slaveUnitIds || []) : []
} else { // 是否有协办单位
tf_helpunitreply = 'NO_HELP_UNIT' let tf_helpunitreply
} if (slaveUnitIds.length > 0) {
tf_helpunitreply = "HAS_HELP_UNIT"
} else {
tf_helpunitreply = "NO_HELP_UNIT"
}
const loading = createLoading('提交中') const loading = createLoading('提交中')
this.$axios.post("/platform/proposal/committeeFiling/merge", { this.$axios.post("/platform/proposal/committeeFiling/merge", {
data: JSON.stringify({ data: JSON.stringify({
...this.formData, ...this.formData,
proposalIds: this.selection.map(v => v.id), tf_masterUnitIds: masterUnitIds,
submitType: val, tf_slaveUnitIds: slaveUnitIds,
tf_masterUnitName: this.formData.tf_masterUnitId ? this.underTakeOptions.find(v => v.id === this.formData.tf_masterUnitId)?.name : null, proposalIds: this.selection.map(v => v.id),
tf_slaveUnitNames: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name), submitType: val,
tf_slaveUnitNameStr: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','), // 兼容旧流程仍按单个主办单位字段取值的场景,这里保留第一个主办单位ID和名称。
tf_helpunitreply: tf_helpunitreply tf_masterUnitId: masterUnitIds.length ? masterUnitIds[0] : "",
tf_masterUnitName: masterUnitIds.length ? this.underTakeOptions.find(v => v.id === masterUnitIds[0])?.name : "",
tf_masterUnitNames: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_masterUnitNameStr: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','),
tf_slaveUnitNames: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_slaveUnitNameStr: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','),
tf_helpunitreply: tf_helpunitreply
})
}).then((res) => {
if (res.code === 0) {
this.$emit('close')
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
loading.close()
})
}) })
}).then((res) => { }
if (res.code === 0) {
this.$emit('close')
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
loading.close()
})
}) })
} }
} }
@@ -103,7 +103,7 @@ layout("/layouts/platform.html"){
<el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix=""> <el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix="">
<el-form-item label="立案结果" prop="tf_caseFilingResult" <el-form-item label="立案结果" prop="tf_caseFilingResult"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-radio-group v-model="formData.tf_caseFilingResult" size="small"> <el-radio-group v-model="formData.tf_caseFilingResult" size="small" @change="handleCaseFilingResultChange">
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code" <el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code"
border>{{item.label}} border>{{item.label}}
</el-radio> </el-radio>
@@ -119,12 +119,13 @@ layout("/layouts/platform.html"){
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="主办单位" :label="formData.tf_caseFilingResult === 'SUGGESTION' ? '承办单位' : '主办单位'"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:'必填',trigger:['change','blur']}]" :rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:formData.tf_caseFilingResult === 'SUGGESTION' ? '请选择承办单位' : '请选择主办单位',trigger:['change','blur']}]"
prop="tf_masterUnitId" prop="tf_masterUnitIds"
> >
<el-select v-model="formData.tf_masterUnitId" filterable clearable style="width: 100%"> <el-select v-model="formData.tf_masterUnitIds" filterable clearable multiple
style="width: 100%">
<el-option <el-option
v-for="item in underTakeOptions" v-for="item in underTakeOptions"
:label="item.name" :label="item.name"
@@ -135,7 +136,7 @@ layout("/layouts/platform.html"){
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="协办单位" <el-form-item label="协办单位"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)" v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'"
prop="tf_slaveUnitIds" prop="tf_slaveUnitIds"
> >
<el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple <el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple
@@ -145,7 +146,7 @@ layout("/layouts/platform.html"){
:label="item.name" :label="item.name"
:value="item.id" :value="item.id"
:key="item.id" :key="item.id"
:disabled="item.id===formData.tf_masterUnitId" :disabled="formData && formData.tf_masterUnitIds.includes(item.id)"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -198,7 +199,7 @@ layout("/layouts/platform.html"){
approval: false approval: false
}, },
formData: { formData: {
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
}, },
showApprovalForm: false, showApprovalForm: false,
@@ -208,9 +209,55 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* “作为建议”场景下只保留承办单位,协办单位和立案类型都要及时清空,
* 避免旧表单值跟着一起提交到流程里。
*/
handleCaseFilingResultChange(caseFilingResult) {
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_caseFilingType", "")
}
if (caseFilingResult === "SUGGESTION") {
this.$set(this.formData, "tf_slaveUnitIds", [])
return
}
if (caseFilingResult !== "CONFIRM_FILING") {
this.$set(this.formData, "tf_masterUnitIds", [])
this.$set(this.formData, "tf_slaveUnitIds", [])
}
},
doUpData(){ doUpData(){
this.$.axios.post(loc()+"/doUpData") this.$.axios.post(loc()+"/doUpData")
}, },
// 兼容流程变量返回数组、JSON 字符串、逗号分隔字符串三种情况,统一转成下拉框需要的数组。
normalizeUnitIds(unitIds) {
if (!unitIds) {
return []
}
if (Array.isArray(unitIds)) {
return unitIds
}
if (typeof unitIds === "string") {
var value = unitIds.trim()
if (!value) {
return []
}
if (value.indexOf("[") === 0) {
try {
var parsedValue = JSON.parse(value)
return Array.isArray(parsedValue) ? parsedValue : []
} catch (e) {
return []
}
}
return value.split(",").filter(function(item) {
return item && item.trim()
}).map(function(item) {
return item.trim()
})
}
return []
},
openView(row) { openView(row) {
this.$refs.guava.edit(() => { this.$refs.guava.edit(() => {
this.showApprovalForm = false this.showApprovalForm = false
@@ -218,11 +265,16 @@ layout("/layouts/platform.html"){
}) })
}, },
doneTasks(val) { doneTasks(val) {
const data = val[val.length - 1] if (!val || !val.length) {
return
}
// 当前页面需要回显上一环节“提案委员会立案”保存的主办/协办单位或承办单位。
var data = val[val.length - 1]
this.$set(this.formData, "tf_caseFilingResult", data.ext.tf_caseFilingResult) this.$set(this.formData, "tf_caseFilingResult", data.ext.tf_caseFilingResult)
this.$set(this.formData, "tf_caseFilingType", data.ext.tf_caseFilingType) this.$set(this.formData, "tf_caseFilingType", data.ext.tf_caseFilingType)
this.$set(this.formData, "tf_masterUnitId", data.ext.tf_masterUnitId) this.$set(this.formData, "tf_masterUnitIds", this.normalizeUnitIds(data.ext.tf_masterUnitIds))
this.$set(this.formData, "tf_slaveUnitIds", data.ext.tf_slaveUnitIds) this.$set(this.formData, "tf_slaveUnitIds", this.normalizeUnitIds(data.ext.tf_slaveUnitIds))
this.handleCaseFilingResultChange(this.formData.tf_caseFilingResult)
}, },
openAudit(row) { openAudit(row) {
this.$refs.guava.edit(() => { this.$refs.guava.edit(() => {
@@ -231,7 +283,7 @@ layout("/layouts/platform.html"){
proposalId: row.id, proposalId: row.id,
processTaskId: row.taskId, processTaskId: row.taskId,
taskName: row.curTaskName, taskName: row.curTaskName,
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
} }
this.$refs.proposalInfoRef.onOpen(row) this.$refs.proposalInfoRef.onOpen(row)
@@ -246,9 +298,12 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}).then(() => { }).then(() => {
// “作为建议”统一按承办单位处理,提交时不再携带协办单位数据。
const masterUnitIds = this.formData.tf_masterUnitIds || []
const slaveUnitIds = this.formData.tf_caseFilingResult === "CONFIRM_FILING" ? (this.formData.tf_slaveUnitIds || []) : []
// 是否有协办单位 // 是否有协办单位
let tf_helpunitreply; let tf_helpunitreply;
if (this.formData.tf_slaveUnitIds && this.formData.tf_slaveUnitIds.length > 0) { if (slaveUnitIds.length > 0) {
tf_helpunitreply = 'HAS_HELP_UNIT' tf_helpunitreply = 'HAS_HELP_UNIT'
} else { } else {
tf_helpunitreply = 'NO_HELP_UNIT' tf_helpunitreply = 'NO_HELP_UNIT'
@@ -258,10 +313,16 @@ layout("/layouts/platform.html"){
this.$axios.post("/platform/proposal/committeeFilingUnit/executeTask", { this.$axios.post("/platform/proposal/committeeFilingUnit/executeTask", {
data: JSON.stringify({ data: JSON.stringify({
...this.formData, ...this.formData,
tf_masterUnitIds: masterUnitIds,
tf_slaveUnitIds: slaveUnitIds,
submitType: val, submitType: val,
tf_masterUnitName: this.formData.tf_masterUnitId ? this.underTakeOptions.find(v => v.id === this.formData.tf_masterUnitId)?.name : null, // 兼容旧流程仍按单个主办单位字段取值的场景,这里保留第一个主办单位ID和名称。
tf_slaveUnitNames: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name), tf_masterUnitId: masterUnitIds.length ? masterUnitIds[0] : "",
tf_slaveUnitNameStr: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','), tf_masterUnitName: masterUnitIds.length ? this.underTakeOptions.find(v => v.id === masterUnitIds[0])?.name : "",
tf_masterUnitNames: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_masterUnitNameStr: masterUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','),
tf_slaveUnitNames: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_slaveUnitNameStr: slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','),
tf_helpunitreply: tf_helpunitreply tf_helpunitreply: tf_helpunitreply
}) })
}).then((res) => { }).then((res) => {
@@ -285,12 +346,12 @@ layout("/layouts/platform.html"){
return return
} }
this.$refs.guava.public(() => { this.$refs.guava.public(() => {
this.$refs.mergeRef.onOpen(selection, { this.$refs.mergeRef.onOpen(selection, {
processTaskIds: selection.map(v => v.taskId), processTaskIds: selection.map(v => v.taskId),
taskName: selection[0].curTaskName, taskName: selection[0].curTaskName,
tf_masterUnitId: null, tf_masterUnitIds: [],
tf_slaveUnitIds: [] tf_slaveUnitIds: []
}) })
}) })
}, },
@@ -72,9 +72,9 @@ layout("/layouts/platform.html"){
<el-tag v-if="row.merge" size="small"></el-tag> <el-tag v-if="row.merge" size="small"></el-tag>
<el-tag v-else type="warning" size="small"></el-tag> <el-tag v-else type="warning" size="small"></el-tag>
</template> </template>
<template v-else-if="column.prop === 'underTakeIsMaster'" scope="{row}"> <template v-else-if="column.prop === 'undertakeUnits'" scope="{row}">
<el-tag v-if="row.underTakeIsMaster" size="small">主办</el-tag> <div v-if="getUndertakeDisplay(row)" class="text-left">{{ getUndertakeDisplay(row) }}</div>
<el-tag v-else type="warning" size="small">协办</el-tag> <span v-else>暂无</span>
</template> </template>
<template v-else-if="column.prop === 'instanceState'" scope="{row}"> <template v-else-if="column.prop === 'instanceState'" scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" <enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
@@ -134,6 +134,7 @@ layout("/layouts/platform.html"){
{label: "提案编号", prop: "code"}, {label: "提案编号", prop: "code"},
{label: "提案名称", prop: "name", width: "200px"}, {label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"}, {label: "提案类别", prop: "typeName"},
{label: "承办单位", prop: "undertakeUnits", width: "260px"},
{label: "是否并案", prop: "merge"}, {label: "是否并案", prop: "merge"},
{label: "当前节点", prop: "curTaskName"}, {label: "当前节点", prop: "curTaskName"},
{label: "审核人", prop: "auditUser"}, {label: "审核人", prop: "auditUser"},
@@ -149,6 +150,34 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
/**
* 校领导审批列表需要按立案结果切换承办单位展示口径:
* 确定立案显示主办/协办,作为建议只显示承办单位。
*/
getUndertakeDisplay(row) {
var masterUnitNames = this.formatUnitNames(row.masterUnitName)
var slaveUnitNames = this.formatUnitNames(row.slaveUnitNames)
if (row.caseFilingResult === "SUGGESTION") {
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return item
}).join("")
}
return ""
},
formatUnitNames(unitNames) {
if (!unitNames) {
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return item
}).join("")
}
return unitNames
},
openView(row) { openView(row) {
this.$refs.guava.edit(() => { this.$refs.guava.edit(() => {
this.showApprovalForm = false this.showApprovalForm = false
@@ -137,11 +137,15 @@ layout("/layouts/platform.html"){
{{row.transferUserName}} {{row.transferUserName}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" fixed="right" min-width="250px"> <el-table-column label="操作" fixed="right" min-width="320px">
<template slot-scope="{row}"> <template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button> <el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">答复 <el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">答复
</el-button> </el-button>
<!--历史答复内容只允许超级管理员修改,且只在已审核数据中开放入口。-->
<el-button v-if="pageForm.approval && row.taskState === 20 && $auth.hasRole('SYSADMIN')"
@click="openReplyEdit(row)" size="mini" type="warning">编辑答复
</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回 <el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button> </el-button>
<!--承办单位领导、超级管理员才能转办--> <!--承办单位领导、超级管理员才能转办-->
@@ -197,7 +201,7 @@ layout("/layouts/platform.html"){
</el-form> </el-form>
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button> <el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button> <!-- <el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button>-->
<el-button @click="handleTaskAction(1)" size="small" type="primary">提交</el-button> <el-button @click="handleTaskAction(1)" size="small" type="primary">提交</el-button>
</el-row> </el-row>
</div> </div>
@@ -247,6 +251,25 @@ layout("/layouts/platform.html"){
</el-row> </el-row>
</el-dialog> </el-dialog>
<el-dialog title="编辑答复内容" :visible.sync="showReplyEditDialog" width="900px" append-to-body>
<el-form ref="replyEditFormRef" :model="replyEditForm" label-width="80px">
<el-form-item label="提案编号">
<el-input :value="replyEditForm.code" disabled></el-input>
</el-form-item>
<el-form-item label="提案名称">
<el-input :value="replyEditForm.name" disabled></el-input>
</el-form-item>
<el-form-item label="答复内容" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<text-editor v-model="replyEditForm.tf_opinion"></text-editor>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="showReplyEditDialog = false" size="small">取消</el-button>
<el-button @click="handleReplyEdit" size="small" type="primary">保存</el-button>
</el-row>
</el-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
@@ -288,6 +311,10 @@ layout("/layouts/platform.html"){
showTransferDialog: false, showTransferDialog: false,
transferForm: {}, transferForm: {},
transferUserOptions: [], transferUserOptions: [],
// 超级管理员编辑历史答复内容
showReplyEditDialog: false,
replyEditForm: {},
} }
}, },
methods: { methods: {
@@ -299,7 +326,7 @@ layout("/layouts/platform.html"){
}, },
async openAudit(row) { async openAudit(row) {
this.loadCandidates(row.taskId) this.loadCandidates(row.taskId)
try{ try{
await this.$confirm("是否已与提案代表进行充分沟通?", "提示", { await this.$confirm("是否已与提案代表进行充分沟通?", "提示", {
confirmButtonText: "是", confirmButtonText: "是",
@@ -325,6 +352,55 @@ layout("/layouts/platform.html"){
}) })
}, },
// 超级管理员编辑已答复任务的答复内容,仅回显和提交 tf_opinion 字段。
openReplyEdit(row) {
const loading = createLoading('加载中')
this.$axios.post("/platform/proposal/unitReply/getReplyOpinion", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.replyEditForm = {
taskId: row.taskId,
code: row.code,
name: row.name,
tf_opinion: res.data ? res.data.tf_opinion : ""
}
this.showReplyEditDialog = true
this.$nextTick(() => {
if (this.$refs.replyEditFormRef) {
this.$refs.replyEditFormRef.clearValidate()
}
})
}
}).finally(() => {
loading.close()
})
},
// 历史答复编辑只保存答复内容,不修改落实情况、附件等其他字段。
handleReplyEdit() {
this.$refs.replyEditFormRef.validate((valid) => {
if (!valid) return
this.$confirm("您确定要保存答复内容吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
const loading = createLoading('保存中')
this.$axios.post("/platform/proposal/unitReply/updateReplyOpinion", {
taskId: this.replyEditForm.taskId,
opinion: this.replyEditForm.tf_opinion
}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.showReplyEditDialog = false
this.doSearch()
}
}).finally(() => {
loading.close()
})
})
})
},
handleTaskAction(val) { handleTaskAction(val) {
if(val === 6) { if(val === 6) {
this.handleTaskConfirm(val) this.handleTaskConfirm(val)
@@ -335,7 +411,7 @@ layout("/layouts/platform.html"){
}) })
} }
}, },
handleTaskConfirm(val) { handleTaskConfirm(val) {
this.$confirm("您确定要提交吗?", "提示", { this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
@@ -343,10 +419,10 @@ layout("/layouts/platform.html"){
type: "warning" type: "warning"
}).then(() => { }).then(() => {
const loading = createLoading('提交中') const loading = createLoading('提交中')
delete this.formData.underTakeIsMaster delete this.formData.underTakeIsMaster
delete this.formData.underTakeName delete this.formData.underTakeName
this.$axios.post("/platform/proposal/unitReply/executeTask", { this.$axios.post("/platform/proposal/unitReply/executeTask", {
data: JSON.stringify({ data: JSON.stringify({
...this.formData, ...this.formData,
@@ -20,9 +20,9 @@ const PROPOSAL_INFO = {
<van-cell title="提案类别"> <van-cell title="提案类别">
{{viewData.typeName}} {{viewData.typeName}}
</van-cell> </van-cell>
<van-cell title="建议承办单位"> <!--<van-cell title="建议承办单位">
{{viewData.suggestUnits}} {{viewData.suggestUnits}}
</van-cell> </van-cell>-->
<van-cell title="立案结果"> <van-cell title="立案结果">
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT" <dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
:value="viewData.caseFilingResult"></dict-tag> :value="viewData.caseFilingResult"></dict-tag>
@@ -123,7 +123,7 @@ const PROPOSAL_INFO = {
<!--提案委员会立案--> <!--提案委员会立案-->
<van-cell-group :title="task.displayName" <van-cell-group :title="task.displayName"
v-else-if="task.taskName === 'committee'"> v-else-if="task.taskName === 'committee'||task.taskName === 'committeeFilingUnit'">
<van-cell title="办理用户">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}}) <van-cell title="办理用户">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}})
</van-cell> </van-cell>
<van-cell title="办理时间">{{ task.finishTime }}</van-cell> <van-cell title="办理时间">{{ task.finishTime }}</van-cell>
@@ -140,12 +140,20 @@ const PROPOSAL_INFO = {
</template> </template>
</div> </div>
</van-cell> </van-cell>
<van-cell title="主办单位">{{ task.ext.tf_masterUnitName }}</van-cell> <template v-if="['CONFIRM_FILING','SUGGESTION'].includes(task.ext.tf_caseFilingResult)">
<van-cell title="办单位">{{ task?.ext?.tf_slaveUnitNameStr }}</van-cell> <van-cell v-if="task.ext.tf_caseFilingResult === 'SUGGESTION'" title="办单位">{{ task?.ext?.tf_masterUnitNameStr || task?.ext?.tf_slaveUnitNameStr }}</van-cell>
<template v-else>
<van-cell title="主办单位">{{ task?.ext?.tf_masterUnitNameStr }}</van-cell>
<van-cell title="协办单位">{{ task?.ext?.tf_slaveUnitNameStr }}</van-cell>
</template>
</template>
<van-cell title="办理意见" v-if="task.taskFormData.tf_opinion" class="direction-column-cell">
<div v-html="task.taskFormData.tf_opinion"></div>
</van-cell>
</van-cell-group> </van-cell-group>
<van-cell-group :title="task.displayName" v-else> <van-cell-group :title="task.displayName" v-else>
<van-cell title="办理单位" v-if="['master_reply','slave_reply'].includes(task.taskName)"> <van-cell title="办理单位" v-if="['opinion_unit_reply','unit_reply','two_unit_reply','master_reply','slave_reply'].includes(task.taskName)">
{{task.ext.underTakeName}} {{task.ext.underTakeName}}
</van-cell> </van-cell>
@@ -151,7 +151,7 @@ layout("/layouts/platform_h5.html"){
delete formData.taskName delete formData.taskName
this.$axios.post("/flow/common/executeTask", { this.$axios.post("/platform/proposal/schoolLeaderApproval/executeTask", {
data: JSON.stringify(formData) data: JSON.stringify(formData)
}).then((res) => { }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
@@ -177,7 +177,7 @@ const todo = {
onView(task) { onView(task) {
const {taskId, taskKey, instanceId, businessNo, formKey, h5FormKey} = task; const {taskId, taskKey, instanceId, businessNo, formKey, h5FormKey} = task;
if (!h5FormKey) { if (!h5FormKey) {
this.$toast("当前流程没有配置地址"); this.$toast("当前流程暂不支持手机端审核,请前往PC端页面审核!");
return; return;
} }
this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey+ "&tab=" + this.activeTab) this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey+ "&tab=" + this.activeTab)