commit
This commit is contained in:
+8
@@ -104,6 +104,14 @@ public class ProposalExportComprehensiveController {
|
||||
proposalExportService.exportProposalStatisticsAsZip(pageForm, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@ApiOperation("导出全部提案为单个DOCX")
|
||||
@SaCheckPermission("proposal.query.comprehensive")
|
||||
public void exportAllProposalAsDocx(@Valid @Param("pageForm") ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) {
|
||||
proposalExportService.exportProposalStatisticsAsDocx(pageForm, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@ApiOperation("导出全部提案反馈表ZIP")
|
||||
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
package com.budwk.app.zhgh.democratic.proposal.controller.export;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
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.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.base.utils.SysOfficeTemplateUtil;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
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.vo.ProcessTaskVO;
|
||||
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTable;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
|
||||
import org.ddr.poi.html.HtmlRenderPolicy;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/proposal/export/singleCustom")
|
||||
@Slf4j
|
||||
@Ok("json:full")
|
||||
@Api(tags = "提案导出-单个提案表自定义")
|
||||
public class ProposalExportSingleCustomController {
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
@Inject
|
||||
private FlowEngine flowEngine;
|
||||
|
||||
@Inject
|
||||
private SysOfficeTemplateUtil sysOfficeTemplateUtil;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/democratic/proposal/export/singleCustom/index.html")
|
||||
@SaCheckPermission("proposal.query.comprehensive")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("分页提案列表")
|
||||
@SaCheckPermission("proposal.query.comprehensive")
|
||||
public Result pageData(@Valid ProposalSearchParam pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.*,
|
||||
type.name AS typeName,
|
||||
tcs.fullName AS sessionName,
|
||||
tcd.name AS delegationName,
|
||||
ins.id AS instanceId,
|
||||
ins.state instanceState,
|
||||
t.displayName curTaskName
|
||||
FROM proposal_info info
|
||||
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
|
||||
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
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
ProposalSearchParam.buildSearch(cnd, pageForm);
|
||||
cnd.groupBy("info.id");
|
||||
cnd.desc("info.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("查询可导出环节")
|
||||
@SaCheckPermission("proposal.query.comprehensive")
|
||||
public Result stageOptions(String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
ProcessInstance instance = baseService.dao().fetch(ProcessInstance.class, Cnd.where("businessNo", "=", id));
|
||||
if (instance == null) {
|
||||
return Result.success(new ArrayList<>());
|
||||
}
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.taskName AS value,
|
||||
MAX(t.displayName) AS label,
|
||||
MIN(t.id) AS seq
|
||||
FROM wf_process_task t
|
||||
WHERE t.processInstanceId = @processInstanceId
|
||||
AND t.taskState IN (@doing, @finished)
|
||||
AND t.taskName != 'startTask'
|
||||
AND IFNULL(t.displayName, '') != ''
|
||||
GROUP BY t.taskName
|
||||
ORDER BY seq ASC
|
||||
""");
|
||||
sql.params().set("processInstanceId", instance.getId());
|
||||
sql.params().set("doing", ProcessTaskStateEnum.DOING.getCode());
|
||||
sql.params().set("finished", ProcessTaskStateEnum.FINISHED.getCode());
|
||||
return Result.success(baseService.listMap(sql));
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@ApiOperation("按选择环节导出单个提案表")
|
||||
@SaCheckPermission("proposal.query.comprehensive")
|
||||
public void exportSelectedDocx(String id, @Param("stageTaskNamesJson") String stageTaskNamesJson, HttpServletResponse response) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
throw new IllegalArgumentException("提案ID不能为空");
|
||||
}
|
||||
|
||||
NutMap proposal = queryProposalBase(id);
|
||||
if (proposal == null) {
|
||||
throw new IllegalArgumentException("提案不存在");
|
||||
}
|
||||
|
||||
List<String> selectedStageTaskNames = parseSelectedStageTaskNames(stageTaskNamesJson);
|
||||
List<NutMap> selectedTasks = querySelectedTasks(id, selectedStageTaskNames);
|
||||
|
||||
byte[] templateBytes = buildTemplateDocxBytes(id);
|
||||
byte[] finalBytes = appendSelectedStageSection(templateBytes, selectedTasks);
|
||||
|
||||
String fileName = proposal.getString("code", "proposal") + "-" + proposal.getString("name", "提案") + "-自定义导出.docx";
|
||||
CommonDownloadUtil.download(fileName, finalBytes, response);
|
||||
}
|
||||
|
||||
private byte[] buildTemplateDocxBytes(String id) {
|
||||
NutMap baseInfo = queryProposalBase(id);
|
||||
if (baseInfo == null) {
|
||||
throw new IllegalArgumentException("提案不存在");
|
||||
}
|
||||
|
||||
String brief = baseInfo.getString("brief", "");
|
||||
String measures = baseInfo.getString("measures", "");
|
||||
baseInfo.put("brief", sysOfficeTemplateUtil.convertRichTextToDocText(brief));
|
||||
baseInfo.put("measures", sysOfficeTemplateUtil.convertRichTextToDocText(measures));
|
||||
|
||||
ProcessInstance processInstance = baseService.dao().fetch(ProcessInstance.class, Cnd.where("businessNo", "=", id));
|
||||
List<ProcessTaskVO> doneTaskVos = new ArrayList<>();
|
||||
if (processInstance != null) {
|
||||
List<ProcessTask> doneTaskList = flowEngine.processTaskService().getDoneTaskList(processInstance.getId(), null);
|
||||
for (ProcessTask doneTask : doneTaskList) {
|
||||
ProcessTaskVO taskVO = flowEngine.processTaskService().findById(doneTask.getId());
|
||||
if (taskVO != null) {
|
||||
doneTaskVos.add(taskVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<ProcessTaskVO>> taskGroups = doneTaskVos.stream().collect(Collectors.groupingBy(ProcessTaskVO::getDisplayName));
|
||||
|
||||
List<NutMap> secondInfos = new ArrayList<>();
|
||||
List<ProcessTaskVO> secondTaskVos = taskGroups.get("提案附议");
|
||||
if (secondTaskVos != null) {
|
||||
for (ProcessTaskVO secondTaskVO : secondTaskVos) {
|
||||
Dict taskFormData = secondTaskVO.getTaskFormData();
|
||||
if (taskFormData == null) {
|
||||
continue;
|
||||
}
|
||||
NutMap secondInfo = new NutMap();
|
||||
secondInfo.put("s_username", taskFormData.getStr("userName"));
|
||||
secondInfo.put("s_unitName", taskFormData.getStr("unitName"));
|
||||
String loginName = taskFormData.getStr("loginName");
|
||||
if (StrUtil.isNotBlank(loginName)) {
|
||||
Sql taskUserSql = Sqls.create("select mobile from sys_user where loginname=@loginname and delFlag != 1 limit 0,1");
|
||||
taskUserSql.setParam("loginname", loginName);
|
||||
taskUserSql.setCallback(Sqls.callback.map());
|
||||
baseService.dao().execute(taskUserSql);
|
||||
NutMap taskUserMap = (NutMap) taskUserSql.getResult();
|
||||
secondInfo.put("s_mobile", taskUserMap == null ? "" : taskUserMap.getString("mobile", ""));
|
||||
} else {
|
||||
secondInfo.put("s_mobile", "");
|
||||
}
|
||||
secondInfos.add(secondInfo);
|
||||
}
|
||||
}
|
||||
|
||||
List<NutMap> delegationAuditList = new ArrayList<>();
|
||||
List<ProcessTaskVO> delegationTaskVos = taskGroups.get("团长审核");
|
||||
if (delegationTaskVos != null) {
|
||||
for (ProcessTaskVO delegationTaskVO : delegationTaskVos) {
|
||||
Dict taskFormData = delegationTaskVO.getTaskFormData();
|
||||
if (taskFormData == null) {
|
||||
continue;
|
||||
}
|
||||
NutMap delegationAudit = new NutMap();
|
||||
delegationAudit.put("opinion", taskFormData.getStr("opinion"));
|
||||
delegationAudit.put("username", taskFormData.getStr("userName"));
|
||||
delegationAudit.put("auditTime", delegationTaskVO.getFinishTime() == null ? "" : DateUtil.format(delegationTaskVO.getFinishTime(), "yyyy-MM-dd HH:mm:ss"));
|
||||
delegationAuditList.add(delegationAudit);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> docCellDataMap = new HashMap<>();
|
||||
docCellDataMap.put("proposalCode", baseInfo.getString("code", ""));
|
||||
docCellDataMap.put("createTime", baseInfo.getString("createTime", ""));
|
||||
docCellDataMap.put("username", baseInfo.getString("createUserName", ""));
|
||||
docCellDataMap.put("delegationName", baseInfo.getString("delegationName", ""));
|
||||
docCellDataMap.put("unitName", baseInfo.getString("unitName", ""));
|
||||
docCellDataMap.put("mobile", baseInfo.getString("mobile", ""));
|
||||
docCellDataMap.put("proposalName", baseInfo.getString("name", ""));
|
||||
docCellDataMap.put("brief", baseInfo.getString("brief", ""));
|
||||
docCellDataMap.put("measures", baseInfo.getString("measures", ""));
|
||||
|
||||
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
|
||||
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
|
||||
htmlRenderPolicy.getConfig().setShowDefaultTableBorderInTableCell(true);
|
||||
|
||||
Configure config = Configure.builder()
|
||||
.bind("secondedList", policy)
|
||||
.bind("seconders", policy)
|
||||
.bind("brief", htmlRenderPolicy)
|
||||
.bind("measures", htmlRenderPolicy)
|
||||
.bind("taskFormData.tf_opinion", htmlRenderPolicy)
|
||||
.build();
|
||||
|
||||
Map<String, Object> renderDocCellDataMap = MapUtil.of("proposal", docCellDataMap);
|
||||
renderDocCellDataMap.put("secondedList", secondInfos);
|
||||
renderDocCellDataMap.put("delegationAuditList", delegationAuditList);
|
||||
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("proposal"), config).render(renderDocCellDataMap).writeAndClose(out);
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
log.error("提案模板导出失败, proposalId={}, error={}", id, e.getMessage());
|
||||
throw new RuntimeException("导出失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] appendSelectedStageSection(byte[] templateBytes, List<NutMap> selectedTasks) {
|
||||
if (templateBytes == null || templateBytes.length == 0) {
|
||||
throw new RuntimeException("模板文档为空");
|
||||
}
|
||||
if (selectedTasks == null) {
|
||||
selectedTasks = new ArrayList<>();
|
||||
}
|
||||
|
||||
try (XWPFDocument doc = new XWPFDocument(new ByteArrayInputStream(templateBytes));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
XWPFParagraph titleP = doc.createParagraph();
|
||||
titleP.setAlignment(ParagraphAlignment.LEFT);
|
||||
XWPFRun titleRun = titleP.createRun();
|
||||
titleRun.setBold(true);
|
||||
titleRun.setText("附加环节内容(按勾选导出)");
|
||||
|
||||
if (selectedTasks.isEmpty()) {
|
||||
XWPFParagraph emptyP = doc.createParagraph();
|
||||
emptyP.createRun().setText("未勾选流程环节,或所选环节暂无数据。");
|
||||
} else {
|
||||
appendStageContent(doc, selectedTasks);
|
||||
}
|
||||
|
||||
doc.write(out);
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("导出失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private NutMap queryProposalBase(String id) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.id,
|
||||
info.code,
|
||||
info.name,
|
||||
info.brief,
|
||||
info.measures,
|
||||
info.createUserName,
|
||||
info.createUserLoginName,
|
||||
DATE_FORMAT(info.createTime, '%Y-%m-%d') AS createTime,
|
||||
IFNULL(type.name, '') AS typeName,
|
||||
IFNULL(tcs.fullName, '') AS sessionName,
|
||||
IFNULL(tcd.name, '') AS delegationName,
|
||||
IFNULL(tcde.unitName, '') AS unitName,
|
||||
IFNULL(tcde.mobile, '') AS mobile
|
||||
FROM proposal_info info
|
||||
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
|
||||
LEFT JOIN teacher_congress_delegate tcde ON tcde.loginName = info.createUserLoginName
|
||||
WHERE info.id = @id
|
||||
""");
|
||||
sql.setParam("id", id);
|
||||
sql.setCallback(Sqls.callback.map());
|
||||
baseService.dao().execute(sql);
|
||||
return (NutMap) sql.getResult();
|
||||
}
|
||||
|
||||
private List<String> parseSelectedStageTaskNames(String stageTaskNamesJson) {
|
||||
if (StrUtil.isBlank(stageTaskNamesJson)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
String[] arr = JSONUtil.toBean(stageTaskNamesJson, String[].class);
|
||||
if (arr == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return Arrays.stream(arr).filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.warn("解析环节参数失败: {}", stageTaskNamesJson);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private List<NutMap> querySelectedTasks(String proposalId, List<String> selectedStageTaskNames) {
|
||||
ProcessInstance instance = baseService.dao().fetch(ProcessInstance.class, Cnd.where("businessNo", "=", proposalId));
|
||||
if (instance == null || selectedStageTaskNames.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.id,
|
||||
t.taskName,
|
||||
t.displayName,
|
||||
t.finishTime,
|
||||
t.taskState
|
||||
FROM wf_process_task t
|
||||
WHERE t.processInstanceId = @processInstanceId
|
||||
AND t.taskState IN (@doing, @finished)
|
||||
AND t.taskName IN (@taskNames)
|
||||
ORDER BY t.id ASC
|
||||
""");
|
||||
sql.params().set("processInstanceId", instance.getId());
|
||||
sql.params().set("doing", ProcessTaskStateEnum.DOING.getCode());
|
||||
sql.params().set("finished", ProcessTaskStateEnum.FINISHED.getCode());
|
||||
sql.params().set("taskNames", selectedStageTaskNames);
|
||||
|
||||
List<NutMap> rawList = baseService.listMap(sql);
|
||||
|
||||
Map<String, Integer> stageOrderMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < selectedStageTaskNames.size(); i++) {
|
||||
stageOrderMap.put(selectedStageTaskNames.get(i), i);
|
||||
}
|
||||
|
||||
rawList.sort((a, b) -> {
|
||||
int orderA = stageOrderMap.getOrDefault(a.getString("taskName", ""), Integer.MAX_VALUE);
|
||||
int orderB = stageOrderMap.getOrDefault(b.getString("taskName", ""), Integer.MAX_VALUE);
|
||||
if (orderA != orderB) {
|
||||
return Integer.compare(orderA, orderB);
|
||||
}
|
||||
return Long.compare(a.getLong("id", 0L), b.getLong("id", 0L));
|
||||
});
|
||||
|
||||
return rawList;
|
||||
}
|
||||
|
||||
private void appendStageContent(XWPFDocument doc, List<NutMap> selectedTasks) {
|
||||
Map<String, List<NutMap>> grouped = selectedTasks.stream()
|
||||
.collect(Collectors.groupingBy(v -> v.getString("taskName", ""), LinkedHashMap::new, Collectors.toList()));
|
||||
|
||||
int stageIndex = 1;
|
||||
for (Map.Entry<String, List<NutMap>> entry : grouped.entrySet()) {
|
||||
List<NutMap> tasks = entry.getValue();
|
||||
String displayName = tasks.get(0).getString("displayName", entry.getKey());
|
||||
|
||||
XWPFParagraph stageP = doc.createParagraph();
|
||||
XWPFRun stageRun = stageP.createRun();
|
||||
stageRun.setBold(true);
|
||||
stageRun.setText(String.format("%d.%s", stageIndex++, displayName));
|
||||
|
||||
int idx = 1;
|
||||
for (NutMap task : tasks) {
|
||||
ProcessTaskVO vo = flowEngine.processTaskService().findById(task.getLong("id"));
|
||||
Dict formData = vo == null ? null : vo.getTaskFormData();
|
||||
|
||||
XWPFTable table = doc.createTable(5, 2);
|
||||
fillCell(table.getRow(0), "序号", String.valueOf(idx++));
|
||||
fillCell(table.getRow(1), "办理人", extractUserName(formData, vo));
|
||||
fillCell(table.getRow(2), "办理时间", task.getTime("finishTime") == null ? "" : DateUtil.formatDateTime(task.getTime("finishTime")));
|
||||
fillCell(table.getRow(3), "办理意见", extractOpinion(formData));
|
||||
fillCell(table.getRow(4), "环节数据", flattenFormData(formData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String extractUserName(Dict formData, ProcessTaskVO vo) {
|
||||
String userName = "";
|
||||
if (formData != null) {
|
||||
userName = formData.getStr("userName");
|
||||
if (StrUtil.isBlank(userName)) {
|
||||
userName = formData.getStr("username");
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(userName)) {
|
||||
userName = vo == null ? "" : vo.getOperator();
|
||||
}
|
||||
return StrUtil.blankToDefault(userName, "");
|
||||
}
|
||||
|
||||
private String extractOpinion(Dict formData) {
|
||||
if (formData == null || formData.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
List<String> keys = List.of("opinion", "auditOpinion", "feedback", "caseFilingResult", "caseFilingType", "tf_opinion", "tf_feedback", "tf_caseFilingResult", "tf_caseFilingType");
|
||||
for (String key : keys) {
|
||||
String val = formData.getStr(key);
|
||||
if (StrUtil.isNotBlank(val)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String flattenFormData(Dict formData) {
|
||||
if (formData == null || formData.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
Set<String> skipKeys = Set.of("tf_userName", "userName", "tf_loginName", "loginName", "tf_unitId", "unitId", "tf_unitName", "unitName", "tf_unionId", "unionId", "finishTimeForDocx");
|
||||
return formData.entrySet().stream()
|
||||
.filter(e -> !skipKeys.contains(e.getKey()))
|
||||
.filter(e -> e.getValue() != null && StrUtil.isNotBlank(String.valueOf(e.getValue())))
|
||||
.map(e -> e.getKey() + "=" + String.valueOf(e.getValue()))
|
||||
.collect(Collectors.joining(";"));
|
||||
}
|
||||
|
||||
private void fillCell(XWPFTableRow row, String key, String value) {
|
||||
row.getCell(0).setText(key);
|
||||
row.getCell(1).setText(StrUtil.blankToDefault(value, ""));
|
||||
}
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package com.budwk.app.zhgh.democratic.proposal.controller.query;
|
||||
|
||||
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.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.sys.models.Sys_dict;
|
||||
import com.budwk.app.sys.services.SysDictService;
|
||||
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.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/proposal/query/analysis")
|
||||
@Slf4j
|
||||
@Ok("json:full")
|
||||
@Api(tags = "提案统计分析")
|
||||
public class ProposalQueryAnalysisController {
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
@Inject
|
||||
private SysDictService sysDictService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/democratic/proposal/query/analysis/index.html")
|
||||
@SaCheckPermission("proposal.query.analysis")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("统计数据")
|
||||
@SaCheckPermission("proposal.query.analysis")
|
||||
public Result statisticsData(String sessionId) {
|
||||
if (StrUtil.isBlank(sessionId)) {
|
||||
return Result.error("请先选择教代会届次");
|
||||
}
|
||||
return Result.success(buildStatisticsRows(sessionId));
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("分析数据")
|
||||
@SaCheckPermission("proposal.query.analysis")
|
||||
public Result analysisData(String sessionId) {
|
||||
if (StrUtil.isBlank(sessionId)) {
|
||||
return Result.error("请先选择教代会届次");
|
||||
}
|
||||
return Result.success(buildAnalysisRows(sessionId));
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@ApiOperation("导出统计数据")
|
||||
@SaCheckPermission("proposal.query.analysis")
|
||||
public void exportStatisticsData(String sessionId, HttpServletResponse response) {
|
||||
List<NutMap> list = buildStatisticsRows(sessionId);
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("统计维度", "dimension", 20));
|
||||
entities.add(new ExcelExportEntity("分类项", "itemName", 30));
|
||||
entities.add(new ExcelExportEntity("数量", "count", 12));
|
||||
entities.add(new ExcelExportEntity("占比", "rate", 12));
|
||||
ExportParams params = new ExportParams();
|
||||
params.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(params, entities, list);
|
||||
CommonDownloadUtil.download("提案统计数据.xlsx", workbook, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@ApiOperation("导出分析数据")
|
||||
@SaCheckPermission("proposal.query.analysis")
|
||||
public void exportAnalysisData(String sessionId, HttpServletResponse response) {
|
||||
List<NutMap> list = buildAnalysisRows(sessionId);
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("统计维度", "dimension", 20));
|
||||
entities.add(new ExcelExportEntity("总量", "total", 12));
|
||||
entities.add(new ExcelExportEntity("分类数量", "categoryCount", 12));
|
||||
entities.add(new ExcelExportEntity("最高项", "topItem", 30));
|
||||
entities.add(new ExcelExportEntity("最高数量", "topCount", 12));
|
||||
entities.add(new ExcelExportEntity("最高占比", "topRate", 12));
|
||||
entities.add(new ExcelExportEntity("分析结论", "analysis", 60));
|
||||
ExportParams params = new ExportParams();
|
||||
params.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(params, entities, list);
|
||||
CommonDownloadUtil.download("提案分析数据.xlsx", workbook, response);
|
||||
}
|
||||
|
||||
private List<NutMap> buildStatisticsRows(String sessionId) {
|
||||
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId);
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
dataMap.forEach((dimension, 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("itemName", item.getString("itemName", "未维护"))
|
||||
.addv("count", count)
|
||||
.addv("rate", formatPercent(count, total)));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<NutMap> buildAnalysisRows(String sessionId) {
|
||||
Map<String, List<NutMap>> dataMap = loadDimensionData(sessionId);
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
dataMap.forEach((dimension, items) -> {
|
||||
if (items.isEmpty()) {
|
||||
result.add(NutMap.NEW()
|
||||
.addv("dimension", dimension)
|
||||
.addv("total", 0)
|
||||
.addv("categoryCount", 0)
|
||||
.addv("topItem", "-")
|
||||
.addv("topCount", 0)
|
||||
.addv("topRate", "0.00%")
|
||||
.addv("analysis", "暂无数据"));
|
||||
return;
|
||||
}
|
||||
|
||||
List<NutMap> sorted = items.stream()
|
||||
.sorted(Comparator.comparingLong((NutMap v) -> v.getLong("count", 0L)).reversed())
|
||||
.toList();
|
||||
|
||||
long total = sorted.stream().mapToLong(v -> v.getLong("count", 0L)).sum();
|
||||
NutMap top = sorted.get(0);
|
||||
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);
|
||||
|
||||
result.add(NutMap.NEW()
|
||||
.addv("dimension", dimension)
|
||||
.addv("total", total)
|
||||
.addv("categoryCount", sorted.size())
|
||||
.addv("topItem", topItem)
|
||||
.addv("topCount", topCount)
|
||||
.addv("topRate", topRate)
|
||||
.addv("analysis", analysis));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, List<NutMap>> loadDimensionData(String sessionId) {
|
||||
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));
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<NutMap> queryUnitStat(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
IFNULL(tcde.unitName, '未维护') AS itemName,
|
||||
COUNT(info.id) AS count
|
||||
FROM proposal_info info
|
||||
LEFT JOIN teacher_congress_delegate tcde ON tcde.loginName = info.createUserLoginName
|
||||
WHERE info.sessionId = @sessionId
|
||||
GROUP BY itemName
|
||||
ORDER BY count DESC, itemName ASC
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
return baseService.listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> queryTypeStat(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
IFNULL(t.name, '未分类') AS itemName,
|
||||
COUNT(info.id) AS count
|
||||
FROM proposal_info info
|
||||
LEFT JOIN proposal_type t ON t.id = info.typeId
|
||||
WHERE info.sessionId = @sessionId
|
||||
GROUP BY itemName
|
||||
ORDER BY count DESC, itemName ASC
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
return baseService.listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> queryDelegationStat(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
IFNULL(d.name, '未分团') AS itemName,
|
||||
COUNT(info.id) AS count
|
||||
FROM proposal_info info
|
||||
LEFT JOIN teacher_congress_delegation d ON d.id = info.delegationId
|
||||
WHERE info.sessionId = @sessionId
|
||||
GROUP BY itemName
|
||||
ORDER BY count DESC, itemName ASC
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
return baseService.listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> queryCaseFilingResultStat(String sessionId) {
|
||||
List<Sys_dict> dictList = sysDictService.getSubListByCode("PROPOSAL_CASE_FILING_RESULT");
|
||||
Map<String, String> dictNameMap = dictList.stream().collect(Collectors.toMap(Sys_dict::getCode, Sys_dict::getName, (a, b) -> a));
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
IFNULL(info.caseFilingResult, '') AS code,
|
||||
COUNT(info.id) AS count
|
||||
FROM proposal_info info
|
||||
WHERE info.sessionId = @sessionId
|
||||
GROUP BY info.caseFilingResult
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
List<NutMap> rawList = baseService.listMap(sql);
|
||||
Map<String, Long> countMap = rawList.stream().collect(Collectors.toMap(v -> v.getString("code", ""), v -> v.getLong("count", 0L), Long::sum));
|
||||
|
||||
LinkedHashSet<String> allCodes = new LinkedHashSet<>(dictNameMap.keySet());
|
||||
allCodes.addAll(countMap.keySet());
|
||||
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
for (String code : allCodes) {
|
||||
String itemName = StrUtil.isBlank(code) ? "未立案结果" : dictNameMap.getOrDefault(code, code);
|
||||
result.add(NutMap.NEW().addv("itemName", itemName).addv("count", countMap.getOrDefault(code, 0L)));
|
||||
}
|
||||
result.sort(Comparator.comparingLong((NutMap v) -> v.getLong("count", 0L)).reversed());
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<NutMap> querySatisfactionStat(String sessionId) {
|
||||
List<Sys_dict> dictList = sysDictService.getSubListByCode("PROPOSAL_FEEDBACK");
|
||||
Map<String, String> dictNameMap = dictList.stream().collect(Collectors.toMap(Sys_dict::getCode, Sys_dict::getName, (a, b) -> a));
|
||||
|
||||
Sql totalSql = Sqls.create("SELECT COUNT(1) AS total FROM proposal_info WHERE sessionId = @sessionId");
|
||||
totalSql.setParam("sessionId", sessionId);
|
||||
List<NutMap> totalList = baseService.listMap(totalSql);
|
||||
long total = totalList.isEmpty() ? 0L : totalList.get(0).getLong("total", 0L);
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.variable
|
||||
FROM proposal_info info
|
||||
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
|
||||
LEFT JOIN (
|
||||
SELECT processInstanceId, MAX(finishTime) AS finishTime
|
||||
FROM wf_process_task
|
||||
WHERE taskName = 'feedback' AND taskState = 20
|
||||
GROUP BY processInstanceId
|
||||
) latest ON latest.processInstanceId = ins.id
|
||||
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id
|
||||
AND t.taskName = 'feedback'
|
||||
AND t.taskState = 20
|
||||
AND t.finishTime = latest.finishTime
|
||||
WHERE info.sessionId = @sessionId
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
List<NutMap> rowList = baseService.listMap(sql);
|
||||
|
||||
Map<String, Long> countMap = new LinkedHashMap<>();
|
||||
long ratedCount = 0L;
|
||||
for (NutMap row : rowList) {
|
||||
String variableStr = row.getString("variable", "");
|
||||
if (StrUtil.isBlank(variableStr)) {
|
||||
continue;
|
||||
}
|
||||
NutMap variable = Json.fromJson(NutMap.class, variableStr);
|
||||
String code = variable.getString("tf_feedback", "");
|
||||
if (StrUtil.isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
countMap.put(code, countMap.getOrDefault(code, 0L) + 1);
|
||||
ratedCount++;
|
||||
}
|
||||
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
for (Sys_dict dict : dictList) {
|
||||
result.add(NutMap.NEW().addv("itemName", dict.getName()).addv("count", countMap.getOrDefault(dict.getCode(), 0L)));
|
||||
}
|
||||
|
||||
countMap.forEach((code, cnt) -> {
|
||||
if (!dictNameMap.containsKey(code)) {
|
||||
result.add(NutMap.NEW().addv("itemName", code).addv("count", cnt));
|
||||
}
|
||||
});
|
||||
|
||||
long unrated = Math.max(total - ratedCount, 0L);
|
||||
result.add(NutMap.NEW().addv("itemName", "未评价").addv("count", unrated));
|
||||
result.sort(Comparator.comparingLong((NutMap v) -> v.getLong("count", 0L)).reversed());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String formatPercent(long count, long total) {
|
||||
if (total <= 0) {
|
||||
return "0.00%";
|
||||
}
|
||||
return String.format("%.2f%%", (count * 100.0D) / total);
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.flow.enums.ProcessTaskPerformTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
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.param.ProposalSearchParam;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/proposal/control")
|
||||
@Slf4j
|
||||
@Ok("json:full")
|
||||
@Api(tags = "提案管理-状态调整")
|
||||
public class ProposalControlController {
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/control/index.html")
|
||||
@SaCheckPermission("proposal.control")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("proposal.control")
|
||||
@ApiOperation("分页列表")
|
||||
public Result pageData(@Valid ProposalSearchParam pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.*,
|
||||
type.name AS typeName,
|
||||
tcs.fullName AS sessionName,
|
||||
tcd.name AS delegationName,
|
||||
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
|
||||
FROM wf_process_task t
|
||||
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
|
||||
LEFT JOIN proposal_info info ON info.id = ins.businessNo
|
||||
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
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
|
||||
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
|
||||
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
|
||||
}
|
||||
|
||||
ProposalSearchParam.buildSearch(cnd, pageForm);
|
||||
cnd.groupBy("t.id");
|
||||
cnd.desc("t.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("proposal.control")
|
||||
@ApiOperation("获取可跳转节点")
|
||||
public Result jumpNodes(Long instanceId) {
|
||||
if (instanceId == null) {
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.taskName AS value,
|
||||
MAX(t.displayName) AS label,
|
||||
MIN(t.id) AS seq
|
||||
FROM wf_process_task t
|
||||
WHERE t.processInstanceId = @instanceId
|
||||
AND t.taskState IN (@doing, @finish)
|
||||
AND t.performType != @countersign
|
||||
GROUP BY t.taskName
|
||||
ORDER BY seq ASC
|
||||
""");
|
||||
sql.params().set("instanceId", instanceId);
|
||||
sql.params().set("doing", ProcessTaskStateEnum.DOING.getCode());
|
||||
sql.params().set("finish", ProcessTaskStateEnum.FINISHED.getCode());
|
||||
sql.params().set("countersign", ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode());
|
||||
return Result.success(baseService.listMap(sql));
|
||||
}
|
||||
}
|
||||
+8
@@ -49,6 +49,14 @@ public interface ProposalExportService extends BaseService<ProposalInfo> {
|
||||
* @param response
|
||||
*/
|
||||
void exportProposalStatisticsAsZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 批量导出提案表为单个汇集文档
|
||||
*
|
||||
* @param pageForm
|
||||
* @param response
|
||||
*/
|
||||
void exportProposalStatisticsAsDocx(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导出反馈表
|
||||
|
||||
+96
-2
@@ -28,6 +28,12 @@ import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xwpf.usermodel.BreakType;
|
||||
import org.apache.poi.xwpf.usermodel.IBodyElement;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTable;
|
||||
import org.ddr.poi.html.HtmlRenderPolicy;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
@@ -40,6 +46,7 @@ import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -326,15 +333,16 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
|
||||
for (NutMap proposalRow : list) {
|
||||
try (ByteArrayOutputStream docxByteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||
proposalCommonService.exportProposalAsDocx(proposalRow.getString("id"), docxByteArrayOutputStream);
|
||||
ZipEntry zipEntry = new ZipEntry(proposalRow.getString("code") + "-" + proposalRow.getString("name") + ".docx");
|
||||
ZipEntry zipEntry = new ZipEntry(proposalRow.getString("code") + "-" + sanitizeZipFileName(proposalRow.getString("name")) + ".docx");
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
docxByteArrayOutputStream.writeTo(zipOutputStream);
|
||||
zipOutputStream.closeEntry();
|
||||
} catch (IOException e) {
|
||||
// 处理单个文件生成失败的情况
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("zip entry write failed, proposalId=" + proposalRow.getString("id"), e);
|
||||
}
|
||||
}
|
||||
zipOutputStream.finish();
|
||||
zipOutputStream.close();
|
||||
CommonDownloadUtil.download("提案汇总压缩包.zip", bos.toByteArray(), response);
|
||||
} catch (Exception e) {
|
||||
@@ -345,6 +353,92 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exportProposalStatisticsAsDocx(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) {
|
||||
List<NutMap> list = queryProposalBaseList(pageForm);
|
||||
if (Lang.isEmpty(list)) {
|
||||
throw Lang.makeThrow("暂无可导出的提案数据");
|
||||
}
|
||||
XWPFDocument mergedDoc = null;
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
for (NutMap proposalRow : list) {
|
||||
try (ByteArrayOutputStream oneDocOut = new ByteArrayOutputStream()) {
|
||||
proposalCommonService.exportProposalAsDocx(proposalRow.getString("id"), oneDocOut);
|
||||
if (mergedDoc == null) {
|
||||
mergedDoc = new XWPFDocument(new ByteArrayInputStream(oneDocOut.toByteArray()));
|
||||
} else {
|
||||
try (XWPFDocument oneDoc = new XWPFDocument(new ByteArrayInputStream(oneDocOut.toByteArray()))) {
|
||||
appendDocument(mergedDoc, oneDoc, true);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("proposal merge export failed, proposalId={}, error={}", proposalRow.getString("id"), e.getMessage());
|
||||
}
|
||||
}
|
||||
if (mergedDoc == null) {
|
||||
throw Lang.makeThrow("暂无可导出的提案数据");
|
||||
}
|
||||
mergedDoc.write(out);
|
||||
CommonDownloadUtil.download("proposal-merged.docx", out.toByteArray(), response);
|
||||
} catch (Exception e) {
|
||||
log.error("proposal merge docx export failed: {}", e.getMessage(), e);
|
||||
throw Lang.makeThrow("导出失败");
|
||||
} finally {
|
||||
if (mergedDoc != null) {
|
||||
try {
|
||||
mergedDoc.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("close merged doc failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<NutMap> queryProposalBaseList(ProposalQueryComprehensiveParam pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.id,
|
||||
info.code,
|
||||
info.name
|
||||
FROM
|
||||
proposal_info info
|
||||
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
|
||||
LEFT JOIN teacher_congress_delegate tcde on tcde.loginName = info.createUserLoginName
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm);
|
||||
cnd.groupBy("info.id");
|
||||
cnd.asc("info.code");
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private void appendDocument(XWPFDocument target, XWPFDocument source, boolean addPageBreakBefore) {
|
||||
if (addPageBreakBefore && !target.getBodyElements().isEmpty()) {
|
||||
XWPFParagraph paragraph = target.createParagraph();
|
||||
XWPFRun run = paragraph.createRun();
|
||||
run.addBreak(BreakType.PAGE);
|
||||
}
|
||||
for (IBodyElement bodyElement : source.getBodyElements()) {
|
||||
if (bodyElement instanceof XWPFParagraph) {
|
||||
XWPFParagraph paragraph = (XWPFParagraph) bodyElement;
|
||||
XWPFParagraph newParagraph = target.createParagraph();
|
||||
newParagraph.getCTP().set(paragraph.getCTP().copy());
|
||||
} else if (bodyElement instanceof XWPFTable) {
|
||||
XWPFTable table = (XWPFTable) bodyElement;
|
||||
XWPFTable newTable = target.createTable();
|
||||
newTable.getCTTbl().set(table.getCTTbl().copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String sanitizeZipFileName(String name) {
|
||||
return StrUtil.blankToDefault(name, "unnamed").replaceAll("[\\\\/:*?\"<>|\\r\\n]+", "_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportFeedBackAsZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) {
|
||||
Sql sql = Sqls.create("""
|
||||
|
||||
Reference in New Issue
Block a user