|
@@ -0,0 +1,507 @@
|
|
|
|
|
+package com.yc.ship.module.trade.service.report.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.excel.EasyExcel;
|
|
|
|
|
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
|
|
|
|
|
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
|
|
|
|
+import com.yc.ship.module.ota.dal.dataobject.distributorcategory.DistributorCategoryDO;
|
|
|
|
|
+import com.yc.ship.module.ota.service.distributorcategory.DistributorCategoryService;
|
|
|
|
|
+import com.yc.ship.module.resource.helper.DateHelper;
|
|
|
|
|
+import com.yc.ship.module.trade.controller.admin.report.vo.AcquisitionDailyReqVO;
|
|
|
|
|
+import com.yc.ship.module.trade.controller.admin.report.vo.CabinMixDailyReqVO;
|
|
|
|
|
+import com.yc.ship.module.trade.dal.mysql.report.AcquisitionDailyMapper;
|
|
|
|
|
+import com.yc.ship.module.trade.service.report.AcquisitionDailyService;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.net.URLEncoder;
|
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class AcquisitionDailyServiceImpl implements AcquisitionDailyService {
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private DistributorCategoryService distributorCategoryService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private AcquisitionDailyMapper acquisitionDailyMapper;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<Map<String, Object>> getList(AcquisitionDailyReqVO reqVO) {
|
|
|
|
|
+ // 查询供应商财务分组
|
|
|
|
|
+ List<DistributorCategoryDO> categoryList= distributorCategoryService.getCategoryList(2);
|
|
|
|
|
+ if (categoryList == null || categoryList.isEmpty()) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Map<String,Object>> currentDataList = acquisitionDailyMapper.getList(reqVO, buildDistributorCategoryColumns(categoryList));
|
|
|
|
|
+ if (currentDataList == null || currentDataList.isEmpty()) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Map<String, Object>> lastYearDataList = queryLastYearData(reqVO, categoryList);
|
|
|
|
|
+
|
|
|
|
|
+ return processAcquisitionDailyData(currentDataList, lastYearDataList, categoryList);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String buildDistributorCategoryColumns(List<DistributorCategoryDO> categoryList) {
|
|
|
|
|
+ StringBuilder columns = new StringBuilder();
|
|
|
|
|
+ for (int i = 0; i < categoryList.size(); i++) {
|
|
|
|
|
+ DistributorCategoryDO categoryDO = categoryList.get(i);
|
|
|
|
|
+ Long categoryId = categoryDO.getId();
|
|
|
|
|
+ String cateName = categoryDO.getCateName();
|
|
|
|
|
+
|
|
|
|
|
+ String columnSql = String.format(
|
|
|
|
|
+ "COALESCE(SUM(CASE WHEN odc.id = %s THEN 1 ELSE 0 END), 0) AS `%s`",
|
|
|
|
|
+ categoryId, cateName
|
|
|
|
|
+ );
|
|
|
|
|
+ columns.append(columnSql);
|
|
|
|
|
+ if (i < categoryList.size() - 1) {
|
|
|
|
|
+ columns.append(", ");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return columns.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Map<String, Object>> queryLastYearData(AcquisitionDailyReqVO reqVO, List<DistributorCategoryDO> categoryList) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String startDate = reqVO.getStartDate();
|
|
|
|
|
+ String endDate = reqVO.getEndDate();
|
|
|
|
|
+
|
|
|
|
|
+ if (startDate == null || startDate.isEmpty()) {
|
|
|
|
|
+ return new ArrayList<>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Date startD = DateHelper.parseDate(startDate);
|
|
|
|
|
+ Date endD = endDate != null && !endDate.isEmpty() ? DateHelper.parseDate(endDate) : null;
|
|
|
|
|
+
|
|
|
|
|
+ AcquisitionDailyReqVO lastYearReq = new AcquisitionDailyReqVO();
|
|
|
|
|
+ lastYearReq.setStartDate(DateHelper.getAppointYearStartDate(startD));
|
|
|
|
|
+
|
|
|
|
|
+ if (endD != null) {
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.setTime(startD);
|
|
|
|
|
+ int daysDiff = (int) ((endD.getTime() - startD.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
|
|
+ cal.add(Calendar.YEAR, -1);
|
|
|
|
|
+ cal.add(Calendar.DATE, daysDiff);
|
|
|
|
|
+ lastYearReq.setEndDate(DateHelper.format(cal.getTime(), "yyyy-MM-dd"));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ lastYearReq.setEndDate(DateHelper.format(new Date(), "yyyy-MM-dd"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return acquisitionDailyMapper.getList(lastYearReq, buildDistributorCategoryColumns(categoryList));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("[queryLastYearData] 查询去年同期数据失败: {}", e.getMessage());
|
|
|
|
|
+ return new ArrayList<>();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Map<String, Object>> processAcquisitionDailyData(
|
|
|
|
|
+ List<Map<String, Object>> currentDataList,
|
|
|
|
|
+ List<Map<String, Object>> lastYearDataList,
|
|
|
|
|
+ List<DistributorCategoryDO> categoryList) {
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 按月份分组当前数据和去年同期数据
|
|
|
|
|
+ Map<String, List<Map<String, Object>>> currentMonthMap = currentDataList.stream()
|
|
|
|
|
+ .collect(Collectors.groupingBy(
|
|
|
|
|
+ item -> String.valueOf(item.get("month")),
|
|
|
|
|
+ TreeMap::new, Collectors.toList()));
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, List<Map<String, Object>>> lastYearMonthMap = lastYearDataList.stream()
|
|
|
|
|
+ .collect(Collectors.groupingBy(
|
|
|
|
|
+ item -> String.valueOf(item.get("month")),
|
|
|
|
|
+ TreeMap::new, Collectors.toList()));
|
|
|
|
|
+
|
|
|
|
|
+ int index = 1;
|
|
|
|
|
+ Map<String, Double> cumulativeCategoryCountMap = new LinkedHashMap<>();
|
|
|
|
|
+ Map<String, Double> lastYearCumulativeMap = new LinkedHashMap<>();
|
|
|
|
|
+ double cumulativeTotalCount = 0.0;
|
|
|
|
|
+ double lastYearCumulativeTotalCount = 0.0;
|
|
|
|
|
+ boolean isFirstMonth = true;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取分销商类别名称列表
|
|
|
|
|
+ List<String> categoryNames = categoryList.stream()
|
|
|
|
|
+ .map(DistributorCategoryDO::getCateName)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (Map.Entry<String, List<Map<String, Object>>> entry : currentMonthMap.entrySet()) {
|
|
|
|
|
+ String month = entry.getKey();
|
|
|
|
|
+ List<Map<String, Object>> dailyItems = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ // 添加每日数据
|
|
|
|
|
+ for (Map<String, Object> data : dailyItems) {
|
|
|
|
|
+ Map<String, Object> row = createBasicRow(data, index++, categoryNames);
|
|
|
|
|
+ row.put("month", formatMonth(month));
|
|
|
|
|
+ row.put("isSubtotal", false);
|
|
|
|
|
+ row.put("isCumulative", false);
|
|
|
|
|
+ result.add(row);
|
|
|
|
|
+
|
|
|
|
|
+ accumulateSingleCategoryCount(cumulativeCategoryCountMap, data, categoryNames);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算月度小计
|
|
|
|
|
+ Map<String, Double> monthCategoryCountMap = calculateMonthCategoryCount(dailyItems, categoryNames);
|
|
|
|
|
+ double totalCount = calculateTotalCountSum(dailyItems);
|
|
|
|
|
+ cumulativeTotalCount += totalCount;
|
|
|
|
|
+ Map<String, Object> subtotalDataVO = createSummaryRow(
|
|
|
|
|
+ month + "月小计", "", "", "数据", monthCategoryCountMap, totalCount, true, false);
|
|
|
|
|
+ subtotalDataVO.put("index", "");
|
|
|
|
|
+ result.add(subtotalDataVO);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算同比
|
|
|
|
|
+ String lastYearMonth = getMonthMinusOneYear(month);
|
|
|
|
|
+ List<Map<String, Object>> lastYearMonthData = lastYearMonthMap.getOrDefault(lastYearMonth, new ArrayList<>());
|
|
|
|
|
+ Map<String, Double> lastYearMonthCategoryCountMap = calculateMonthCategoryCount(lastYearMonthData, categoryNames);
|
|
|
|
|
+ double lastYearMonthTotal = calculateTotalCountSum(lastYearMonthData); // 新增:去年同期当月 totalCount
|
|
|
|
|
+ lastYearCumulativeTotalCount += lastYearMonthTotal; // 新增:累加到去年同期累计
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> subtotalYoyVO = createYoyRow(
|
|
|
|
|
+ monthCategoryCountMap, lastYearMonthCategoryCountMap,
|
|
|
|
|
+ totalCount, lastYearMonthTotal,
|
|
|
|
|
+ categoryNames, true, false);
|
|
|
|
|
+ result.add(subtotalYoyVO);
|
|
|
|
|
+
|
|
|
|
|
+ accumulateListCategoryCount(lastYearCumulativeMap, lastYearMonthData, categoryNames);
|
|
|
|
|
+ // 累计(从第二个月开始)
|
|
|
|
|
+ if (!isFirstMonth) {
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> cumulativeDataVO = createSummaryRow(
|
|
|
|
|
+ "累计", "", "", "数据", cumulativeCategoryCountMap, cumulativeTotalCount, false, true);
|
|
|
|
|
+ cumulativeDataVO.put("index", "");
|
|
|
|
|
+ result.add(cumulativeDataVO);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> cumulativeYoyVO = createYoyRow(
|
|
|
|
|
+ cumulativeCategoryCountMap, lastYearCumulativeMap,
|
|
|
|
|
+ cumulativeTotalCount, lastYearCumulativeTotalCount,
|
|
|
|
|
+ categoryNames, false, true);
|
|
|
|
|
+ result.add(cumulativeYoyVO);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isFirstMonth = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String getMonthMinusOneYear(String month) {
|
|
|
|
|
+ if (month == null || month.length() < 7) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ int year = Integer.parseInt(month.substring(0, 4));
|
|
|
|
|
+ String rest = month.substring(4);
|
|
|
|
|
+ return (year - 1) + rest;
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String formatMonth(String month) {
|
|
|
|
|
+ if (month == null || month.length() < 5) {
|
|
|
|
|
+ return month;
|
|
|
|
|
+ }
|
|
|
|
|
+ return month.substring(5);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建基础行
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createBasicRow(Map<String, Object> data, int index, List<String> categoryNames) {
|
|
|
|
|
+ Map<String, Object> row = new LinkedHashMap<>();
|
|
|
|
|
+ row.put("index", index);
|
|
|
|
|
+ row.put("date", String.valueOf(data.get("date")));
|
|
|
|
|
+ row.put("ship", String.valueOf(data.get("ship")));
|
|
|
|
|
+ row.put("route", String.valueOf(data.get("route")));
|
|
|
|
|
+ row.put("voyageNo", String.valueOf(data.get("voyageNo")));
|
|
|
|
|
+
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ Double count = data.get(categoryName) != null ?
|
|
|
|
|
+ ((Number) data.get(categoryName)).doubleValue() : 0.0;
|
|
|
|
|
+ row.put(categoryName, count);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Object totalCount = data.get("totalCount");
|
|
|
|
|
+ if (totalCount != null) {
|
|
|
|
|
+ row.put("totalCount", ((Number) totalCount).doubleValue());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算月度分销商类别统计
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Double> calculateMonthCategoryCount(List<Map<String, Object>> dataList, List<String> categoryNames) {
|
|
|
|
|
+ Map<String, Double> categoryCountMap = new LinkedHashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ double sum = dataList.stream()
|
|
|
|
|
+ .mapToDouble(data -> {
|
|
|
|
|
+ Object value = data.get(categoryName);
|
|
|
|
|
+ return value != null ? ((Number) value).doubleValue() : 0.0;
|
|
|
|
|
+ })
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ categoryCountMap.put(categoryName, sum);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return categoryCountMap;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 累计单个数据的分销商类别数量
|
|
|
|
|
+ */
|
|
|
|
|
+ private void accumulateSingleCategoryCount(Map<String, Double> cumulativeMap, Map<String, Object> data, List<String> categoryNames) {
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ Double count = data.get(categoryName) != null ?
|
|
|
|
|
+ ((Number) data.get(categoryName)).doubleValue() : 0.0;
|
|
|
|
|
+ cumulativeMap.merge(categoryName, count, Double::sum);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 累计列表数据的分销商类别数量
|
|
|
|
|
+ */
|
|
|
|
|
+ private void accumulateListCategoryCount(Map<String, Double> cumulativeMap, List<Map<String, Object>> dataList, List<String> categoryNames) {
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ double sum = dataList.stream()
|
|
|
|
|
+ .mapToDouble(data -> {
|
|
|
|
|
+ Object value = data.get(categoryName);
|
|
|
|
|
+ return value != null ? ((Number) value).doubleValue() : 0.0;
|
|
|
|
|
+ })
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ cumulativeMap.merge(categoryName, sum, Double::sum);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建汇总行
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createSummaryRow(
|
|
|
|
|
+ String month, String date, String ship, String voyageNo,
|
|
|
|
|
+ Map<String, Double> categoryCountMap, double totalCount,
|
|
|
|
|
+ boolean isSubtotal, boolean isCumulative) {
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> row = new LinkedHashMap<>();
|
|
|
|
|
+ row.put("month", month);
|
|
|
|
|
+ row.put("date", date);
|
|
|
|
|
+ row.put("ship", ship);
|
|
|
|
|
+ row.put("route", "");
|
|
|
|
|
+ row.put("voyageNo", voyageNo);
|
|
|
|
|
+
|
|
|
|
|
+ for (Map.Entry<String, Double> entry : categoryCountMap.entrySet()) {
|
|
|
|
|
+ row.put(entry.getKey(), entry.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ row.put("totalCount", totalCount);
|
|
|
|
|
+ row.put("isSubtotal", isSubtotal);
|
|
|
|
|
+ row.put("isCumulative", isCumulative);
|
|
|
|
|
+
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建同比行
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createYoyRow(
|
|
|
|
|
+ Map<String, Double> currentMap,
|
|
|
|
|
+ Map<String, Double> lastYearMap,
|
|
|
|
|
+ double currentTotal,
|
|
|
|
|
+ double lastYearTotal,
|
|
|
|
|
+ List<String> categoryNames, boolean isSubtotal, boolean isCumulative) {
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> yoyRow = new LinkedHashMap<>();
|
|
|
|
|
+ yoyRow.put("month", "");
|
|
|
|
|
+ yoyRow.put("date", "");
|
|
|
|
|
+ yoyRow.put("ship", "");
|
|
|
|
|
+ yoyRow.put("route", "");
|
|
|
|
|
+ yoyRow.put("voyageNo", "同比");
|
|
|
|
|
+ yoyRow.put("isSubtotal", isSubtotal);
|
|
|
|
|
+ yoyRow.put("isCumulative", isCumulative);
|
|
|
|
|
+
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ Double currentVal = currentMap.getOrDefault(categoryName, 0.0);
|
|
|
|
|
+ Double lastYearVal = lastYearMap.getOrDefault(categoryName, 0.0);
|
|
|
|
|
+ String yoyPercent = calcYoyPercent(currentVal, lastYearVal);
|
|
|
|
|
+ yoyRow.put(categoryName, yoyPercent);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ yoyRow.put("totalCount", calcYoyPercent(currentTotal, lastYearTotal)); // 修改:使用传入的 totalCount
|
|
|
|
|
+
|
|
|
|
|
+ return yoyRow;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算同比百分比
|
|
|
|
|
+ */
|
|
|
|
|
+ private String calcYoyPercent(Number current, Number last) {
|
|
|
|
|
+ if (current == null || last == null || last.doubleValue() == 0) {
|
|
|
|
|
+ return "-";
|
|
|
|
|
+ }
|
|
|
|
|
+ double diff = current.doubleValue() - last.doubleValue();
|
|
|
|
|
+ double percent = (diff / last.doubleValue()) * 100;
|
|
|
|
|
+ if (percent >= 0) {
|
|
|
|
|
+ return String.format("+%.1f%%", percent);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return String.format("%.1f%%", percent);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算列表的 totalCount 之和
|
|
|
|
|
+ */
|
|
|
|
|
+ private double calculateTotalCountSum(List<Map<String, Object>> dataList) {
|
|
|
|
|
+ return dataList.stream()
|
|
|
|
|
+ .mapToDouble(data -> {
|
|
|
|
|
+ Object totalCount = data.get("totalCount");
|
|
|
|
|
+ return totalCount != null ? ((Number) totalCount).doubleValue() : 0.0;
|
|
|
|
|
+ })
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void exportCabinMixDaily(AcquisitionDailyReqVO reqVO, HttpServletResponse response) throws IOException {
|
|
|
|
|
+ // 1. 查询供应商财务分组
|
|
|
|
|
+ List<DistributorCategoryDO> categoryList = distributorCategoryService.getCategoryList(2);
|
|
|
|
|
+ if (categoryList == null || categoryList.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("暂无供应商财务分组配置");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 查询数据
|
|
|
|
|
+ List<Map<String, Object>> dataList = getList(reqVO);
|
|
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("暂无数据可导出");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 3. 构建表头
|
|
|
|
|
+ List<List<String>> headers = buildExportHeaders(categoryList);
|
|
|
|
|
+ // 4. 转换数据
|
|
|
|
|
+ List<List<Object>> exportData = transformExportData(dataList, categoryList);
|
|
|
|
|
+ // 5. 导出Excel
|
|
|
|
|
+ EasyExcel.write(response.getOutputStream())
|
|
|
|
|
+ .head(headers)
|
|
|
|
|
+ .autoCloseStream(false)
|
|
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
|
|
+ .registerConverter(new LongStringConverter())
|
|
|
|
|
+ .sheet("游轮获客情况")
|
|
|
|
|
+ .doWrite(exportData);
|
|
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
|
|
|
|
|
+ response.addHeader("Content-Disposition",
|
|
|
|
|
+ "attachment;filename=" + URLEncoder.encode("省际度假游轮获客情况日报表.xlsx", StandardCharsets.UTF_8.name()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<List<String>> buildExportHeaders(List<DistributorCategoryDO> categoryList) {
|
|
|
|
|
+ List<List<String>> headers = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("序号")));
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("月份")));
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("日期")));
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("船舶")));
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("航线")));
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("航次号")));
|
|
|
|
|
+
|
|
|
|
|
+ List<DistributorCategoryDO> domesticList = categoryList.stream()
|
|
|
|
|
+ .filter(c -> c.getMarketArea() != null && c.getMarketArea() == 1)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ List<DistributorCategoryDO> overseasList = categoryList.stream()
|
|
|
|
|
+ .filter(c -> c.getMarketArea() != null && c.getMarketArea() == 2)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 国内市场 二级表头
|
|
|
|
|
+ for (DistributorCategoryDO category : domesticList) {
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("国内市场", category.getCateName())));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 国外市场 二级表头
|
|
|
|
|
+ for (DistributorCategoryDO category : overseasList) {
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("国外市场", category.getCateName())));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 合计列
|
|
|
|
|
+ headers.add(new ArrayList<>(Arrays.asList("合计")));
|
|
|
|
|
+
|
|
|
|
|
+ return headers;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 转换导出数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<List<Object>> transformExportData(List<Map<String, Object>> dataList,
|
|
|
|
|
+ List<DistributorCategoryDO> categoryList) {
|
|
|
|
|
+ List<List<Object>> result = new ArrayList<>(dataList.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取分销商类别名称列表
|
|
|
|
|
+ List<String> categoryNames = categoryList.stream()
|
|
|
|
|
+ .map(DistributorCategoryDO::getCateName)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (Map<String, Object> row : dataList) {
|
|
|
|
|
+ List<Object> rowData = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ boolean isYoy = "同比".equals(row.get("voyageNo"));
|
|
|
|
|
+ boolean isSubtotalOrCumulative = Boolean.TRUE.equals(row.get("isSubtotal"))
|
|
|
|
|
+ || Boolean.TRUE.equals(row.get("isCumulative"));
|
|
|
|
|
+
|
|
|
|
|
+ if (isYoy) {
|
|
|
|
|
+ // 同比行:前面固定列留空,voyageNo 显示"同比"
|
|
|
|
|
+ rowData.add("");
|
|
|
|
|
+ rowData.add("");
|
|
|
|
|
+ rowData.add("");
|
|
|
|
|
+ rowData.add("");
|
|
|
|
|
+ rowData.add("");
|
|
|
|
|
+ rowData.add("同比");
|
|
|
|
|
+
|
|
|
|
|
+ // 各分销商类别的同比数据
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ rowData.add(row.get(categoryName) != null ? row.get(categoryName) : "-");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 合计列同比
|
|
|
|
|
+ rowData.add(row.get("totalCount") != null ? row.get("totalCount") : "-");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 普通数据行/小计行/累计行
|
|
|
|
|
+ rowData.add(row.get("index") != null ? String.valueOf(row.get("index")) : "");
|
|
|
|
|
+ rowData.add(row.get("month") != null ? row.get("month") : "");
|
|
|
|
|
+ rowData.add(row.get("date") != null ? row.get("date") : "");
|
|
|
|
|
+ rowData.add(row.get("ship") != null ? row.get("ship") : "");
|
|
|
|
|
+ rowData.add(row.get("route") != null ? row.get("route") : "");
|
|
|
|
|
+ rowData.add(row.get("voyageNo") != null ? row.get("voyageNo") : "");
|
|
|
|
|
+
|
|
|
|
|
+ // 各分销商类别的数据
|
|
|
|
|
+ for (String categoryName : categoryNames) {
|
|
|
|
|
+ Object value = row.get(categoryName);
|
|
|
|
|
+ if (value != null) {
|
|
|
|
|
+ double num = ((Number) value).doubleValue();
|
|
|
|
|
+ if (num == (int) num) {
|
|
|
|
|
+ rowData.add(String.valueOf((int) num));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rowData.add(String.format("%.1f", num));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rowData.add("-");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 合计列
|
|
|
|
|
+ Object totalCount = row.get("totalCount");
|
|
|
|
|
+ if (totalCount != null) {
|
|
|
|
|
+ double num = ((Number) totalCount).doubleValue();
|
|
|
|
|
+ if (num == (int) num) {
|
|
|
|
|
+ rowData.add(String.valueOf((int) num));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rowData.add(String.format("%.1f", num));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rowData.add("-");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result.add(rowData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|