You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
20 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. package com.example.demo.serviceImpl;
  2. import com.example.demo.domain.entity.Statistics;
  3. import com.example.demo.domain.vo.WorkbenchCard;
  4. import com.example.demo.domain.vo.WorkbenchFullStatistics;
  5. import com.example.demo.domain.vo.WorkbenchMarketCard;
  6. import com.example.demo.domain.vo.WorkbenchMarketGraph;
  7. import com.example.demo.mapper.StatisticsMapper;
  8. import com.example.demo.mapper.WorkBenchMapper;
  9. import com.example.demo.service.GeneralService;
  10. import com.example.demo.service.StatisticsService;
  11. import com.example.demo.service.WorkbenchService;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.cache.annotation.Cacheable;
  14. import org.springframework.data.redis.core.RedisTemplate;
  15. import org.springframework.stereotype.Service;
  16. import java.time.LocalDate;
  17. import java.time.DayOfWeek;
  18. import java.time.LocalDateTime;
  19. import java.time.LocalTime;
  20. import java.time.ZoneId;
  21. import java.util.*;
  22. import java.util.function.Function;
  23. import java.util.stream.Collectors;
  24. /**
  25. * @program: gold-java
  26. * @ClassName WorkbenchServiceImpl
  27. * @description:
  28. * @author: Ethan
  29. * @create: 202506-18 10:47
  30. * @Version 1.0
  31. **/
  32. @Service
  33. public class WorkbenchServiceImpl implements WorkbenchService {
  34. private final RedisTemplate<String, WorkbenchCard> redisTemplate;
  35. // private final StatisticsMapper statisticsMapper;
  36. @Autowired
  37. private WorkBenchMapper workBenchMapper;
  38. @Autowired
  39. private GeneralService generalService;
  40. @Autowired
  41. private StatisticsMapper statisticsMapper;
  42. @Autowired
  43. public WorkbenchServiceImpl(RedisTemplate<String, WorkbenchCard> redisTemplate, StatisticsMapper statisticsMapper) {
  44. this.redisTemplate = redisTemplate;
  45. this.statisticsMapper = statisticsMapper;
  46. }
  47. private static final String CACHE_KEY = "workbench_card_cache";
  48. @Override
  49. public WorkbenchCard getCard( List<String> markets) {
  50. Date date=new Date();
  51. // 获取开始时间和结束时间(当天)
  52. LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIN);
  53. LocalDateTime endOfDay = startOfDay.plusDays(1).minusSeconds(1);
  54. // 获取开始时间和结束时间(昨天)
  55. LocalDateTime startOfYday = startOfDay.minusDays(1);
  56. LocalDateTime endOfYday = endOfDay.minusDays(1);
  57. // 获取当前年份的第一天
  58. LocalDate firstDayOfYear = LocalDate.now().withDayOfYear(1);
  59. Date yearlyStartDate = Date.from(firstDayOfYear.atStartOfDay(ZoneId.systemDefault()).toInstant());
  60. markets = generalService.getMarket();
  61. // 批量获取统计数据
  62. List<Statistics> currentStatsList = statisticsMapper.selectByMarketsAndDate(markets,
  63. Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant()),
  64. Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant()));
  65. List<Statistics> ydayStatsList = statisticsMapper.selectByMarketsAndDate(markets,
  66. Date.from(startOfYday.atZone(ZoneId.systemDefault()).toInstant()),
  67. Date.from(endOfYday.atZone(ZoneId.systemDefault()).toInstant()));
  68. // 将 List<Statistics> 转换为 Map<String, Statistics>
  69. Map<String, Statistics> currentStatsMap = currentStatsList.stream()
  70. .collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing));
  71. Map<String, Statistics> ydayStatsMap = ydayStatsList.stream()
  72. .collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing));
  73. // 并行处理市场列表,创建市场卡片列表
  74. List<WorkbenchMarketCard> marketCards = markets.parallelStream()
  75. .filter(Objects::nonNull)
  76. .map(market -> createWorkbenchMarketCard(
  77. market,
  78. currentStatsMap.getOrDefault(market, null),
  79. ydayStatsMap.getOrDefault(market, null),
  80. new Date()))
  81. .collect(Collectors.toList());
  82. Integer sumWow= calculateAllWeekOverWeek(date);
  83. Integer sumDaily=calculateAllDayOverDay(date);
  84. Date updateTime = findLatestUpdateTime(currentStatsList);
  85. return new WorkbenchCard(marketCards, new ArrayList<>(), markets, new Date(), new Date(),sumWow,sumDaily,updateTime);
  86. }
  87. @Override
  88. public WorkbenchCard getCardCache(List<String> markets) {
  89. markets = generalService.getMarket();
  90. //从缓存中获取工作台数据
  91. WorkbenchCard cached = redisTemplate.opsForValue().get(CACHE_KEY);
  92. if (cached != null) {
  93. //直接返回缓存数据
  94. System.out.println("读取缓存数据: " + new Date());
  95. return cached;
  96. }
  97. try {
  98. //获取新的工作台数据
  99. WorkbenchCard freshData = getCard(markets);
  100. //存入缓存,一小时后过期(与统计表更新时间对应)
  101. redisTemplate.opsForValue().set(CACHE_KEY, freshData, 1, java.util.concurrent.TimeUnit.HOURS);
  102. System.out.println("刷新缓存并存储新数据: " + new Date());
  103. return freshData;
  104. } catch (Exception e) {
  105. System.err.println("查询数据库失败,尝试使用旧缓存数据或抛出异常:" + e.getMessage());
  106. throw e; // 或者你可以选择返回上次的缓存数据(如果有)
  107. }
  108. }
  109. /*
  110. 获取卡片数据
  111. */
  112. @Override
  113. public WorkbenchMarketCard createWorkbenchMarketCard(String market,Statistics currentStatistics, Statistics ydayStatistics, Date currentDate) {
  114. Date date=new Date();
  115. WorkbenchMarketCard card = new WorkbenchMarketCard();
  116. card.setMarket(market);
  117. if (currentStatistics != null&& ydayStatistics != null) {
  118. // 一次性获取全年统计数据(从年初到今天)
  119. // Map<String, Integer> yearlyStats = calculateAllSum(market, yearlyStartDate, date);
  120. // 卡片一:当前金币相关
  121. card.setCurrentPermanent(currentStatistics.getCurrentPermanent());//余量-永久金币
  122. card.setCurrentFreeJune(currentStatistics.getCurrentFreeJune()); //余量-免费六月金币
  123. card.setCurrentFreeDecember(currentStatistics.getCurrentFreeDecember()); //余量-免费十二月金币
  124. card.setCurrentTask(currentStatistics.getCurrentTask()); //余量-任务金币
  125. card.setCurrentFree(card.getCurrentFreeJune() + card.getCurrentFreeDecember()); //余量-免费金币
  126. card.setCurrentGold(card.getCurrentPermanent() + card.getCurrentFree() + card.getCurrentTask()); //余量-总金币
  127. card.setDailyChange(currentStatistics.getDailyChange()); //较前一日变化
  128. // 卡片二:充值相关
  129. card.setRecharge(ydayStatistics.getRecharge()); //充值-昨日充值
  130. card.setMoney(ydayStatistics.getMoney()); //充值-昨日金额(永久)
  131. card.setYearlyRecharge(currentStatistics.getYearlyRecharge()); // 充值-全年累计充值
  132. card.setYearlyMoney(currentStatistics.getYearlyMoney()); // 充值-全年累计金额(永久)//充值-全年累计金额(永久)
  133. // 卡片三:消费与退款
  134. card.setConsumePermanent(ydayStatistics.getConsumePermanent());//昨日消费-永久金币
  135. card.setConsumeFreeJune(ydayStatistics.getConsumeFreeJune());//昨日消费-免费六月金币
  136. card.setConsumeFreeDecember(ydayStatistics.getConsumeFreeDecember());//昨日消费-免费十二月金币
  137. card.setConsumeTask(ydayStatistics.getConsumeTask());//昨日消费-任务金币
  138. card.setRefundPermanent(ydayStatistics.getRefundPermanent());//昨日退款-永久金币
  139. card.setRefundFreeJune(ydayStatistics.getRefundFreeJune());//昨日退款-免费六月金币
  140. card.setRefundFreeDecember(ydayStatistics.getRefundFreeDecember());//昨日退款-免费十二月金币
  141. card.setRefundTask(ydayStatistics.getRefundTask());//昨日退款-任务金币
  142. //昨日总消费
  143. int totalConsume = card.getConsumePermanent() + card.getConsumeFreeJune() + card.getConsumeFreeDecember() + card.getConsumeTask();
  144. //昨日总退款
  145. int totalRefund = card.getRefundPermanent() + card.getRefundFreeJune() + card.getRefundFreeDecember() + card.getRefundTask();
  146. card.setDailyReduce(totalConsume - totalRefund);//昨日总消耗
  147. card.setYearlyConsume(currentStatistics.getYearlyConsume()); // 年累计消费
  148. card.setYearlyRefund(currentStatistics.getYearlyRefund()); // 年累计退款
  149. card.setYearlyReduce(card.getYearlyConsume() - card.getYearlyRefund());//年累计消耗
  150. // 卡片四:人头数相关
  151. card.setRechargeNum(currentStatistics.getRechargeNum());
  152. card.setYdayRechargeNum(ydayStatistics.getRechargeNum());
  153. card.setFirstRecharge(currentStatistics.getFirstRecharge());
  154. card.setYearlyRechargeNum(currentStatistics.getYearlyRechargeNum());
  155. // 周环比、日同比
  156. card.setWow(calculateWeekOverWeek(market, currentDate));
  157. card.setDaily(calculateDayOverDay(market, currentDate));
  158. //更新时间
  159. card.setUpdateTime(currentStatistics.getUpdateTime());
  160. }
  161. return card;
  162. }
  163. @Override
  164. public WorkbenchCard getGraph( Date startDate, Date endDate, List<String> markets) {
  165. if (markets == null || markets.isEmpty()) {
  166. return new WorkbenchCard(new ArrayList<>(), new ArrayList<>(), markets, startDate, endDate,0,0,new Date());
  167. }
  168. // 单次批量查询
  169. List<WorkbenchFullStatistics> statsList = workBenchMapper.getFullStatisticsByMarketAndDate1(markets, startDate, endDate);
  170. // 构建 map: market -> statistics
  171. Map<String, WorkbenchFullStatistics> statMap = statsList.stream()
  172. .collect(Collectors.toMap(WorkbenchFullStatistics::getMarket, Function.identity()));
  173. // 构建最终结果
  174. List<WorkbenchMarketGraph> marketGraphs = new ArrayList<>();
  175. for (String market : markets) {
  176. WorkbenchFullStatistics stats = statMap.getOrDefault(market, new WorkbenchFullStatistics());
  177. Map<String, Integer> sums = new HashMap<>();
  178. sums.put("recharge", stats.getTotalRecharge() != null ? stats.getTotalRecharge() : 0);
  179. sums.put("money", stats.getTotalMoney() != null ? stats.getTotalMoney() : 0);
  180. sums.put("rFree", sums.get("recharge") - sums.get("money"));
  181. sums.put("cPermanent", stats.getTotalConsumePermanent() != null ? stats.getTotalConsumePermanent() : 0);
  182. sums.put("cFree", stats.getTotalConsumeFree() != null ? stats.getTotalConsumeFree() : 0);
  183. sums.put("cTask", stats.getTotalConsumeTask() != null ? stats.getTotalConsumeTask() : 0);
  184. sums.put("consume", sums.get("cPermanent") + sums.get("cFree") + sums.get("cTask"));
  185. sums.put("refund", stats.getTotalRefund() != null ? stats.getTotalRefund() : 0);
  186. sums.put("rechargeNum", stats.getTotalRechargeNum() != null ? stats.getTotalRechargeNum() : 0);
  187. WorkbenchMarketGraph graph = new WorkbenchMarketGraph();
  188. graph.setMarket(market);
  189. graph.setSumRechargePermanent(sums.get("money"));
  190. graph.setSumRechargeFree(sums.get("rFree"));
  191. graph.setSumConsumePermanent(sums.get("cPermanent"));
  192. graph.setSumConsumeFree(sums.get("cFree"));
  193. graph.setSumConsumeTask(sums.get("cTask"));
  194. graph.setSumConsume(sums.get("consume"));
  195. marketGraphs.add(graph);
  196. }
  197. return new WorkbenchCard(new ArrayList<>(), marketGraphs, markets, startDate, endDate,0,0,new Date());
  198. }
  199. /*
  200. 根据类型获取统计数据
  201. */
  202. @Override
  203. public Map<String, Integer> calculateAllSum(String market, Date startDate, Date endDate) {
  204. WorkbenchFullStatistics stats = workBenchMapper.getFullStatisticsByMarketAndDate(market, startDate, endDate);
  205. Map<String, Integer> result = new HashMap<>();
  206. result.put("recharge", stats.getTotalRecharge() != null ? stats.getTotalRecharge() : 0);//获取充值统计数据
  207. result.put("money", stats.getTotalMoney() != null ? stats.getTotalMoney() : 0); //充值金额统计数据
  208. result.put("rFree", (stats.getTotalRecharge() != null ? stats.getTotalRecharge() : 0) //获取充值的免费金币统计数据
  209. - (stats.getTotalMoney() != null ? stats.getTotalMoney() : 0));
  210. result.put("cPermanent", stats.getTotalConsumePermanent() != null ? stats.getTotalConsumePermanent() : 0); //获取消费的永久金币统计数据
  211. result.put("cFree", stats.getTotalConsumeFree() != null ? stats.getTotalConsumeFree() : 0); //获取消费的免费金币统计数据
  212. result.put("cTask", stats.getTotalConsumeTask() != null ? stats.getTotalConsumeTask() : 0);//获取消费的任金币统计数据
  213. result.put("consume",(stats.getTotalConsumePermanent() != null ? stats.getTotalConsumePermanent() : 0)//获取全部消费统计数据
  214. +(stats.getTotalConsumeFree() != null ? stats.getTotalConsumeFree() : 0)
  215. +(stats.getTotalConsumeTask() != null ? stats.getTotalConsumeTask() : 0));
  216. result.put("refund", stats.getTotalRefund() != null ? stats.getTotalRefund() : 0); //获取退款统计数据
  217. result.put("rechargeNum", stats.getTotalRechargeNum() != null ? stats.getTotalRechargeNum() : 0);//获取充值人数统计数据
  218. return result;
  219. }
  220. /*
  221. 获取该日期该市场的日环比
  222. */
  223. @Override
  224. public Integer calculateDayOverDay(String market, Date date) {
  225. //传入日期的数据
  226. LocalDateTime startTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN);
  227. LocalDateTime endTime= startTime.plusDays(1).minusSeconds(1);
  228. Statistics currentStatistics = statisticsMapper.selectByMarketAndDate(market,
  229. Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
  230. Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()));
  231. //空数据或数量为零,为避免0除数错误,返回增长率为0
  232. if (currentStatistics == null || currentStatistics.getRechargeNum() == null) {
  233. return 0;
  234. }
  235. Date yesterday = addDays(date, -1); //传入日期减一,昨天
  236. Statistics yesterdayStatistics = statisticsMapper.selectByMarketAndDate(market,
  237. Date.from(yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()),
  238. Date.from(yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant()));
  239. //空数据或数量为零,为避免0除数错误,返回增长率为0
  240. if (yesterdayStatistics == null || yesterdayStatistics.getRechargeNum() == null || yesterdayStatistics.getRechargeNum() == 0) {
  241. return 0;
  242. }
  243. //计算增长率
  244. double rate = ((double) (currentStatistics.getRechargeNum() - yesterdayStatistics.getRechargeNum()) / yesterdayStatistics.getRechargeNum()) * 100;
  245. return (int) Math.round(rate);
  246. }
  247. // 计算所有市场总体日环比
  248. @Override
  249. public Integer calculateAllDayOverDay(Date date) {
  250. //获取今天的开始时间和结束时间
  251. LocalDateTime startTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN);
  252. LocalDateTime endTime= startTime.plusDays(1).minusSeconds(1);
  253. //获取地区列表
  254. List<String> markets = generalService.getMarket();
  255. int currentTotal = 0; //今日所有地区总的充值人数
  256. int yesterdayTotal = 0; //昨日所有地区总的充值人数
  257. Date yesterday = addDays(date, -1);
  258. //过去昨天的开始时间和结束时间
  259. LocalDateTime ydayStartTime = yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN);
  260. LocalDateTime ydayEndTime= ydayStartTime.plusDays(1).minusSeconds(1);
  261. for (String market : markets) {
  262. currentTotal += statisticsMapper.countRechargeNum(market,
  263. Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
  264. Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()));
  265. yesterdayTotal += statisticsMapper.countRechargeNum(market,
  266. Date.from(ydayStartTime.atZone(ZoneId.systemDefault()).toInstant()),
  267. Date.from(ydayEndTime.atZone(ZoneId.systemDefault()).toInstant()));
  268. }
  269. if (yesterdayTotal == 0) return 0; // 避免除以零的情况
  270. double rate = ((double)(currentTotal - yesterdayTotal) / yesterdayTotal) * 100;
  271. return (int)Math.round(rate);
  272. }
  273. /*
  274. 获取该日期该市场的周环比
  275. */
  276. @Override
  277. public Integer calculateWeekOverWeek(String market, Date date) {
  278. //获取传入日期所在周的周一
  279. Date thisWeekStart = getStartOfWeek(date);
  280. //获取传入日期上一周的周一
  281. Date lastWeekStart = addDays(thisWeekStart, -7);
  282. // 获取本周周一至今天的充值人数总和
  283. int thisWeekTotal = statisticsMapper.countRechargeNum(market, thisWeekStart, date);
  284. // 获取上周同一时间段的充值人数总和
  285. int lastWeekTotal = statisticsMapper.countRechargeNum(market, lastWeekStart, addDays(date, -7));
  286. if (lastWeekTotal == 0) {
  287. return 0; // 避免除以零的情况
  288. }
  289. double rate = ((double) (thisWeekTotal - lastWeekTotal) / lastWeekTotal) * 100;
  290. return (int) Math.round(rate);
  291. }
  292. /*
  293. 获取改天总体的的周环比
  294. */
  295. @Override
  296. public Integer calculateAllWeekOverWeek( Date date) {
  297. List<String> markets = generalService.getMarket();
  298. int thisWeekTotal = 0; //本周至当天充值人数
  299. int lastWeekTotal = 0; //上周至当天充值人数
  300. //获取本周周一
  301. Date thisWeekStart = getStartOfWeek(date);
  302. //获取传入日期上一周的周一
  303. Date lastWeekStart = addDays(thisWeekStart, -7);
  304. for (String market : markets) {
  305. // 获取本周周一至今天的充值人数总和
  306. thisWeekTotal += statisticsMapper.countRechargeNum(market, thisWeekStart, date);
  307. // 获取上周同一时间段的充值人数总和
  308. lastWeekTotal += statisticsMapper.countRechargeNum(market, lastWeekStart, addDays(date, -7));
  309. }
  310. if (lastWeekTotal == 0) {
  311. return 0; // 避免除以零的情况
  312. }
  313. double rate = ((double) (thisWeekTotal - lastWeekTotal) / lastWeekTotal) * 100;
  314. return (int) Math.round(rate);
  315. }
  316. @Override
  317. public Date addDays(Date date, int days) {
  318. Calendar cal = Calendar.getInstance();
  319. cal.setTime(date); //设置日期为传入的日期
  320. cal.add(Calendar.DATE, days); //传入日期加上多少天
  321. return cal.getTime(); //返回处理后的日期
  322. }
  323. @Override
  324. public Date getStartOfWeek(Date date) {
  325. // 将 Date 转换为 LocalDate
  326. LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
  327. // 获取周一作为一周的第一天
  328. DayOfWeek firstDayOfWeek = DayOfWeek.MONDAY;
  329. // 返回所在周的第一天,转换回Date格式
  330. return Date.from(localDate.with(firstDayOfWeek)
  331. .atStartOfDay(ZoneId.systemDefault())
  332. .toInstant());
  333. }
  334. //获取最近的更新时间
  335. private Date findLatestUpdateTime(List<Statistics> statsList) {
  336. // 使用流式处理来找到最新的 updateTime
  337. Optional<Date> latestUpdateTime = statsList.stream()
  338. .map(Statistics::getUpdateTime)
  339. .filter(Objects::nonNull)
  340. .max(Date::compareTo);
  341. return latestUpdateTime.orElse(new Date(0)); // 如果没有找到,则返回一个较早的时间点
  342. }
  343. }