StatManagerImpl.java 16 KB

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