diff --git a/excel-handle/pom.xml b/excel-handle/pom.xml
index ade6b58..4784242 100644
--- a/excel-handle/pom.xml
+++ b/excel-handle/pom.xml
@@ -59,6 +59,10 @@
fastjson
1.2.83
+
+ com.ruoyi
+ ruoyi-system
+
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CustomerExportData.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CustomerExportData.java
index 8d30fe0..0b70e56 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CustomerExportData.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CustomerExportData.java
@@ -66,6 +66,9 @@ public class CustomerExportData implements Serializable {
*/
private Date addDate;
+ //成交日期
+ private Date finishDate;
+
/**
* 来源
*/
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java
index b64d3b7..a3778cc 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java
@@ -519,6 +519,20 @@ import java.util.concurrent.atomic.AtomicInteger;
GROUP_FIELD_MAP.put("AE组", "tagGroup18");
}
+ private static final Map GROUP_ATTR_MAP = new LinkedHashMap<>();
+ static {
+ GROUP_ATTR_MAP.put("N组", "tag_group1");
+ GROUP_ATTR_MAP.put("O组", "tag_group2");
+ GROUP_ATTR_MAP.put("P组", "tag_group3");
+ GROUP_ATTR_MAP.put("W组", "tag_group10");
+ GROUP_ATTR_MAP.put("X组", "tag_group11");
+ GROUP_ATTR_MAP.put("Y组", "tag_group12");
+ GROUP_ATTR_MAP.put("Z组", "tag_group13");
+ GROUP_ATTR_MAP.put("AA组", "tag_group14");
+ GROUP_ATTR_MAP.put("AC组", "tag_group16");
+ GROUP_ATTR_MAP.put("AD组", "tag_group17");
+ GROUP_ATTR_MAP.put("AE组", "tag_group18");
+ }
/**
* 字段反射缓存
*/
@@ -729,7 +743,6 @@ import java.util.concurrent.atomic.AtomicInteger;
String customerAttr = data.getTagGroup6();
if (customerAttr != null && !customerAttr.trim().isEmpty()) {
stats.setTotalCustomerAttr(stats.getTotalCustomerAttr() + 1);
-
if (customerAttr.contains("家长")) {
stats.setParentCount(stats.getParentCount() + 1);
//todo 存在订单未完成 但是有标签的场景
@@ -751,9 +764,24 @@ import java.util.concurrent.atomic.AtomicInteger;
if (matchesQValue(data, date) && isTimelyOrder(data)) {
stats.setStudentDailyOrderCount(stats.getStudentDailyOrderCount() + 1);
}
+ } else if (customerAttr.contains("老师")) {
+ stats.setTeacherCount(stats.getTeacherCount() + 1);
+ stats.setTeacherOrderCount(stats.getTeacherOrderCount() + 1);
- } else {
- stats.setUnknownAttrCount(stats.getUnknownAttrCount() + 1);
+ // 新增:N组老师出单率统计(当日)
+ stats.setTeacherDailyCount(stats.getTeacherDailyCount() + 1);
+ if (matchesQValue(data, date) && isTimelyOrder(data)) {
+ stats.setTeacherDailyOrderCount(stats.getTeacherDailyOrderCount() + 1);
+ }
+ }
+ } else {
+ stats.setUnknownAttrCount(stats.getUnknownAttrCount() + 1);
+ stats.setUnkownOrderCount(stats.getUnkownOrderCount() + 1);
+
+ // 新增:N组未知出单率统计(当日)
+ stats.setUnkownDailyCount(stats.getUnkownDailyCount() + 1);
+ if (matchesQValue(data, date) && isTimelyOrder(data)) {
+ stats.setUnkownDailyOrderCount(stats.getUnkownDailyOrderCount() + 1);
}
}
@@ -825,6 +853,7 @@ import java.util.concurrent.atomic.AtomicInteger;
int sortNo = 0;
// 为每个组生成23个统计指标
for (Map.Entry entry : accumulator.getGroupStatsMap().entrySet()) {
+
String groupName = entry.getKey();
StatisticsAccumulator.GroupStatistics stats = entry.getValue();
@@ -834,7 +863,9 @@ import java.util.concurrent.atomic.AtomicInteger;
setIndicatorValue(corpId,indicatorMap,curDate, "成单成本(当日)", groupName, "需手工填写",(10*sortNo++));
// 1. 成单数
- setIndicatorValue(corpId,indicatorMap,curDate, "成单数(当日)", groupName, String.valueOf(stats.getOrderCount()),(10*sortNo++));
+ //成单数 需要从历史的所有数据中获取 成交日期 = date的数据
+ Long finishCount = customerExportDataMapper.selectByFinishDate(corpId,curDate,GROUP_ATTR_MAP.get(groupName));
+ setIndicatorValue(corpId,indicatorMap,curDate, "成单数(当日)", groupName, String.valueOf(finishCount),(10*sortNo++));
// 2. 进粉数
setIndicatorValue(corpId,indicatorMap,curDate, "进粉数(当日)", groupName, String.valueOf(stats.getCustomerCount()),(10*sortNo++));
@@ -866,6 +897,11 @@ import java.util.concurrent.atomic.AtomicInteger;
setIndicatorValue(corpId,indicatorMap,curDate, "学生占比(当日)", groupName, studentRate,(10*sortNo++));
setIndicatorValue(corpId,indicatorMap,curDate, "学生数量(当日)", groupName, String.valueOf(stats.getStudentCount()),(10*sortNo++),true);
+ // 8. 老师占比
+ String teacherRate = calculateRate(stats.getTeacherCount(), stats.getTotalCustomerAttr());
+ setIndicatorValue(corpId,indicatorMap,curDate, "老师占比(当日)", groupName, teacherRate,(10*sortNo++));
+ setIndicatorValue(corpId,indicatorMap,curDate, "老师数量(当日)", groupName, String.valueOf(stats.getTeacherCount()),(10*sortNo++),true);
+
// 9. 未知占比
String unknownRate = calculateRate(stats.getUnknownAttrCount(), stats.getTotalCustomerAttr());
setIndicatorValue(corpId,indicatorMap,curDate, "未知占比(当日)", groupName, unknownRate,(10*sortNo++));
@@ -914,12 +950,12 @@ import java.util.concurrent.atomic.AtomicInteger;
// 19. 家长出单占比
String parentOrderRate = calculateRate(stats.getParentOrderCount(), stats.getParentCount());
- setIndicatorValue(corpId,indicatorMap,curDate, "家长出单占比(当日)", groupName, parentOrderRate,(10*sortNo++));
+ setIndicatorValue(corpId,indicatorMap,curDate, "家长出单占比(当日)", groupName, parentOrderRate,(10*sortNo++),true);
setIndicatorValue(corpId,indicatorMap,curDate, "家长出单数量(当日)", groupName, String.valueOf(stats.getParentOrderCount()),(10*sortNo++),true);
// 20. 学生出单占比
String studentOrderRate = calculateRate(stats.getStudentOrderCount(), stats.getStudentCount());
- setIndicatorValue(corpId,indicatorMap,curDate, "学生出单占比(当日)", groupName, studentOrderRate,(10*sortNo++));
+ setIndicatorValue(corpId,indicatorMap,curDate, "学生出单占比(当日)", groupName, studentOrderRate,(10*sortNo++),true);
setIndicatorValue(corpId,indicatorMap,curDate, "学生出单数量(当日)", groupName, String.valueOf(stats.getStudentOrderCount()),(10*sortNo++),true);
// 21. 家长出单率(当日)
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/StatisticsAccumulator.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/StatisticsAccumulator.java
index 2ef4c58..ea37654 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/StatisticsAccumulator.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/StatisticsAccumulator.java
@@ -78,6 +78,10 @@ public class StatisticsAccumulator {
* 学生数
*/
private int studentCount = 0;
+ /**
+ * 老师
+ */
+ private int teacherCount = 0;
/**
* 未知(空白)数
@@ -141,6 +145,13 @@ public class StatisticsAccumulator {
* 学生出单数
*/
private int studentOrderCount = 0;
+ /**
+ * 老师出单数
+ *
+ */
+ private int teacherOrderCount = 0;
+
+ private int unkownOrderCount = 0;
// ========== 新增:家长/学生出单率统计(当日) ==========
/**
@@ -158,10 +169,29 @@ public class StatisticsAccumulator {
*/
private int studentDailyOrderCount = 0;
+
/**
* 学生总数(当日进粉)- 用于出单率分母
*/
private int studentDailyCount = 0;
+ /**
+ * 老师总数(当日进粉)- 用于出单率分母
+ */
+ private int teacherDailyCount = 0;
+ /**
+ * 未知总数(当日进粉)- 用于出单率分母
+ */
+ private int unkownDailyCount = 0;
+
+ /**
+ * 学生出单数(当日成交)- 满足Q列=当日 AND T列=已成交及时单9元+
+ */
+ private int teacherDailyOrderCount = 0;
+
+ /**
+ * 学生出单数(当日成交)- 满足Q列=当日 AND T列=已成交及时单9元+
+ */
+ private int unkownDailyOrderCount = 0;
// ========== 成本数据 ==========
/**
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CustomerExportDataMapper.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CustomerExportDataMapper.java
index de61cc1..f33cb29 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CustomerExportDataMapper.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CustomerExportDataMapper.java
@@ -57,4 +57,42 @@ public interface CustomerExportDataMapper extends BaseMapper
@Param("addUserAccount") String addUserAccount,
@Param("addTime") Date addTime
);
+
+ Long selectByFinishDate(
+ @Param("corpId") String corpId,@Param("date") Date date,@Param("attr") String attr);
+
+ /**
+ * 统计客户导出数据VO数量(用于异步导出)
+ * @param corpId 企业ID
+ * @param startDate 开始日期
+ * @param endDate 结束日期
+ * @param customerName 客户名称(可选)
+ * @return 数据总数
+ */
+ int countCustomerExportDataVOList(
+ @Param("corpId") String corpId,
+ @Param("startDate") Date startDate,
+ @Param("endDate") Date endDate,
+ @Param("customerName") String customerName
+ );
+
+ /**
+ * 分页查询客户导出数据VO列表(用于异步导出)
+ * @param corpId 企业ID
+ * @param startDate 开始日期
+ * @param endDate 结束日期
+ * @param customerName 客户名称(可选)
+ * @param offset 偏移量
+ * @param limit 每页数量
+ * @return 客户导出数据VO列表
+ */
+ List selectCustomerExportDataVOListByPage(
+ @Param("corpId") String corpId,
+ @Param("startDate") Date startDate,
+ @Param("endDate") Date endDate,
+ @Param("customerName") String customerName,
+ @Param("offset") int offset,
+ @Param("limit") int limit
+ );
}
+
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerDataChangeTrackingService.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerDataChangeTrackingService.java
index 6897a33..d267b47 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerDataChangeTrackingService.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerDataChangeTrackingService.java
@@ -116,6 +116,7 @@ public class CustomerDataChangeTrackingService {
sb.append(data.getTagGroup15()).append("|");
sb.append(data.getTagGroup16()).append("|");
sb.append(data.getTagGroup17()).append("|");
+ sb.append(data.getFinishDate()).append("|");
sb.append(data.getTagGroup18());
// 计算MD5
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportAsyncService.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportAsyncService.java
new file mode 100644
index 0000000..3e6306f
--- /dev/null
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportAsyncService.java
@@ -0,0 +1,250 @@
+package com.ruoyi.excel.wecom.service;
+
+import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.excel.wecom.mapper.CustomerExportDataMapper;
+import com.ruoyi.excel.wecom.vo.CustomerExportDataVO;
+import com.ruoyi.system.service.ISysExportTaskService;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 客户数据异步导出服务
+ */
+@Service
+public class CustomerExportAsyncService
+{
+ private static final Logger log = LoggerFactory.getLogger(CustomerExportAsyncService.class);
+
+ private static final int BATCH_SIZE = 5000;
+ private static final int ROW_ACCESS_WINDOW = 1000;
+
+ @Autowired
+ private CustomerExportDataMapper customerExportDataMapper;
+
+ @Autowired
+ private ISysExportTaskService exportTaskService;
+
+ @Async("threadPoolTaskExecutor")
+ public void executeExport(Long taskId, String corpId, Date startDate, Date endDate, String customerName)
+ {
+ log.info("开始执行异步导出任务,taskId: {}, corpId: {}", taskId, corpId);
+ String filePath = null;
+ String fileName = null;
+ File file = null;
+ SXSSFWorkbook workbook = null;
+ FileOutputStream fos = null;
+
+ try
+ {
+ int totalCount = customerExportDataMapper.countCustomerExportDataVOList(corpId, startDate, endDate, customerName);
+ log.info("导出任务 taskId: {}, 总数据量: {}", taskId, totalCount);
+
+ if (totalCount == 0)
+ {
+ exportTaskService.markSuccess(taskId, "", "无数据.xlsx", 0L);
+ return;
+ }
+
+ exportTaskService.updateProgress(taskId, 0, totalCount);
+
+ String dateStr = DateUtils.dateTimeNow("yyyyMMddHHmmss");
+ fileName = "客户统计数据_" + dateStr + ".xlsx";
+ String downloadPath = RuoYiConfig.getDownloadPath();
+ File dir = new File(downloadPath);
+ if (!dir.exists())
+ {
+ dir.mkdirs();
+ }
+ filePath = downloadPath + fileName;
+ file = new File(filePath);
+
+ workbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW);
+ Sheet sheet = workbook.createSheet("客户统计数据");
+
+ CellStyle headerStyle = createHeaderStyle(workbook);
+ CellStyle dataStyle = createDataStyle(workbook);
+
+ String[] headers = {
+ "客户名称", "描述", "添加人", "添加人账号", "添加人所属部门", "添加时间", "来源",
+ "手机", "企业", "邮箱", "地址", "职务", "电话",
+ "标签组1(投放)", "标签组2(公司孵化)", "标签组3(商务)", "标签组4(成交日期)", "标签组5(年级组)",
+ "标签组6(客户属性)", "标签组7(成交)", "标签组8(成交品牌)", "标签组9(线索通标签)", "标签组10(A1组)",
+ "标签组11(B1组)", "标签组12(C1组)", "标签组13(D1组)", "标签组14(E1组)", "标签组15(意向度)",
+ "标签组16(自然流)", "标签组17(F1组)", "标签组18(G1组)"
+ };
+
+ Row headerRow = sheet.createRow(0);
+ for (int i = 0; i < headers.length; i++)
+ {
+ Cell cell = headerRow.createCell(i);
+ cell.setCellValue(headers[i]);
+ cell.setCellStyle(headerStyle);
+ sheet.setColumnWidth(i, 20 * 256);
+ }
+
+ int processedCount = 0;
+ int rowIndex = 1;
+ int offset = 0;
+
+ while (offset < totalCount)
+ {
+ List dataList = customerExportDataMapper.selectCustomerExportDataVOListByPage(
+ corpId, startDate, endDate, customerName, offset, BATCH_SIZE);
+
+ if (dataList == null || dataList.isEmpty())
+ {
+ break;
+ }
+
+ for (CustomerExportDataVO data : dataList)
+ {
+ Row row = sheet.createRow(rowIndex++);
+ fillRowData(row, data, dataStyle);
+ processedCount++;
+ }
+
+ offset += BATCH_SIZE;
+
+ exportTaskService.updateProgress(taskId, processedCount, totalCount);
+ }
+
+ fos = new FileOutputStream(file);
+ workbook.write(fos);
+ fos.flush();
+
+ long fileSize = file.length();
+ exportTaskService.markSuccess(taskId, filePath, fileName, fileSize);
+ log.info("导出任务完成,taskId: {}, 文件: {}, 大小: {} bytes", taskId, fileName, fileSize);
+ }
+ catch (Exception e)
+ {
+ log.error("导出任务执行失败,taskId: {}", taskId, e);
+ exportTaskService.markFailed(taskId, "导出失败:" + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (fos != null)
+ {
+ fos.close();
+ }
+ if (workbook != null)
+ {
+ workbook.dispose();
+ workbook.close();
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("关闭资源失败", e);
+ }
+ }
+ }
+
+ private CellStyle createHeaderStyle(Workbook workbook)
+ {
+ CellStyle style = workbook.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.THIN);
+ style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ Font font = workbook.createFont();
+ font.setBold(true);
+ font.setFontName("Arial");
+ font.setFontHeightInPoints((short) 10);
+ style.setFont(font);
+ return style;
+ }
+
+ private CellStyle createDataStyle(Workbook workbook)
+ {
+ CellStyle style = workbook.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.THIN);
+ style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ Font font = workbook.createFont();
+ font.setFontName("Arial");
+ font.setFontHeightInPoints((short) 10);
+ style.setFont(font);
+ return style;
+ }
+
+ private void fillRowData(Row row, CustomerExportDataVO data, CellStyle style)
+ {
+ int col = 0;
+ createCell(row, col++, data.getCustomerName(), style);
+ createCell(row, col++, data.getDescription(), style);
+ createCell(row, col++, data.getAddUserName(), style);
+ createCell(row, col++, data.getAddUserAccount(), style);
+ createCell(row, col++, data.getAddUserDepartment(), style);
+ createCell(row, col++, formatDate(data.getAddTime()), style);
+ createCell(row, col++, data.getSource(), style);
+ createCell(row, col++, data.getMobile(), style);
+ createCell(row, col++, data.getCompany(), style);
+ createCell(row, col++, data.getEmail(), style);
+ createCell(row, col++, data.getAddress(), style);
+ createCell(row, col++, data.getPosition(), style);
+ createCell(row, col++, data.getPhone(), style);
+ createCell(row, col++, data.getTagGroup1(), style);
+ createCell(row, col++, data.getTagGroup2(), style);
+ createCell(row, col++, data.getTagGroup3(), style);
+ createCell(row, col++, data.getTagGroup4(), style);
+ createCell(row, col++, data.getTagGroup5(), style);
+ createCell(row, col++, data.getTagGroup6(), style);
+ createCell(row, col++, data.getTagGroup7(), style);
+ createCell(row, col++, data.getTagGroup8(), style);
+ createCell(row, col++, data.getTagGroup9(), style);
+ createCell(row, col++, data.getTagGroup10(), style);
+ createCell(row, col++, data.getTagGroup11(), style);
+ createCell(row, col++, data.getTagGroup12(), style);
+ createCell(row, col++, data.getTagGroup13(), style);
+ createCell(row, col++, data.getTagGroup14(), style);
+ createCell(row, col++, data.getTagGroup15(), style);
+ createCell(row, col++, data.getTagGroup16(), style);
+ createCell(row, col++, data.getTagGroup17(), style);
+ createCell(row, col++, data.getTagGroup18(), style);
+ }
+
+ private void createCell(Row row, int column, String value, CellStyle style)
+ {
+ Cell cell = row.createCell(column);
+ cell.setCellValue(value == null ? "" : value);
+ cell.setCellStyle(style);
+ }
+
+ private String formatDate(Date date)
+ {
+ if (date == null)
+ {
+ return "";
+ }
+ return DateUtils.parseDateToStr("yyyy-MM-dd", date);
+ }
+}
diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportService.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportService.java
index 5c18864..8b9a7f9 100644
--- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportService.java
+++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/CustomerExportService.java
@@ -235,6 +235,79 @@ public class CustomerExportService {
return exportData;
}
+ private Date parseCompleteDate(String orderDate, Date addDate) {
+ if (orderDate == null || orderDate.trim().isEmpty()) {
+ return null;
+ }
+
+ if (addDate == null) {
+ log.warn("parseCompleteDate: addDate is null, orderDate: {}", orderDate);
+ return null;
+ }
+
+ try {
+ String[] dates = orderDate.trim().split(",");
+ String lastDateStr = dates[dates.length - 1].trim();
+ log.debug("parseCompleteDate: input orderDate: {}, lastDateStr: {}", orderDate, lastDateStr);
+
+ Calendar orderCal = Calendar.getInstance();
+ orderCal.setTime(addDate);
+ orderCal.set(Calendar.HOUR_OF_DAY, 0);
+ orderCal.set(Calendar.MINUTE, 0);
+ orderCal.set(Calendar.SECOND, 0);
+ orderCal.set(Calendar.MILLISECOND, 0);
+
+ boolean parsed = false;
+
+ java.util.regex.Pattern fullDatePattern = java.util.regex.Pattern.compile("(\\d{4})[/\\-](\\d{1,2})[/\\-](\\d{1,2})");
+ java.util.regex.Matcher fullDateMatcher = fullDatePattern.matcher(lastDateStr);
+ if (fullDateMatcher.find()) {
+ int year = Integer.parseInt(fullDateMatcher.group(1));
+ int month = Integer.parseInt(fullDateMatcher.group(2));
+ int day = Integer.parseInt(fullDateMatcher.group(3));
+ orderCal.set(Calendar.YEAR, year);
+ orderCal.set(Calendar.MONTH, month - 1);
+ orderCal.set(Calendar.DAY_OF_MONTH, day);
+ parsed = true;
+ log.info("parseCompleteDate: matched full date pattern, input: {}, result: {}-{}-{}", lastDateStr, year, month, day);
+ }
+
+ if (!parsed) {
+ java.util.regex.Pattern monthDayPattern = java.util.regex.Pattern.compile("^(\\d{1,2})[.\\-/](\\d{1,2})");
+ java.util.regex.Matcher monthDayMatcher = monthDayPattern.matcher(lastDateStr);
+ if (monthDayMatcher.find()) {
+ int month = Integer.parseInt(monthDayMatcher.group(1));
+ int day = Integer.parseInt(monthDayMatcher.group(2));
+ orderCal.set(Calendar.MONTH, month - 1);
+ orderCal.set(Calendar.DAY_OF_MONTH, day);
+ Calendar tempCal = Calendar.getInstance();
+ tempCal.setTime(addDate);
+ tempCal.set(Calendar.MONTH, month - 1);
+ tempCal.set(Calendar.DAY_OF_MONTH, day);
+ tempCal.set(Calendar.HOUR_OF_DAY, 0);
+ tempCal.set(Calendar.MINUTE, 0);
+ tempCal.set(Calendar.SECOND, 0);
+ tempCal.set(Calendar.MILLISECOND, 0);
+ if (tempCal.getTime().before(addDate)) {
+ orderCal.add(Calendar.YEAR, 1);
+ }
+ parsed = true;
+ log.info("parseCompleteDate: matched month-day pattern, input: {}, result: {}-{}-{}", lastDateStr, orderCal.get(Calendar.YEAR), month, day);
+ }
+ }
+
+ if (!parsed) {
+ log.warn("parseCompleteDate: no pattern matched, input: {}", lastDateStr);
+ return null;
+ }
+ return orderCal.getTime();
+ } catch (Exception e) {
+ log.error("parseCompleteDate error, orderDate: {}, addDate: {}", orderDate, addDate, e);
+ return null;
+ }
+ }
+
+
/**
* 处理客户标签,按标签组分类填充到对应字段
*
@@ -277,8 +350,7 @@ public class CustomerExportService {
* @param tagNames 标签名称(逗号分隔)
*/
private void fillTagGroupField(CustomerExportData exportData, String groupName, String tagNames) {
- // 根据标签组名称映射到对应字段
- // 这里的映射关系需要根据实际业务调整
+
if (groupName.contains("投放")) {
exportData.setTagGroup1(tagNames);
} else if (groupName.contains("公司孵化")) {
@@ -287,12 +359,15 @@ public class CustomerExportService {
exportData.setTagGroup3(tagNames);
} else if (groupName.contains("成交日期")) {
exportData.setTagGroup4(tagNames);
+ Date finishDate = parseCompleteDate(tagNames, exportData.getAddTime());
+ exportData.setFinishDate(finishDate);
} else if (groupName.contains("年级组")) {
exportData.setTagGroup5(tagNames);
} else if (groupName.contains("客户属性")) {
exportData.setTagGroup6(tagNames);
} else if (groupName.contains("成交") && !groupName.contains("日期") && !groupName.contains("品牌")) {
exportData.setTagGroup7(tagNames);
+
} else if (groupName.contains("成交品牌")) {
exportData.setTagGroup8(tagNames);
} else if (groupName.contains("线索通")) {
diff --git a/excel-handle/src/main/resources/mapper/wecom/CustomerExportDataMapper.xml b/excel-handle/src/main/resources/mapper/wecom/CustomerExportDataMapper.xml
index 384ecd7..a58e19d 100644
--- a/excel-handle/src/main/resources/mapper/wecom/CustomerExportDataMapper.xml
+++ b/excel-handle/src/main/resources/mapper/wecom/CustomerExportDataMapper.xml
@@ -1,6 +1,22 @@
+
+
+
+
+
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CustomerExportDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CustomerExportDataController.java
index 9079a00..f51a7e5 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CustomerExportDataController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CustomerExportDataController.java
@@ -2,19 +2,26 @@ package com.ruoyi.web.controller.wocom;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.CorpContextHolder;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.excel.wecom.domain.CustomerExportData;
+import com.ruoyi.excel.wecom.service.CustomerExportAsyncService;
import com.ruoyi.excel.wecom.service.ICustomerExportDataService;
import com.ruoyi.excel.wecom.vo.CustomerExportDataVO;
+import com.ruoyi.system.domain.SysExportTask;
+import com.ruoyi.system.service.ISysExportTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
@@ -23,11 +30,17 @@ import java.util.List;
*/
@RestController
@RequestMapping("/wecom/customerExport")
-public class CustomerExportDataController extends BaseController {
-
+public class CustomerExportDataController extends BaseController
+{
@Autowired
private ICustomerExportDataService customerExportDataService;
+ @Autowired
+ private CustomerExportAsyncService customerExportAsyncService;
+
+ @Autowired
+ private ISysExportTaskService exportTaskService;
+
/**
* 查询客户统计数据列表
*/
@@ -36,16 +49,17 @@ public class CustomerExportDataController extends BaseController {
public TableDataInfo list(
@RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
@RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
- @RequestParam(value = "customerName", required = false) String customerName) {
+ @RequestParam(value = "customerName", required = false) String customerName)
+ {
String corpId = CorpContextHolder.getCurrentCorpId();
startPage();
- List list = customerExportDataService.selectCustomerExportDataList(corpId,startDate, endDate, customerName);
+ List list = customerExportDataService.selectCustomerExportDataList(corpId, startDate, endDate, customerName);
return getDataTable(list);
}
/**
- * 导出客户统计数据列表
+ * 导出客户统计数据列表(同步方式,保留兼容)
*/
@PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
@Log(title = "客户统计数据", businessType = BusinessType.EXPORT)
@@ -53,13 +67,149 @@ public class CustomerExportDataController extends BaseController {
public void export(HttpServletResponse response,
@RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
@RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
- @RequestParam(value = "customerName", required = false) String customerName) {
+ @RequestParam(value = "customerName", required = false) String customerName)
+ {
String corpId = CorpContextHolder.getCurrentCorpId();
- List list = customerExportDataService.selectCustomerExportDataVOList(corpId,startDate, endDate, customerName);
+ List list = customerExportDataService.selectCustomerExportDataVOList(corpId, startDate, endDate, customerName);
ExcelUtil util = new ExcelUtil<>(CustomerExportDataVO.class);
util.exportExcel(response, list, "客户统计数据");
}
+ /**
+ * 异步导出客户统计数据列表
+ */
+ @PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
+ @Log(title = "客户统计数据-异步导出", businessType = BusinessType.EXPORT)
+ @PostMapping("/exportAsync")
+ public AjaxResult exportAsync(
+ @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+ @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+ @RequestParam(value = "customerName", required = false) String customerName)
+ {
+ String corpId = CorpContextHolder.getCurrentCorpId();
+ SysExportTask task = new SysExportTask();
+ task.setTaskName("客户统计数据导出");
+ task.setTaskType("customer_export");
+ task.setCorpId(corpId);
+ task.setCreateBy(getUsername());
+
+ Long taskId = exportTaskService.createExportTask(task);
+
+ customerExportAsyncService.executeExport(taskId, corpId, startDate, endDate, customerName);
+
+ return AjaxResult.success("导出任务已创建,请稍后查看进度", taskId);
+ }
+
+ /**
+ * 查询导出任务状态
+ */
+ @PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
+ @GetMapping("/export/status/{taskId}")
+ public AjaxResult getExportStatus(@PathVariable Long taskId)
+ {
+ SysExportTask task = exportTaskService.selectExportTaskById(taskId);
+ if (task == null)
+ {
+ return AjaxResult.error("任务不存在");
+ }
+ return AjaxResult.success(task);
+ }
+
+ /**
+ * 下载导出文件
+ */
+ @PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
+ @GetMapping("/export/download/{taskId}")
+ public void downloadExportFile(@PathVariable Long taskId, HttpServletResponse response)
+ {
+ SysExportTask task = exportTaskService.selectExportTaskById(taskId);
+ if (task == null)
+ {
+ return;
+ }
+
+ if (!SysExportTask.STATUS_SUCCESS.equals(task.getStatus()))
+ {
+ return;
+ }
+
+ String filePath = task.getFilePath();
+ if (filePath == null || filePath.isEmpty())
+ {
+ return;
+ }
+
+ File file = new File(filePath);
+ if (!file.exists())
+ {
+ return;
+ }
+
+ try
+ {
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.setCharacterEncoding("utf-8");
+ String fileName = URLEncoder.encode(task.getFileName(), StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ";filename*=utf-8''" + fileName);
+ response.setHeader("Content-Length", String.valueOf(file.length()));
+
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ java.io.OutputStream os = response.getOutputStream();
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = fis.read(buffer)) != -1)
+ {
+ os.write(buffer, 0, bytesRead);
+ }
+ fis.close();
+ os.flush();
+ }
+ catch (Exception e)
+ {
+ logger.error("下载文件失败", e);
+ }
+ }
+
+ /**
+ * 查询导出任务列表
+ */
+ @PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
+ @GetMapping("/export/list")
+ public TableDataInfo exportTaskList(SysExportTask task)
+ {
+ String corpId = CorpContextHolder.getCurrentCorpId();
+ task.setCorpId(corpId);
+ task.setCreateBy(getUsername());
+ startPage();
+ List list = exportTaskService.selectExportTaskList(task);
+ return getDataTable(list);
+ }
+
+ /**
+ * 删除导出任务
+ */
+ @PreAuthorize("@ss.hasPermi('wecom:customerExport:export')")
+ @Log(title = "客户统计数据-删除导出任务", businessType = BusinessType.DELETE)
+ @DeleteMapping("/export/{taskId}")
+ public AjaxResult deleteExportTask(@PathVariable Long taskId)
+ {
+ SysExportTask task = exportTaskService.selectExportTaskById(taskId);
+ if (task == null)
+ {
+ return AjaxResult.error("任务不存在");
+ }
+
+ if (task.getFilePath() != null && !task.getFilePath().isEmpty())
+ {
+ File file = new File(task.getFilePath());
+ if (file.exists())
+ {
+ file.delete();
+ }
+ }
+
+ return toAjax(exportTaskService.deleteExportTaskById(taskId));
+ }
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysExportTask.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysExportTask.java
new file mode 100644
index 0000000..7a4c0bf
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysExportTask.java
@@ -0,0 +1,209 @@
+package com.ruoyi.system.domain;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+import java.util.Date;
+
+/**
+ * 导出任务表 sys_export_task
+ */
+public class SysExportTask extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ public static final String STATUS_PENDING = "0";
+ public static final String STATUS_PROCESSING = "1";
+ public static final String STATUS_SUCCESS = "2";
+ public static final String STATUS_FAILED = "3";
+
+ @Excel(name = "任务ID", cellType = ColumnType.NUMERIC)
+ private Long taskId;
+
+ @Excel(name = "任务名称")
+ private String taskName;
+
+ @Excel(name = "任务类型")
+ private String taskType;
+
+ private String corpId;
+
+ private String queryParams;
+
+ @Excel(name = "状态", readConverterExp = "0=待处理,1=处理中,2=成功,3=失败")
+ private String status;
+
+ @Excel(name = "进度")
+ private Integer progress;
+
+ @Excel(name = "总数据量")
+ private Integer totalCount;
+
+ @Excel(name = "已处理数量")
+ private Integer processedCount;
+
+ private String filePath;
+
+ @Excel(name = "文件名")
+ private String fileName;
+
+ private Long fileSize;
+
+ private String errorMsg;
+
+ @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "完成时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date finishTime;
+
+ public Long getTaskId()
+ {
+ return taskId;
+ }
+
+ public void setTaskId(Long taskId)
+ {
+ this.taskId = taskId;
+ }
+
+ public String getTaskName()
+ {
+ return taskName;
+ }
+
+ public void setTaskName(String taskName)
+ {
+ this.taskName = taskName;
+ }
+
+ public String getTaskType()
+ {
+ return taskType;
+ }
+
+ public void setTaskType(String taskType)
+ {
+ this.taskType = taskType;
+ }
+
+ public String getCorpId()
+ {
+ return corpId;
+ }
+
+ public void setCorpId(String corpId)
+ {
+ this.corpId = corpId;
+ }
+
+ public String getQueryParams()
+ {
+ return queryParams;
+ }
+
+ public void setQueryParams(String params)
+ {
+ this.queryParams = params;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ public Integer getProgress()
+ {
+ return progress;
+ }
+
+ public void setProgress(Integer progress)
+ {
+ this.progress = progress;
+ }
+
+ public Integer getTotalCount()
+ {
+ return totalCount;
+ }
+
+ public void setTotalCount(Integer totalCount)
+ {
+ this.totalCount = totalCount;
+ }
+
+ public Integer getProcessedCount()
+ {
+ return processedCount;
+ }
+
+ public void setProcessedCount(Integer processedCount)
+ {
+ this.processedCount = processedCount;
+ }
+
+ public String getFilePath()
+ {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath)
+ {
+ this.filePath = filePath;
+ }
+
+ public String getFileName()
+ {
+ return fileName;
+ }
+
+ public void setFileName(String fileName)
+ {
+ this.fileName = fileName;
+ }
+
+ public Long getFileSize()
+ {
+ return fileSize;
+ }
+
+ public void setFileSize(Long fileSize)
+ {
+ this.fileSize = fileSize;
+ }
+
+ public String getErrorMsg()
+ {
+ return errorMsg;
+ }
+
+ public void setErrorMsg(String errorMsg)
+ {
+ this.errorMsg = errorMsg;
+ }
+
+ public Date getFinishTime()
+ {
+ return finishTime;
+ }
+
+ public void setFinishTime(Date finishTime)
+ {
+ this.finishTime = finishTime;
+ }
+
+ public boolean isFinished()
+ {
+ return STATUS_SUCCESS.equals(status) || STATUS_FAILED.equals(status);
+ }
+
+ public boolean isSuccess()
+ {
+ return STATUS_SUCCESS.equals(status);
+ }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysExportTaskMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysExportTaskMapper.java
new file mode 100644
index 0000000..7a33d80
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysExportTaskMapper.java
@@ -0,0 +1,66 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysExportTask;
+
+/**
+ * 导出任务 数据层
+ */
+public interface SysExportTaskMapper
+{
+ /**
+ * 新增导出任务
+ *
+ * @param task 导出任务对象
+ * @return 结果
+ */
+ public int insertExportTask(SysExportTask task);
+
+ /**
+ * 修改导出任务
+ *
+ * @param task 导出任务对象
+ * @return 结果
+ */
+ public int updateExportTask(SysExportTask task);
+
+ /**
+ * 根据ID查询导出任务
+ *
+ * @param taskId 任务ID
+ * @return 导出任务对象
+ */
+ public SysExportTask selectExportTaskById(Long taskId);
+
+ /**
+ * 查询导出任务列表
+ *
+ * @param task 导出任务对象
+ * @return 导出任务集合
+ */
+ public List selectExportTaskList(SysExportTask task);
+
+ /**
+ * 删除导出任务
+ *
+ * @param taskId 任务ID
+ * @return 结果
+ */
+ public int deleteExportTaskById(Long taskId);
+
+ /**
+ * 批量删除导出任务
+ *
+ * @param taskIds 任务ID数组
+ * @return 结果
+ */
+ public int deleteExportTaskByIds(Long[] taskIds);
+
+ /**
+ * 查询过期的导出任务
+ *
+ * @param expireDays 过期天数
+ * @return 导出任务集合
+ */
+ public List selectExpiredExportTasks(int expireDays);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysExportTaskService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysExportTaskService.java
new file mode 100644
index 0000000..43240c6
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysExportTaskService.java
@@ -0,0 +1,93 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysExportTask;
+
+/**
+ * 导出任务Service接口
+ */
+public interface ISysExportTaskService
+{
+ /**
+ * 创建导出任务
+ *
+ * @param task 导出任务对象
+ * @return 任务ID
+ */
+ public Long createExportTask(SysExportTask task);
+
+ /**
+ * 更新导出任务
+ *
+ * @param task 导出任务对象
+ * @return 结果
+ */
+ public int updateExportTask(SysExportTask task);
+
+ /**
+ * 根据ID查询导出任务
+ *
+ * @param taskId 任务ID
+ * @return 导出任务对象
+ */
+ public SysExportTask selectExportTaskById(Long taskId);
+
+ /**
+ * 查询导出任务列表
+ *
+ * @param task 导出任务对象
+ * @return 导出任务集合
+ */
+ public List selectExportTaskList(SysExportTask task);
+
+ /**
+ * 删除导出任务
+ *
+ * @param taskId 任务ID
+ * @return 结果
+ */
+ public int deleteExportTaskById(Long taskId);
+
+ /**
+ * 批量删除导出任务
+ *
+ * @param taskIds 任务ID数组
+ * @return 结果
+ */
+ public int deleteExportTaskByIds(Long[] taskIds);
+
+ /**
+ * 更新任务进度
+ *
+ * @param taskId 任务ID
+ * @param processedCount 已处理数量
+ * @param totalCount 总数量
+ */
+ public void updateProgress(Long taskId, int processedCount, int totalCount);
+
+ /**
+ * 标记任务成功
+ *
+ * @param taskId 任务ID
+ * @param filePath 文件路径
+ * @param fileName 文件名
+ * @param fileSize 文件大小
+ */
+ public void markSuccess(Long taskId, String filePath, String fileName, Long fileSize);
+
+ /**
+ * 标记任务失败
+ *
+ * @param taskId 任务ID
+ * @param errorMsg 错误信息
+ */
+ public void markFailed(Long taskId, String errorMsg);
+
+ /**
+ * 查询过期的导出任务
+ *
+ * @param expireDays 过期天数
+ * @return 导出任务集合
+ */
+ public List selectExpiredExportTasks(int expireDays);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysExportTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysExportTaskServiceImpl.java
new file mode 100644
index 0000000..2b7c9ff
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysExportTaskServiceImpl.java
@@ -0,0 +1,107 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.Date;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.domain.SysExportTask;
+import com.ruoyi.system.mapper.SysExportTaskMapper;
+import com.ruoyi.system.service.ISysExportTaskService;
+
+/**
+ * 导出任务Service业务层处理
+ */
+@Service
+public class SysExportTaskServiceImpl implements ISysExportTaskService
+{
+ @Autowired
+ private SysExportTaskMapper exportTaskMapper;
+
+ @Override
+ public Long createExportTask(SysExportTask task)
+ {
+ task.setStatus(SysExportTask.STATUS_PENDING);
+ task.setProgress(0);
+ task.setCreateTime(DateUtils.getNowDate());
+ exportTaskMapper.insertExportTask(task);
+ return task.getTaskId();
+ }
+
+ @Override
+ public int updateExportTask(SysExportTask task)
+ {
+ return exportTaskMapper.updateExportTask(task);
+ }
+
+ @Override
+ public SysExportTask selectExportTaskById(Long taskId)
+ {
+ return exportTaskMapper.selectExportTaskById(taskId);
+ }
+
+ @Override
+ public List selectExportTaskList(SysExportTask task)
+ {
+ return exportTaskMapper.selectExportTaskList(task);
+ }
+
+ @Override
+ public int deleteExportTaskById(Long taskId)
+ {
+ return exportTaskMapper.deleteExportTaskById(taskId);
+ }
+
+ @Override
+ public int deleteExportTaskByIds(Long[] taskIds)
+ {
+ return exportTaskMapper.deleteExportTaskByIds(taskIds);
+ }
+
+ @Override
+ public void updateProgress(Long taskId, int processedCount, int totalCount)
+ {
+ SysExportTask task = new SysExportTask();
+ task.setTaskId(taskId);
+ task.setProcessedCount(processedCount);
+ task.setTotalCount(totalCount);
+ if (totalCount > 0)
+ {
+ int progress = (int) ((processedCount * 100.0) / totalCount);
+ task.setProgress(Math.min(progress, 99));
+ }
+ task.setStatus(SysExportTask.STATUS_PROCESSING);
+ exportTaskMapper.updateExportTask(task);
+ }
+
+ @Override
+ public void markSuccess(Long taskId, String filePath, String fileName, Long fileSize)
+ {
+ SysExportTask task = new SysExportTask();
+ task.setTaskId(taskId);
+ task.setStatus(SysExportTask.STATUS_SUCCESS);
+ task.setProgress(100);
+ task.setFilePath(filePath);
+ task.setFileName(fileName);
+ task.setFileSize(fileSize);
+ task.setFinishTime(new Date());
+ exportTaskMapper.updateExportTask(task);
+ }
+
+ @Override
+ public void markFailed(Long taskId, String errorMsg)
+ {
+ SysExportTask task = new SysExportTask();
+ task.setTaskId(taskId);
+ task.setStatus(SysExportTask.STATUS_FAILED);
+ task.setErrorMsg(errorMsg);
+ task.setFinishTime(new Date());
+ exportTaskMapper.updateExportTask(task);
+ }
+
+ @Override
+ public List selectExpiredExportTasks(int expireDays)
+ {
+ return exportTaskMapper.selectExpiredExportTasks(expireDays);
+ }
+}
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysExportTaskMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysExportTaskMapper.xml
new file mode 100644
index 0000000..e39fd91
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/SysExportTaskMapper.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select task_id, task_name, task_type, corp_id, query_params, status, progress, total_count, processed_count,
+ file_path, file_name, file_size, error_msg, create_by, create_time, finish_time
+ from sys_export_task
+
+
+
+
+
+
+
+
+
+ insert into sys_export_task
+
+ task_name,
+ task_type,
+ corp_id,
+ query_params,
+ status,
+ progress,
+ total_count,
+ processed_count,
+ file_path,
+ file_name,
+ file_size,
+ error_msg,
+ create_by,
+ create_time,
+ finish_time,
+
+
+ #{taskName},
+ #{taskType},
+ #{corpId},
+ #{queryParams},
+ #{status},
+ #{progress},
+ #{totalCount},
+ #{processedCount},
+ #{filePath},
+ #{fileName},
+ #{fileSize},
+ #{errorMsg},
+ #{createBy},
+ #{createTime},
+ #{finishTime},
+
+
+
+
+ update sys_export_task
+
+ task_name = #{taskName},
+ task_type = #{taskType},
+ corp_id = #{corpId},
+ query_params = #{queryParams},
+ status = #{status},
+ progress = #{progress},
+ total_count = #{totalCount},
+ processed_count = #{processedCount},
+ file_path = #{filePath},
+ file_name = #{fileName},
+ file_size = #{fileSize},
+ error_msg = #{errorMsg},
+ finish_time = #{finishTime},
+
+ where task_id = #{taskId}
+
+
+
+ delete from sys_export_task where task_id = #{taskId}
+
+
+
+ delete from sys_export_task where task_id in
+
+ #{taskId}
+
+
+
+
diff --git a/ruoyi-ui/src/api/wecom/customerExport.js b/ruoyi-ui/src/api/wecom/customerExport.js
index 32b2598..0437cf9 100644
--- a/ruoyi-ui/src/api/wecom/customerExport.js
+++ b/ruoyi-ui/src/api/wecom/customerExport.js
@@ -52,3 +52,37 @@ export function exportCustomerExport(query) {
responseType: 'blob'
})
}
+
+// 异步导出客户导出数据
+export function exportAsync(query) {
+ return request({
+ url: '/wecom/customerExport/exportAsync',
+ method: 'post',
+ params: query
+ })
+}
+
+// 查询导出任务状态
+export function getExportStatus(taskId) {
+ return request({
+ url: '/wecom/customerExport/export/status/' + taskId,
+ method: 'get'
+ })
+}
+
+// 查询导出任务列表
+export function listExportTask(query) {
+ return request({
+ url: '/wecom/customerExport/export/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除导出任务
+export function delExportTask(taskId) {
+ return request({
+ url: '/wecom/customerExport/export/' + taskId,
+ method: 'delete'
+ })
+}
diff --git a/ruoyi-ui/src/views/wecom/customerExport/index.vue b/ruoyi-ui/src/views/wecom/customerExport/index.vue
index 4ecb9a2..e084fca 100644
--- a/ruoyi-ui/src/views/wecom/customerExport/index.vue
+++ b/ruoyi-ui/src/views/wecom/customerExport/index.vue
@@ -42,7 +42,27 @@
size="mini"
@click="handleExport"
v-hasPermi="['wecom:customerExport:export']"
- >导出
+ >同步导出
+
+
+ 异步导出
+
+
+ 导出任务
@@ -99,45 +119,105 @@
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
+
+
+
+
+
+
+
+
+ {{ getStatusLabel(scope.row.status) }}
+
+
+
+
+
+
+
+
+
+ {{ formatFileSize(scope.row.fileSize) }}
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 下载
+ 删除
+
+
+
+
+