This commit is contained in:
=
2026-04-24 15:29:18 +08:00
66 changed files with 2595 additions and 419 deletions
@@ -285,21 +285,28 @@ public class SysUnitController {
@SaCheckLogin
public Object tree(@Param("pid") String pid, HttpServletRequest req) {
try {
String rootId = StrUtil.blankToDefault(pid, "0");
String virtualRootId = "root";
Cnd cnd = Cnd.NEW();
cnd.andEX("id", "=", pid);
cnd.asc("unitcode");
List<Sys_unit> list = sysUnitService.query(cnd);
List<TreeNode<String>> nodeList = CollUtil.newArrayList();
for (int i = 0; i < list.size(); i++) {
nodeList.add(new TreeNode<>(list.get(i).getId(), list.get(i).getParentId(), list.get(i).getName(), i)
Sys_unit unit = list.get(i);
/*
* 单位根节点存在 id 与 parentId 都为 0 的自引用数据。
* 构建树时把当前查询根挂到虚拟根下,避免“中国地质大学”和 parentId=0 的学院被构造成同级。
*/
String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId();
nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i)
.setExtra(
Map.of(
"unitTypeCode", list.get(i).getUnitTypeCode()
"unitTypeCode", unit.getUnitTypeCode()
)
));
}
List<Tree<String>> treeList = TreeUtil.build(nodeList, StrUtil.blankToDefault(pid, "0"));
List<Tree<String>> treeList = TreeUtil.build(nodeList, virtualRootId);
return Result.success(treeList);
} catch (Exception e) {
return Result.error();
@@ -54,29 +54,41 @@ public class ProposalQueryAnalysisController {
@At
@ApiOperation("统计数据")
@SaCheckPermission("proposal.query.analysis")
public Result statisticsData(String sessionId) {
public Result statisticsData(String sessionId, String dimension) {
/*
* 查询参数说明:
* sessionId:教代会届次ID,用于限定统计提案范围;
* dimension:统计维度,可传“提案人单位、提案类型、代表团、立案结果、满意度”,为空时返回全部维度。
* 返回值:Result.data 为统计行列表,包含 dimension、itemName、count、rate。
*/
if (StrUtil.isBlank(sessionId)) {
return Result.error("请先选择教代会届次");
}
return Result.success(buildStatisticsRows(sessionId));
return Result.success(buildStatisticsRows(sessionId, dimension));
}
@At
@ApiOperation("分析数据")
@SaCheckPermission("proposal.query.analysis")
public Result analysisData(String sessionId) {
public Result analysisData(String sessionId, String dimension) {
/*
* 查询参数说明:
* sessionId:教代会届次ID,用于限定分析提案范围;
* dimension:统计维度,可传“提案人单位、提案类型、代表团、立案结果、满意度”,为空时返回全部维度。
* 返回值:Result.data 为分析行列表,包含 dimension、total、categoryCount、topItem、topCount、topRate、analysis。
*/
if (StrUtil.isBlank(sessionId)) {
return Result.error("请先选择教代会届次");
}
return Result.success(buildAnalysisRows(sessionId));
return Result.success(buildAnalysisRows(sessionId, dimension));
}
@At
@Ok("void")
@ApiOperation("导出统计数据")
@SaCheckPermission("proposal.query.analysis")
public void exportStatisticsData(String sessionId, HttpServletResponse response) {
List<NutMap> list = buildStatisticsRows(sessionId);
public void exportStatisticsData(String sessionId, String dimension, HttpServletResponse response) {
List<NutMap> list = buildStatisticsRows(sessionId, dimension);
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("统计维度", "dimension", 20));
entities.add(new ExcelExportEntity("分类项", "itemName", 30));
@@ -92,8 +104,8 @@ public class ProposalQueryAnalysisController {
@Ok("void")
@ApiOperation("导出分析数据")
@SaCheckPermission("proposal.query.analysis")
public void exportAnalysisData(String sessionId, HttpServletResponse response) {
List<NutMap> list = buildAnalysisRows(sessionId);
public void exportAnalysisData(String sessionId, String dimension, HttpServletResponse response) {
List<NutMap> list = buildAnalysisRows(sessionId, dimension);
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("统计维度", "dimension", 20));
entities.add(new ExcelExportEntity("总量", "total", 12));
@@ -108,15 +120,15 @@ public class ProposalQueryAnalysisController {
CommonDownloadUtil.download("提案分析数据.xlsx", workbook, response);
}
private List<NutMap> buildStatisticsRows(String sessionId) {
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId);
private List<NutMap> buildStatisticsRows(String sessionId, String dimension) {
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId, dimension);
List<NutMap> result = new ArrayList<>();
dataMap.forEach((dimension, items) -> {
dataMap.forEach((dimensionName, items) -> {
long total = items.stream().mapToLong(v -> v.getLong("count", 0L)).sum();
for (NutMap item : items) {
long count = item.getLong("count", 0L);
result.add(NutMap.NEW()
.addv("dimension", dimension)
.addv("dimension", dimensionName)
.addv("itemName", item.getString("itemName", "未维护"))
.addv("count", count)
.addv("rate", formatPercent(count, total)));
@@ -125,13 +137,13 @@ public class ProposalQueryAnalysisController {
return result;
}
private List<NutMap> buildAnalysisRows(String sessionId) {
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId);
private List<NutMap> buildAnalysisRows(String sessionId, String dimension) {
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId, dimension);
List<NutMap> result = new ArrayList<>();
dataMap.forEach((dimension, items) -> {
dataMap.forEach((dimensionName, items) -> {
if (items.isEmpty()) {
result.add(NutMap.NEW()
.addv("dimension", dimension)
.addv("dimension", dimensionName)
.addv("total", 0)
.addv("categoryCount", 0)
.addv("topItem", "-")
@@ -150,10 +162,10 @@ public class ProposalQueryAnalysisController {
long topCount = top.getLong("count", 0L);
String topItem = top.getString("itemName", "未维护");
String topRate = formatPercent(topCount, total);
String analysis = String.format("%s主要集中在【%s】,数量为%d,占比%s。", dimension, topItem, topCount, topRate);
String analysis = String.format("%s主要集中在【%s】,数量为%d,占比%s。", dimensionName, topItem, topCount, topRate);
result.add(NutMap.NEW()
.addv("dimension", dimension)
.addv("dimension", dimensionName)
.addv("total", total)
.addv("categoryCount", sorted.size())
.addv("topItem", topItem)
@@ -164,13 +176,23 @@ public class ProposalQueryAnalysisController {
return result;
}
private Map<String, List<NutMap>> loadDimensionData(String sessionId) {
private Map<String, List<NutMap>> loadDimensionData(String sessionId, String dimension) {
Map<String, List<NutMap>> map = new LinkedHashMap<>();
map.put("提案人单位", queryUnitStat(sessionId));
map.put("提案类型", queryTypeStat(sessionId));
map.put("代表团", queryDelegationStat(sessionId));
map.put("立案结果", queryCaseFilingResultStat(sessionId));
map.put("满意度", querySatisfactionStat(sessionId));
if (StrUtil.isBlank(dimension) || "提案人单位".equals(dimension)) {
map.put("提案人单位", queryUnitStat(sessionId));
}
if (StrUtil.isBlank(dimension) || "提案类型".equals(dimension)) {
map.put("提案类型", queryTypeStat(sessionId));
}
if (StrUtil.isBlank(dimension) || "代表团".equals(dimension)) {
map.put("代表团", queryDelegationStat(sessionId));
}
if (StrUtil.isBlank(dimension) || "立案结果".equals(dimension)) {
map.put("立案结果", queryCaseFilingResultStat(sessionId));
}
if (StrUtil.isBlank(dimension) || "满意度".equals(dimension)) {
map.put("满意度", querySatisfactionStat(sessionId));
}
return map;
}
@@ -73,9 +73,10 @@ public class ProposalQueryComprehensiveController {
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id
LEFT JOIN wf_process_task replyTask on replyTask.processInstanceId = ins.id AND replyTask.taskState = 20 AND replyTask.taskName in ('master_reply','slave_reply','opinion_master_reply')
LEFT JOIN wf_process_task replyTask on replyTask.processInstanceId = ins.id
$condition
""");
// AND replyTask.taskState = 20 AND replyTask.taskName in ('master_reply','slave_reply','opinion_master_reply')
Cnd cnd = Cnd.NEW();
if (ArrayUtil.isNotEmpty(pageForm.getOrigins()) && StrUtil.isNotBlank(pageForm.getCommonKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
@@ -6,6 +6,7 @@ import com.budwk.app.flow.engine.util.FlowUtil;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
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.service.common.ProposalCommonService;
import lombok.extern.slf4j.Slf4j;
@@ -44,16 +45,25 @@ public class ProposalQueryUnitReplyController {
@At
@SaCheckPermission("proposal.query.unitReply")
public Result data(String sessionId, String undertakeUnitId) {
public Result data(String sessionId, String undertakeUnitId, String caseFilingResult) {
/*
* 查询参数说明:
* sessionId:教代会届次ID,用于限定统计的提案范围;
* undertakeUnitId:承办单位ID,用于只统计某个承办单位,为空时统计全部承办单位;
* caseFilingResult:立案结果字典值,对应 proposal_info.caseFilingResult,为空时不限制立案结果。
* 返回值:Result.data 中包含 tableData 统计行列表,以及 slaveNeedReply 协办单位是否需要答复配置。
*/
// 提案配置 协办是否需要答复
ProposalConfig proposalConfig = dao.fetch(ProposalConfig.class, Cnd.NEW());
boolean slaveNeedReply = proposalConfig.getSlaveUnitNeedReply();
// 当前届次所有的提案ID
Sql sql = Sqls.create("select id from proposal_info where sessionId = @sessionId").setParam("sessionId", sessionId);
sql.setCallback(Sqls.callback.strList());
dao.execute(sql);
List<String> proposalIds = sql.getList(String.class);
Cnd proposalCnd = Cnd.where(ProposalInfo::getSessionId, "=", sessionId);
proposalCnd.andEX(ProposalInfo::getCaseFilingResult, "=", caseFilingResult);
List<String> proposalIds = dao.query(ProposalInfo.class, proposalCnd)
.stream()
.map(ProposalInfo::getId)
.collect(Collectors.toList());
if(proposalIds.isEmpty()){
return Result.success().addData(Map.of("tableData", Collections.EMPTY_LIST, "slaveNeedReply", slaveNeedReply));
@@ -73,7 +83,7 @@ public class ProposalQueryUnitReplyController {
List<ProposalReplyUnit> replyUnits = dao.query(ProposalReplyUnit.class, cnd);
// 承办单位答复记录
List<ProcessTask> replyTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "in", instIds).and(ProcessTask::getTaskName, "in", List.of("master_reply", "slave_reply", "opinion_master_reply")).and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.DOING.getCode())));
List<ProcessTask> replyTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "in", instIds).and(ProcessTask::getTaskName, "in", List.of("two_unit_reply","unit_reply","master_reply", "slave_reply", "opinion_master_reply")).and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.DOING.getCode())));
// 承办单位
List<NutMap> tableData = new ArrayList<>();
@@ -45,7 +45,7 @@ public class ProposalCaseCheckController {
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/caseCheck/index.html")
@SaCheckPermission("proposal.mine")
@SaCheckPermission("proposal.caseCheck")
public void index() {
}
@@ -83,6 +83,7 @@ public class ProposalCaseCheckController {
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN vw_user vu ON vu.id = info.createUserId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
@@ -99,6 +100,8 @@ public class ProposalCaseCheckController {
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
// 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
@@ -83,6 +83,7 @@ public class ProposalCommitteeFilingController {
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN vw_user vu ON vu.id = info.createUserId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
@@ -97,6 +98,8 @@ public class ProposalCommitteeFilingController {
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
// 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
@@ -99,6 +99,7 @@ public class ProposalCommitteeFilingUnitController {
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN vw_user vu ON vu.id = info.createUserId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
@@ -113,6 +114,8 @@ public class ProposalCommitteeFilingUnitController {
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
// 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
@@ -42,7 +42,7 @@ public class ProposalPreAuditController {
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/preAudit/index.html")
@SaCheckPermission("proposal.mine")
@SaCheckPermission("proposal.preAudit")
public void index() {
}
@@ -81,6 +81,7 @@ public class ProposalPreAuditController {
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN vw_user vu ON vu.id = info.createUserId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
@@ -97,6 +98,8 @@ public class ProposalPreAuditController {
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
// 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
@@ -114,6 +114,7 @@ public class ProposalSchoolLeaderApprovalController {
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id
LEFT JOIN vw_user vu ON vu.id = info.createUserId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
@@ -130,6 +131,8 @@ public class ProposalSchoolLeaderApprovalController {
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
// 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
@@ -2,24 +2,25 @@ package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.aspose.slides.internal.og.and;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.entity.ProcessTaskActor;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -28,11 +29,12 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@@ -55,11 +57,9 @@ public class ProposalSecondedController {
@Inject
private BaseService baseService;
@Inject
private ProposalCommonService proposalCommonService;
@Inject
private BpmService bpmService;
@Inject
private ProposalSecondedService proposalSecondedService;
@Inject
private FlowCommonService flowCommonService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/seconded/index.html")
@@ -96,6 +96,7 @@ public class ProposalSecondedController {
t.finishTime,
t.taskParentId,
t.variable taskVariable,
nt.taskName curTaskCode,
if(t.taskName = 'second', ta.actorId, ps.seconderId) taskActorUserId,
if(t.taskName = 'second', ta.actorName, ps.userName) taskActorName,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
@@ -108,36 +109,40 @@ public class ProposalSecondedController {
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_second ps ON ps.proposalId = info.id
LEFT JOIN proposal_second ps ON ps.proposalId = info.id AND ps.seconderId = @seconderId
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
""");
sql.setParam("seconderId", SecurityUtil.getUserId());
sql.setParam("seconderId", SecurityUtil.getUserId());
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "in", List.of("invite", "second", "delegation", "committee"));
cnd.and("ps.isAgree", "is", null);
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if (approval) {
// 已附议页签只展示当前登录附议人已经处理过的记录。
cnd.and("ps.isAgree", "is not", null);
cnd.and("t.taskName", "in", List.of("invite", "second", "delegation", "preAudit", "committee"));
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
// 未附议页签只展示当前登录附议人尚未处理的记录。
cnd.and("ps.isAgree", "is", null);
// 待附议列表只展示两类任务:
// 1. invite / second 节点任务
// 2. delegation / committee 节点,且任务状态必须是已废弃(99)
SqlExpressionGroup taskGroup = Cnd.exps("t.taskName", "in", List.of("invite", "second"));
SqlExpressionGroup delegationOrCommitteeGroup = Cnd.exps("t.taskName", "in", List.of("delegation", "preAudit", "committee"));
delegationOrCommitteeGroup.and("t.taskState", "=", ProcessTaskStateEnum.ABANDON.getCode());
taskGroup.or(delegationOrCommitteeGroup);
cnd.and(taskGroup);
}
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> list = pagination.getList(NutMap.class);
for (NutMap row : list) {
// String instanceId = row.getString("instanceId");
}
return Result.success(pagination);
}
@@ -146,37 +151,146 @@ public class ProposalSecondedController {
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("修改提案附议信息")
@SLog(tag = "提案管理系统-附议提案", msg = "修改提案附议信息")
/**
* 仅更新附议结果和附议任务状态,不调用流程引擎推进流程。
* 参数说明:
* 1. proposalId:提案主键,用于定位 proposal_second 附议记录。
* 2. taskId:当前页面点击的任务主键,用于定位本次需要完成的 wf_process_task。
* 3. taskActorUserId:附议人用户ID,用于校验附议记录和任务参与人是否匹配。
* 4. opinion:附议意见,写入 proposal_second.opinion。
* 5. submitType:附议结果,1 表示同意,20 表示不同意。
* 返回值:
* Result.success() 表示 proposal_second 和 wf_process_task 更新成功;
* Result.error(...) 表示参数错误或未找到对应业务数据。
*/
public Result updateInfo(@Param("proposalId") String proposalId,
@Param("taskId") Long taskId,
@Param("taskActorUserId") String taskActorUserId,
@Param("opinion") String opinion,
@Param("submitType") Integer submitType) {
// 查询附议信息表
if (Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || taskId == null || submitType == null) {
return Result.error("参数错误");
}
// 先定位 wf_process_task,确保本次请求能准确命中需要完成的附议任务,再更新两张业务表。
ProcessTask processTask = this.getSecondTask(taskId, taskActorUserId);
if (processTask == null) {
return Result.error("未找到附议任务");
}
// 更新 proposal_second,记录当前附议人的最终附议结果、附议意见和附议时间。
ProposalSecond proposalSecond = dao.fetch(
ProposalSecond.class,
Cnd.where(ProposalSecond::getProposalId, "=", proposalId)
.and(ProposalSecond::getSeconderId, "=", taskActorUserId)
);
if (proposalSecond == null) {
return Result.error("未找到附议记录");
}
proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode()));
proposalSecond.setOpinion(opinion);
proposalSecond.setIsAgree(submitType == 1);
proposalSecond.setSecondedTime(DateUtil.date());
dao.update(proposalSecond);
// 流程实例
ProcessInstance processInstance = dao.fetch(ProcessInstance.class,Cnd.where(ProcessInstance::getBusinessNo, "=", proposalId));
if(processInstance == null) return Result.success();
ProcessTask processTask = dao.fetch(ProcessTask.class,Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId())
.and(ProcessTask::getTaskName, "=", "second")
.and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.ABANDON.getCode())
.and(new Static("id in (select processTaskId from wf_process_task_actor where actorId='" + taskActorUserId + "')"))
.limit(1,1));
if(processTask == null) return Result.success();
// 仅更新本次附议对应的 wf_process_task,不推动实例、也不创建后续任务。
View_user user = dao.fetch(View_user.class, Cnd.where(View_user::getId, "=", taskActorUserId));
Dict taskArgs = Json.fromJson(Dict.class, processTask.getVariable());
taskArgs.set("u_userId", user.getId());
taskArgs.set("initiator", user.getId());
taskArgs.set("initiatorName", user.getUsername());
taskArgs.set("initiatorAccount", user.getLoginname());
taskArgs.set("initiatorUnitId", user.getUnitId());
taskArgs.set("initiatorUnitName", user.getUnitName());
taskArgs.set("initiatorUnionId", user.getUnionId());
taskArgs.set("initiatorUnionName", user.getUnionName());
taskArgs.set("tf_loginName", user.getLoginname());
taskArgs.set("tf_unionId", user.getUnionId());
taskArgs.set("tf_unitId", user.getUnitId());
taskArgs.set("tf_unitName", user.getUnitName());
taskArgs.set("tf_userId", user.getId());
taskArgs.set("tf_userName", user.getUsername());
processTask.setVariable(JSONUtil.toJsonStr(taskArgs));
processTask.setTaskState(ProcessTaskStateEnum.FINISHED.getCode());
processTask.setFinishTime(DateUtil.date());
processTask.setOperator(taskActorUserId);
dao.update(processTask);
return Result.success();
}
/**
* second 节点审核专用接口。
* 参数说明:
* 1. data:前端提交的 JSON 字符串,必须包含 processTaskId、proposalId、taskActorUserId、tf_opinion、submitType。
* 2. processTaskIdwf_process_task 主键,公共流程执行服务依赖该参数定位当前审核任务。
* 3. proposalId:提案主键,附议业务表 proposal_second 通过该字段定位当前提案记录。
* 4. taskActorUserId:附议人用户ID,用于定位当前登录人的附议记录。
* 5. tf_opinion:审核意见,会同步写入流程表单变量和附议业务表 opinion 字段。
* 6. submitType:审核结果,1 表示同意,20 表示不同意。
* 返回值:
* Result.success() 表示公共流程任务执行成功且 proposal_second 已同步更新;
* Result.error(...) 表示请求参数不完整。
*/
@At
@SaCheckPermission("proposal.seconded")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("执行附议审核任务")
@SLog(tag = "提案管理系统-附议提案", msg = "执行附议审核任务")
public Result executeTask(@Param("data") String data) {
if (Strings.isBlank(data)) {
return Result.error("参数错误");
}
Dict args = Json.fromJson(Dict.class, data);
String proposalId = args.getStr("proposalId");
String taskActorUserId = args.getStr("taskActorUserId");
String opinion = args.getStr("tf_opinion");
Integer submitType = args.getInt("submitType");
if (args.getLong("processTaskId") == null || Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || submitType == null) {
return Result.error("参数错误");
}
flowCommonService.executeTask(args);
proposalSecondedService.updateSecondRecord(proposalId, taskActorUserId, opinion, submitType);
return Result.success();
}
/**
* 根据当前点击的任务和附议人,定位实际需要完成的 second 节点任务。
* 处理规则:
* 1. 如果页面点击的就是 second 节点任务,则直接更新该任务。
* 2. 如果页面点击的是 invite、delegation、committee 等关联节点,则回查同实例下当前附议人的 second 任务。
*/
private ProcessTask getSecondTask(Long taskId, String taskActorUserId) {
ProcessTask currentTask = dao.fetch(ProcessTask.class, taskId);
if (currentTask == null) {
return null;
}
if ("second".equals(currentTask.getTaskName())
&& List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.ABANDON.getCode()).contains(currentTask.getTaskState())
&& this.isTaskActorMatched(currentTask.getId(), taskActorUserId)) {
return currentTask;
}
List<ProcessTask> secondTaskList = dao.query(ProcessTask.class,
Cnd.where(ProcessTask::getProcessInstanceId, "=", currentTask.getProcessInstanceId())
.and(ProcessTask::getTaskName, "=", "second")
.and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.ABANDON.getCode()))
.desc(ProcessTask::getCreatedAt));
for (ProcessTask secondTask : secondTaskList) {
if (this.isTaskActorMatched(secondTask.getId(), taskActorUserId)) {
return secondTask;
}
}
return null;
}
/**
* 校验任务参与人是否为当前附议人,避免误更新其他代表的 second 任务。
*/
private boolean isTaskActorMatched(Long processTaskId, String taskActorUserId) {
return dao.count(ProcessTaskActor.class,
Cnd.where(ProcessTaskActor::getProcessTaskId, "=", processTaskId)
.and(ProcessTaskActor::getActorId, "=", taskActorUserId)) > 0;
}
}
@@ -83,6 +83,7 @@ public class ProposalWriteController {
@ApiOperation("保存提案")
@SLog(tag = "提案管理系统-我的提案", msg = "保存提案")
public Result save(@Param("info") ProposalInfo proposalInfo) {
proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId());
if (StrUtil.isBlank(proposalInfo.getCode())) {
proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId()));
}
@@ -98,6 +99,7 @@ public class ProposalWriteController {
@ApiOperation("提交提案")
@SLog(tag = "提案管理系统-我的提案", msg = "提交提案")
public Result submit(@Param("info") ProposalInfo proposalInfo) {
proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId());
if (StrUtil.isBlank(proposalInfo.getCode())) {
proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId()));
}
@@ -1,19 +1,9 @@
package com.budwk.app.zhgh.democratic.proposal.listenter;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessEventListener;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import com.budwk.app.flow.service.ProcessInstanceService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalMerge;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
/**
* @version 1.0
@@ -25,27 +15,11 @@ import org.nutz.lang.Lang;
@IocBean
public class ProposalCommitteeFilingRevokeEventListener implements ProcessEventListener {
@Inject
private Dao dao;
@Inject
private ProcessInstanceService processInstanceService;
@Override
public void onEvent(ProcessEvent event) {
if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_REVOKE) {
Long taskId = event.getSourceId();
ProcessTask task = dao.fetch(ProcessTask.class, taskId);
ProcessInstance processInstance = processInstanceService.getById(task.getProcessInstanceId());
// 处理并案信息撤回
if ("committee".equals(task.getTaskName())) {
// 如果有并案,删掉并案数据(傻逼提的需求,nmsl)
ProposalMerge proposalMerge = dao.fetch(ProposalMerge.class, Cnd.where("proposalId", "=", processInstance.getBusinessNo()));
if (Lang.isNotEmpty(proposalMerge)) {
dao.clear(ProposalMerge.class, Cnd.where(ProposalMerge::getProposalId, "=", proposalMerge.getProposalId()));
}
}
// proposal_merge 的删除逻辑已统一收口到 ProposalCommitteeFilingServiceImpl.revokeTask
// 这里不再重复处理业务表,避免事件监听和 service 两处口径不一致。
}
}
}
@@ -77,7 +77,7 @@ public class ProposalQueryComprehensiveParam extends PageForm {
if (Lang.isNotEmpty(searchParam.getSessionIds())) {
cnd.and("info.sessionId", "in", searchParam.getSessionIds());
} else {
cnd.andEX("info.sessionId", "=", searchParam.getSessionId());
// cnd.andEX("info.sessionId", "=", searchParam.getSessionId());
}
//提案名称
@@ -19,6 +19,7 @@ public class ProposalSearchParam extends PageForm {
private String code;
private String sessionId;
private String delegationId;
private String unionId;
private String createUserName;
private String createUserLoginName;
private String createUserKeyword;
@@ -5,5 +5,13 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond;
public interface ProposalSecondedService extends BaseService<ProposalSecond> {
/**
* 仅更新附议表 proposal_second 中当前附议人的附议结果。
* 参数说明:
* 1. proposalId:提案ID,用于定位当前提案的附议记录。
* 2. taskActorUserId:附议人用户ID,用于定位当前登录人的附议记录。
* 3. opinion:附议意见,写入附议记录的 opinion 字段。
* 4. submitType:附议结果,1 表示同意,20 表示不同意。
*/
void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType);
}
@@ -22,5 +22,15 @@ public interface ProposalWriteService extends BaseService<ProposalInfo> {
List<Sys_dict> listSourceByCode(@Valid String code);
/**
* 校验当前登录用户在指定教代会届次下是否具备代表身份。
* 参数说明:
* 1. sessionId:教代会届次ID,保存和提交提案时通过该字段定位当前届次代表信息。
* 返回值说明:
* 1. 校验通过时无返回值。
* 2. 校验不通过时抛出业务异常,由上层统一返回提示信息。
*/
void checkCurrentUserDelegate(String sessionId);
ProposalInfo importProposal(Map<String, String> stringStringMap);
}
@@ -234,10 +234,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
// 提案附议
List<NutMap> secondInfos = new ArrayList<>();
List<ProcessTaskVO> secondTaskVos = taskGroups.get("提案附议");
if(secondTaskVos != null && secondTaskVos.size() > 0) {
if (secondTaskVos != null && secondTaskVos.size() > 0) {
for (ProcessTaskVO secondTaskVO : secondTaskVos) {
Dict taskFormData = secondTaskVO.getTaskFormData();
if(taskFormData == null) {
if (taskFormData == null) {
continue;
}
NutMap secondInfo = new NutMap();
@@ -248,7 +248,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
taskUserSql.setCallback(Sqls.callback.map());
execute(taskUserSql);
NutMap taskUserMap = (NutMap) taskUserSql.getResult();
secondInfo.put("s_mobile", taskUserMap.getString("mobile"));
secondInfo.put("s_mobile", Objects.isNull(taskUserMap) ? "" : taskUserMap.getString("mobile"));
secondInfos.add(secondInfo);
}
}
@@ -256,10 +256,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
// 代表团意见
List<NutMap> delegationAuditList = new ArrayList<>();
List<ProcessTaskVO> delegationTaskVos = taskGroups.get("团长审核");
if(delegationTaskVos != null && delegationTaskVos.size() > 0) {
if (delegationTaskVos != null && delegationTaskVos.size() > 0) {
for (ProcessTaskVO delegationTaskVO : delegationTaskVos) {
Dict taskFormData = delegationTaskVO.getTaskFormData();
if(taskFormData == null) {
if (taskFormData == null) {
continue;
}
@@ -601,10 +601,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
List<NutMap> caseUnitList = listMap(caseUnitSql);
for (NutMap v : caseUnitList) {
String hostUnitName = v.getString("masterUnitName");
if (StrUtil.isNotBlank(hostUnitName)){
if (StrUtil.isNotBlank(hostUnitName)) {
underTakeNames.add(hostUnitName);
}
if (StrUtil.isNotBlank(v.getString("slaveUnitNames"))){
if (StrUtil.isNotBlank(v.getString("slaveUnitNames"))) {
List<String> helpUnitNames = Arrays.asList(v.getString("slaveUnitNames").split(""));
underTakeNames.addAll(helpUnitNames);
}
@@ -725,73 +725,73 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
String proposalMeasures = MapUtil.getStr(dataMap, "建议措施", "");
// 工号
if(StrUtil.isBlank(loginName)) {
if (StrUtil.isBlank(loginName)) {
throw new RuntimeException("工号不能为空");
}
Sys_user user = sysUserService.getByLoginName(loginName);
if(user == null){
if (user == null) {
throw new RuntimeException("工号不存在");
}
if(StrUtil.isNotBlank(username) && !username.equals(user.getUsername())) {
if (StrUtil.isNotBlank(username) && !username.equals(user.getUsername())) {
throw new RuntimeException("工号和提案人不一致");
}
// 提案时间
if(StrUtil.isBlank(proposalDate)) {
if (StrUtil.isBlank(proposalDate)) {
throw new RuntimeException("提案时间不能为空");
}
// 提案类型
if(StrUtil.isBlank(proposalType)) {
if (StrUtil.isBlank(proposalType)) {
throw new RuntimeException("提案类型不能为空");
}
ProposalType proposalTypeObj = dao().fetch(ProposalType.class, Cnd.where(ProposalType::getName, "=", proposalType));
if(proposalTypeObj == null) {
if (proposalTypeObj == null) {
throw new RuntimeException("提案类型不存在");
}
// 提案方式
if(StrUtil.isBlank(proposalSource)) {
if (StrUtil.isBlank(proposalSource)) {
throw new RuntimeException("提案方式不能为空");
}
Sys_dict dict = sysDictService.fetch(Cnd.where("code", "=", "PROPOSAL_SOURCE"));
if(dict == null) {
if (dict == null) {
throw new RuntimeException("提案方式字典配置不存在");
}
List<Sys_dict> sysDicts = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(dict.getId())).asc("location"));
if(sysDicts == null || sysDicts.size() == 0) {
if (sysDicts == null || sysDicts.size() == 0) {
throw new RuntimeException("提案方式字典配置不存在");
}
Sys_dict proposalSourceDict = sysDicts.stream().filter(v -> v.getName().equals(proposalSource)).findFirst().orElse(null);
if(proposalSourceDict == null) {
if (proposalSourceDict == null) {
throw new RuntimeException("提案方式不存在");
}
// 提案名称
if(StrUtil.isBlank(proposalName)) {
if (StrUtil.isBlank(proposalName)) {
throw new RuntimeException("提案名称不能为空");
}
// 案由
if(StrUtil.isBlank(proposalContent)) {
if (StrUtil.isBlank(proposalContent)) {
throw new RuntimeException("案由不能为空");
}
// 建议措施
if(StrUtil.isBlank(proposalMeasures)) {
if (StrUtil.isBlank(proposalMeasures)) {
throw new RuntimeException("建议措施不能为空");
}
// 判断当前导入用户是否是代表
Teacher_congress_delegate delegate = teacherCongressDelegateService.fetch(Cnd.where(Teacher_congress_delegate::getUserId, "=", SecurityUtil.getUserId())
.and(Teacher_congress_delegate::getDelFlag, "!=", 1).limit(1));
if(delegate == null) {
if (delegate == null) {
throw new RuntimeException("当前用户不是代表");
}
// 教代会
List<Teacher_congress_session> teacherCongressSessions = this.dao().query(Teacher_congress_session.class, Cnd.where("enable", "=", 1).desc("startDate"));
if(teacherCongressSessions == null || teacherCongressSessions.size() <= 0) {
if (teacherCongressSessions == null || teacherCongressSessions.size() <= 0) {
throw new RuntimeException("没有开启的教代会");
}
Teacher_congress_session teacherCongressSession = teacherCongressSessions.get(0);
@@ -83,6 +83,9 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<Proposal
for (ProcessTask mergeTask : mergeTasks) {
flowCommonService.revokeTask(mergeTask.getId());
}
// 并案整组撤回后,需要同步删除 proposal_merge 中整组并案关系,避免列表仍按并案数据展示。
dao().clear(ProposalMerge.class, Cnd.where(ProposalMerge::getProposalId, "in", mergeProposalIds));
}
}
@@ -1,21 +1,37 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.web.controllers.open.commons.service.CommonService;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
@IocBean(args = {"refer:dao"})
public class ProposalSecondedServiceImpl extends BaseServiceImpl<ProposalSecond> implements ProposalSecondedService {
@Inject
private CommonService commonService;
public ProposalSecondedServiceImpl(Dao dao) {
super(dao);
}
@Override
public void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType) {
if (Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || submitType == null) {
return;
}
// 仅更新当前提案、当前附议人的附议结果,不处理流程任务表。
ProposalSecond proposalSecond = this.fetch(Cnd.where(ProposalSecond::getProposalId, "=", proposalId)
.and(ProposalSecond::getSeconderId, "=", taskActorUserId));
if (proposalSecond == null) {
return;
}
proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode()));
proposalSecond.setOpinion(opinion);
proposalSecond.setSecondedTime(DateUtil.date());
this.update(proposalSecond);
}
}
@@ -121,6 +121,23 @@ public class ProposalWriteServiceImpl extends BaseServiceImpl<ProposalInfo> impl
return dict == null ? new ArrayList<>() : sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(dict.getId())).asc("location"));
}
@Override
public void checkCurrentUserDelegate(String sessionId) {
if (StrUtil.isBlank(sessionId)) {
throw new RuntimeException("所属教代会不能为空");
}
// 撰写提案必须绑定当前届次代表身份,避免非代表用户保存或提交提案。
Teacher_congress_delegate delegate = teacherCongressDelegateService.fetch(
Cnd.where(Teacher_congress_delegate::getSessionId, "=", sessionId)
.and(Teacher_congress_delegate::getUserId, "=", SecurityUtil.getUserId())
.and(Teacher_congress_delegate::getDelFlag, "!=", 1)
.limit(1)
);
if (delegate == null) {
throw new RuntimeException("当前用户不是代表,无法撰写提案");
}
}
@Override
public ProposalInfo importProposal(Map<String, String> dataMap) {
String loginName = MapUtil.getStr(dataMap, "工号", "");
@@ -129,7 +129,8 @@ public class AidFundApplyController {
}
//如果有两个工号并且有一个工号已经是会员就不让他申请了
if (userList.size() >= 2) {
int size = userList.stream().filter(Sys_user::getAidFundMember).toList().size();
// 历史用户数据中 aidFundMember 可能为空,这里只统计明确为基金会员的工号,避免空值自动拆箱报错
int size = userList.stream().filter(sysUser -> Boolean.TRUE.equals(sysUser.getAidFundMember())).toList().size();
if (size > 0) {
return Result.success(Map.of("disabled", true, "label", "请用新工号登陆办理申请业务!"));
}
@@ -149,7 +150,7 @@ public class AidFundApplyController {
aidFundMemberChangeRecord.setUserId(SecurityUtil.getUserId());
Sys_user user = dao.fetch(Sys_user.class, SecurityUtil.getUserId());
if (StrUtil.isBlank(aidFundMemberChangeRecord.getId())) aidFundMemberChangeRecord.setApplyTime(new Date());
if (user.getAidFundMember() == AidFundMemberMode.NORMAL.getCode()) {
if (Boolean.TRUE.equals(user.getAidFundMember())) {
aidFundMemberChangeRecord.setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_FOUR");
} else {
aidFundMemberChangeRecord.setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_ONE");
@@ -159,7 +160,8 @@ public class AidFundApplyController {
Dict args = Dict.create();
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, aidFundMemberChangeRecord);
args.set("aidFundMemberUserType", user.getAidFundMemberUserType());
// 流程变量中的基金会员类型以本次申请提交值为准,避免继续使用用户表中的旧值。
args.set("aidFundMemberUserType", aidFundMemberChangeRecord.getAidFundMemberUserType());
ProcessInstance instance = flowEngine.startProcessInstanceByKey("JJHY", aidFundMemberChangeRecord.getId(), SecurityUtil.getUserId(), args);
// 自动完成第一个申请任务
@@ -194,7 +196,7 @@ public class AidFundApplyController {
us.username,
us.loginname,
us.mobile,
us.aidFundMemberUserType,
IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType,
us.arrivalAtSchoolDate,
us.sex,
us.unitName,
@@ -76,14 +76,17 @@ public class AidFundApplyMineController {
@Param(value = "year") Integer year) {
Sql sql = Sqls.create("""
SELECT
info.*,
info.id,
info.userId,
info.changeType,
info.applyTime,
us.arrivalAtSchoolDate,
us.mobile,
us.loginname loginName,
us.username userName,
us.unitName,
us.unionName,
us.aidFundMemberUserType,
IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType,
us.sex,
ins.id AS instanceId,
ins.businessNo,
@@ -113,6 +116,8 @@ public class AidFundApplyMineController {
cnd.andEX("year(info.applyTime)", "=", year);
cnd.and("info.userId", "=", SecurityUtil.getUserId());
// 管理端代操作产生的记录不属于“我的申请”,历史空值按普通申请处理。
cnd.andEX("IFNULL(info.adminOperate, 0)", "=", 0);
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
@@ -58,7 +58,6 @@ public class AidFundChangeRecordController {
us.mobile,
us.unitName,
us.unionName,
us.aidFundMemberUserType,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
@@ -1,24 +1,24 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -27,6 +27,8 @@ import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
@@ -44,8 +46,6 @@ public class AidFundFoundationAuditController {
@Inject
private AidFundMemberChangeRecordService changeRecordService;
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/aidFund/foundationAudit/index.html")
@SaCheckPermission("medicalMutualAid.aidFund.foundationAudit")
@@ -61,79 +61,8 @@ public class AidFundFoundationAuditController {
@At
@ApiOperation("分页查询")
@SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm,
@Param(value = "year") Integer year,
@Param(value = "aidFundMemberUserType") String aidFundMemberUserType,
@Param(value = "unionId") String unionId,
@Param(value = "unitId") String unitId,
@Param(value = "changeType") String changeType,
@Param(value = "approval") Boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
us.arrivalAtSchoolDate,
us.mobile,
us.loginname loginName,
us.username userName,
us.unitName,
us.unionName,
us.aidFundMemberUserType,
us.sex,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN aid_fund_member_change_record info ON info.id = ins.businessNo
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("us.loginname", "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or("us.username", "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("YEAR(info.applyTime)", "=", year);
cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType);
cnd.andEX("us.unionId", "=", unionId);
cnd.andEX("us.unitId", "=", unitId);
cnd.andEX("info.changeType", "=", changeType);
cnd.and("t.taskName", "=", "85c7d148-44f5-4333-b854-ab225357fd3e");
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("info.applyTime");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("t.id");
sql.setCondition(cnd);
public Result pageData(AidFundPageForm pageForm) {
Sql sql = changeRecordService.getFoundationAuditSql(pageForm);
Pagination<NutMap> pageVO = changeRecordService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> list = pageVO.getList();
@@ -144,6 +73,39 @@ public class AidFundFoundationAuditController {
return Result.success(pageVO);
}
/**
* 导出互管会审核页面申请记录。
*
* @param pageForm 页面筛选参数,和列表页查询条件保持一致
* @param response HTTP 响应对象,用于输出 Excel 文件流
*/
@At
@Ok("void")
@ApiOperation("导出申请记录")
@SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR)
public void exportApplyRecord(AidFundPageForm pageForm, HttpServletResponse response) {
List<NutMap> listMap = changeRecordService.getFoundationAuditExportList(pageForm);
List<ExcelExportEntity> entityList = new ArrayList<>();
entityList.add(new ExcelExportEntity("工号", "loginName", 20));
entityList.add(new ExcelExportEntity("姓名", "userName", 20));
entityList.add(new ExcelExportEntity("性别", "sex", 15));
entityList.add(new ExcelExportEntity("电话", "mobile", 20));
entityList.add(new ExcelExportEntity("工会", "unionName", 20));
entityList.add(new ExcelExportEntity("单位", "unitName", 20));
entityList.add(new ExcelExportEntity("入校时间", "arrivalAtSchoolDate", 20));
entityList.add(new ExcelExportEntity("基金会员类型", "aidFundMemberUserType", 20));
entityList.add(new ExcelExportEntity("缴纳金额", "money", 20));
entityList.add(new ExcelExportEntity("变更类型", "changeType", 20));
entityList.add(new ExcelExportEntity("申请时间", "applyTime", 20));
entityList.add(new ExcelExportEntity("审核状态", "approvalStatus", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, listMap);
String fileName = (pageForm.getYear() == null ? "" : pageForm.getYear()) + "基金会员审核记录.xlsx";
CommonDownloadUtil.download(fileName, workbook, response);
}
@At
@ApiOperation("查询需要缴费多少")
@SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR)
@@ -163,4 +125,23 @@ public class AidFundFoundationAuditController {
return Result.success();
}
/**
* 批量审核互管会待办记录。
*
* @param param 统一的审批参数,包含审批意见和审核动作
* @param ids 勾选的申请记录 ID 数组
* @return 批量审核结果统计
*/
@At
@SLog(tag = "基金会员", msg = "互管会批量审核基金会员记录")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("批量审核")
@SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR)
public Result batchExecuteTask(@Param("data") String param, @Param("ids") String[] ids) {
if (ids == null || ids.length == 0 || StrUtil.isBlank(param)) {
return Result.error("参数错误");
}
return Result.success(changeRecordService.batchExecuteTask(param, ids));
}
}
@@ -10,18 +10,12 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
@@ -35,7 +29,6 @@ import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -79,55 +72,32 @@ public class AidFundManageController {
@ApiOperation("分页查询")
@SaCheckPermission("medicalMutualAid.aidFund.manage")
public Result pageData(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
id,
loginname,
username,
sex,
arrivalAtSchoolDate,
unitName,
unionName,
aidFundMemberUserType,
aidFundMemberJoinTime
FROM
vw_user
$condition
""");
Cnd cnd = Cnd.NEW();
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())) {
if (AuthUtil.hasRole(RoleConstant.RETIREMENT_WORKPLACE.name())) {
cnd.and(Sys_user::getAidFundMemberUserType, "=", "离退休人员");
} else {
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
}
}
if (pageForm.getNotPayedCurrentYear()) {
cnd.and("id", "in", Sqls.create("SELECT userid FROM `aid_fund_member_pay` where `year` = YEAR(CURDATE()) and isPayed = 0"));
}
cnd.andEX(Sys_user::getAidFundMemberUserType, "=", pageForm.getAidFundMemberUserType());
cnd.andEX(View_user::getUnionId, "=", pageForm.getUnionId());
cnd.andEX(View_user::getUnitId, "=", pageForm.getUnitId());
sql.setCondition(cnd);
cnd.and(Sys_user::getAidFundMember, "=", AidFundMemberMode.NORMAL.getCode());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("loginname", pageForm.getSearchKeyword());
seg.orLike("username", pageForm.getSearchKeyword());
cnd.and(seg);
}
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("unitCode");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
sql.setCondition(cnd);
Pagination pagination = userService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
Sql sql = aidFundMemberService.getManageSql(pageForm);
Pagination pagination = aidFundMemberService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
@At
@SaCheckPermission("medicalMutualAid.aidFund.manage")
@Ok("void")
@ApiOperation("导出会员名单")
public void exportMemberList(AidFundPageForm pageForm, HttpServletResponse response) {
List<NutMap> listMap = aidFundMemberService.listMap(aidFundMemberService.getManageSql(pageForm));
List<ExcelExportEntity> entityList = new ArrayList<>();
entityList.add(new ExcelExportEntity("姓名", "username", 20));
entityList.add(new ExcelExportEntity("工号", "loginname", 20));
entityList.add(new ExcelExportEntity("性别", "sex", 20));
entityList.add(new ExcelExportEntity("会员类型", "aidFundMemberUserType", 20));
entityList.add(new ExcelExportEntity("单位", "unitName", 20));
entityList.add(new ExcelExportEntity("工会", "unionName", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, listMap);
CommonDownloadUtil.download("会员名单.xlsx", workbook, response);
}
@At
@SaCheckPermission("medicalMutualAid.aidFund.manage")
@@ -198,9 +168,12 @@ public class AidFundManageController {
return Result.error("请选择要退会的人员");
}
List<AidFundMemberChangeRecord> recordArrayList = new ArrayList<>();
List<Sys_user> sysUserList = userService.query(Cnd.where("id", "in", ids));
for (String id : ids) {
Sys_user user = sysUserList.stream().filter(sysUser -> sysUser.getId().equals(id)).findFirst().orElse(null);
AidFundMemberChangeRecord record = new AidFundMemberChangeRecord()
.setUserId(id)
.setAidFundMemberUserType(user.getAidFundMemberUserType())
.setApplyTime(new Date())
.setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_SEVEN");
recordArrayList.add(record);
@@ -220,25 +193,7 @@ public class AidFundManageController {
if (ObjectUtil.isEmpty(ids) || StrUtil.isBlank(changeType)) {
return Result.error("参数错误");
}
List<AidFundMemberChangeRecord> recordArrayList = new ArrayList<>();
for (String id : ids) {
AidFundMemberChangeRecord record = new AidFundMemberChangeRecord()
.setUserId(id)
.setApplyTime(new Date())
.setChangeType(changeType);
recordArrayList.add(record);
}
Chain add;
if (changeType.equals("AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) {
//会员退休
add = Chain.make("aidFundMemberUserType", "离退休人员");
} else {
add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode())
.add("aidFundMemberQuitTime", new Date());
}
userService.update(add, Cnd.where("id", "in", ids));
userService.insert(recordArrayList);
aidFundMemberService.doBatchChangeTypeByAdmin(ids, changeType);
return Result.success();
}
@@ -258,21 +213,10 @@ public class AidFundManageController {
@ApiOperation("变更基金会员")
@Aop(TransAop.READ_COMMITTED)
public Result doEditChangeType(String userId, String changeType) {
AidFundMemberChangeRecord record = new AidFundMemberChangeRecord()
.setUserId(userId)
.setApplyTime(new Date())
.setChangeType(changeType);
aidFundMemberService.insert(record);
Chain add;
if (changeType.equals("AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) {
//会员退休
add = Chain.make("aidFundMemberUserType", "离退休人员");
} else {
add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode())
.add("aidFundMemberQuitTime", new Date());
if (StrUtil.isBlank(userId) || StrUtil.isBlank(changeType)) {
return Result.error("参数错误");
}
userService.update(add, Cnd.where("id", "=", userId));
aidFundMemberService.doEditChangeTypeByAdmin(userId, changeType);
return Result.success();
}
@@ -63,14 +63,17 @@ public class AidFundRetirementAuditController {
@Param(value = "approval") Boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
info.id,
info.userId,
info.changeType,
info.applyTime,
us.arrivalAtSchoolDate,
us.mobile,
us.loginname loginName,
us.username userName,
us.unitName,
us.unionName,
us.aidFundMemberUserType,
IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType,
us.sex,
ins.id AS instanceId,
ins.businessNo,
@@ -106,7 +109,7 @@ public class AidFundRetirementAuditController {
cnd.and(seg);
}
cnd.andEX("YEAR(info.applyTime)", "=", year);
cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType);
cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", aidFundMemberUserType);
cnd.andEX("info.changeType", "=", changeType);
cnd.and("t.taskName", "=", "f756c818-6bb7-4f50-b8fa-24390fc7b0b8");
@@ -65,14 +65,17 @@ public class AidFundUnionAuditController {
@Param(value = "approval") Boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
info.id,
info.userId,
info.changeType,
info.applyTime,
us.arrivalAtSchoolDate,
us.mobile,
us.loginname loginName,
us.username userName,
us.unitName,
us.unionName,
us.aidFundMemberUserType,
IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType,
us.sex,
ins.id AS instanceId,
ins.businessNo,
@@ -108,7 +111,7 @@ public class AidFundUnionAuditController {
cnd.and(seg);
}
cnd.andEX("YEAR(info.applyTime)", "=", year);
cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType);
cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", aidFundMemberUserType);
cnd.andEX("info.changeType", "=", changeType);
cnd.and("t.taskName", "=", "06ed1d6d-5f96-485f-9150-ed1ef4a082d6");
@@ -38,10 +38,20 @@ public class AidFundMemberChangeRecord extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 50)
private String changeType;
@Column
@Comment("申请时选择的基金会员类型(dict")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberUserType;
@Column
@Comment("申请时间")
@ColDefine(type = ColType.DATETIME)
private Date applyTime;
@Column
@Comment("是否系统管理员操作")
@ColDefine(type = ColType.BOOLEAN)
private Boolean adminOperate;
}
@@ -22,6 +22,9 @@ public class AidFundPageForm extends PageForm {
private String changeType;
private Integer year;
//是否已审核
private Boolean approval;
//是否扣款null全部、1已缴费、0未缴费
private Integer isPayed;
}
@@ -1,6 +1,8 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import org.nutz.dao.sql.Sql;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import org.nutz.lang.util.NutMap;
@@ -9,10 +11,53 @@ import java.util.List;
public interface AidFundMemberChangeRecordService extends BaseService<AidFundMemberChangeRecord> {
/**
* 构建互管会审核页面查询 SQL。
*
* @param pageForm 页面筛选参数,包含年度、姓名/工号、工会、单位、会员类型、变更类型和审核状态
* @return 可直接用于分页或导出的查询 SQL
*/
Sql getFoundationAuditSql(AidFundPageForm pageForm);
/**
* 查询互管会审核页面导出数据。
*
* @param pageForm 页面筛选参数,和列表页保持一致
* @return 导出所需的列表数据,每条记录对应一条基金会员变更申请
*/
List<NutMap> getFoundationAuditExportList(AidFundPageForm pageForm);
/**
* 执行流程审核动作。
*
* @param param 流程提交参数 JSON,包含提交流程所需的任务、意见和业务字段
* @param id 基金会员变更记录 ID
*/
void executeTask( String param, String id);
/**
* 批量执行互管会审核任务。
*
* @param param 流程提交参数 JSON,包含统一的审批意见和提交类型
* @param ids 选中的基金会员变更记录 ID 数组
* @return 批量审核结果,包含成功数、失败数和失败原因
*/
NutMap batchExecuteTask(String param, String[] ids);
/**
* 计算基金会员应补缴金额。
*
* @param arrivalAtSchoolDate 入校时间,格式为 yyyy-MM-dd
* @param userId 用户 ID,空值时默认取当前登录人
* @return 当前规则下计算出的应缴金额
*/
int getPayMoney(String arrivalAtSchoolDate,String userId);
/**
* 查询用户历年缴费记录。
*
* @param userId 用户 ID,空值时默认取当前登录人
* @return 用户及其旧工号对应的缴费记录列表
*/
List<AidFundMemberPay> getUserPayRecordList(String userId);
}
@@ -2,6 +2,8 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.util.NutMap;
import java.util.List;
@@ -31,5 +33,30 @@ public interface AidFundMemberService extends BaseService {
Double getPayMoney(Sys_user user, Integer year);
/**
* 获取基金会员台账列表查询SQL。
*
* @param pageForm 页面查询条件,包含会员类型、工会、单位、关键字、排序及未缴费筛选参数
* @return 与基金会员台账页面一致的查询SQL
*/
Sql getManageSql(AidFundPageForm pageForm);
List<NutMap> findChangeRecordList(String userId);
/**
* 管理端批量变更基金会员状态,并记录“系统管理员操作”来源。
*
* @param ids 需要变更的用户ID数组,来自台账页面勾选行
* @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE
*/
void doBatchChangeTypeByAdmin(String[] ids, String changeType);
/**
* 管理端对单个用户执行“变更”操作,并记录“系统管理员操作”来源。
*
* @param userId 被变更的用户ID
* @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE
*/
void doEditChangeTypeByAdmin(String userId, String changeType);
}
@@ -3,20 +3,34 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
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.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -33,6 +47,9 @@ import java.util.*;
*/
@IocBean(args = {"refer:dao"})
public class AidFundMemberChangeRecordServiceImpl extends BaseServiceImpl<AidFundMemberChangeRecord> implements AidFundMemberChangeRecordService {
private static final String FOUNDATION_AUDIT_TASK_NAME = "85c7d148-44f5-4333-b854-ab225357fd3e";
private static final String CHANGE_TYPE_JOIN = "AIDFUND_MEMBER_CHANGE_TYPE_ONE";
public AidFundMemberChangeRecordServiceImpl(Dao dao) {
super(dao);
}
@@ -41,21 +58,228 @@ public class AidFundMemberChangeRecordServiceImpl extends BaseServiceImpl<AidFun
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProcessTaskService processTaskService;
@Inject
private SysDictService sysDictService;
@Override
public Sql getFoundationAuditSql(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.userId,
info.changeType,
info.applyTime,
us.arrivalAtSchoolDate,
us.mobile,
us.loginname loginName,
us.username userName,
us.unitName,
us.unionName,
IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType,
us.sex,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN aid_fund_member_change_record info ON info.id = ins.businessNo
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("us.loginname", "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or("us.username", "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("YEAR(info.applyTime)", "=", pageForm.getYear());
// 新申请优先按变更表中的基金会员类型筛选,历史数据为空时兜底用户表。
cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", pageForm.getAidFundMemberUserType());
cnd.andEX("us.unionId", "=", pageForm.getUnionId());
cnd.andEX("us.unitId", "=", pageForm.getUnitId());
cnd.andEX("info.changeType", "=", pageForm.getChangeType());
cnd.and("t.taskName", "=", FOUNDATION_AUDIT_TASK_NAME);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())){
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
if (Boolean.TRUE.equals(pageForm.getApproval())) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("info.applyTime");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("t.id");
sql.setCondition(cnd);
return sql;
}
@Override
public List<NutMap> getFoundationAuditExportList(AidFundPageForm pageForm) {
List<NutMap> list = listMap(getFoundationAuditSql(pageForm));
Map<String, String> memberTypeMap = getDictNameMap("AIDFUND_MEMBER_USER_TYPE");
Map<String, String> changeTypeMap = getDictNameMap("AIDFUND_MEMBER_CHANGE_TYPE");
list.forEach(map -> {
String arrivalAtSchoolDate = map.getString("arrivalAtSchoolDate");
String changeType = map.getString("changeType");
map.put("approvalStatus", ObjectUtil.equals(map.getInt("taskState"), ProcessTaskStateEnum.DOING.getCode()) ? "未审核" : "已审核");
map.put("aidFundMemberUserType", memberTypeMap.getOrDefault(map.getString("aidFundMemberUserType"), map.getString("aidFundMemberUserType")));
map.put("changeType", changeTypeMap.getOrDefault(changeType, changeType));
map.put("arrivalAtSchoolDate", StrUtil.isBlank(arrivalAtSchoolDate) ? "" : DateUtil.format(DateUtil.parse(arrivalAtSchoolDate), "yyyy-MM-dd"));
map.put("applyTime", map.getTime("applyTime") == null ? "" : DateUtil.formatDateTime(map.getTime("applyTime")));
// 仅加入基金时需要计算补缴金额,其他变更类型保持空白,避免误导为固定收费。
map.put("money", ObjectUtil.equals(changeType, CHANGE_TYPE_JOIN) && StrUtil.isNotBlank(arrivalAtSchoolDate)
? getPayMoney(arrivalAtSchoolDate, map.getString("userId")) : "");
});
return list;
}
private Map<String, String> getDictNameMap(String dictCode) {
return sysDictService.getSubListByCode(dictCode).stream().collect(HashMap::new, (map, dict) -> map.put(dict.getCode(), dict.getName()), HashMap::putAll);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void executeTask(String param, String id) {
Dict args = Json.fromJson(Dict.class, param);
executeTask(args, id);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public NutMap batchExecuteTask(String param, String[] ids) {
NutMap result = NutMap.NEW();
if (ArrayUtil.isEmpty(ids)) {
return result.setv("successCount", 0).setv("failCount", 0).setv("failMessages", List.of("请选择需要审核的记录"));
}
Dict baseArgs = Json.fromJson(Dict.class, param);
List<ProcessTask> doingTasks = processTaskService.getDoingTaskByBizIdTaskName(Arrays.asList(ids), FOUNDATION_AUDIT_TASK_NAME);
Map<String, ProcessTask> taskMap = new HashMap<>();
List<Long> instanceIds = doingTasks.stream().map(ProcessTask::getProcessInstanceId).filter(Objects::nonNull).distinct().toList();
Map<Long, String> businessNoMap = new HashMap<>();
if (CollectionUtil.isNotEmpty(instanceIds)) {
List<ProcessInstance> instances = dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "in", instanceIds));
instances.forEach(instance -> businessNoMap.put(instance.getId(), instance.getBusinessNo()));
}
doingTasks.forEach(task -> {
String businessNo = businessNoMap.get(task.getProcessInstanceId());
if (StrUtil.isNotBlank(businessNo)) {
taskMap.put(businessNo, task);
}
});
List<String> failMessages = new ArrayList<>();
int successCount = 0;
for (String id : ids) {
AidFundMemberChangeRecord changeRecord = fetch(id);
if (changeRecord == null) {
failMessages.add(id + ":申请记录不存在");
continue;
}
ProcessTask processTask = taskMap.get(id);
if (processTask == null) {
failMessages.add(resolveRecordName(changeRecord) + ":审核任务不存在或已处理");
continue;
}
if (!processTaskService.isAllowed(processTask, SecurityUtil.getUserId())) {
failMessages.add(resolveRecordName(changeRecord) + ":当前账号无权审核该记录");
continue;
}
try {
Dict args = buildBatchTaskArgs(baseArgs, changeRecord, processTask);
executeTask(args, id);
successCount++;
} catch (Exception e) {
failMessages.add(resolveRecordName(changeRecord) + "" + e.getMessage());
}
}
result.setv("successCount", successCount);
result.setv("failCount", failMessages.size());
result.setv("failMessages", failMessages);
return result;
}
/**
* 批量审核沿用单条审核逻辑,但需要为每条记录补齐流程任务 ID 和加入基金时的入校时间。
*/
private Dict buildBatchTaskArgs(Dict baseArgs, AidFundMemberChangeRecord changeRecord, ProcessTask processTask) {
Dict args = baseArgs.clone();
args.set(FlowConst.PROCESS_TASK_ID_KEY, processTask.getId());
args.set("changeType", changeRecord.getChangeType());
if (ObjectUtil.equals(args.getInt("submitType"), 1) && ObjectUtil.equals(changeRecord.getChangeType(), CHANGE_TYPE_JOIN)) {
Sys_user user = dao().fetch(Sys_user.class, changeRecord.getUserId());
if (user == null || StrUtil.isBlank(user.getArrivalAtSchoolDate())) {
throw new IllegalArgumentException("缺少入校时间,无法批量通过");
}
args.set("arrivalAtSchoolDate", user.getArrivalAtSchoolDate());
}
return args;
}
private String resolveRecordName(AidFundMemberChangeRecord changeRecord) {
Sys_user user = dao().fetch(Sys_user.class, changeRecord.getUserId());
if (user == null || StrUtil.isBlank(user.getUsername())) {
return changeRecord.getId();
}
return user.getUsername();
}
/**
* 统一处理互管会审核通过后的用户状态同步,并继续推进流程任务。
*/
private void executeTask(Dict args, String id) {
AidFundMemberChangeRecord changeRecord = fetch(id);
if (changeRecord == null) {
throw new IllegalArgumentException("申请记录不存在");
}
if (args.getInt("submitType") == 1) {
//如果是通过
if (ObjectUtil.equals(args.getStr("changeType"), "AIDFUND_MEMBER_CHANGE_TYPE_ONE")) {
//如果是加入基金
dao().update(Sys_user.class, Chain.make("arrivalAtSchoolDate", args.getStr("arrivalAtSchoolDate")).add("aidFundMemberJoinTime", new Date())
.add("aidFundMember", AidFundMemberMode.NORMAL.getCode()),
// 审核通过后需要把本次申请的基金会员类型同步回用户表,历史空值则保留用户原值不覆盖。
String aidFundMemberUserType = changeRecord.getAidFundMemberUserType();
// 如果是通过,加入基金需要回写入校时间和入会状态,其他类型按退会逻辑处理。
if (ObjectUtil.equals(args.getStr("changeType"), CHANGE_TYPE_JOIN)) {
if (StrUtil.isBlank(args.getStr("arrivalAtSchoolDate"))) {
throw new IllegalArgumentException("缺少入校时间,无法通过审核");
}
Chain chain = Chain.make("arrivalAtSchoolDate", args.getStr("arrivalAtSchoolDate"))
.add("aidFundMemberJoinTime", new Date())
.add("aidFundMember", AidFundMemberMode.NORMAL.getCode());
if (StrUtil.isNotBlank(aidFundMemberUserType)) {
chain.add("aidFundMemberUserType", aidFundMemberUserType);
}
dao().update(Sys_user.class, chain,
Cnd.where(Sys_user::getId, "=", changeRecord.getUserId()));
} else {
dao().update(Sys_user.class, Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode())
.add("aidFundMemberQuitTime", new Date()),
Chain chain = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode())
.add("aidFundMemberQuitTime", new Date());
if (StrUtil.isNotBlank(aidFundMemberUserType)) {
chain.add("aidFundMemberUserType", aidFundMemberUserType);
}
dao().update(Sys_user.class, chain,
Cnd.where(Sys_user::getId, "=", changeRecord.getUserId()));
}
}
@@ -2,18 +2,28 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberHistory;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
@@ -121,6 +131,62 @@ public class AidFundMemberServiceImpl extends BaseServiceImpl implements AidFund
return (year + 1 - beforeYear) * money + (year - beforeYear) * money * 0.1;
}
/**
* 统一封装基金会员台账列表查询口径,分页查询和导出都复用这里,避免“所见”和“所得”不一致。
*
* @param pageForm 页面查询条件,包含会员类型、工会、单位、关键字、排序及未缴费筛选参数
* @return 基金会员台账查询SQL
*/
@Override
public Sql getManageSql(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
id,
loginname,
username,
sex,
arrivalAtSchoolDate,
unitName,
unionName,
aidFundMemberUserType,
aidFundMemberJoinTime
FROM
vw_user
$condition
""");
Cnd cnd = Cnd.NEW();
// 非系统管理员按现有页面权限口径控制数据范围,确保导出结果与列表展示一致。
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())) {
if (AuthUtil.hasRole(RoleConstant.RETIREMENT_WORKPLACE.name())) {
cnd.and(Sys_user::getAidFundMemberUserType, "=", "离退休人员");
} else {
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
}
}
if (pageForm.getNotPayedCurrentYear()) {
cnd.and("id", "in", Sqls.create("SELECT userid FROM `aid_fund_member_pay` where `year` = YEAR(CURDATE()) and isPayed = 0"));
}
cnd.andEX(Sys_user::getAidFundMemberUserType, "=", pageForm.getAidFundMemberUserType());
cnd.andEX(View_user::getUnionId, "=", pageForm.getUnionId());
cnd.andEX(View_user::getUnitId, "=", pageForm.getUnitId());
cnd.and(Sys_user::getAidFundMember, "=", AidFundMemberMode.NORMAL.getCode());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("loginname", pageForm.getSearchKeyword());
seg.orLike("username", pageForm.getSearchKeyword());
cnd.and(seg);
}
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("unitCode");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
sql.setCondition(cnd);
return sql;
}
@Override
public List<NutMap> findChangeRecordList(String userId) {
Sql sql = Sqls.create("""
@@ -168,5 +234,93 @@ public class AidFundMemberServiceImpl extends BaseServiceImpl implements AidFund
return listMap(sql);
}
/**
* 管理端批量变更基金会员状态时,统一同步用户表并记录“系统管理员操作”的变更记录。
*
* @param ids 需要变更的用户ID数组,来自台账页面勾选结果
* @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void doBatchChangeTypeByAdmin(String[] ids, String changeType) {
List<Sys_user> sysUserList = dao().query(Sys_user.class, Cnd.where("id", "in", ids));
List<AidFundMemberChangeRecord> recordArrayList = new ArrayList<>();
for (String id : ids) {
Sys_user user = sysUserList.stream().filter(sysUser -> sysUser.getId().equals(id)).findFirst().orElse(null);
if (user == null) {
continue;
}
recordArrayList.add(buildAdminChangeRecord(user, changeType));
}
updateUserChangeType(ids, changeType);
if (!recordArrayList.isEmpty()) {
dao().insert(recordArrayList);
}
}
/**
* 管理端单个“变更”操作时,统一同步用户表并记录“系统管理员操作”的变更记录。
*
* @param userId 被变更的用户ID
* @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void doEditChangeTypeByAdmin(String userId, String changeType) {
Sys_user user = dao().fetch(Sys_user.class, userId);
if (user == null) {
return;
}
dao().insert(buildAdminChangeRecord(user, changeType));
updateUserChangeType(new String[]{userId}, changeType);
}
/**
* 管理端变更落记录时,需要明确标记记录来源,避免被“我的申请”误识别为本人发起。
*
* @param user 当前被操作的用户
* @param changeType 变更类型字典值
* @return 已补齐管理员操作标记和会员类型快照的变更记录
*/
private AidFundMemberChangeRecord buildAdminChangeRecord(Sys_user user, String changeType) {
return new AidFundMemberChangeRecord()
.setUserId(user.getId())
.setAidFundMemberUserType(resolveChangeRecordMemberType(user, changeType))
.setApplyTime(new Date())
.setChangeType(changeType)
.setAdminOperate(true);
}
/**
* 退休变更需要把记录中的会员类型同步成退休类型,其余情况沿用当前用户档案中的会员类型。
*
* @param user 当前被操作的用户
* @param changeType 变更类型字典值
* @return 变更记录中应保存的基金会员类型
*/
private String resolveChangeRecordMemberType(Sys_user user, String changeType) {
if (ObjectUtil.equals(changeType, "AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) {
return "离退休人员";
}
return user.getAidFundMemberUserType();
}
/**
* 管理端“变更”与“批量变更”共用同一套用户表更新规则,确保页面操作口径一致。
*
* @param ids 需要更新的用户ID数组
* @param changeType 变更类型字典值
*/
private void updateUserChangeType(String[] ids, String changeType) {
Chain add;
if (ObjectUtil.equals(changeType, "AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) {
add = Chain.make("aidFundMemberUserType", "离退休人员");
} else {
add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode())
.add("aidFundMemberQuitTime", new Date());
}
dao().update(Sys_user.class, add, Cnd.where("id", "in", ids));
}
}
@@ -73,6 +73,29 @@ public class MedicalCalculateMoneyController {
return Result.success(applyData);
}
@At
@ApiOperation("更新医疗互助补助金额明细")
@SaCheckPermission("medicalMutualAid.medical.calculateMoney")
public Object setGrantsOrUnionInto(String keys, double value) {
//获取申请最新年份
Sql yearSql = Sqls.create("SELECT MAX(YEAR(applyTime)) AS currentYear FROM medical_apply");
yearSql.setCallback(Sqls.callback.longs());
medicalCalculateMoneyService.execute(yearSql);
long year = yearSql.getLong();
MedicalMoneyDetail moneyDetail = medicalCalculateMoneyService.dao().fetch(MedicalMoneyDetail.class, Cnd.where("year", "=", year));
if (Lang.isEmpty(moneyDetail)) {
moneyDetail = new MedicalMoneyDetail();
moneyDetail.setYear((int) year);
}
if (keys.equals("grants")) {
moneyDetail.setSchoolGrants(new BigDecimal(value));
} else if (keys.equals("into")) {
moneyDetail.setUnionInto(new BigDecimal(value));
}
medicalCalculateMoneyService.insertOrUpdate(moneyDetail);
return null;
}
@At
@ApiOperation("按比例修改补助金额")
@@ -0,0 +1,126 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDashBoardService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.List;
/**
* @author Codex
* @description 医疗互助数据看板
*/
@IocBean
@At("/platform/medicalMutualAid/medical/dashboard")
@Api("医疗互助数据看板")
@Ok("json:full")
public class MedicalDashBoardController {
@Inject
private MedicalDashBoardService medicalDashBoardService;
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/medical/dashboard/index.html")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public void index() {
}
/**
* 查询医疗互助总览统计。
* 返回值字段说明:
* 1. fundMemberCount:当前基金会员总数
* 2. currentYearApplyCount:本年度已办结申请数
* 3. majorDiseaseApplyCount:重疾已办结申请数
* 4. commonDiseaseApplyCount:普通疾病已办结申请数
* 5. totalSubsidyMoney:补助金额合计
* 6. totalLoveSubsidyMoney:爱心基金金额合计
*
* @return 医疗互助总览统计 VO
*/
@At
@ApiOperation("查询医疗互助总览统计")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public Result overview() {
MedicalDashboardOverviewVO vo = medicalDashBoardService.getOverview();
return Result.success(vo);
}
/**
* 按工会统计医疗互助已办结申请数据。
* 返回值字段说明:
* 1. tableData:表格使用的工会统计列表
* 2. 列表元素中 applyCount 为已办结申请数,subsidyMoney 为补助金额合计
*
* @return 工会统计结果 VO
*/
@At
@ApiOperation("按工会统计医疗互助数据")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public Result unionStat() {
MedicalDashboardUnionResultVO vo = medicalDashBoardService.getUnionStat();
return Result.success(vo);
}
/**
* 按在职状态统计医疗互助已办结申请数据。
* 返回列表字段说明:
* 1. code:在职状态编码
* 2. name:在职状态名称
* 3. applyCount:该状态下已办结申请数
* 4. subsidyMoney:该状态下补助金额合计
*
* @return 在职状态统计 VO 列表
*/
@At
@ApiOperation("按在职状态统计医疗互助数据")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public Result userStateStat() {
List<MedicalDashboardCategoryStatVO> list = medicalDashBoardService.getUserStateStat();
return Result.success(list);
}
/**
* 按人员类型统计医疗互助已办结申请数据。
* 返回列表字段说明:
* 1. code:人员类型编码
* 2. name:人员类型名称
* 3. applyCount:该类型下已办结申请数
* 4. subsidyMoney:该类型下补助金额合计
*
* @return 人员类型统计 VO 列表
*/
@At
@ApiOperation("按人员类型统计医疗互助数据")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public Result personTypeStat() {
List<MedicalDashboardCategoryStatVO> list = medicalDashBoardService.getPersonTypeStat();
return Result.success(list);
}
/**
* 按疾病类型统计医疗互助已办结申请数据。
* 返回列表字段说明:
* 1. code:疾病类型 ID
* 2. name:疾病名称
* 3. applyCount:该疾病下已办结申请数
* 4. subsidyMoney:该疾病下补助金额合计
*
* @return 疾病类型统计 VO 列表
*/
@At
@ApiOperation("按疾病类型统计医疗互助数据")
@SaCheckPermission("medicalMutualAid.medical.dashboard")
public Result diseaseTypeStat() {
List<MedicalDashboardCategoryStatVO> list = medicalDashBoardService.getDiseaseTypeStat();
return Result.success(list);
}
}
@@ -55,6 +55,7 @@ public class MedicalUnionAuditController {
@SaCheckPermission("medicalMutualAid.medical.unionAudit")
public Result pageData(MedicalPageForm pageForm) {
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "a9bbf69c-5edf-4161-a364-47996d212472");
Sql sql = medicalAuditService.getAuditSql(pageForm, cnd);
sql.setCondition(cnd);
@@ -0,0 +1,61 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO;
import java.util.List;
/**
* @author Codex
* @description 医疗互助数据看板 service
*/
public interface MedicalDashBoardService extends BaseService<MedicalApply> {
/**
* 查询医疗互助数据看板总览数据。
* 返回值包含:
* 1. 已办结申请总数
* 2. 本年度已办结申请数
* 3. 重疾/普通疾病申请数
* 4. 补助金额与爱心基金金额汇总
*
* @return 医疗互助总览统计 VO
*/
MedicalDashboardOverviewVO getOverview();
/**
* 按工会统计医疗互助已办结申请数据。
* 返回值中的 chartData 与 tableData 当前来源相同,
* 便于前端图表和表格按既有字段直接取值。
*
* @return 工会统计结果 VO
*/
MedicalDashboardUnionResultVO getUnionStat();
/**
* 按人员在职状态统计医疗互助已办结申请数据。
* code 为在职状态编码,name 为字典名称。
*
* @return 在职状态统计列表
*/
List<MedicalDashboardCategoryStatVO> getUserStateStat();
/**
* 按人员类型统计医疗互助已办结申请数据。
* code 为人员类型编码,name 为字典名称。
*
* @return 人员类型统计列表
*/
List<MedicalDashboardCategoryStatVO> getPersonTypeStat();
/**
* 按疾病类型统计医疗互助已办结申请数据。
* code 为疾病类型 ID,name 为疾病名称。
*
* @return 疾病类型统计列表
*/
List<MedicalDashboardCategoryStatVO> getDiseaseTypeStat();
}
@@ -186,6 +186,7 @@ public class MedicalApplyServiceImpl extends BaseServiceImpl<MedicalApply> imple
// 查询申请成功的记录
List<NutMap> applyRecords = getApplyRecord(allUserIds);
applyRecords.removeIf(item -> applyId.equals(item.getString("id")));
//是否首次申请 爱心互助基金 重大疾病
boolean isFirstApply = applyRecords.stream().filter(v -> v.getBoolean("isMajorDiseases")).toList().isEmpty();
@@ -230,7 +231,7 @@ public class MedicalApplyServiceImpl extends BaseServiceImpl<MedicalApply> imple
Boolean isBeyondAllMoney = allMoney.compareTo(medicalSetting.getPreviousYearMaxMoney()) > 0;
Sys_user user = dao().fetch(Sys_user.class, SecurityUtil.getUserId());
Sys_user user = dao().fetch(Sys_user.class, applyInfo.getString("userId"));
boolean fullTenYears = DateUtil.thisYear() - Integer.parseInt(user.getAidFundDeductTime()) >= 10;
boolean fullFifYears = DateUtil.thisYear() - Integer.parseInt(user.getAidFundDeductTime()) >= 15;
@@ -72,7 +72,9 @@ public class MedicalAuditServiceImpl extends BaseServiceImpl<MedicalApply> imple
seg.orLike("us.username", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if(!SecurityUtil.getUserLoginname().equals("superadmin")){
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
cnd.andEX("year(info.applyTime)", "=", pageForm.getYear());
cnd.andEX("info.diseaseId", "=", pageForm.getDiseaseId());
cnd.andEX("us.unionid", "=", pageForm.getUnionId());
@@ -0,0 +1,248 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDashBoardService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionStatVO;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* @author Codex
* @description 医疗互助数据看板 service 实现
*/
@IocBean(args = {"refer:dao"})
public class MedicalDashBoardServiceImpl extends BaseServiceImpl<MedicalApply> implements MedicalDashBoardService {
public MedicalDashBoardServiceImpl(Dao dao) {
super(dao);
}
@Override
public MedicalDashboardOverviewVO getOverview() {
Sql sql = Sqls.create("""
SELECT
(SELECT COUNT(1) FROM sys_user WHERE aidFundMember = 1) AS fundMemberCount,
COUNT(DISTINCT CASE WHEN YEAR(info.applyTime) = YEAR(NOW()) THEN info.id END) AS currentYearApplyCount,
COUNT(DISTINCT CASE WHEN type.isMajorDiseases = 1 THEN info.id END) AS majorDiseaseApplyCount,
COUNT(DISTINCT CASE WHEN type.isMajorDiseases = 0 THEN info.id END) AS commonDiseaseApplyCount,
COALESCE(SUM(info.subsidyMoney), 0) AS totalSubsidyMoney,
COALESCE(SUM(info.loveSubsidyMoney), 0) AS totalLoveSubsidyMoney
FROM
medical_apply info
INNER JOIN wf_process_instance ins ON ins.businessNo = info.id
LEFT JOIN medical_disease_type type ON type.id = info.diseaseId
WHERE
ins.state = @state
""");
sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode());
NutMap data = fetchOne(sql);
MedicalDashboardOverviewVO vo = new MedicalDashboardOverviewVO();
vo.setFundMemberCount(data.getLong("fundMemberCount", 0L));
vo.setCurrentYearApplyCount(data.getLong("currentYearApplyCount", 0L));
vo.setMajorDiseaseApplyCount(data.getLong("majorDiseaseApplyCount", 0L));
vo.setCommonDiseaseApplyCount(data.getLong("commonDiseaseApplyCount", 0L));
vo.setTotalSubsidyMoney(getBigDecimalValue(data, "totalSubsidyMoney"));
vo.setTotalLoveSubsidyMoney(getBigDecimalValue(data, "totalLoveSubsidyMoney"));
return vo;
}
@Override
public MedicalDashboardUnionResultVO getUnionStat() {
Sql sql = Sqls.create("""
SELECT
un.id AS unionId,
un.`name` AS unionName,
un.unionCode AS unionCode,
COUNT(applyInfo.id) AS applyCount,
COALESCE(SUM(applyInfo.subsidyMoney), 0) AS subsidyMoney
FROM
sys_union un
LEFT JOIN vw_user us ON us.unionId = un.id
LEFT JOIN (
SELECT
info.id,
info.userId,
info.subsidyMoney
FROM
medical_apply info
INNER JOIN wf_process_instance ins ON ins.businessNo = info.id
WHERE
ins.state = @state
) applyInfo ON applyInfo.userId = us.id
GROUP BY
un.id, un.`name`, un.unionCode
ORDER BY
un.unionCode
""");
sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode());
List<MedicalDashboardUnionStatVO> list = toUnionStatList(listMap(sql));
MedicalDashboardUnionResultVO resultVO = new MedicalDashboardUnionResultVO();
resultVO.setTableData(new ArrayList<>(list));
return resultVO;
}
@Override
public List<MedicalDashboardCategoryStatVO> getUserStateStat() {
Sql sql = Sqls.create("""
SELECT
dict.`code` AS code,
dict.`name` AS name,
COUNT(info.id) AS applyCount,
COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney
FROM
sys_dict dict
LEFT JOIN sys_dict parent ON parent.id = dict.parentId
LEFT JOIN vw_user us ON us.userState = dict.`code`
LEFT JOIN medical_apply info ON info.userId = us.id
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state
WHERE
parent.`code` = 'USER_STATE'
AND ins.id IS NOT NULL
GROUP BY
dict.`code`, dict.`name`, dict.location
ORDER BY
dict.location
""");
sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode());
return toCategoryStatList(listMap(sql));
}
@Override
public List<MedicalDashboardCategoryStatVO> getPersonTypeStat() {
Sql sql = Sqls.create("""
SELECT
dict.`code` AS code,
dict.`name` AS name,
COUNT(info.id) AS applyCount,
COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney
FROM
sys_dict dict
LEFT JOIN sys_dict parent ON parent.id = dict.parentId
LEFT JOIN vw_user us ON us.personType = dict.`code`
LEFT JOIN medical_apply info ON info.userId = us.id
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state
WHERE
parent.`code` = 'USER_PERSON_TYPE'
AND ins.id IS NOT NULL
GROUP BY
dict.`code`, dict.`name`, dict.location
ORDER BY
dict.location
""");
sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode());
return toCategoryStatList(listMap(sql));
}
@Override
public List<MedicalDashboardCategoryStatVO> getDiseaseTypeStat() {
Sql sql = Sqls.create("""
SELECT
type.id AS code,
type.diseaseName AS name,
COUNT(info.id) AS applyCount,
COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney
FROM
medical_disease_type type
LEFT JOIN medical_apply info ON info.diseaseId = type.id
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state
WHERE
ins.id IS NOT NULL
GROUP BY
type.id, type.diseaseName, type.diseaseCode
ORDER BY
type.diseaseCode
""");
sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode());
return toCategoryStatList(listMap(sql));
}
/**
* 执行单行统计 SQL。
* 该方法用于处理总览类统计,返回结果仅一行。
*
* @param sql 已设置好查询参数的 SQL 对象
* @return 单行统计数据,字段不存在时返回空 NutMap,避免上层空指针
*/
private NutMap fetchOne(Sql sql) {
sql.setCallback(Sqls.callback.map());
dao().execute(sql);
Object result = sql.getResult();
if (result instanceof NutMap) {
return (NutMap) result;
}
return NutMap.NEW();
}
/**
* 将工会统计查询结果转换为 VO。
*
* @param dataList SQL 查询返回的 map 列表,字段需包含 unionId、unionName、unionCode、applyCount、subsidyMoney
* @return 工会统计 VO 列表
*/
private List<MedicalDashboardUnionStatVO> toUnionStatList(List<NutMap> dataList) {
List<MedicalDashboardUnionStatVO> list = new ArrayList<>();
for (NutMap data : dataList) {
MedicalDashboardUnionStatVO vo = new MedicalDashboardUnionStatVO();
vo.setUnionId(data.getString("unionId"));
vo.setUnionName(data.getString("unionName"));
vo.setUnionCode(data.getString("unionCode"));
vo.setApplyCount(data.getLong("applyCount", 0L));
vo.setSubsidyMoney(getBigDecimalValue(data, "subsidyMoney"));
list.add(vo);
}
return list;
}
/**
* 将通用分类统计查询结果转换为 VO。
*
* @param dataList SQL 查询返回的 map 列表,字段需包含 code、name、applyCount、subsidyMoney
* @return 分类统计 VO 列表
*/
private List<MedicalDashboardCategoryStatVO> toCategoryStatList(List<NutMap> dataList) {
List<MedicalDashboardCategoryStatVO> list = new ArrayList<>();
for (NutMap data : dataList) {
MedicalDashboardCategoryStatVO vo = new MedicalDashboardCategoryStatVO();
vo.setCode(data.getString("code"));
vo.setName(data.getString("name"));
vo.setApplyCount(data.getLong("applyCount", 0L));
vo.setSubsidyMoney(getBigDecimalValue(data, "subsidyMoney"));
list.add(vo);
}
return list;
}
/**
* 从 NutMap 中安全读取金额字段。
* SQL 聚合查询返回的金额字段有时会是 BigDecimal、Double、Long 或字符串,
* 因此这里统一做一次转换,避免 controller/service 层重复判空和类型判断。
*
* @param data NutMap 查询结果对象
* @param key 金额字段名
* @return 对应金额,字段为空时返回 0
*/
private BigDecimal getBigDecimalValue(NutMap data, String key) {
Object value = data.get(key);
if (value == null) {
return BigDecimal.ZERO;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(String.valueOf(value));
}
}
@@ -0,0 +1,34 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author Codex
* @description 医疗互助分类统计返回值
*/
@Data
public class MedicalDashboardCategoryStatVO {
/**
* 分类编码。
* 不同接口场景下可能表示在职状态编码、人员类型编码、疾病ID 等。
*/
private String code;
/**
* 分类名称,用于前端直接展示图表/表格标题。
*/
private String name;
/**
* 当前分类下的已办结申请数
*/
private Long applyCount;
/**
* 当前分类下的已办结补助金额合计
*/
private BigDecimal subsidyMoney;
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author Codex
* @description 医疗互助数据看板总览统计返回值
*/
@Data
public class MedicalDashboardOverviewVO {
/**
* 当前基金会员总数
*/
private Long fundMemberCount;
/**
* 本年度已办结申请数
*/
private Long currentYearApplyCount;
/**
* 已办结重疾申请数
*/
private Long majorDiseaseApplyCount;
/**
* 已办结普通疾病申请数
*/
private Long commonDiseaseApplyCount;
/**
* 已办结补助金额合计
*/
private BigDecimal totalSubsidyMoney;
/**
* 已办结爱心基金金额合计
*/
private BigDecimal totalLoveSubsidyMoney;
}
@@ -0,0 +1,19 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo;
import lombok.Data;
import java.util.List;
/**
* @author Codex
* @description 医疗互助按工会统计返回值
*/
@Data
public class MedicalDashboardUnionResultVO {
/**
* 表格数据。
* 当前页面仅保留工会统计表格,因此只返回表格所需列表。
*/
private List<MedicalDashboardUnionStatVO> tableData;
}
@@ -0,0 +1,38 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author Codex
* @description 医疗互助按工会统计返回值
*/
@Data
public class MedicalDashboardUnionStatVO {
/**
* 工会ID
*/
private String unionId;
/**
* 工会名称
*/
private String unionName;
/**
* 工会编码
*/
private String unionCode;
/**
* 已办结申请数
*/
private Long applyCount;
/**
* 已办结补助金额合计
*/
private BigDecimal subsidyMoney;
}
@@ -82,6 +82,7 @@ layout("/layouts/platform.html"){
:expand-on-click-node="false"
:props="{ children: 'children', label: 'name' }"
node-key="id"
:default-expanded-keys="defaultExpandedKeys"
@node-click="treeNodeClick"
:filter-node-method="filterNode"
highlight-current
@@ -156,6 +157,7 @@ layout("/layouts/platform.html"){
filterText: null,
currentTreeNode: null,
currentTreeData: null,
defaultExpandedKeys: [],
treeData: []
}
},
@@ -175,6 +177,7 @@ layout("/layouts/platform.html"){
this.treeData = res.data
if (this.treeData) {
this.currentTreeData = this.treeData[0]
this.defaultExpandedKeys = this.treeData.length > 0 ? [this.treeData[0].id] : []
}
})
},
@@ -232,7 +232,7 @@ layout("/layouts/platform.html"){
return masterUnitNames || slaveUnitNames
}
if (row.caseFilingResult === "CONFIRM_FILING") {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function(item) {
return [masterUnitNames ? "主办:" + masterUnitNames : "", slaveUnitNames ? "协办:" + slaveUnitNames : ""].filter(function (item) {
return item
}).join("")
}
@@ -243,7 +243,7 @@ layout("/layouts/platform.html"){
return ""
}
if (Array.isArray(unitNames)) {
return unitNames.filter(function(item) {
return unitNames.filter(function (item) {
return item
}).join("")
}
@@ -363,6 +363,10 @@ layout("/layouts/platform.html"){
})
.then((res) => {
if (res.code === 0) {
res.data.list.map((item) => {
item.brief = removeHTMLTag(item.brief)
item.measures = removeHTMLTag(item.measures)
})
this.tableData = res.data.list
this.pageForm.totalCount = res.data.totalCount
}
@@ -25,9 +25,56 @@ layout("/layouts/platform.html"){
.dimension-pills {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
.analysis-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.analysis-toolbar-main {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px 18px;
flex: 1;
min-width: 0;
}
.analysis-field {
display: flex;
align-items: center;
gap: 8px;
min-width: 330px;
}
.analysis-field-label {
flex: 0 0 auto;
color: #4b617f;
font-size: 13px;
font-weight: 600;
}
.analysis-field-select {
width: 240px;
}
.analysis-dimension-box {
display: flex;
align-items: center;
gap: 10px;
min-width: 420px;
}
.analysis-view-switch {
flex: 0 0 auto;
}
.dimension-pill {
padding: 3px 10px;
border-radius: 999px;
@@ -36,6 +83,14 @@ layout("/layouts/platform.html"){
background: #edf4ff;
border: 1px solid #cfe1ff;
line-height: 1.4;
cursor: pointer;
transition: all 0.2s;
}
.dimension-pill.is-active {
color: #ffffff;
background: #2f7df6;
border-color: #2f7df6;
}
.analysis-tip {
@@ -112,15 +167,28 @@ layout("/layouts/platform.html"){
</style>
<div id="app" v-cloak>
<el-card shadow="never">
<div style="display:flex;justify-content:space-between;align-items:center;gap:12px;">
<search @search="doSearch" style="flex:1;">
<search-item label="教代会届次">
<el-select @change="doSearch" clearable filterable placeholder="请选择教代会届次" v-model="pageForm.sessionId">
<div class="analysis-toolbar">
<div class="analysis-toolbar-main">
<div class="analysis-field">
<span class="analysis-field-label">教代会届次</span>
<el-select class="analysis-field-select" @change="doSearch" clearable filterable placeholder="请选择教代会届次" v-model="pageForm.sessionId">
<el-option :key="item.id" :label="item.fullName" :value="item.id" v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
</search>
<el-button size="mini" type="primary" @click="toggleChartView">
</div>
<div class="analysis-dimension-box">
<span class="analysis-field-label">统计维度</span>
<div class="dimension-pills">
<span
class="dimension-pill"
:class="{'is-active': pageForm.dimension === item.value}"
@click="toggleDimension(item.value)"
v-for="item in dimensionOptions"
:key="item.value"
>{{ item.label }}</span>
</div>
</div>
</div>
<el-button class="analysis-view-switch" size="mini" type="primary" @click="toggleChartView">
{{ showChart ? '切换表格展示' : '切换图形化展示' }}
</el-button>
</div>
@@ -133,17 +201,10 @@ layout("/layouts/platform.html"){
<span class="page-title-accent"></span>
<span class="page-title-text">统计数据</span>
</div>
<div class="dimension-pills mt10">
<span class="dimension-pill">提案人单位</span>
<span class="dimension-pill">提案类型</span>
<span class="dimension-pill">代表团</span>
<span class="dimension-pill">立案结果</span>
<span class="dimension-pill">满意度</span>
</div>
</div>
<el-button size="mini" type="primary" @click="exportStatistics">导出统计数据</el-button>
</div>
<el-table :data="statisticsRows" border stripe style="width: 100%">
<el-table :data="statisticsRows" border stripe style="width: 100%" v-loading="tableLoading">
<el-table-column prop="dimension" label="统计维度" width="180"></el-table-column>
<el-table-column prop="itemName" label="分类项" min-width="260" show-overflow-tooltip></el-table-column>
<el-table-column prop="count" label="数量" width="120"></el-table-column>
@@ -159,7 +220,7 @@ layout("/layouts/platform.html"){
</div>
<el-button size="mini" type="primary" @click="exportAnalysis">导出分析数据</el-button>
</div>
<el-table :data="analysisRows" border stripe style="width: 100%">
<el-table :data="analysisRows" border stripe style="width: 100%" v-loading="tableLoading">
<el-table-column prop="dimension" label="统计维度" width="160"></el-table-column>
<el-table-column prop="total" label="总量" width="100"></el-table-column>
<el-table-column prop="categoryCount" label="分类数量" width="120"></el-table-column>
@@ -177,13 +238,6 @@ layout("/layouts/platform.html"){
<span class="page-title-accent"></span>
<span class="page-title-text">图形化统计展示(混合图)</span>
</div>
<div class="mt10">
<span class="keyword-tag">提案人单位</span>
<span class="keyword-tag">提案类型</span>
<span class="keyword-tag">代表团</span>
<span class="keyword-tag">立案结果</span>
<span class="keyword-tag">满意度</span>
</div>
</div>
<div>
<el-button size="mini" type="primary" @click="exportStatistics">导出统计数据</el-button>
@@ -228,11 +282,20 @@ layout("/layouts/platform.html"){
data() {
return {
pageForm: {
sessionId: ""
sessionId: "",
dimension: ""
},
dimensionOptions: [
{label: "提案人单位", value: "提案人单位"},
{label: "提案类型", value: "提案类型"},
{label: "代表团", value: "代表团"},
{label: "立案结果", value: "立案结果"},
{label: "满意度", value: "满意度"}
],
sessionOptions: [],
statisticsRows: [],
analysisRows: [],
tableLoading: false,
showChart: false,
chartMap: {}
}
@@ -276,6 +339,10 @@ layout("/layouts/platform.html"){
isPieDimension(dimension) {
return ["立案结果", "满意度"].includes(dimension)
},
toggleDimension(dimension) {
this.pageForm.dimension = this.pageForm.dimension === dimension ? "" : dimension
this.doSearch()
},
renderCharts() {
this.destroyCharts()
this.chartGroups.forEach(group => {
@@ -343,18 +410,24 @@ layout("/layouts/platform.html"){
this.destroyCharts()
return
}
this.$axios.post(loc() + "/statisticsData", this.pageForm).then((res) => {
if (res.code === 0) {
this.statisticsRows = res.data || []
this.tableLoading = true
Promise.all([
this.$axios.post(loc() + "/statisticsData", this.pageForm),
this.$axios.post(loc() + "/analysisData", this.pageForm)
]).then((resList) => {
const statisticsRes = resList[0]
const analysisRes = resList[1]
if (statisticsRes.code === 0) {
this.statisticsRows = statisticsRes.data || []
if (this.showChart) {
this.$nextTick(() => this.renderCharts())
}
}
})
this.$axios.post(loc() + "/analysisData", this.pageForm).then((res) => {
if (res.code === 0) {
this.analysisRows = res.data || []
if (analysisRes.code === 0) {
this.analysisRows = analysisRes.data || []
}
}).finally(() => {
this.tableLoading = false
})
},
exportStatistics() {
@@ -376,4 +449,4 @@ layout("/layouts/platform.html"){
</script>
<!--#
}
#-->
#-->
@@ -368,7 +368,7 @@ layout("/layouts/platform.html"){
},
listSession() {
this.$axios.post("/platform/proposal/common/listSession", {
orderBy: "asc"
orderBy: "desc"
}).then((res) => {
if (res.code === 0) {
this.sessionOptions = res.data
@@ -15,6 +15,11 @@ layout("/layouts/platform.html"){
<el-option :key="item.code" :label="item.name" :value="item.id" v-for="item in underTakeOptions"></el-option>
</el-select>
</search-item>
<search-item label="立案结果">
<el-select clearable filterable placeholder="立案结果" v-model="pageForm.caseFilingResult">
<el-option :key="item.code" :label="item.label" :value="item.code" v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT"></el-option>
</el-select>
</search-item>
</search>
</el-card>
@@ -42,6 +47,7 @@ layout("/layouts/platform.html"){
new Vue({
el: "#app",
dicts: ["PROPOSAL_CASE_FILING_RESULT"],
mixins: [initTableMixins],
components: {
"proposal-info": PROPOSAL_INFO
@@ -12,6 +12,18 @@ layout("/layouts/platform.html"){
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="代表团">
<el-select clearable filterable placeholder="所属代表团" v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in delegationOptions"></el-option>
</el-select>
</search-item>
<search-item label="工会">
<el-select clearable filterable placeholder="所属工会" v-model="pageForm.unionId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in unionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
@@ -149,11 +161,14 @@ layout("/layouts/platform.html"){
showApprovalForm: false,
replyUnitOptions: [],
sessionOptions: [],
delegationOptions: [],
unionOptions: [],
}
},
created() {
this.pageData()
this.listOpenSession()
this.listUnion()
},
methods: {
openView(row) {
@@ -179,10 +194,40 @@ layout("/layouts/platform.html"){
this.$refs.proposalInfoRef.onOpen(row)
})
},
// 教代会
async meetingChange(val) {
/**
* 教代会切换后需要同步刷新代表团下拉,避免上一届次代表团条件残留。
*/
meetingChange() {
this.listDelegation()
this.doSearch()
},
/**
* 根据当前教代会加载代表团筛选项,没有选教代会时代表团下拉置空。
*/
listDelegation() {
this.delegationOptions = []
this.$set(this.pageForm, "delegationId", null)
if (!this.pageForm.sessionId) {
return
}
this.$axios.post("/platform/proposal/common/listDelegation", {
sessionId: this.pageForm.sessionId
}).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
},
/**
* 工会下拉按系统分工会列表加载,用于按提案人所属工会筛选当前页面列表。
*/
listUnion() {
this.$businessTool.listUnion().then((res) => {
this.unionOptions = res
})
},
// 查询开启的教代会
listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
@@ -190,8 +235,8 @@ layout("/layouts/platform.html"){
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.pageData()
this.listDelegation()
this.pageData()
}
}
})
@@ -13,6 +13,18 @@ layout("/layouts/platform.html"){
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="代表团">
<el-select clearable filterable placeholder="所属代表团" v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in delegationOptions"></el-option>
</el-select>
</search-item>
<search-item label="工会">
<el-select clearable filterable placeholder="所属工会" v-model="pageForm.unionId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in unionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案编号">
<el-input v-model="pageForm.code" placeholder="提案编号" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
@@ -53,7 +65,7 @@ layout("/layouts/platform.html"){
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" ref="tableRef" row-key="id" style="width: 100%">
<el-table :data="tableData" @sort-change="pageOrder" ref="tableRef" row-key="id" style="width: 100%" v-loading="tableLoading">
<el-table-column type="selection" width="50" fixed="left" v-if="!pageForm.approval"></el-table-column>
<el-table-column label="序号" width="50" type="index" :index="indexMethod"
fixed="left"></el-table-column>
@@ -164,7 +176,7 @@ layout("/layouts/platform.html"){
</template>
<template #public>
<merge ref="mergeRef" @close="$refs.guava.index()"></merge>
<merge ref="mergeRef" @close="handleMergeClose"></merge>
</template>
</guava>
</div>
@@ -210,6 +222,7 @@ layout("/layouts/platform.html"){
showApprovalForm: false,
sessionOptions: [],
delegationOptions: [],
unionOptions: [],
underTakeOptions: []
}
},
@@ -316,6 +329,22 @@ layout("/layouts/platform.html"){
})
},
/**
* 并案审核完成后由父页面统一收口处理:
* 1. 关闭弹层
* 2. 清空左侧勾选框
* 3. 刷新父列表,避免仍显示审核前数据
*/
handleMergeClose() {
this.$refs.guava.index()
this.$nextTick(() => {
if (this.$refs.tableRef) {
this.$refs.tableRef.clearSelection()
}
})
this.doSearch()
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定",
@@ -332,11 +361,42 @@ layout("/layouts/platform.html"){
})
},
// 教代会
async meetingChange(val) {
/**
* 教代会切换后重新加载当前届次代表团,并清空旧的代表团筛选条件,
* 避免跨届次继续沿用旧代表团数据。
*/
meetingChange() {
this.listDelegation()
this.doSearch()
},
/**
* 代表团下拉按教代会联动查询,没有选教代会时保持空列表。
*/
listDelegation() {
this.delegationOptions = []
this.$set(this.pageForm, "delegationId", null)
if (!this.pageForm.sessionId) {
return
}
this.$axios.post("/platform/proposal/common/listDelegation", {
sessionId: this.pageForm.sessionId
}).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
},
/**
* 工会下拉按系统分工会列表加载,用于按提案人所属工会筛选当前页面列表。
*/
listUnion() {
this.$businessTool.listUnion().then((res) => {
this.unionOptions = res
})
},
// 查询开启的教代会
listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
@@ -344,6 +404,7 @@ layout("/layouts/platform.html"){
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.listDelegation()
this.pageData()
}
}
@@ -362,6 +423,7 @@ layout("/layouts/platform.html"){
created() {
this.pageData()
this.listOpenSession()
this.listUnion()
this.listUnderTake()
}
})
@@ -180,7 +180,6 @@ const merge = {
if (res.code === 0) {
this.$emit('close')
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
loading.close()
@@ -13,6 +13,18 @@ layout("/layouts/platform.html"){
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="代表团">
<el-select clearable filterable placeholder="所属代表团" v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in delegationOptions"></el-option>
</el-select>
</search-item>
<search-item label="工会">
<el-select clearable filterable placeholder="所属工会" v-model="pageForm.unionId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in unionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案编号">
<el-input v-model="pageForm.code" placeholder="提案编号" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
@@ -205,6 +217,7 @@ layout("/layouts/platform.html"){
showApprovalForm: false,
sessionOptions: [],
delegationOptions: [],
unionOptions: [],
underTakeOptions: [],
}
},
@@ -373,11 +386,42 @@ layout("/layouts/platform.html"){
})
},
// 教代会
async meetingChange(val) {
/**
* 教代会变更后,按当前届次联动刷新代表团下拉并清空旧值,
* 避免查询条件串到其他届次。
*/
meetingChange() {
this.listDelegation()
this.doSearch()
},
/**
* 根据教代会查询代表团列表,未选教代会时不加载代表团数据。
*/
listDelegation() {
this.delegationOptions = []
this.$set(this.pageForm, "delegationId", null)
if (!this.pageForm.sessionId) {
return
}
this.$axios.post("/platform/proposal/common/listDelegation", {
sessionId: this.pageForm.sessionId
}).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
},
/**
* 工会下拉按系统分工会列表加载,用于按提案人所属工会筛选当前页面列表。
*/
listUnion() {
this.$businessTool.listUnion().then((res) => {
this.unionOptions = res
})
},
// 查询开启的教代会
listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
@@ -385,6 +429,7 @@ layout("/layouts/platform.html"){
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.listDelegation()
this.pageData()
}
}
@@ -403,6 +448,7 @@ layout("/layouts/platform.html"){
created() {
this.pageData()
this.listOpenSession()
this.listUnion()
this.listUnderTake()
}
})
@@ -12,6 +12,18 @@ layout("/layouts/platform.html"){
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="代表团">
<el-select clearable filterable placeholder="所属代表团" v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in delegationOptions"></el-option>
</el-select>
</search-item>
<search-item label="工会">
<el-select clearable filterable placeholder="所属工会" v-model="pageForm.unionId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in unionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
@@ -109,12 +121,16 @@ layout("/layouts/platform.html"){
pageForm: {
approval: false
},
showApprovalForm: false
showApprovalForm: false,
sessionOptions: [],
delegationOptions: [],
unionOptions: []
}
},
created() {
this.pageData()
this.listOpenSession()
this.listUnion()
},
methods: {
openView(row) {
@@ -143,12 +159,49 @@ layout("/layouts/platform.html"){
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.listDelegation()
this.pageData()
}
}
})
},
/**
* 根据当前教代会加载代表团下拉,切换教代会时同步清空已选代表团,
* 避免把上一届教代会的代表团条件带到当前查询里。
*/
listDelegation() {
this.delegationOptions = []
this.$set(this.pageForm, "delegationId", null)
if (!this.pageForm.sessionId) {
return
}
this.$axios.post("/platform/proposal/common/listDelegation", {
sessionId: this.pageForm.sessionId
}).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
},
/**
* 教代会切换后,先联动刷新代表团下拉,再按新的筛选条件重新查询列表。
*/
meetingChange() {
this.listDelegation()
this.doSearch()
},
/**
* 工会下拉按系统分工会列表加载,用于按提案人所属工会筛选当前页面列表。
*/
listUnion() {
this.$businessTool.listUnion().then((res) => {
this.unionOptions = res
})
},
handleTaskAction(val) {
this.$refs.formRef.validate(valid => {
if (!valid) return
@@ -13,6 +13,18 @@ layout("/layouts/platform.html"){
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="代表团">
<el-select clearable filterable placeholder="所属代表团" v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in delegationOptions"></el-option>
</el-select>
</search-item>
<search-item label="工会">
<el-select clearable filterable placeholder="所属工会" v-model="pageForm.unionId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in unionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案编号">
<el-input v-model="pageForm.code" placeholder="提案编号" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
@@ -147,6 +159,7 @@ layout("/layouts/platform.html"){
showApprovalForm: false,
sessionOptions: [],
delegationOptions: [],
unionOptions: [],
}
},
methods: {
@@ -242,11 +255,42 @@ layout("/layouts/platform.html"){
})
},
// 教代会
async meetingChange(val) {
/**
* 教代会切换时重新联动查询代表团,并清空上一届次已选代表团,
* 保证列表始终按当前教代会范围查询。
*/
meetingChange() {
this.listDelegation()
this.doSearch()
},
/**
* 代表团下拉只展示当前教代会下的数据,未选教代会时保持空列表。
*/
listDelegation() {
this.delegationOptions = []
this.$set(this.pageForm, "delegationId", null)
if (!this.pageForm.sessionId) {
return
}
this.$axios.post("/platform/proposal/common/listDelegation", {
sessionId: this.pageForm.sessionId
}).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
},
/**
* 工会下拉按系统分工会列表加载,用于按提案人所属工会筛选当前页面列表。
*/
listUnion() {
this.$businessTool.listUnion().then((res) => {
this.unionOptions = res
})
},
// 查询开启的教代会
listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
@@ -254,6 +298,7 @@ layout("/layouts/platform.html"){
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.listDelegation()
this.pageData()
}
}
@@ -263,6 +308,7 @@ layout("/layouts/platform.html"){
created() {
this.pageData()
this.listOpenSession()
this.listUnion()
}
})
</script>
@@ -28,7 +28,8 @@ layout("/layouts/platform.html"){
</el-radio-group>
</table-tool>
<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"
fixed="left"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
@@ -54,10 +55,11 @@ layout("/layouts/platform.html"){
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10 || row.count <= 0" @click="openAudit(row)" size="mini" type="primary">附议
<el-button v-if="showSecondAction(row)"
@click="openAudit(row)" size="mini" type="primary">附议
</el-button>
<!-- <el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回-->
<!-- </el-button>-->
<!-- <el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回-->
<!-- </el-button>-->
</template>
</el-table-column>
</el-table>
@@ -68,7 +70,8 @@ layout("/layouts/platform.html"){
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{formData.taskName}}
<!-- {{formData.taskName}}-->
提案附议
</div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix=""
class="flow-task-form">
@@ -77,16 +80,16 @@ layout("/layouts/platform.html"){
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<el-row type="flex" justify="end" v-if="row.taskKey ==='second'">
<el-row type="flex" justify="end" v-if="row.curTaskCode ==='second'">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(20)" size="small" type="danger">不同意</el-button>
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意</el-button>
</el-row>
<el-row type="flex" justify="end" v-else>
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="updateSecond(2)" size="small" type="danger">不同意</el-button>
<el-button @click="updateSecond(1)" size="small" type="primary">同意</el-button>
</el-row>
<el-row type="flex" justify="end" v-else>
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="updateSecond(2)" size="small" type="danger">不同意</el-button>
<el-button @click="updateSecond(1)" size="small" type="primary">同意</el-button>
</el-row>
</div>
</proposal-info>
</template>
@@ -124,6 +127,12 @@ layout("/layouts/platform.html"){
}
},
methods: {
// 附议按钮显示规则:
// 1. 当前任务进行中(taskState=10)时显示
// 2. 当前任务已废弃(taskState=99)时,仅 invite、second、delegation、committee 节点显示
showSecondAction(row) {
return row.taskState === 10 || (row.taskState === 99 && ["invite", "second", "delegation", "preAudit", "committee"].includes(row.taskKey))
},
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
@@ -131,11 +140,13 @@ layout("/layouts/platform.html"){
})
},
openAudit(row) {
this.row = row
this.row = row
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
proposalId: row.id,
taskActorUserId: row.taskActorUserId,
taskName: row.curTaskName,
tf_opinion: "同意该提案"
}
@@ -151,7 +162,7 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
this.$axios.post("/platform/proposal/seconded/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
@@ -161,27 +172,34 @@ layout("/layouts/platform.html"){
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
this.updateSecond(val, false)
}
})
})
})
},
updateSecond(submitType, flag = true) {
this.$axios.post("/platform/proposal/seconded/updateInfo", {
proposalId: this.row.id,
taskActorUserId: this.row.taskActorUserId,
opinion: this.formData.tf_opinion,
submitType: submitType,
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
if(flag) this.$message.success(res.msg)
this.doSearch()
}
})
},
updateSecond(submitType) {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/proposal/seconded/updateInfo", {
proposalId: this.row.id,
taskId: this.row.taskId,
taskActorUserId: this.row.taskActorUserId,
opinion: this.formData.tf_opinion,
submitType: submitType,
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
})
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
@@ -144,7 +144,7 @@ layout("/layouts/platform.html"){
</el-button>
<!--历史答复内容只允许超级管理员修改,且只在已审核数据中开放入口。-->
<el-button v-if="pageForm.approval && row.taskState === 20 && $auth.hasRole('SYSADMIN')"
@click="openReplyEdit(row)" size="mini" type="warning">编辑答复
@click="openReplyEdit(row)" size="mini" type="primary">编辑答复
</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button>
@@ -9,6 +9,10 @@ const APPLY_INFO = {
<el-descriptions-item label="工号">{{viewData.loginname}}</el-descriptions-item>
<el-descriptions-item label="联系方式">{{viewData.mobile}}</el-descriptions-item>
<el-descriptions-item label="性别">{{viewData.sex}}</el-descriptions-item>
<el-descriptions-item label="基金会员类型">
<dict-tag :options="dict.type.AIDFUND_MEMBER_USER_TYPE"
:value="viewData.aidFundMemberUserType"></dict-tag>
</el-descriptions-item>
<el-descriptions-item label="入校时间">{{$moment(viewData.arrivalAtSchoolDate).format('YYYY-MM-DD')}}
</el-descriptions-item>
<el-descriptions-item label="所在单位">{{viewData.unitName}}</el-descriptions-item>
@@ -60,6 +60,16 @@ layout("/layouts/platform.html"){
<el-card shadow="never" class="mt10">
<table-tool label="申请记录">
<el-button @click="openBatchAudit"
:disabled="pageForm.approval===true||pageForm.approval==='true'"
size="small"
type="primary">批量审核
</el-button>
<el-button @click="exportApplyRecord"
icon="el-icon-download"
size="small"
type="primary">导出
</el-button>
<el-radio-group @change="doSearch" class="mr5" size="small" v-model="pageForm.approval">
<el-radio-button label="true">已审核</el-radio-button>
<el-radio-button label="false">未审核</el-radio-button>
@@ -68,6 +78,11 @@ layout("/layouts/platform.html"){
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table" row-key="id" style="width: 100%">
<el-table-column
:reserve-selection="true"
type="selection"
width="55">
</el-table-column>
<el-table-column :index="indexMethod" header-align="center" label="序号"
type="index" width="60px"></el-table-column>
<el-table-column
@@ -90,19 +105,8 @@ layout("/layouts/platform.html"){
:value="row.aidFundMemberUserType"></dict-tag>
</template>
<template scope="{row}" v-else-if="column.prop=='changeType'">
<template v-if="row.changeType==='AIDFUND_MEMBER_CHANGE_TYPE_ONE'">
<el-tag type="success">
<dict-tag :options="dict.type.AIDFUND_MEMBER_CHANGE_TYPE"
:value="row.changeType"></dict-tag>
</el-tag>
</template>
<template v-else>
<el-tag type="danger">
<dict-tag :options="dict.type.AIDFUND_MEMBER_CHANGE_TYPE"
:value="row.changeType"></dict-tag>
</el-tag>
</template>
<dict-tag :options="dict.type.AIDFUND_MEMBER_CHANGE_TYPE"
:value="row.changeType"></dict-tag>
</template>
<template scope="{row}" v-else-if="column.prop=='money'">
<template v-if="row.changeType==='AIDFUND_MEMBER_CHANGE_TYPE_ONE'">
@@ -124,6 +128,43 @@ layout("/layouts/platform.html"){
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog
:visible.sync="batchAuditVisible"
title="批量审核"
width="40%">
<el-form :model="batchForm" :rules="batchFormRules" label-width="100px" ref="batchFormRef">
<el-form-item label="审核状态" prop="submitType">
<el-radio-group v-model="batchForm.submitType">
<el-radio-button :label="2">拒绝</el-radio-button>
<el-radio-button :label="1">通过</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="审核名单">
<el-table :data="batchAuditTableData" row-key="id" style="width: 100%" max-height="300">
<el-table-column label="姓名" prop="userName" min-width="100"></el-table-column>
<el-table-column label="工号" prop="loginName" min-width="120"></el-table-column>
<el-table-column label="单位" prop="unitName" min-width="140" show-overflow-tooltip></el-table-column>
<el-table-column label="入校时间" prop="arrivalAtSchoolDate" min-width="110"></el-table-column>
<el-table-column label="缴费金额" prop="money" min-width="100"></el-table-column>
</el-table>
</el-form-item>
<el-alert
:closable="false"
class="mb10"
title="批量通过时,将按人员当前入校时间自动提交;缺少入校时间的记录会审核失败。"
type="warning"
v-if="batchForm.submitType===1">
</el-alert>
<el-form-item label="审批意见" prop="tf_opinion">
<user-opinion-textarea v-model="batchForm.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<span class="dialog-footer" slot="footer">
<el-button @click="batchAuditVisible = false">取 消</el-button>
<el-button @click="submitBatchAudit" type="primary">提 交</el-button>
</span>
</el-dialog>
<template #edit>
<info ref="infoRef">
<div v-if="showApprovalForm">
@@ -202,12 +243,27 @@ layout("/layouts/platform.html"){
{prop: "unitName", label: "所属单位"},
{prop: "unionName", label: "所属工会"},
{prop: "aidFundMemberUserType", label: "基金会员类型"},
{prop: "money", label: "缴费金额"},
{prop: "changeType", label: "变更类型"},
{prop: "applyTime", label: "申请日期"},
{prop: "curTaskName", label: "当前节点"},
{prop: "instanceState", label: "流程状态"}
],
showApprovalForm: false,
batchAuditVisible: false,
batchAuditTableData: [],
batchForm: {
submitType: 1,
tf_opinion: ""
},
batchFormRules: {
submitType: [
{required: true, message: "必填", trigger: ["change", "blur"]}
],
tf_opinion: [
{required: true, message: "必填", trigger: ["change", "blur"]}
]
},
unionOptions: [],
unitOptions: [],
pickerOptions: {
@@ -221,6 +277,91 @@ layout("/layouts/platform.html"){
'info': APPLY_INFO,
},
methods: {
openBatchAudit() {
const selection = this.$refs.table.selection
if (!selection || selection.length === 0) {
this.$message.warning("请先勾选需要审核的记录")
return
}
this.$set(this, "batchForm", {
submitType: 1,
tf_opinion: ""
})
this.$set(this, "batchAuditTableData", selection.map(row => {
return {
id: row.id,
userName: row.userName,
loginName: row.loginName,
unitName: row.unitName,
arrivalAtSchoolDate: row.arrivalAtSchoolDate,
money: row.money
}
}))
this.$set(this, "batchAuditVisible", true)
this.$nextTick(() => {
if (this.$refs.batchFormRef) {
this.$refs.batchFormRef.clearValidate()
}
})
},
submitBatchAudit() {
const selection = this.$refs.table.selection
if (!selection || selection.length === 0) {
this.$message.warning("请先勾选需要审核的记录")
return
}
this.$refs.batchFormRef.validate((valid) => {
if (!valid) {
return
}
let message = "确定要审核这 " + selection.length + " 条记录吗?"
const joinCount = selection.filter(row => row.changeType === "AIDFUND_MEMBER_CHANGE_TYPE_ONE").length
if (this.batchForm.submitType === 1 && joinCount > 0) {
message = "确定要批量通过这 " + selection.length + " 条记录吗?其中有 " + joinCount + " 条加入基金记录将按当前人员入校时间自动审核。"
}
this.$confirm(message, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
const ids = selection.map(row => row.id)
const loading = this.$loading({
lock: true,
text: "正在批量审核...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
})
this.$axios.post("/platform/medicalMutualAid/aidFund/foundationAudit/batchExecuteTask", {
ids: JSON.stringify(ids),
data: JSON.stringify(this.batchForm)
}).then((res) => {
if (res.code === 0) {
const result = res.data || {}
const successCount = result.successCount || 0
const failCount = result.failCount || 0
const failMessages = result.failMessages || []
if (failCount > 0) {
let failText = failMessages.slice(0, 3).join("")
if (failMessages.length > 3) {
failText = failText + ";其余记录请查看列表后重试"
}
this.$message.warning("批量审核完成:成功 " + successCount + " 条,失败 " + failCount + " 条。" + failText)
} else {
this.$message.success("批量审核完成:成功 " + successCount + " 条")
}
this.$set(this, "batchAuditVisible", false)
this.doSearch()
this.$refs.table.clearSelection()
}
}).finally(() => {
loading.close()
})
})
})
},
exportApplyRecord() {
this.$downLoad("/platform/medicalMutualAid/aidFund/foundationAudit/exportApplyRecord", this.pageForm)
},
getPayMoney() {
this.$axios.post('/platform/medicalMutualAid/aidFund/foundationAudit/getPayMoney', {
arrivalAtSchoolDate: this.formData.arrivalAtSchoolDate,
@@ -56,6 +56,11 @@ layout("/layouts/platform.html"){
批量变更
</el-button>
<el-button @click="exportMemberList" icon="el-icon-download" size="small" type="primary"
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage')">
导出会员名单
</el-button>
<el-button @click="exportNewMember" icon="el-icon-download" size="small" type="primary"
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage.exportNewMember')" class="mr10">
导出{{new
@@ -448,6 +453,9 @@ layout("/layouts/platform.html"){
exportNewMember() {
this.$downLoad(loc() + "/exportNewMember")
},
exportMemberList() {
this.$downLoad(loc() + "/exportMemberList", this.pageForm)
},
getYearOptions() {
let year = new Date().getFullYear();
let arr = []
@@ -534,6 +534,7 @@ layout("/layouts/platform.html"){
} else {
await this.createRemoteMethod(this.$store.state.user.id)
this.$set(this.formData, 'sex', this.$store.state.user.sex)
this.$set(this.formData, 'userName', this.$store.state.user.username)
if (this.$store.state.user.birthday) {
this.$set(this.formData, 'age', this.$moment().diff(this.$store.state.user.birthday, 'years'))
}
@@ -216,6 +216,7 @@ layout("/layouts/platform.html"){
}
},
computed: {
//申请费用总额
applyLoveSum() {
const {applySum, loveSubsidyMoney} = this.calcData
@@ -244,6 +245,16 @@ layout("/layouts/platform.html"){
},
},
methods: {
async grantOrIntoChange(key, val) {
if (val === '') {
if (key === 'grants') {
this.calcData.schoolGrants = 0
} else if (key === 'into') {
this.calcData.unionInto = 0
}
}
const resp = await this.$axios.post(loc() + '/setGrantsOrUnionInto', {keys: key, value: val})
},
getSummaries(param) {
const {columns, data} = param
const sums = []
@@ -0,0 +1,418 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.medical-dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 180px 1fr 1fr;
gap: 20px;
width: 100%;
min-height: calc(100vh - 126px);
box-sizing: border-box;
}
.dashboard-card {
background: #ffffff;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
border: 1px solid #eef1f6;
}
.dashboard-title {
padding: 16px 20px 8px;
font-size: 16px;
font-weight: 600;
color: #333333;
border-bottom: 1px solid #f0f2f5;
flex-shrink: 0;
}
.overview-card {
padding: 20px;
box-sizing: border-box;
justify-content: space-between;
}
.overview-label {
font-size: 14px;
color: #7a7f87;
line-height: 1;
}
.overview-value {
font-size: 34px;
font-weight: 700;
color: #1f6f5f;
line-height: 1.2;
margin-top: 18px;
}
.overview-sub {
font-size: 13px;
color: #949aa3;
line-height: 1.6;
margin-top: auto;
}
.wide-panel {
grid-column: span 2;
min-height: 0;
}
.full-panel {
grid-column: 1 / -1;
min-height: 0;
}
.chart-panel {
min-height: 0;
}
.table-panel {
min-height: 0;
}
.panel-body {
flex: 1;
min-height: 0;
padding: 12px 16px 16px;
}
.chart-box {
width: 100%;
height: 100%;
min-height: 260px;
}
.money-text {
color: #cf6a32;
}
@media (max-width: 1400px) {
.medical-dashboard {
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 180px) auto auto auto;
}
.wide-panel {
grid-column: span 2;
}
.full-panel {
grid-column: 1 / -1;
}
}
@media (max-width: 768px) {
.medical-dashboard {
grid-template-columns: 1fr;
grid-template-rows: repeat(4, 160px) auto auto auto;
gap: 15px;
}
.wide-panel {
grid-column: span 1;
}
.full-panel {
grid-column: span 1;
}
.overview-value {
font-size: 28px;
}
.chart-box {
min-height: 220px;
}
}
</style>
<div id="app" v-cloak>
<guava ref="guava">
<div class="medical-dashboard">
<div class="dashboard-card overview-card" v-loading="loading.overview">
<div class="overview-label">基金会员总数</div>
<div class="overview-value">{{ overview.fundMemberCount || 0 }}</div>
<div class="overview-sub">
本年度申请数:{{ overview.currentYearApplyCount || 0 }}
</div>
</div>
<div class="dashboard-card overview-card" v-loading="loading.overview">
<div class="overview-label">重疾 / 普疾申请数</div>
<div class="overview-value">{{ overview.majorDiseaseApplyCount || 0 }} / {{ overview.commonDiseaseApplyCount || 0 }}</div>
<div class="overview-sub">
统计口径:流程已办结
</div>
</div>
<div class="dashboard-card overview-card" v-loading="loading.overview">
<div class="overview-label">补助金额合计</div>
<div class="overview-value money-text">{{ formatMoney(overview.totalSubsidyMoney) }}</div>
<div class="overview-sub">
单位:元
</div>
</div>
<div class="dashboard-card overview-card" v-loading="loading.overview">
<div class="overview-label">爱心基金合计</div>
<div class="overview-value money-text">{{ formatMoney(overview.totalLoveSubsidyMoney) }}</div>
<div class="overview-sub">
单位:元
</div>
</div>
<div class="dashboard-card wide-panel table-panel">
<div class="dashboard-title">各工会医疗互助统计表</div>
<div class="panel-body" v-loading="loading.unionStat">
<el-table
:data="unionTableData"
:header-cell-style="{background:'#FAFAFA'}"
height="100%"
style="width: 100%">
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="unionName" label="工会名称" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column prop="applyCount" label="申请数" width="100" align="center"></el-table-column>
<el-table-column label="补助金额" min-width="140" align="right">
<template slot-scope="{row}">
{{ formatMoney(row.subsidyMoney) }}
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="dashboard-card wide-panel chart-panel">
<div class="dashboard-title">在职状态分布</div>
<div class="panel-body" v-loading="loading.userStateStat">
<div id="userStateChart" class="chart-box"></div>
</div>
</div>
<div class="dashboard-card full-panel chart-panel">
<div class="dashboard-title">人员类型统计</div>
<div class="panel-body" v-loading="loading.personTypeStat">
<div id="personTypeChart" class="chart-box"></div>
</div>
</div>
<div class="dashboard-card full-panel">
<div class="dashboard-title">疾病类型统计</div>
<div class="panel-body" v-loading="loading.diseaseTypeStat">
<div id="diseaseTypeChart" class="chart-box"></div>
</div>
</div>
</div>
</guava>
</div>
<script nonce="${cspNonce!}">
const chartMap = {
userStateChart: null,
personTypeChart: null,
diseaseTypeChart: null
}
new Vue({
el: "#app",
data() {
return {
loading: {
overview: false,
unionStat: false,
userStateStat: false,
personTypeStat: false,
diseaseTypeStat: false
},
overview: {
fundMemberCount: 0,
currentYearApplyCount: 0,
majorDiseaseApplyCount: 0,
commonDiseaseApplyCount: 0,
totalSubsidyMoney: 0,
totalLoveSubsidyMoney: 0
},
unionTableData: [],
userStateData: [],
personTypeData: [],
diseaseTypeData: []
}
},
methods: {
formatMoney(value) {
if (value === null || value === undefined || value === "") {
return "0.00"
}
const num = Number(value)
if (isNaN(num)) {
return "0.00"
}
return num.toFixed(2)
},
getOverview() {
this.$set(this.loading, "overview", true)
this.$axios.post("/platform/medicalMutualAid/medical/dashboard/overview").then((res) => {
this.$set(this.loading, "overview", false)
if (res.code === 0) {
this.$set(this, "overview", res.data || {})
} else {
this.$message.warning(res.msg)
}
})
},
getUnionStat() {
this.$set(this.loading, "unionStat", true)
this.$axios.post("/platform/medicalMutualAid/medical/dashboard/unionStat").then((res) => {
this.$set(this.loading, "unionStat", false)
if (res.code === 0) {
const tableData = res.data && res.data.tableData ? res.data.tableData : []
this.$set(this, "unionTableData", tableData)
} else {
this.$message.warning(res.msg)
}
})
},
getUserStateStat() {
this.$set(this.loading, "userStateStat", true)
this.$axios.post("/platform/medicalMutualAid/medical/dashboard/userStateStat").then((res) => {
this.$set(this.loading, "userStateStat", false)
if (res.code === 0) {
this.$set(this, "userStateData", res.data || [])
this.renderUserStateChart()
} else {
this.$message.warning(res.msg)
}
})
},
getPersonTypeStat() {
this.$set(this.loading, "personTypeStat", true)
this.$axios.post("/platform/medicalMutualAid/medical/dashboard/personTypeStat").then((res) => {
this.$set(this.loading, "personTypeStat", false)
if (res.code === 0) {
this.$set(this, "personTypeData", res.data || [])
this.renderPersonTypeChart()
} else {
this.$message.warning(res.msg)
}
})
},
getDiseaseTypeStat() {
this.$set(this.loading, "diseaseTypeStat", true)
this.$axios.post("/platform/medicalMutualAid/medical/dashboard/diseaseTypeStat").then((res) => {
this.$set(this.loading, "diseaseTypeStat", false)
if (res.code === 0) {
this.$set(this, "diseaseTypeData", res.data || [])
this.renderDiseaseTypeChart()
} else {
this.$message.warning(res.msg)
}
})
},
renderUserStateChart() {
if (chartMap.userStateChart) {
chartMap.userStateChart.destroy()
}
chartMap.userStateChart = new G2Plot.Pie("userStateChart", {
data: this.userStateData,
angleField: "applyCount",
colorField: "name",
radius: 0.85,
label: {
type: "outer",
content: "{name} {percentage}"
},
legend: {
position: "bottom"
},
color: ["#1f6f5f", "#f59f45", "#5b8ff9", "#9270ca"]
})
chartMap.userStateChart.render()
},
renderPersonTypeChart() {
if (chartMap.personTypeChart) {
chartMap.personTypeChart.destroy()
}
chartMap.personTypeChart = new G2Plot.Column("personTypeChart", {
data: this.personTypeData,
autoFit: true,
xField: "name",
yField: "applyCount",
color: "#4e8f81",
columnStyle: {
radius: [6, 6, 0, 0]
},
label: {
position: "top",
style: {
fill: "#1f2d3d"
}
},
xAxis: {
label: {
autoHide: true,
autoRotate: false
}
},
meta: {
name: {
alias: "人员类型"
},
applyCount: {
alias: "申请数"
}
}
})
chartMap.personTypeChart.render()
},
renderDiseaseTypeChart() {
if (chartMap.diseaseTypeChart) {
chartMap.diseaseTypeChart.destroy()
}
chartMap.diseaseTypeChart = new G2Plot.Bar("diseaseTypeChart", {
data: this.diseaseTypeData,
autoFit: true,
xField: "applyCount",
yField: "name",
legend: false,
color: "#cf6a32",
barStyle: {
radius: [0, 6, 6, 0]
},
label: {
position: "right",
style: {
fill: "#1f2d3d"
}
},
meta: {
name: {
alias: "疾病类型"
},
applyCount: {
alias: "申请数"
}
}
})
chartMap.diseaseTypeChart.render()
},
initData() {
this.getOverview()
this.getUnionStat()
this.getUserStateStat()
this.getPersonTypeStat()
this.getDiseaseTypeStat()
}
},
mounted() {
this.initData()
}
})
</script>
<!--#
}
#-->
@@ -38,20 +38,21 @@ layout("/layouts/platform_h5.html"){
<i class="fa fa-eye"></i>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<div class="action-btn" v-if="showSecondAction(row)" @click="onApproval(row)">
<i class="fa fa-edit"></i>
<span>附议</span>
</div>
<!-- <div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">-->
<!-- <i class="fa fa-reply"></i>-->
<!-- <span>撤回</span>-->
<!-- </div>-->
<!-- <div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">-->
<!-- <i class="fa fa-reply"></i>-->
<!-- <span>撤回</span>-->
<!-- </div>-->
</template>
</table-list>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<div class="process-title">提案附议</div>
<!-- <div class="process-title">{{formData.taskName}}</div>-->
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
@@ -67,10 +68,15 @@ layout("/layouts/platform_h5.html"){
<!-- <h5-signature v-model="formData.approvalSignature" slot="input"></h5-signature>-->
<!-- </van-field>-->
</van-form>
<div style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<div v-if="row && row.curTaskCode === 'second'"
style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="danger" block @click="handleTaskAction(20)">不同意</van-button>
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
</div>
<div v-else style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="danger" block @click="updateSecond(2)">不同意</van-button>
<van-button type="primary" block @click="updateSecond(1)">同意</van-button>
</div>
</div>
</proposal-info>
@@ -94,6 +100,7 @@ layout("/layouts/platform_h5.html"){
},
sessionOptions: [],
row: null,
formData: {},
showApprovalForm: false,
rules: {
@@ -105,6 +112,12 @@ layout("/layouts/platform_h5.html"){
"proposal-info": PROPOSAL_INFO
},
methods: {
// 附议按钮显示规则:
// 1. 当前任务进行中(taskState=10)时显示
// 2. 当前任务已废弃(taskState=99)时,仅 invite、second、delegation、committee 节点显示
showSecondAction(row) {
return row.taskState === 10 || (row.taskState === 99 && ["invite", "second", "preAudit", "delegation", "committee"].includes(row.taskKey))
},
onReady() {
this.listSession()
},
@@ -131,10 +144,14 @@ layout("/layouts/platform_h5.html"){
},
onApproval(row) {
this.row = row
this.showApprovalForm = true
this.$refs.proposalInfoRef.onOpen(row)
this.formData = {
processTaskId: row.taskId,
taskId: row.taskId,
proposalId: row.id,
taskActorUserId: row.taskActorUserId,
taskName: row.curTaskName,
tf_opinion: "同意该提案"
}
@@ -147,7 +164,7 @@ layout("/layouts/platform_h5.html"){
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
this.$axios.post("/platform/proposal/seconded/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
@@ -167,6 +184,29 @@ layout("/layouts/platform_h5.html"){
}
},
updateSecond(submitType) {
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/platform/proposal/seconded/updateInfo", {
proposalId: this.formData.proposalId,
taskId: this.formData.taskId,
taskActorUserId: this.formData.taskActorUserId,
opinion: this.formData.tf_opinion,
submitType: submitType,
}).then((res) => {
if (res.code === 0) {
this.$refs.proposalInfoRef.onClose()
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {
})
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
@@ -225,7 +225,7 @@ layout("/layouts/platform_h5.html"){
></van-picker>
</van-popup>
<van-field
<!-- <van-field
:value="formData.suggestUnits.length > 0 ? formData.suggestUnits.join(', ') : ''"
is-link
readonly
@@ -235,7 +235,7 @@ layout("/layouts/platform_h5.html"){
@click="showSuggestionUnitPicker = true"
:rules="[{ required: true, message: '请选择建议承办单位' }]"
required
></van-field>
></van-field>-->
<van-popup v-model="showSuggestionUnitPicker" position="bottom" :style="{ height: '70%' }">
<div class="suggestion-unit-picker">
<!-- 头部工具栏 -->
@@ -719,6 +719,8 @@ layout("/layouts/platform_h5.html"){
if (this.bizId) {
this.$axios.post("/platform/proposal/write/detail", {id: this.bizId}).then((res) => {
if (res.code === 0) {
res.data.brief=removeHTMLTag(res.data.brief)
res.data.measures=removeHTMLTag(res.data.measures)
this.formData = res.data;
this.listOpenSession(true);
}
@@ -29,7 +29,7 @@ layout("/layouts/platform_h5.html"){
<i class="fa fa-mail-forward"></i>
<span>提交</span>
</div>
<div class="action-btn delete" @click="onDelete(row)"
<div class="action-btn delete" @click="onDelete(row.id)"
v-if="row.taskKey === 'startTask' || !row.instanceId">
<i class="fa fa-trash"></i>
<span>删除</span>