1
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
package com.budwk.app.zhgh.democratic.opinion.mode;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import com.budwk.app.base.model.ExcelImportError;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 校工会审核结果导入模板行数据。
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class OpinionSchoolAuditImportExcelMode extends ExcelImportError {
|
||||
|
||||
@Excel(name = "意见编号")
|
||||
private String code;
|
||||
|
||||
@Excel(name = "意见和建议")
|
||||
private String name;
|
||||
|
||||
@Excel(name = "意见类别")
|
||||
private String typeName;
|
||||
|
||||
@Excel(name = "建议人")
|
||||
private String createUserName;
|
||||
|
||||
@Excel(name = "建议人所属代表团")
|
||||
private String delegationName;
|
||||
|
||||
@Excel(name = "建议人所属单位")
|
||||
private String unitName;
|
||||
|
||||
@Excel(name = "主办")
|
||||
private String masterUnitName;
|
||||
|
||||
@Excel(name = "协办")
|
||||
private String slaveUnitNames;
|
||||
|
||||
@Excel(name = "审核结果")
|
||||
private String auditResult;
|
||||
|
||||
@Excel(name = "审核意见")
|
||||
private String auditOpinion;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.budwk.app.zhgh.democratic.opinion.service;
|
||||
|
||||
import com.budwk.app.base.model.ExcelImportRes;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.democratic.opinion.mode.OpinionSchoolAuditImportExcelMode;
|
||||
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 校工会审核业务处理。
|
||||
*/
|
||||
public interface OpinionSchoolAuditService extends BaseService<OpinionInfo> {
|
||||
|
||||
/**
|
||||
* 下载校工会审核结果导入模板。
|
||||
*
|
||||
* @param response 文件响应对象
|
||||
*/
|
||||
void downloadAuditImportTemplate(HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 批量导入校工会审核结果,并按页面审核按钮的流程参数执行待办任务。
|
||||
*
|
||||
* @param file 上传的 Excel 文件
|
||||
* @param sessionId 当前页面选中的教代会届次 ID,用于限定意见编号匹配范围
|
||||
* @return 导入审核结果,包含成功数、失败数和错误明细
|
||||
*/
|
||||
ExcelImportRes<OpinionSchoolAuditImportExcelMode> importAuditResult(TempFile file, String sessionId);
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
package com.budwk.app.zhgh.democratic.opinion.service.impl;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
|
||||
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.model.ExcelImportRes;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.util.FlowUtil;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import com.budwk.app.flow.service.FlowCommonService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.democratic.opinion.mode.OpinionSchoolAuditImportExcelMode;
|
||||
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
|
||||
import com.budwk.app.zhgh.democratic.opinion.models.OpinionType;
|
||||
import com.budwk.app.zhgh.democratic.opinion.service.OpinionSchoolAuditService;
|
||||
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.entity.Record;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 校工会审核业务处理实现。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class OpinionSchoolAuditServiceImpl extends BaseServiceImpl<OpinionInfo> implements OpinionSchoolAuditService {
|
||||
|
||||
@Inject
|
||||
private FlowEngine flowEngine;
|
||||
@Inject
|
||||
private FlowCommonService flowCommonService;
|
||||
|
||||
public OpinionSchoolAuditServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadAuditImportTemplate(HttpServletResponse response) {
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("序号", "index", 10));
|
||||
entities.add(new ExcelExportEntity("意见编号", "code", 20));
|
||||
entities.add(new ExcelExportEntity("意见和建议", "name", 50));
|
||||
entities.add(new ExcelExportEntity("意见类别", "typeName", 20));
|
||||
entities.add(new ExcelExportEntity("建议人", "createUserName", 20));
|
||||
entities.add(new ExcelExportEntity("建议人所属代表团", "delegationName", 25));
|
||||
entities.add(new ExcelExportEntity("建议人所属单位", "unitName", 25));
|
||||
entities.add(new ExcelExportEntity("主办", "masterUnitName", 35));
|
||||
entities.add(new ExcelExportEntity("协办", "slaveUnitNames", 35));
|
||||
entities.add(new ExcelExportEntity("审核结果", "auditResult", 20));
|
||||
entities.add(new ExcelExportEntity("审核意见", "auditOpinion", 35));
|
||||
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
|
||||
CommonDownloadUtil.download("校工会审核结果导入模板.xlsx", workbook, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExcelImportRes<OpinionSchoolAuditImportExcelMode> importAuditResult(TempFile file, String sessionId) {
|
||||
List<OpinionSchoolAuditImportExcelMode> importRows = ExcelImportUtil.importExcel(file.getFile(), OpinionSchoolAuditImportExcelMode.class, new ImportParams());
|
||||
ExcelImportRes<OpinionSchoolAuditImportExcelMode> excelImportRes = new ExcelImportRes<>();
|
||||
excelImportRes.setTotalRecords(importRows.size());
|
||||
|
||||
Set<String> importedCodes = new HashSet<>();
|
||||
for (int i = 0; i < importRows.size(); i++) {
|
||||
OpinionSchoolAuditImportExcelMode row = importRows.get(i);
|
||||
if (isEmptyRow(row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 逐行校验并执行流程,导入失败的行只记录错误,不影响后续行继续处理。
|
||||
handleAuditImportRow(row, i, sessionId, importedCodes);
|
||||
}
|
||||
|
||||
List<OpinionSchoolAuditImportExcelMode> errorDetails = importRows.stream()
|
||||
.filter(row -> StrUtil.isNotBlank(row.getErrMsg()))
|
||||
.collect(Collectors.toList());
|
||||
excelImportRes.setErrorDetails(errorDetails);
|
||||
excelImportRes.setFailedCount(errorDetails.size());
|
||||
excelImportRes.setSuccessCount((int) importRows.stream()
|
||||
.filter(row -> !isEmptyRow(row))
|
||||
.filter(row -> StrUtil.isBlank(row.getErrMsg()))
|
||||
.count());
|
||||
return excelImportRes;
|
||||
}
|
||||
|
||||
private void handleAuditImportRow(OpinionSchoolAuditImportExcelMode row, int rowIndex, String sessionId, Set<String> importedCodes) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
String code = StrUtil.trim(row.getCode());
|
||||
if (StrUtil.isBlank(code)) {
|
||||
errors.add("意见编号不能为空");
|
||||
} else if (!importedCodes.add(code)) {
|
||||
errors.add("意见编号重复");
|
||||
}
|
||||
|
||||
AuditResult auditResult = parseAuditResult(row.getAuditResult(), errors);
|
||||
if (StrUtil.isBlank(row.getAuditOpinion())) {
|
||||
errors.add("审核意见不能为空");
|
||||
}
|
||||
|
||||
Record taskInfo = StrUtil.isBlank(code) ? null : fetchDoingSchoolAuditTask(code, sessionId);
|
||||
if (taskInfo == null && StrUtil.isNotBlank(code)) {
|
||||
errors.add("未找到当前届次下可审核的校工会待办意见");
|
||||
}
|
||||
|
||||
OpinionType type = null;
|
||||
ProposalUndertake masterUnit = null;
|
||||
List<ProposalUndertake> slaveUnits = new ArrayList<>();
|
||||
if (auditResult == AuditResult.PASS) {
|
||||
type = fetchOpinionType(row.getTypeName());
|
||||
if (type == null) {
|
||||
errors.add("意见类别不存在");
|
||||
}
|
||||
|
||||
masterUnit = fetchUndertake(row.getMasterUnitName());
|
||||
if (masterUnit == null) {
|
||||
errors.add("主办单位不存在");
|
||||
}
|
||||
|
||||
slaveUnits = parseSlaveUnits(row.getSlaveUnitNames(), errors);
|
||||
String masterUnitId = masterUnit.getId();
|
||||
if (masterUnit != null && slaveUnits.stream().anyMatch(unit -> StrUtil.equals(unit.getId(), masterUnitId))) {
|
||||
errors.add("协办单位不能包含主办单位");
|
||||
}
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
// Excel 行号按用户看到的表头后一行数据计算,便于定位错误。
|
||||
row.setErrInfo(StrUtil.join(";", errors), rowIndex + 2);
|
||||
return;
|
||||
}
|
||||
|
||||
executeSchoolAuditTask(row, taskInfo, auditResult, type, masterUnit, slaveUnits);
|
||||
}
|
||||
|
||||
private void executeSchoolAuditTask(OpinionSchoolAuditImportExcelMode row, Record taskInfo, AuditResult auditResult,
|
||||
OpinionType type, ProposalUndertake masterUnit, List<ProposalUndertake> slaveUnits) {
|
||||
String opinionId = taskInfo.getString("opinionId");
|
||||
Long taskId = taskInfo.getLong("taskId");
|
||||
String taskName = taskInfo.getString("taskName");
|
||||
|
||||
Dict taskData = Dict.create();
|
||||
taskData.set("id", opinionId);
|
||||
taskData.set(FlowConst.PROCESS_TASK_ID_KEY, taskId);
|
||||
taskData.set(FlowConst.TASK_NAME, taskName);
|
||||
taskData.set("roleCode", "OFFICE_MANAGER");
|
||||
taskData.set("tf_opinion", StrUtil.trim(row.getAuditOpinion()));
|
||||
taskData.set(FlowConst.SUBMIT_TYPE, auditResult.submitType);
|
||||
|
||||
if (auditResult != AuditResult.ROLLBACK) {
|
||||
taskData.set("tf_schoolResult", auditResult.flowValue);
|
||||
}
|
||||
|
||||
if (auditResult == AuditResult.PASS) {
|
||||
// 通过审核时与页面单条审核保持一致:先保存意见类别,再提交主办、协办等流程表单字段。
|
||||
updateOpinionType(opinionId, type.getId());
|
||||
taskData.set("typeId", type.getId());
|
||||
taskData.set("tf_masterUnitId", masterUnit.getId());
|
||||
taskData.set("tf_masterUnitName", masterUnit.getName());
|
||||
List<String> slaveUnitIds = slaveUnits.stream().map(ProposalUndertake::getId).collect(Collectors.toList());
|
||||
List<String> slaveUnitNames = slaveUnits.stream().map(ProposalUndertake::getName).collect(Collectors.toList());
|
||||
taskData.set("tf_slaveUnitIds", slaveUnitIds);
|
||||
taskData.set("tf_slaveUnitNames", slaveUnitNames);
|
||||
taskData.set("tf_slaveUnitNameStr", StrUtil.join(",", slaveUnitNames));
|
||||
}
|
||||
|
||||
flowCommonService.executeTask(taskData);
|
||||
}
|
||||
|
||||
private Record fetchDoingSchoolAuditTask(String code, String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.id AS opinionId,
|
||||
t.id AS taskId,
|
||||
t.displayName AS taskName
|
||||
FROM wf_process_task t
|
||||
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
|
||||
LEFT JOIN opinion_info info ON info.id = ins.businessNo
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("info.code", "=", StrUtil.trim(code));
|
||||
cnd.andEX("info.sessionId", "=", sessionId);
|
||||
cnd.and("t.taskName", "=", "schoolAudit");
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
|
||||
sql.setCondition(cnd);
|
||||
sql.setCallback(Sqls.callback.record());
|
||||
dao().execute(sql);
|
||||
return sql.getObject(Record.class);
|
||||
}
|
||||
|
||||
private void updateOpinionType(String opinionId, Integer typeId) {
|
||||
dao().update(OpinionInfo.class, Chain.make("typeId", typeId), Cnd.where("id", "=", opinionId));
|
||||
|
||||
OpinionInfo opinionInfo = new OpinionInfo();
|
||||
opinionInfo.setId(opinionId);
|
||||
opinionInfo.setTypeId(typeId);
|
||||
ProcessInstance instance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", opinionId));
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 同步流程实例和当前待办变量,保持后续节点读取到的表单数据与单条审核一致。
|
||||
Dict instanceArgs = FlowUtil.variableToDict(instance.getVariable());
|
||||
instanceArgs.set(FlowConst.FORM_DATA, opinionInfo);
|
||||
dao().update(instance);
|
||||
|
||||
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
|
||||
for (ProcessTask task : doingTaskList) {
|
||||
Dict taskArgs = FlowUtil.variableToDict(task.getVariable());
|
||||
taskArgs.set(FlowConst.FORM_DATA, opinionInfo);
|
||||
}
|
||||
dao().update(doingTaskList);
|
||||
}
|
||||
|
||||
private OpinionType fetchOpinionType(String typeName) {
|
||||
if (StrUtil.isBlank(typeName)) {
|
||||
return null;
|
||||
}
|
||||
String name = StrUtil.trim(typeName);
|
||||
List<OpinionType> types = dao().query(OpinionType.class,
|
||||
Cnd.where("name", "=", name).or("code", "=", name).and("delFlag", "=", false));
|
||||
return types.size() == 1 ? types.get(0) : null;
|
||||
}
|
||||
|
||||
private ProposalUndertake fetchUndertake(String unitName) {
|
||||
if (StrUtil.isBlank(unitName)) {
|
||||
return null;
|
||||
}
|
||||
String name = StrUtil.trim(unitName);
|
||||
List<ProposalUndertake> undertakes = dao().query(ProposalUndertake.class,
|
||||
Cnd.where("name", "=", name).or("code", "=", name).and("delFlag", "=", false));
|
||||
return undertakes.size() == 1 ? undertakes.get(0) : null;
|
||||
}
|
||||
|
||||
private List<ProposalUndertake> parseSlaveUnits(String slaveUnitText, List<String> errors) {
|
||||
List<ProposalUndertake> units = new ArrayList<>();
|
||||
if (StrUtil.isBlank(slaveUnitText)) {
|
||||
return units;
|
||||
}
|
||||
String[] items = slaveUnitText.split("[,,、;;\\n\\r]+");
|
||||
Set<String> names = new HashSet<>();
|
||||
for (String item : items) {
|
||||
String unitName = StrUtil.trim(item);
|
||||
if (StrUtil.isBlank(unitName)) {
|
||||
continue;
|
||||
}
|
||||
if (!names.add(unitName)) {
|
||||
errors.add("协办单位重复:" + unitName);
|
||||
continue;
|
||||
}
|
||||
ProposalUndertake undertake = fetchUndertake(unitName);
|
||||
if (undertake == null) {
|
||||
errors.add("协办单位不存在:" + unitName);
|
||||
continue;
|
||||
}
|
||||
units.add(undertake);
|
||||
}
|
||||
return units;
|
||||
}
|
||||
|
||||
private AuditResult parseAuditResult(String auditResultText, List<String> errors) {
|
||||
String value = StrUtil.trim(auditResultText);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
errors.add("审核结果不能为空");
|
||||
return null;
|
||||
}
|
||||
if ("通过".equals(value) || "同意".equals(value) || "YES".equalsIgnoreCase(value)) {
|
||||
return AuditResult.PASS;
|
||||
}
|
||||
if ("不通过".equals(value) || "不同意".equals(value) || "NO".equalsIgnoreCase(value)) {
|
||||
return AuditResult.REJECT;
|
||||
}
|
||||
if ("退回发起人修改".equals(value) || "退回".equals(value) || "ROLLBACK".equalsIgnoreCase(value)) {
|
||||
return AuditResult.ROLLBACK;
|
||||
}
|
||||
errors.add("审核结果只能填写通过、不通过或退回发起人修改");
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isEmptyRow(OpinionSchoolAuditImportExcelMode row) {
|
||||
return StrUtil.isBlank(row.getCode())
|
||||
&& StrUtil.isBlank(row.getName())
|
||||
&& StrUtil.isBlank(row.getTypeName())
|
||||
&& StrUtil.isBlank(row.getCreateUserName())
|
||||
&& StrUtil.isBlank(row.getDelegationName())
|
||||
&& StrUtil.isBlank(row.getUnitName())
|
||||
&& StrUtil.isBlank(row.getMasterUnitName())
|
||||
&& StrUtil.isBlank(row.getSlaveUnitNames())
|
||||
&& StrUtil.isBlank(row.getAuditResult())
|
||||
&& StrUtil.isBlank(row.getAuditOpinion());
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入审核结果与流程参数之间的映射。
|
||||
*/
|
||||
private enum AuditResult {
|
||||
PASS("YES", ProcessSubmitTypeEnum.AGREE.getCode()),
|
||||
REJECT("NO", ProcessSubmitTypeEnum.AGREE.getCode()),
|
||||
ROLLBACK("ROLLBACK", ProcessSubmitTypeEnum.ROLLBACK_TO_OPERATOR.getCode());
|
||||
|
||||
private final String flowValue;
|
||||
private final Integer submitType;
|
||||
|
||||
AuditResult(String flowValue, Integer submitType) {
|
||||
this.flowValue = flowValue;
|
||||
this.submitType = submitType;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user