DataBoardServiceImpl.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. package com.zsElectric.boot.system.service.impl;
  2. import cn.hutool.core.util.StrUtil;
  3. import com.zsElectric.boot.system.mapper.DataBoardMapper;
  4. import com.zsElectric.boot.system.model.dto.ChurnUserExportDTO;
  5. import com.zsElectric.boot.system.model.query.HistoryBusinessQuery;
  6. import com.zsElectric.boot.system.model.query.StationRankQuery;
  7. import com.zsElectric.boot.system.model.vo.*;
  8. import com.zsElectric.boot.system.service.DataBoardService;
  9. import lombok.RequiredArgsConstructor;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.stereotype.Service;
  12. import java.math.BigDecimal;
  13. import java.math.RoundingMode;
  14. import java.time.LocalDate;
  15. import java.time.LocalDateTime;
  16. import java.time.YearMonth;
  17. import java.time.format.DateTimeFormatter;
  18. import java.util.*;
  19. import java.util.stream.Collectors;
  20. import java.util.stream.IntStream;
  21. /**
  22. * 数据看板服务实现类
  23. *
  24. * @author zsElectric
  25. * @since 2026-03-09
  26. */
  27. @Slf4j
  28. @Service
  29. @RequiredArgsConstructor
  30. public class DataBoardServiceImpl implements DataBoardService {
  31. private final DataBoardMapper dataBoardMapper;
  32. private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  33. private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  34. private static final DateTimeFormatter MM_DD_FORMATTER = DateTimeFormatter.ofPattern("MM-dd");
  35. @Override
  36. public DataBoardRealTimeVO getRealTimeData() {
  37. DataBoardRealTimeVO realTimeVO = dataBoardMapper.selectRealTimeData();
  38. if (realTimeVO == null) {
  39. realTimeVO = new DataBoardRealTimeVO();
  40. }
  41. // 获取累计退款金额
  42. BigDecimal totalRefundAmount = dataBoardMapper.selectTotalRefundAmount();
  43. realTimeVO.setTotalRefundAmount(totalRefundAmount != null ? totalRefundAmount : BigDecimal.ZERO);
  44. // 获取累计首单金额
  45. BigDecimal totalFirstOrderAmount = dataBoardMapper.selectTotalFirstOrderAmount();
  46. realTimeVO.setTotalFirstOrderAmount(totalFirstOrderAmount != null ? totalFirstOrderAmount : BigDecimal.ZERO);
  47. // 确保所有字段非空
  48. ensureRealTimeDataNotNull(realTimeVO);
  49. return realTimeVO;
  50. }
  51. @Override
  52. public DataBoardTodayVO getTodayData() {
  53. LocalDate today = LocalDate.now();
  54. String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
  55. String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);
  56. DataBoardTodayVO todayVO = dataBoardMapper.selectTodayData(todayStart, todayEnd);
  57. if (todayVO == null) {
  58. todayVO = new DataBoardTodayVO();
  59. }
  60. // 获取今日退款金额
  61. BigDecimal todayRefundAmount = dataBoardMapper.selectTodayRefundAmount(todayStart, todayEnd);
  62. todayVO.setTodayRefundAmount(todayRefundAmount != null ? todayRefundAmount : BigDecimal.ZERO);
  63. // 获取今日首单金额
  64. BigDecimal todayFirstOrderAmount = dataBoardMapper.selectTodayFirstOrderAmount(todayStart, todayEnd);
  65. todayVO.setTodayFirstOrderAmount(todayFirstOrderAmount != null ? todayFirstOrderAmount : BigDecimal.ZERO);
  66. // 确保所有字段非空
  67. ensureTodayDataNotNull(todayVO);
  68. return todayVO;
  69. }
  70. @Override
  71. public ChargePowerTrendVO getChargePowerTrend(String compareDate) {
  72. ChargePowerTrendVO trendVO = new ChargePowerTrendVO();
  73. // 生成0-23小时列表
  74. List<Integer> hours = IntStream.rangeClosed(0, 23).boxed().collect(Collectors.toList());
  75. trendVO.setHours(hours);
  76. // 今日数据
  77. LocalDate today = LocalDate.now();
  78. String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
  79. String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);
  80. List<Map<String, Object>> todayHourlyData = dataBoardMapper.selectHourlyChargePower(todayStart, todayEnd);
  81. Map<Integer, BigDecimal> todayMap = convertHourlyDataToMap(todayHourlyData);
  82. List<BigDecimal> todayData = hours.stream()
  83. .map(hour -> todayMap.getOrDefault(hour, BigDecimal.ZERO))
  84. .collect(Collectors.toList());
  85. trendVO.setTodayData(todayData);
  86. // 对比日数据
  87. LocalDate compareDateParsed;
  88. if (StrUtil.isNotBlank(compareDate)) {
  89. compareDateParsed = LocalDate.parse(compareDate, DATE_FORMATTER);
  90. } else {
  91. // 默认对比昨天
  92. compareDateParsed = today.minusDays(1);
  93. }
  94. String compareDateStart = compareDateParsed.atStartOfDay().format(DATETIME_FORMATTER);
  95. String compareDateEnd = compareDateParsed.atTime(23, 59, 59).format(DATETIME_FORMATTER);
  96. List<Map<String, Object>> compareHourlyData = dataBoardMapper.selectHourlyChargePower(compareDateStart, compareDateEnd);
  97. Map<Integer, BigDecimal> compareMap = convertHourlyDataToMap(compareHourlyData);
  98. List<BigDecimal> compareData = hours.stream()
  99. .map(hour -> compareMap.getOrDefault(hour, BigDecimal.ZERO))
  100. .collect(Collectors.toList());
  101. trendVO.setCompareData(compareData);
  102. trendVO.setCompareDate(compareDateParsed.format(DATE_FORMATTER));
  103. return trendVO;
  104. }
  105. @Override
  106. public HistoryBusinessDataVO getHistoryBusinessData(HistoryBusinessQuery query) {
  107. HistoryBusinessDataVO dataVO = new HistoryBusinessDataVO();
  108. dataVO.setDataType(query.getDataType());
  109. dataVO.setTimeDimension(query.getTimeDimension());
  110. LocalDate today = LocalDate.now();
  111. List<String> labels = new ArrayList<>();
  112. List<BigDecimal> values = new ArrayList<>();
  113. String dataType = query.getDataType();
  114. if (StrUtil.isBlank(dataType)) {
  115. dataType = "chargePower";
  116. }
  117. if ("month".equalsIgnoreCase(query.getTimeDimension())) {
  118. // 月趋势:yearMonth 格式为 yyyy,查询该年每月汇总数据
  119. String yearStr = query.getYearMonth();
  120. if (yearStr.contains("-")) {
  121. throw new IllegalArgumentException("月趋势时yearMonth格式应为yyyy(如2026),当前传入:" + yearStr);
  122. }
  123. int year = Integer.parseInt(yearStr);
  124. int currentYear = today.getYear();
  125. int endMonth = (year == currentYear) ? today.getMonthValue() : 12;
  126. for (int month = 1; month <= endMonth; month++) {
  127. YearMonth ym = YearMonth.of(year, month);
  128. LocalDate monthStart = ym.atDay(1);
  129. LocalDate monthEnd = ym.atEndOfMonth();
  130. // 如果是当月,截止到今天
  131. if (year == currentYear && month == today.getMonthValue()) {
  132. monthEnd = today;
  133. }
  134. String startDateStr = monthStart.format(DATE_FORMATTER);
  135. String endDateStr = monthEnd.format(DATE_FORMATTER);
  136. BigDecimal monthValue = getMonthlyDataByType(dataType, startDateStr, endDateStr);
  137. labels.add(String.format("%02d", month));
  138. values.add(monthValue);
  139. }
  140. } else {
  141. // 日趋势:yearMonth 格式为 yyyy-MM,查询该月每日数据
  142. String yearMonthStr = query.getYearMonth();
  143. if (!yearMonthStr.contains("-")) {
  144. throw new IllegalArgumentException("日趋势时yearMonth格式应为yyyy-MM(如2026-03),当前传入:" + yearMonthStr);
  145. }
  146. YearMonth yearMonth = YearMonth.parse(yearMonthStr);
  147. LocalDate startDate = yearMonth.atDay(1);
  148. LocalDate endDate;
  149. // 如果是当月则到今天
  150. if (yearMonth.equals(YearMonth.from(today))) {
  151. endDate = today;
  152. } else {
  153. endDate = yearMonth.atEndOfMonth();
  154. }
  155. String startDateStr = startDate.format(DATE_FORMATTER);
  156. String endDateStr = endDate.format(DATE_FORMATTER);
  157. List<Map<String, Object>> rawData = getDailyDataByType(dataType, startDateStr, endDateStr);
  158. // 转换数据
  159. Map<String, BigDecimal> dataMap = new LinkedHashMap<>();
  160. for (Map<String, Object> item : rawData) {
  161. String label = String.valueOf(item.get("dateLabel"));
  162. BigDecimal value = item.get("value") != null ?
  163. new BigDecimal(String.valueOf(item.get("value"))) : BigDecimal.ZERO;
  164. dataMap.put(label, value);
  165. }
  166. // 填充所有日期
  167. LocalDate current = startDate;
  168. DateTimeFormatter labelFormatter = DateTimeFormatter.ofPattern("MM.dd");
  169. while (!current.isAfter(endDate)) {
  170. String label = current.format(labelFormatter);
  171. labels.add(label);
  172. values.add(dataMap.getOrDefault(label, BigDecimal.ZERO));
  173. current = current.plusDays(1);
  174. }
  175. }
  176. dataVO.setLabels(labels);
  177. dataVO.setValues(values);
  178. return dataVO;
  179. }
  180. /**
  181. * 根据数据类型获取每日数据
  182. */
  183. private List<Map<String, Object>> getDailyDataByType(String dataType, String startDateStr, String endDateStr) {
  184. switch (dataType) {
  185. case "chargeAmount":
  186. return dataBoardMapper.selectDailyChargeAmount(startDateStr, endDateStr);
  187. case "validOrders":
  188. return dataBoardMapper.selectDailyValidOrders(startDateStr, endDateStr);
  189. case "registerUsers":
  190. return dataBoardMapper.selectDailyRegisterUsers(startDateStr, endDateStr);
  191. case "chargePower":
  192. default:
  193. return dataBoardMapper.selectDailyChargePower(startDateStr, endDateStr);
  194. }
  195. }
  196. /**
  197. * 根据数据类型获取月度汇总数据
  198. */
  199. private BigDecimal getMonthlyDataByType(String dataType, String startDateStr, String endDateStr) {
  200. List<Map<String, Object>> rawData = getDailyDataByType(dataType, startDateStr, endDateStr);
  201. BigDecimal total = BigDecimal.ZERO;
  202. for (Map<String, Object> item : rawData) {
  203. BigDecimal value = item.get("value") != null ?
  204. new BigDecimal(String.valueOf(item.get("value"))) : BigDecimal.ZERO;
  205. total = total.add(value);
  206. }
  207. return total;
  208. }
  209. @Override
  210. public StationRankListVO getStationRankData(StationRankQuery query) {
  211. StationRankListVO rankListVO = new StationRankListVO();
  212. // 计算时间范围
  213. LocalDate startDate;
  214. LocalDate endDate = LocalDate.now();
  215. String timeRange = query.getTimeRange();
  216. if (StrUtil.isBlank(timeRange)) {
  217. timeRange = "week";
  218. }
  219. switch (timeRange) {
  220. case "today":
  221. startDate = endDate;
  222. break;
  223. case "month":
  224. startDate = endDate.minusDays(29);
  225. break;
  226. case "custom":
  227. if (StrUtil.isNotBlank(query.getStartDate()) && StrUtil.isNotBlank(query.getEndDate())) {
  228. startDate = LocalDate.parse(query.getStartDate(), DATE_FORMATTER);
  229. endDate = LocalDate.parse(query.getEndDate(), DATE_FORMATTER);
  230. } else {
  231. startDate = endDate.minusDays(6);
  232. }
  233. break;
  234. case "week":
  235. default:
  236. startDate = endDate.minusDays(6);
  237. break;
  238. }
  239. String startDateStr = startDate.format(DATE_FORMATTER);
  240. String endDateStr = endDate.format(DATE_FORMATTER);
  241. // 计算前一个周期的日期范围(用于波动计算)
  242. long daysDiff = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
  243. LocalDate prevStartDate = startDate.minusDays(daysDiff);
  244. LocalDate prevEndDate = startDate.minusDays(1);
  245. String prevStartDateStr = prevStartDate.format(DATE_FORMATTER);
  246. String prevEndDateStr = prevEndDate.format(DATE_FORMATTER);
  247. // 获取热门充电站
  248. List<StationRankVO> hotStations = dataBoardMapper.selectHotStations(startDateStr, endDateStr, 5);
  249. // 设置排名
  250. for (int i = 0; i < hotStations.size(); i++) {
  251. hotStations.get(i).setRank(i + 1);
  252. }
  253. rankListVO.setHotStations(hotStations);
  254. // 计算波动充电站(基于充电度数波动)
  255. List<StationRankVO> fluctuationStations = calculateFluctuationStations(
  256. hotStations, prevStartDateStr, prevEndDateStr, startDateStr, endDateStr);
  257. rankListVO.setFluctuationStations(fluctuationStations);
  258. // 设置日期范围描述
  259. rankListVO.setDateRange(startDateStr + " 至 " + endDateStr);
  260. return rankListVO;
  261. }
  262. @Override
  263. public UserChurnRateVO getUserChurnRate() {
  264. UserChurnRateVO churnRateVO = new UserChurnRateVO();
  265. LocalDate today = LocalDate.now();
  266. LocalDate yesterday = today.minusDays(1);
  267. // 计算近7天流失率(近7天=昨天之前7天,含昨天,即 today-7 到 today-1)
  268. // 早期用户:7天之前有充电的用户(today-14 到 today-8)
  269. // 流失判断:这些用户在近7天内(today-7 到 today-1)没有充电
  270. BigDecimal sevenDayRate = calculateChurnRate(
  271. today.minusDays(14), today.minusDays(8),
  272. today.minusDays(7), yesterday);
  273. churnRateVO.setSevenDayChurnRate(sevenDayRate);
  274. // 计算近1个月流失率(近1个月=昨天之前30天,含昨天,即 today-30 到 today-1)
  275. // 早期用户:30天之前有充电的用户(today-60 到 today-31)
  276. // 流失判断:这些用户在近30天内(today-30 到 today-1)没有充电
  277. BigDecimal oneMonthRate = calculateChurnRate(
  278. today.minusDays(60), today.minusDays(31),
  279. today.minusDays(30), yesterday);
  280. churnRateVO.setOneMonthChurnRate(oneMonthRate);
  281. // 计算近3个月流失率(近3个月=昨天之前90天,含昨天,即 today-90 到 today-1)
  282. // 早期用户:90天之前有充电的用户(today-180 到 today-91)
  283. // 流失判断:这些用户在近90天内(today-90 到 today-1)没有充电
  284. BigDecimal threeMonthRate = calculateChurnRate(
  285. today.minusDays(180), today.minusDays(91),
  286. today.minusDays(90), yesterday);
  287. churnRateVO.setThreeMonthChurnRate(threeMonthRate);
  288. return churnRateVO;
  289. }
  290. /**
  291. * 计算流失率
  292. */
  293. private BigDecimal calculateChurnRate(LocalDate earlyStart, LocalDate earlyEnd,
  294. LocalDate recentStart, LocalDate recentEnd) {
  295. String earlyStartStr = earlyStart.format(DATE_FORMATTER);
  296. String earlyEndStr = earlyEnd.format(DATE_FORMATTER);
  297. String recentStartStr = recentStart.format(DATE_FORMATTER);
  298. String recentEndStr = recentEnd.format(DATE_FORMATTER);
  299. Long activeCount = dataBoardMapper.selectActiveUserCount(earlyStartStr, earlyEndStr);
  300. if (activeCount == null || activeCount == 0) {
  301. return BigDecimal.ZERO;
  302. }
  303. Long churnCount = dataBoardMapper.selectChurnUserCount(earlyStartStr, earlyEndStr, recentStartStr, recentEndStr);
  304. if (churnCount == null) {
  305. churnCount = 0L;
  306. }
  307. return BigDecimal.valueOf(churnCount)
  308. .multiply(BigDecimal.valueOf(100))
  309. .divide(BigDecimal.valueOf(activeCount), 2, RoundingMode.HALF_UP);
  310. }
  311. /**
  312. * 计算波动充电站
  313. */
  314. private List<StationRankVO> calculateFluctuationStations(List<StationRankVO> hotStations,
  315. String prevStartDate, String prevEndDate,
  316. String currStartDate, String currEndDate) {
  317. List<StationRankVO> fluctuationStations = new ArrayList<>();
  318. for (StationRankVO station : hotStations) {
  319. if (station.getStationId() == null) {
  320. continue;
  321. }
  322. StationRankVO fluctuationStation = new StationRankVO();
  323. fluctuationStation.setRank(station.getRank());
  324. fluctuationStation.setStationId(station.getStationId());
  325. fluctuationStation.setStationName(station.getStationName());
  326. fluctuationStation.setChargePower(station.getChargePower());
  327. // 获取前一周期的充电度数
  328. BigDecimal prevPower = dataBoardMapper.selectStationChargePower(
  329. String.valueOf(station.getStationId()), prevStartDate, prevEndDate);
  330. if (prevPower == null) {
  331. prevPower = BigDecimal.ZERO;
  332. }
  333. BigDecimal currPower = station.getChargePower();
  334. if (currPower == null) {
  335. currPower = BigDecimal.ZERO;
  336. }
  337. // 计算波动百分比
  338. if (prevPower.compareTo(BigDecimal.ZERO) == 0) {
  339. if (currPower.compareTo(BigDecimal.ZERO) > 0) {
  340. fluctuationStation.setFluctuation(BigDecimal.valueOf(100));
  341. fluctuationStation.setFluctuationDirection("up");
  342. } else {
  343. fluctuationStation.setFluctuation(BigDecimal.ZERO);
  344. fluctuationStation.setFluctuationDirection("up");
  345. }
  346. } else {
  347. BigDecimal diff = currPower.subtract(prevPower);
  348. BigDecimal fluctuation = diff.multiply(BigDecimal.valueOf(100))
  349. .divide(prevPower, 2, RoundingMode.HALF_UP).abs();
  350. fluctuationStation.setFluctuation(fluctuation);
  351. fluctuationStation.setFluctuationDirection(diff.compareTo(BigDecimal.ZERO) >= 0 ? "up" : "down");
  352. }
  353. fluctuationStations.add(fluctuationStation);
  354. }
  355. // 按波动幅度排序
  356. fluctuationStations.sort((a, b) -> b.getFluctuation().compareTo(a.getFluctuation()));
  357. // 重新设置排名
  358. for (int i = 0; i < fluctuationStations.size(); i++) {
  359. fluctuationStations.get(i).setRank(i + 1);
  360. }
  361. return fluctuationStations;
  362. }
  363. /**
  364. * 将小时数据转换为Map
  365. */
  366. private Map<Integer, BigDecimal> convertHourlyDataToMap(List<Map<String, Object>> hourlyData) {
  367. Map<Integer, BigDecimal> map = new HashMap<>();
  368. for (Map<String, Object> item : hourlyData) {
  369. Integer hour = ((Number) item.get("hour")).intValue();
  370. BigDecimal power = item.get("chargePower") != null ?
  371. new BigDecimal(String.valueOf(item.get("chargePower"))) : BigDecimal.ZERO;
  372. map.put(hour, power);
  373. }
  374. return map;
  375. }
  376. /**
  377. * 确保实时数据所有字段非空
  378. */
  379. private void ensureRealTimeDataNotNull(DataBoardRealTimeVO vo) {
  380. if (vo.getTotalChargePower() == null) vo.setTotalChargePower(BigDecimal.ZERO);
  381. if (vo.getTotalChargeAmount() == null) vo.setTotalChargeAmount(BigDecimal.ZERO);
  382. if (vo.getTotalDiscountAmount() == null) vo.setTotalDiscountAmount(BigDecimal.ZERO);
  383. if (vo.getTotalServiceFeeAmount() == null) vo.setTotalServiceFeeAmount(BigDecimal.ZERO);
  384. if (vo.getTotalRefundAmount() == null) vo.setTotalRefundAmount(BigDecimal.ZERO);
  385. if (vo.getTotalActualPayAmount() == null) vo.setTotalActualPayAmount(BigDecimal.ZERO);
  386. if (vo.getTotalFirstOrderAmount() == null) vo.setTotalFirstOrderAmount(BigDecimal.ZERO);
  387. if (vo.getTotalCouponAmount() == null) vo.setTotalCouponAmount(BigDecimal.ZERO);
  388. if (vo.getTotalFirmDiscountAmount() == null) vo.setTotalFirmDiscountAmount(BigDecimal.ZERO);
  389. if (vo.getTotalCommissionAmount() == null) vo.setTotalCommissionAmount(BigDecimal.ZERO);
  390. }
  391. /**
  392. * 确保今日数据所有字段非空
  393. */
  394. private void ensureTodayDataNotNull(DataBoardTodayVO vo) {
  395. if (vo.getTodayChargePower() == null) vo.setTodayChargePower(BigDecimal.ZERO);
  396. if (vo.getTodayChargeAmount() == null) vo.setTodayChargeAmount(BigDecimal.ZERO);
  397. if (vo.getTodayDiscountAmount() == null) vo.setTodayDiscountAmount(BigDecimal.ZERO);
  398. if (vo.getTodayServiceFeeAmount() == null) vo.setTodayServiceFeeAmount(BigDecimal.ZERO);
  399. if (vo.getTodayRefundAmount() == null) vo.setTodayRefundAmount(BigDecimal.ZERO);
  400. if (vo.getTodayActualPayAmount() == null) vo.setTodayActualPayAmount(BigDecimal.ZERO);
  401. if (vo.getTodayFirstOrderAmount() == null) vo.setTodayFirstOrderAmount(BigDecimal.ZERO);
  402. if (vo.getTodayCouponAmount() == null) vo.setTodayCouponAmount(BigDecimal.ZERO);
  403. if (vo.getTodayFirmDiscountAmount() == null) vo.setTodayFirmDiscountAmount(BigDecimal.ZERO);
  404. if (vo.getTodayCommissionAmount() == null) vo.setTodayCommissionAmount(BigDecimal.ZERO);
  405. }
  406. @Override
  407. public List<ChurnUserExportDTO> getChurnUserExportList(String periodType) {
  408. LocalDate today = LocalDate.now();
  409. LocalDate yesterday = today.minusDays(1);
  410. LocalDate earlyStart;
  411. LocalDate earlyEnd;
  412. LocalDate recentStart;
  413. LocalDate recentEnd = yesterday;
  414. // 根据时段类型计算日期范围
  415. switch (periodType) {
  416. case "7d":
  417. // 近7天流失用户
  418. earlyStart = today.minusDays(14);
  419. earlyEnd = today.minusDays(8);
  420. recentStart = today.minusDays(7);
  421. break;
  422. case "3m":
  423. // 近3个月流失用户
  424. earlyStart = today.minusDays(180);
  425. earlyEnd = today.minusDays(91);
  426. recentStart = today.minusDays(90);
  427. break;
  428. case "1m":
  429. default:
  430. // 近1个月流失用户(默认)
  431. earlyStart = today.minusDays(60);
  432. earlyEnd = today.minusDays(31);
  433. recentStart = today.minusDays(30);
  434. break;
  435. }
  436. List<ChurnUserExportDTO> list = dataBoardMapper.selectChurnUserList(
  437. earlyStart.format(DATE_FORMATTER),
  438. earlyEnd.format(DATE_FORMATTER),
  439. recentStart.format(DATE_FORMATTER),
  440. recentEnd.format(DATE_FORMATTER)
  441. );
  442. // 设置序号
  443. for (int i = 0; i < list.size(); i++) {
  444. list.get(i).setRowNum(i + 1);
  445. }
  446. return list;
  447. }
  448. }