|
|
@@ -7,10 +7,12 @@ import com.jeeplus.common.utils.DateUtils;
|
|
|
import com.jeeplus.common.utils.StringUtils;
|
|
|
import com.jeeplus.common.utils.excel.ExportExcel;
|
|
|
import com.jeeplus.common.utils.excel.ExportKnowledgeBaseExcel;
|
|
|
+import com.jeeplus.common.utils.excel.ExportKnowledgeBaseAuditExcel;
|
|
|
import com.jeeplus.common.utils.excel.ImportExcel;
|
|
|
import com.jeeplus.common.utils.excel.utils.FileMatcher;
|
|
|
import com.jeeplus.common.web.BaseController;
|
|
|
import com.jeeplus.modules.WorkKnowledgeBase.entity.*;
|
|
|
+import com.jeeplus.modules.WorkKnowledgeBase.service.WorkKnowledgeBaseAuditRecordService;
|
|
|
import com.jeeplus.modules.WorkKnowledgeBase.service.WorkKnowledgeBasePointRuleService;
|
|
|
import com.jeeplus.modules.WorkKnowledgeBase.service.WorkKnowledgeBaseReadLikeService;
|
|
|
import com.jeeplus.modules.WorkKnowledgeBase.service.WorkKnowledgeBaseShareService;
|
|
|
@@ -29,7 +31,9 @@ import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import javax.servlet.http.HttpSession;
|
|
|
import java.io.OutputStream;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.LinkedHashMap;
|
|
|
@@ -63,6 +67,9 @@ public class WorkKnowledgeBaseShareController extends BaseController {
|
|
|
@Autowired
|
|
|
private WorkKnowledgeBaseReadLikeService readLikeService;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private WorkKnowledgeBaseAuditRecordService auditRecordService;
|
|
|
+
|
|
|
@ModelAttribute
|
|
|
public WorkKnowledgeBaseShareInfo get(@RequestParam(required = false) String id) {
|
|
|
WorkKnowledgeBaseShareInfo entity = null;
|
|
|
@@ -1161,6 +1168,291 @@ public class WorkKnowledgeBaseShareController extends BaseController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ======================== 临时导出用(含审核信息,后期可删除) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出所有页签(含审核人、审核时间、审核间隔)
|
|
|
+ * 临时方法,后期生产环境不需要可直接删除
|
|
|
+ */
|
|
|
+ @RequiresPermissions("workKnowledgeBase:share:export")
|
|
|
+ @RequestMapping(value = "exportAllTabsWithAudit")
|
|
|
+ public void exportAllTabsWithAudit(WorkKnowledgeBaseShareInfo shareInfo,
|
|
|
+ HttpServletRequest request, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ // 获取所有根节点(parent_id='0')
|
|
|
+ List<WorkKnowledgeBaseTreeInfo> rootNodes = shareService.findRootNodes();
|
|
|
+ if (rootNodes == null || rootNodes.isEmpty()) {
|
|
|
+ throw new RuntimeException("没有找到知识库根节点");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 收集固定筛选参数
|
|
|
+ String name = request.getParameter("name");
|
|
|
+ String beginDate = request.getParameter("beginDate");
|
|
|
+ String endDate = request.getParameter("endDate");
|
|
|
+
|
|
|
+ // 加载所有树节点,构建 nodeId -> treeName 映射
|
|
|
+ Map<String, String> nodeNameMap = new HashMap<>();
|
|
|
+ for (WorkKnowledgeBaseTreeInfo rn : rootNodes) {
|
|
|
+ nodeNameMap.put(rn.getId(), rn.getTreeName());
|
|
|
+ List<WorkKnowledgeBaseTreeInfo> descendants = shareService.findAllDescendantsByRootId(rn.getId());
|
|
|
+ if (descendants != null) {
|
|
|
+ for (WorkKnowledgeBaseTreeInfo n : descendants) {
|
|
|
+ nodeNameMap.put(n.getId(), n.getTreeName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ExportKnowledgeBaseAuditExcel excel = new ExportKnowledgeBaseAuditExcel();
|
|
|
+ Set<String> usedSheetNames = new HashSet<>();
|
|
|
+ List<Map<String, Object>> allExportData = new ArrayList<>(); // 汇总所有分类数据,用于最后的统计页签
|
|
|
+
|
|
|
+ // 为每个根节点创建一个Sheet
|
|
|
+ for (WorkKnowledgeBaseTreeInfo rootNode : rootNodes) {
|
|
|
+ String rootId = rootNode.getId();
|
|
|
+
|
|
|
+ // 获取该根节点配置的动态字段
|
|
|
+ List<WorkKnowledgeBaseDynamicInfo> rootDynamicFields = shareService.findDynamicFields(rootId);
|
|
|
+ if (rootDynamicFields == null) {
|
|
|
+ rootDynamicFields = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 收集该根节点的动态字段筛选条件
|
|
|
+ Map<String, String> dynamicConditions = new HashMap<>();
|
|
|
+ for (WorkKnowledgeBaseDynamicInfo field : rootDynamicFields) {
|
|
|
+ String paramValue = request.getParameter("dynamic_" + field.getId());
|
|
|
+ if (StringUtils.isNotBlank(paramValue)) {
|
|
|
+ dynamicConditions.put(field.getId(), paramValue.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建导出列:序号 + 固定列 + 动态列 + 所属子节点 + 审核信息(最多3位审核人)
|
|
|
+ List<String> exportHeaders = new ArrayList<>();
|
|
|
+ exportHeaders.add("序号|_rowNum");
|
|
|
+ exportHeaders.add("文件名称|name");
|
|
|
+ exportHeaders.add("文件地址|fileName");
|
|
|
+ for (WorkKnowledgeBaseDynamicInfo field : rootDynamicFields) {
|
|
|
+ exportHeaders.add(field.getFieldLabel() + "|dynamic_" + field.getFieldKey());
|
|
|
+ }
|
|
|
+ exportHeaders.add("内容属性|contentAttribute");
|
|
|
+ exportHeaders.add("创建人|createByName");
|
|
|
+ exportHeaders.add("创建时间|createTime");
|
|
|
+ exportHeaders.add("状态|statusLabel");
|
|
|
+ exportHeaders.add("是否人工赋分|isManualScoreLabel");
|
|
|
+ exportHeaders.add("所属子节点|childNodeName");
|
|
|
+ // 审核信息列(最多3位审核人,每人3列:姓名、时间、间隔)
|
|
|
+ for (int i = 1; i <= 3; i++) {
|
|
|
+ exportHeaders.add("审核人" + i + "|auditor" + i + "Name");
|
|
|
+ exportHeaders.add("审核时间" + i + "|auditor" + i + "Time");
|
|
|
+ exportHeaders.add("审核间隔" + i + "(秒)|auditor" + i + "Gap");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询该根节点下所有数据
|
|
|
+ WorkKnowledgeBaseShareInfo queryEntity = new WorkKnowledgeBaseShareInfo();
|
|
|
+ queryEntity.setRootId(rootId);
|
|
|
+ if (StringUtils.isNotBlank(name)) {
|
|
|
+ queryEntity.setName(name);
|
|
|
+ }
|
|
|
+ queryEntity.setBeginDate(beginDate);
|
|
|
+ queryEntity.setEndDate(endDate);
|
|
|
+ String isManualScore = request.getParameter("isManualScore");
|
|
|
+ if (StringUtils.isNotBlank(isManualScore)) {
|
|
|
+ queryEntity.setIsManualScore(isManualScore);
|
|
|
+ }
|
|
|
+ List<Map<String, Object>> dataList = shareService.findDynamicListAll(queryEntity, rootDynamicFields, dynamicConditions);
|
|
|
+ shareService.fillFirstAttachmentData(dataList);
|
|
|
+
|
|
|
+ // ===== 批量查询审核数据(LEFT JOIN 临时表获取 time_gap_seconds) =====
|
|
|
+ List<String> allFileIds = new ArrayList<>();
|
|
|
+ for (Map<String, Object> row : dataList) {
|
|
|
+ Object idObj = row.get("id");
|
|
|
+ if (idObj != null) {
|
|
|
+ allFileIds.add(idObj.toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 按 file_id 分组:fileId -> List<Map>(已按 audit_time ASC 排序)
|
|
|
+ Map<String, List<Map<String, Object>>> auditDataMap = new HashMap<>();
|
|
|
+ if (!allFileIds.isEmpty()) {
|
|
|
+ List<Map<String, Object>> allAuditData = auditRecordService.findAuditExportData(allFileIds);
|
|
|
+ for (Map<String, Object> ad : allAuditData) {
|
|
|
+ String fid = ad.get("fileId") != null ? ad.get("fileId").toString() : "";
|
|
|
+ List<Map<String, Object>> list = auditDataMap.get(fid);
|
|
|
+ if (list == null) {
|
|
|
+ list = new ArrayList<>();
|
|
|
+ auditDataMap.put(fid, list);
|
|
|
+ }
|
|
|
+ list.add(ad);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理导出数据
|
|
|
+ List<Map<String, Object>> exportData = new ArrayList<>();
|
|
|
+ int rowNum = 1;
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ for (Map<String, Object> row : dataList) {
|
|
|
+ Map<String, Object> exportRow = new HashMap<>(row);
|
|
|
+ exportRow.put("_rowNum", rowNum++);
|
|
|
+
|
|
|
+ // 创建时间使用附件上传时间(work_attachment.create_date)替代原记录创建时间
|
|
|
+ Object attachCreateDate = row.get("attachmentCreateDate");
|
|
|
+ if (attachCreateDate instanceof Date) {
|
|
|
+ exportRow.put("createTime", sdf.format((Date) attachCreateDate));
|
|
|
+ } else if (attachCreateDate != null) {
|
|
|
+ exportRow.put("createTime", attachCreateDate.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 状态映射
|
|
|
+ String auditStatus = row.get("auditStatus") != null ? row.get("auditStatus").toString() : "";
|
|
|
+ String statusLabel;
|
|
|
+ switch (auditStatus) {
|
|
|
+ case "0": statusLabel = "草稿"; break;
|
|
|
+ case "1": statusLabel = "未审核"; break;
|
|
|
+ case "2": statusLabel = "审核中"; break;
|
|
|
+ case "4": statusLabel = "审核未通过"; break;
|
|
|
+ case "5": statusLabel = "审核通过"; break;
|
|
|
+ case "6": statusLabel = "待回答"; break;
|
|
|
+ case "7": statusLabel = "回答中"; break;
|
|
|
+ case "8": statusLabel = "已结束"; break;
|
|
|
+ default: statusLabel = ""; break;
|
|
|
+ }
|
|
|
+ exportRow.put("statusLabel", statusLabel);
|
|
|
+ // 是否人工赋分转换
|
|
|
+ String manualScore = row.get("isManualScore") != null ? row.get("isManualScore").toString() : "";
|
|
|
+ exportRow.put("isManualScoreLabel", "1".equals(manualScore) ? "是" : "否");
|
|
|
+ // 内容属性转换
|
|
|
+ String contentAttr = row.get("contentAttribute") != null ? row.get("contentAttribute").toString() : "";
|
|
|
+ if ("0".equals(contentAttr)) {
|
|
|
+ exportRow.put("contentAttribute", "原创");
|
|
|
+ } else if ("1".equals(contentAttr)) {
|
|
|
+ exportRow.put("contentAttribute", "转载");
|
|
|
+ } else {
|
|
|
+ exportRow.put("contentAttribute", "");
|
|
|
+ }
|
|
|
+ // 所属子节点名称
|
|
|
+ String treeNodeId = row.get("treeNodeId") != null ? row.get("treeNodeId").toString() : "";
|
|
|
+ String childNodeName = nodeNameMap.getOrDefault(treeNodeId, "");
|
|
|
+ exportRow.put("childNodeName", childNodeName);
|
|
|
+
|
|
|
+ // ===== 填充审核信息(最多3位审核人,数据来自临时表) =====
|
|
|
+ String fileId = row.get("id") != null ? row.get("id").toString() : "";
|
|
|
+ List<Map<String, Object>> auditDataList = auditDataMap.get(fileId);
|
|
|
+ for (int i = 1; i <= 3; i++) {
|
|
|
+ if (auditDataList != null && i <= auditDataList.size()) {
|
|
|
+ Map<String, Object> ad = auditDataList.get(i - 1);
|
|
|
+ // 审核人
|
|
|
+ exportRow.put("auditor" + i + "Name", ad.get("auditorName") != null ? ad.get("auditorName").toString() : "");
|
|
|
+ // 审核时间
|
|
|
+ Object auditTimeObj = ad.get("auditTime");
|
|
|
+ if (auditTimeObj instanceof Date) {
|
|
|
+ exportRow.put("auditor" + i + "Time", sdf.format((Date) auditTimeObj));
|
|
|
+ } else {
|
|
|
+ exportRow.put("auditor" + i + "Time", auditTimeObj != null ? auditTimeObj.toString() : "");
|
|
|
+ }
|
|
|
+ // 审核间隔(秒,来自临时表 time_gap_seconds)
|
|
|
+ Object gapObj = ad.get("timeGapSeconds");
|
|
|
+ if (gapObj instanceof Number) {
|
|
|
+ exportRow.put("auditor" + i + "Gap", ((Number) gapObj).longValue());
|
|
|
+ } else {
|
|
|
+ exportRow.put("auditor" + i + "Gap", "");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ exportRow.put("auditor" + i + "Name", "");
|
|
|
+ exportRow.put("auditor" + i + "Time", "");
|
|
|
+ exportRow.put("auditor" + i + "Gap", "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ exportData.add(exportRow);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Sheet名称处理
|
|
|
+ String sheetName = rootNode.getTreeName();
|
|
|
+ if (sheetName == null || sheetName.isEmpty()) {
|
|
|
+ sheetName = "未命名分类";
|
|
|
+ }
|
|
|
+ sheetName = sheetName.replaceAll("[\\\\/:*?\\[\\]]", "_");
|
|
|
+ if (sheetName.length() > 31) {
|
|
|
+ sheetName = sheetName.substring(0, 31);
|
|
|
+ }
|
|
|
+ String baseSheetName = sheetName;
|
|
|
+ int suffix = 2;
|
|
|
+ while (usedSheetNames.contains(sheetName)) {
|
|
|
+ String suffixStr = "(" + suffix + ")";
|
|
|
+ int maxBaseLen = 31 - suffixStr.length();
|
|
|
+ sheetName = (baseSheetName.length() > maxBaseLen ? baseSheetName.substring(0, maxBaseLen) : baseSheetName) + suffixStr;
|
|
|
+ suffix++;
|
|
|
+ }
|
|
|
+ usedSheetNames.add(sheetName);
|
|
|
+
|
|
|
+ excel.addSheet(sheetName, rootNode.getTreeName(), exportHeaders, exportData);
|
|
|
+ allExportData.addAll(exportData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== 最后一个页签:汇总统计 ========================
|
|
|
+ int totalCount = allExportData.size();
|
|
|
+ int passedCount = 0, auditingCount = 0, pendingCount = 0, draftCount = 0, rejectedCount = 0;
|
|
|
+ for (Map<String, Object> row : allExportData) {
|
|
|
+ String status = row.get("auditStatus") != null ? row.get("auditStatus").toString() : "";
|
|
|
+ switch (status) {
|
|
|
+ case "5": passedCount++; break;
|
|
|
+ case "2": auditingCount++; break;
|
|
|
+ case "1": pendingCount++; break;
|
|
|
+ case "0": draftCount++; break;
|
|
|
+ case "4": rejectedCount++; break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ List<String> summaryHeaders = new ArrayList<>();
|
|
|
+ summaryHeaders.add("统计项|label");
|
|
|
+ summaryHeaders.add("数量|count");
|
|
|
+ List<Map<String, Object>> summaryData = new ArrayList<>();
|
|
|
+ String[] labels = {"总数据量", "审核通过", "审核中", "未审核", "草稿", "审核未通过"};
|
|
|
+ int[] counts = {totalCount, passedCount, auditingCount, pendingCount, draftCount, rejectedCount};
|
|
|
+ for (int i = 0; i < labels.length; i++) {
|
|
|
+ Map<String, Object> row = new HashMap<>();
|
|
|
+ row.put("label", labels[i]);
|
|
|
+ row.put("count", counts[i]);
|
|
|
+ summaryData.add(row);
|
|
|
+ }
|
|
|
+ excel.addSheet("汇总统计", "汇总统计", summaryHeaders, summaryData);
|
|
|
+
|
|
|
+ String fileName = "知识库数据导出(含审核)_" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
|
|
|
+ excel.write(response, fileName);
|
|
|
+ excel.dispose();
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("导出失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== 月度汇总统计导出 ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 月度汇总统计导出(上传量、点赞量、阅读量)
|
|
|
+ */
|
|
|
+ @RequiresPermissions("workKnowledgeBase:share:export")
|
|
|
+ @RequestMapping(value = "monthlyStatsExport")
|
|
|
+ public void monthlyStatsExport(@RequestParam String beginMonth,
|
|
|
+ @RequestParam String endMonth,
|
|
|
+ HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ List<Map<String, Object>> statsData = shareService.getMonthlyStats(beginMonth, endMonth);
|
|
|
+
|
|
|
+ ExportKnowledgeBaseExcel excel = new ExportKnowledgeBaseExcel();
|
|
|
+ List<String> headers = new ArrayList<>();
|
|
|
+ headers.add("月份|month");
|
|
|
+ headers.add("上传数量|uploadCount");
|
|
|
+ headers.add("点赞数量|likeCount");
|
|
|
+ headers.add("阅读数量|readCount");
|
|
|
+
|
|
|
+ excel.addSheet("月度汇总", "月度汇总统计", headers, statsData);
|
|
|
+
|
|
|
+ String fileName = "月度汇总统计_" + beginMonth + "_" + endMonth + "_" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
|
|
|
+ excel.write(response, fileName);
|
|
|
+ excel.dispose();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("导出失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// ======================== 审核相关接口 ========================
|
|
|
|
|
|
/**
|