StatManagerImpl.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. package com.punchsettle.server.service.manager.impl;
  2. import java.math.BigDecimal;
  3. import java.math.RoundingMode;
  4. import java.time.LocalDate;
  5. import java.util.ArrayList;
  6. import java.util.Arrays;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Objects;
  10. import java.util.Optional;
  11. import java.util.function.Function;
  12. import java.util.stream.Collectors;
  13. import com.punchsettle.server.atomic.entity.User;
  14. import com.punchsettle.server.atomic.service.IUserService;
  15. import com.punchsettle.server.common.utils.Assert;
  16. import com.punchsettle.server.constant.UserCategoryEnum;
  17. import com.punchsettle.server.service.manager.IUserManager;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.cache.annotation.Cacheable;
  20. import org.springframework.stereotype.Component;
  21. import org.springframework.transaction.annotation.Transactional;
  22. import org.springframework.util.CollectionUtils;
  23. import com.punchsettle.server.atomic.IStatPiTaskService;
  24. import com.punchsettle.server.atomic.StatPiTask;
  25. import com.punchsettle.server.atomic.entity.PiTask;
  26. import com.punchsettle.server.atomic.entity.PiTaskHistory;
  27. import com.punchsettle.server.atomic.entity.SettleTaskRelaHistory;
  28. import com.punchsettle.server.atomic.entity.StatNewUser;
  29. import com.punchsettle.server.atomic.entity.StatPiTaskMonth;
  30. import com.punchsettle.server.atomic.entity.StatPiTaskWeek;
  31. import com.punchsettle.server.atomic.entity.StatPiTaskYear;
  32. import com.punchsettle.server.atomic.entity.StatPoints;
  33. import com.punchsettle.server.atomic.service.IPiTaskHistoryService;
  34. import com.punchsettle.server.atomic.service.IPiTaskService;
  35. import com.punchsettle.server.atomic.service.ISettleTaskRelaHistoryService;
  36. import com.punchsettle.server.atomic.service.IStatNewUserService;
  37. import com.punchsettle.server.atomic.service.IStatPiTaskWeekService;
  38. import com.punchsettle.server.atomic.service.IStatPointsService;
  39. import com.punchsettle.server.constant.CacheNameConstant;
  40. import com.punchsettle.server.constant.PunchInResultEnum;
  41. import com.punchsettle.server.constant.SettleResultEnum;
  42. import com.punchsettle.server.constant.StatPeriodEnum;
  43. import com.punchsettle.server.core.config.BizProperties;
  44. import com.punchsettle.server.pojo.punchIn.PiTaskHistoryQuery;
  45. import com.punchsettle.server.pojo.settle.SettleTaskRelaHistoryQuery;
  46. import com.punchsettle.server.pojo.stat.StatNewUserQuery;
  47. import com.punchsettle.server.pojo.stat.StatPiTaskQuery;
  48. import com.punchsettle.server.pojo.stat.StatPointsQuery;
  49. import com.punchsettle.server.pojo.ucharts.LineSeriesVO;
  50. import com.punchsettle.server.pojo.ucharts.LineVO;
  51. import com.punchsettle.server.service.manager.IStatManager;
  52. import com.punchsettle.server.utiis.DateUtils;
  53. import com.punchsettle.server.utiis.UserUtils;
  54. /**
  55. * @author myou
  56. * @version 1.0.0
  57. * @date 2025/5/3 9:32
  58. * @description 统计数据服务类
  59. */
  60. @Component
  61. public class StatManagerImpl implements IStatManager {
  62. /**
  63. * 数值100
  64. */
  65. private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);
  66. @Autowired
  67. private IStatPointsService statPointsService;
  68. @Autowired
  69. private IStatNewUserService statNewUserService;
  70. @Autowired
  71. private IPiTaskHistoryService piTaskHistoryService;
  72. @Autowired
  73. private BizProperties bizProperties;
  74. @Autowired
  75. private IStatPiTaskWeekService statPiTaskWeekService;
  76. @Autowired
  77. private ISettleTaskRelaHistoryService settleTaskRelaHistoryService;
  78. @Autowired
  79. private IPiTaskService piTaskService;
  80. @Autowired
  81. private IUserManager userManager;
  82. @Autowired
  83. private IUserService userService;
  84. @Override
  85. @Cacheable(cacheNames = CacheNameConstant.STAT_POINTS_LINE, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()")
  86. public LineVO queryStatPointsLine() {
  87. Long currentUserId = UserUtils.getCurrentUserId();
  88. LocalDate endDate = LocalDate.now();
  89. LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount());
  90. // 获取折线图时间范围
  91. List<LocalDate> dateRange = DateUtils.getDateRange(startDate, endDate);
  92. String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new);
  93. StatPointsQuery statPointsQuery = new StatPointsQuery();
  94. statPointsQuery.setUserId(currentUserId);
  95. statPointsQuery.setStatStartTime(startDate.toString());
  96. statPointsQuery.setStatEndTime(endDate.toString());
  97. List<StatPoints> statPointsList = statPointsService.queryByCondition(statPointsQuery);
  98. Map<String, StatPoints> statPointsMap = statPointsList.stream().collect(Collectors.toMap(v -> v.getStatTime().substring(5), Function.identity(), (v1, v2) -> v1));
  99. List<Integer> settlePointsList = new ArrayList<>(lineCategories.length);
  100. List<Integer> consumePointsList = new ArrayList<>(lineCategories.length);
  101. List<Integer> totalPointsList = new ArrayList<>(lineCategories.length);
  102. for (String lineCategory : lineCategories) {
  103. StatPoints statPoints = statPointsMap.get(lineCategory);
  104. if (Objects.isNull(statPoints)) {
  105. settlePointsList.add(null);
  106. consumePointsList.add(null);
  107. totalPointsList.add(null);
  108. continue;
  109. }
  110. settlePointsList.add(statPoints.getSettlePoints());
  111. consumePointsList.add(statPoints.getConsumePoints());
  112. totalPointsList.add(statPoints.getTotalPoints());
  113. }
  114. LineSeriesVO settlePointsLineSeriesVO = new LineSeriesVO();
  115. settlePointsLineSeriesVO.setName("结算积分");
  116. settlePointsLineSeriesVO.setData(settlePointsList.toArray(Integer[]::new));
  117. LineSeriesVO consumePointsLineSeriesVO = new LineSeriesVO();
  118. consumePointsLineSeriesVO.setName("消耗积分");
  119. consumePointsLineSeriesVO.setData(consumePointsList.toArray(Integer[]::new));
  120. LineSeriesVO totalPointsLineSeriesVO = new LineSeriesVO();
  121. totalPointsLineSeriesVO.setName("总积分");
  122. totalPointsLineSeriesVO.setData(totalPointsList.toArray(Integer[]::new));
  123. LineVO lineVO = new LineVO();
  124. lineVO.setCategories(lineCategories);
  125. lineVO.setSeries(Arrays.asList(settlePointsLineSeriesVO, consumePointsLineSeriesVO, totalPointsLineSeriesVO));
  126. return lineVO;
  127. }
  128. @Override
  129. @Cacheable(cacheNames = CacheNameConstant.STAT_NEW_USER_LINE)
  130. public LineVO queryStatNewUserLine() {
  131. User user = userManager.getByIdWithCache(UserUtils.getCurrentUserId());
  132. if (UserCategoryEnum.NORMAL.equals(user.getUserCategory())) {
  133. return null;
  134. }
  135. LocalDate endDate = LocalDate.now();
  136. LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount());
  137. // 获取折线图时间范围
  138. List<LocalDate> dateRange = DateUtils.getDateRange(startDate, endDate);
  139. String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new);
  140. StatNewUserQuery statNewUserQuery = new StatNewUserQuery();
  141. statNewUserQuery.setStatStartTime(startDate.toString());
  142. statNewUserQuery.setStatEndTime(endDate.toString());
  143. List<StatNewUser> statNewUserList = statNewUserService.queryByCondition(statNewUserQuery);
  144. Map<String, Integer> statNewUserMap = statNewUserList.stream().collect(Collectors.toMap(v -> v.getStatTime().substring(5), StatNewUser::getNewUserCount, (v1, v2) -> v1));
  145. List<Integer> newUserCountList = new ArrayList<>(lineCategories.length);
  146. for (String lineCategory : lineCategories) {
  147. Integer newUserCount = Optional.ofNullable(statNewUserMap.get(lineCategory)).orElse(0);
  148. newUserCountList.add(newUserCount);
  149. }
  150. LineSeriesVO newUserLineSeriesVO = new LineSeriesVO();
  151. newUserLineSeriesVO.setName("新增用户数");
  152. newUserLineSeriesVO.setData(newUserCountList.toArray(Integer[]::new));
  153. LineVO lineVO = new LineVO();
  154. lineVO.setCategories(lineCategories);
  155. lineVO.setSeries(Arrays.asList(newUserLineSeriesVO));
  156. return lineVO;
  157. }
  158. @Override
  159. @Cacheable(cacheNames = CacheNameConstant.STAT_TASK_LINE, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()")
  160. public LineVO queryStatTaskLine() {
  161. Long currentUserId = UserUtils.getCurrentUserId();
  162. LocalDate endDate = LocalDate.now();
  163. LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount());
  164. // 获取折线图时间范围
  165. List<LocalDate> dateRange = DateUtils.getDateRange(startDate, endDate);
  166. String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new);
  167. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  168. piTaskHistoryQuery.setUserIds(Arrays.asList(currentUserId));
  169. piTaskHistoryQuery.setPunchInDateFrom(startDate.toString());
  170. piTaskHistoryQuery.setPunchInDateTo(endDate.toString());
  171. List<PiTaskHistory> piTaskHistoryList = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  172. Map<String, List<PiTaskHistory>> piTaskHistoryMap = piTaskHistoryList.stream().collect(Collectors.groupingBy(v -> v.getPunchInDate().substring(5)));
  173. // 总任务数
  174. List<Integer> totalTaskCountList = new ArrayList<>(lineCategories.length);
  175. // 打卡任务数
  176. List<Integer> punchInTaskCountList = new ArrayList<>(lineCategories.length);
  177. // 完成打卡任务数
  178. List<Integer> doneTaskCountList = new ArrayList<>(lineCategories.length);
  179. for (String lineCategory : lineCategories) {
  180. List<PiTaskHistory> piTaskHistories = piTaskHistoryMap.get(lineCategory);
  181. if (CollectionUtils.isEmpty(piTaskHistories)) {
  182. totalTaskCountList.add(0);
  183. punchInTaskCountList.add(0);
  184. doneTaskCountList.add(0);
  185. continue;
  186. }
  187. // 总任数
  188. totalTaskCountList.add(piTaskHistories.size());
  189. // TODO 这里要单独弄一个统计表,不然无法计算
  190. punchInTaskCountList.add(piTaskHistories.size());
  191. // 任务完成数
  192. doneTaskCountList.add(piTaskHistories.stream().filter(piTaskHistory -> piTaskHistory.getPunchInResult().equals(PunchInResultEnum.DONE)).collect(Collectors.toList()).size());
  193. }
  194. LineSeriesVO totalTaskCountLineSeriesVO = new LineSeriesVO();
  195. totalTaskCountLineSeriesVO.setName("总任务数");
  196. totalTaskCountLineSeriesVO.setData(totalTaskCountList.toArray(Integer[]::new));
  197. LineSeriesVO punchInTaskCountLineSeriesVO = new LineSeriesVO();
  198. punchInTaskCountLineSeriesVO.setName("打卡数量");
  199. punchInTaskCountLineSeriesVO.setData(punchInTaskCountList.toArray(Integer[]::new));
  200. LineSeriesVO doneTaskCountLineSeriesVO = new LineSeriesVO();
  201. doneTaskCountLineSeriesVO.setName("完成数量");
  202. doneTaskCountLineSeriesVO.setData(doneTaskCountList.toArray(Integer[]::new));
  203. LineVO lineVO = new LineVO();
  204. lineVO.setCategories(lineCategories);
  205. lineVO.setSeries(Arrays.asList(totalTaskCountLineSeriesVO, punchInTaskCountLineSeriesVO, doneTaskCountLineSeriesVO));
  206. return lineVO;
  207. }
  208. @Override
  209. @Transactional(rollbackFor = Exception.class)
  210. public void statPiTaskData(IStatPiTaskService statPiTaskService, StatPeriodEnum statPeriod, List<Long> userIds, LocalDate statTime, LocalDate statFirstDay, LocalDate statLastDay) {
  211. // 查询任务数据
  212. List<PiTask> piTasks = piTaskService.getActiveTask(userIds);
  213. // 没有待统计任务跳过
  214. if (CollectionUtils.isEmpty(piTasks)) {
  215. return;
  216. }
  217. StatPiTaskQuery statPiTaskQuery = new StatPiTaskQuery();
  218. statPiTaskQuery.setStatTime(statTime.toString());
  219. statPiTaskQuery.setUserIds(userIds);
  220. List<StatPiTask> statPiTaskDatas = statPiTaskService.queryByCondition(statPiTaskQuery);
  221. // 任务唯一ID - 周统计数据
  222. Map<Long, StatPiTask> statPiTaskDataMap = statPiTaskDatas.stream().collect(Collectors.toMap(StatPiTask::getTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  223. // 查询已结算数据
  224. SettleTaskRelaHistoryQuery settleTaskRelaHistoryQuery = new SettleTaskRelaHistoryQuery();
  225. settleTaskRelaHistoryQuery.setUserIds(userIds);
  226. settleTaskRelaHistoryQuery.setSettleDateFrom(statFirstDay.toString());
  227. settleTaskRelaHistoryQuery.setSettleDateTo(statLastDay.toString());
  228. settleTaskRelaHistoryQuery.setSettleResult(SettleResultEnum.SETTLE);
  229. List<SettleTaskRelaHistory> settleTaskRelaHistorieList = settleTaskRelaHistoryService.queryByCondition(settleTaskRelaHistoryQuery);
  230. // 任务唯一ID - 结算任务关联记录
  231. Map<Long, List<SettleTaskRelaHistory>> settleTaskRelaHistoryMap = settleTaskRelaHistorieList.stream().collect(Collectors.groupingBy(SettleTaskRelaHistory::getPiTaskUniqueId));
  232. // 新增
  233. List<StatPiTask> addStatPiTaskWeekList = new ArrayList<>();
  234. // 更新
  235. List<StatPiTask> updateStatPiTaskWeekList = new ArrayList<>();
  236. for (PiTask piTask : piTasks) {
  237. // 获取结算数据
  238. List<SettleTaskRelaHistory> settleTaskRelaHistories = settleTaskRelaHistoryMap.get(piTask.getUniqueId());
  239. // 统计数据
  240. StatPiTask newStatPiTask = statPiTaskData(statPeriod, settleTaskRelaHistories);
  241. // 获取统计数据
  242. StatPiTask oldStatPiTaskWeek = statPiTaskDataMap.get(piTask.getUniqueId());
  243. // 如果没有统计数据,则创建
  244. if (Objects.isNull(oldStatPiTaskWeek)) {
  245. newStatPiTask.setUserId(piTask.getCreatedBy());
  246. newStatPiTask.setTaskUniqueId(piTask.getUniqueId());
  247. newStatPiTask.setStatTime(statTime.toString());
  248. addStatPiTaskWeekList.add(newStatPiTask);
  249. } else {
  250. newStatPiTask.setId(oldStatPiTaskWeek.getId());
  251. updateStatPiTaskWeekList.add(newStatPiTask);
  252. }
  253. }
  254. if (!CollectionUtils.isEmpty(addStatPiTaskWeekList)) {
  255. statPiTaskWeekService.insertList(addStatPiTaskWeekList);
  256. }
  257. if (!CollectionUtils.isEmpty(updateStatPiTaskWeekList)) {
  258. statPiTaskWeekService.batchUpdate(updateStatPiTaskWeekList);
  259. }
  260. }
  261. /**
  262. * 重新统计打卡任务数据,如果不存在结算记录,则所有数据为0
  263. * @param statPeriod 统计周期
  264. * @param settleTaskRelaHistories 结算记录
  265. * @return
  266. */
  267. private StatPiTask statPiTaskData(StatPeriodEnum statPeriod, List<SettleTaskRelaHistory> settleTaskRelaHistories) {
  268. List<SettleTaskRelaHistory> list = CollectionUtils.isEmpty(settleTaskRelaHistories) ? List.of() : settleTaskRelaHistories.stream().filter(v -> SettleResultEnum.SETTLE.equals(v.getSettleResult())).toList();
  269. // 总需打卡数
  270. int punchInTotalCount = list.size();
  271. // 打卡数
  272. int punchInCount = (int) list.stream().filter(v -> Objects.nonNull(v.getPiTaskHistoryId())).count();
  273. // 打卡完成数
  274. int punchInDoneCount = (int) list.stream().filter(v -> Optional.ofNullable(v.getSettlePoints()).orElse(0) > 0).count();
  275. // 总结算积分
  276. Integer settlePoints = list.stream().map(v -> Optional.ofNullable(v.getSettlePoints()).orElse(0)).reduce(0, Integer::sum);
  277. // 打卡率
  278. BigDecimal punchInRate = BigDecimal.ZERO;
  279. // 打卡完成率
  280. BigDecimal punchInDoneRate = BigDecimal.ZERO;
  281. if (punchInTotalCount != 0) {
  282. BigDecimal punchInTotalCountVal = BigDecimal.valueOf(punchInTotalCount);
  283. punchInRate = BigDecimal.valueOf(punchInCount).divide(punchInTotalCountVal, 4, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  284. punchInDoneRate = BigDecimal.valueOf(punchInDoneCount).divide(punchInTotalCountVal, 4, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  285. }
  286. StatPiTask statPiTask = null;
  287. if (StatPeriodEnum.WEEK.equals(statPeriod)) {
  288. statPiTask = new StatPiTaskWeek();
  289. } else if (StatPeriodEnum.MONTH.equals(statPeriod)) {
  290. statPiTask = new StatPiTaskMonth();
  291. } else if (StatPeriodEnum.YEAR.equals(statPeriod)) {
  292. statPiTask = new StatPiTaskYear();
  293. }
  294. statPiTask.setPunchInTotalCount(punchInTotalCount);
  295. statPiTask.setPunchInCount(punchInCount);
  296. statPiTask.setPunchInDoneCount(punchInDoneCount);
  297. statPiTask.setPunchInRate(punchInRate);
  298. statPiTask.setPunchInDoneRate(punchInDoneRate);
  299. statPiTask.setPoints(settlePoints);
  300. return statPiTask;
  301. }
  302. @Override
  303. @Transactional(rollbackFor = Exception.class)
  304. public void statNewUser(String statTime) {
  305. Assert.isNullInBusiness(statTime, "统计日期不能为空");
  306. // 构建查询的时间范围
  307. String creationTimeFrom = String.format("%s 00:00:00.000", statTime);
  308. String creationTimeTo = String.format("%s 23:59:59.999", statTime);
  309. // 统计
  310. int newUserCount = userService.countByCondition(creationTimeFrom, creationTimeTo);
  311. // 数据入库
  312. StatNewUser addStatNewUser = new StatNewUser();
  313. addStatNewUser.setStatTime(statTime.toString());
  314. addStatNewUser.setNewUserCount(newUserCount);
  315. statNewUserService.insert(addStatNewUser);
  316. }
  317. }