package com.punchsettle.server.service.manager.impl; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import com.punchsettle.server.atomic.entity.User; import com.punchsettle.server.atomic.service.IUserService; import com.punchsettle.server.common.utils.Assert; import com.punchsettle.server.constant.UserCategoryEnum; import com.punchsettle.server.service.manager.IUserManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import com.punchsettle.server.atomic.IStatPiTaskService; import com.punchsettle.server.atomic.StatPiTask; import com.punchsettle.server.atomic.entity.PiTask; import com.punchsettle.server.atomic.entity.PiTaskHistory; import com.punchsettle.server.atomic.entity.SettleTaskRelaHistory; import com.punchsettle.server.atomic.entity.StatNewUser; import com.punchsettle.server.atomic.entity.StatPiTaskMonth; import com.punchsettle.server.atomic.entity.StatPiTaskWeek; import com.punchsettle.server.atomic.entity.StatPiTaskYear; import com.punchsettle.server.atomic.entity.StatPoints; import com.punchsettle.server.atomic.service.IPiTaskHistoryService; import com.punchsettle.server.atomic.service.IPiTaskService; import com.punchsettle.server.atomic.service.ISettleTaskRelaHistoryService; import com.punchsettle.server.atomic.service.IStatNewUserService; import com.punchsettle.server.atomic.service.IStatPiTaskWeekService; import com.punchsettle.server.atomic.service.IStatPointsService; import com.punchsettle.server.constant.CacheNameConstant; import com.punchsettle.server.constant.PunchInResultEnum; import com.punchsettle.server.constant.SettleResultEnum; import com.punchsettle.server.constant.StatPeriodEnum; import com.punchsettle.server.core.config.BizProperties; import com.punchsettle.server.pojo.punchIn.PiTaskHistoryQuery; import com.punchsettle.server.pojo.settle.SettleTaskRelaHistoryQuery; import com.punchsettle.server.pojo.stat.StatNewUserQuery; import com.punchsettle.server.pojo.stat.StatPiTaskQuery; import com.punchsettle.server.pojo.stat.StatPointsQuery; import com.punchsettle.server.pojo.ucharts.LineSeriesVO; import com.punchsettle.server.pojo.ucharts.LineVO; import com.punchsettle.server.service.manager.IStatManager; import com.punchsettle.server.utiis.DateUtils; import com.punchsettle.server.utiis.UserUtils; /** * @author myou * @version 1.0.0 * @date 2025/5/3 9:32 * @description 统计数据服务类 */ @Component public class StatManagerImpl implements IStatManager { /** * 数值100 */ private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100); @Autowired private IStatPointsService statPointsService; @Autowired private IStatNewUserService statNewUserService; @Autowired private IPiTaskHistoryService piTaskHistoryService; @Autowired private BizProperties bizProperties; @Autowired private IStatPiTaskWeekService statPiTaskWeekService; @Autowired private ISettleTaskRelaHistoryService settleTaskRelaHistoryService; @Autowired private IPiTaskService piTaskService; @Autowired private IUserManager userManager; @Autowired private IUserService userService; @Override @Cacheable(cacheNames = CacheNameConstant.STAT_POINTS_LINE, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()") public LineVO queryStatPointsLine() { Long currentUserId = UserUtils.getCurrentUserId(); LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount()); // 获取折线图时间范围 List dateRange = DateUtils.getDateRange(startDate, endDate); String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new); StatPointsQuery statPointsQuery = new StatPointsQuery(); statPointsQuery.setUserId(currentUserId); statPointsQuery.setStatStartTime(startDate.toString()); statPointsQuery.setStatEndTime(endDate.toString()); List statPointsList = statPointsService.queryByCondition(statPointsQuery); Map statPointsMap = statPointsList.stream().collect(Collectors.toMap(v -> v.getStatTime().substring(5), Function.identity(), (v1, v2) -> v1)); List settlePointsList = new ArrayList<>(lineCategories.length); List consumePointsList = new ArrayList<>(lineCategories.length); List totalPointsList = new ArrayList<>(lineCategories.length); for (String lineCategory : lineCategories) { StatPoints statPoints = statPointsMap.get(lineCategory); if (Objects.isNull(statPoints)) { settlePointsList.add(null); consumePointsList.add(null); totalPointsList.add(null); continue; } settlePointsList.add(statPoints.getSettlePoints()); consumePointsList.add(statPoints.getConsumePoints()); totalPointsList.add(statPoints.getTotalPoints()); } LineSeriesVO settlePointsLineSeriesVO = new LineSeriesVO(); settlePointsLineSeriesVO.setName("结算积分"); settlePointsLineSeriesVO.setData(settlePointsList.toArray(Integer[]::new)); LineSeriesVO consumePointsLineSeriesVO = new LineSeriesVO(); consumePointsLineSeriesVO.setName("消耗积分"); consumePointsLineSeriesVO.setData(consumePointsList.toArray(Integer[]::new)); LineSeriesVO totalPointsLineSeriesVO = new LineSeriesVO(); totalPointsLineSeriesVO.setName("总积分"); totalPointsLineSeriesVO.setData(totalPointsList.toArray(Integer[]::new)); LineVO lineVO = new LineVO(); lineVO.setCategories(lineCategories); lineVO.setSeries(Arrays.asList(settlePointsLineSeriesVO, consumePointsLineSeriesVO, totalPointsLineSeriesVO)); return lineVO; } @Override @Cacheable(cacheNames = CacheNameConstant.STAT_NEW_USER_LINE) public LineVO queryStatNewUserLine() { User user = userManager.getByIdWithCache(UserUtils.getCurrentUserId()); if (UserCategoryEnum.NORMAL.equals(user.getUserCategory())) { return null; } LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount()); // 获取折线图时间范围 List dateRange = DateUtils.getDateRange(startDate, endDate); String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new); StatNewUserQuery statNewUserQuery = new StatNewUserQuery(); statNewUserQuery.setStatStartTime(startDate.toString()); statNewUserQuery.setStatEndTime(endDate.toString()); List statNewUserList = statNewUserService.queryByCondition(statNewUserQuery); Map statNewUserMap = statNewUserList.stream().collect(Collectors.toMap(v -> v.getStatTime().substring(5), StatNewUser::getNewUserCount, (v1, v2) -> v1)); List newUserCountList = new ArrayList<>(lineCategories.length); for (String lineCategory : lineCategories) { Integer newUserCount = Optional.ofNullable(statNewUserMap.get(lineCategory)).orElse(0); newUserCountList.add(newUserCount); } LineSeriesVO newUserLineSeriesVO = new LineSeriesVO(); newUserLineSeriesVO.setName("新增用户数"); newUserLineSeriesVO.setData(newUserCountList.toArray(Integer[]::new)); LineVO lineVO = new LineVO(); lineVO.setCategories(lineCategories); lineVO.setSeries(Arrays.asList(newUserLineSeriesVO)); return lineVO; } @Override @Cacheable(cacheNames = CacheNameConstant.STAT_TASK_LINE, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()") public LineVO queryStatTaskLine() { Long currentUserId = UserUtils.getCurrentUserId(); LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusDays(bizProperties.getLineItemCount()); // 获取折线图时间范围 List dateRange = DateUtils.getDateRange(startDate, endDate); String[] lineCategories = dateRange.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new); PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery(); piTaskHistoryQuery.setUserIds(Arrays.asList(currentUserId)); piTaskHistoryQuery.setPunchInDateFrom(startDate.toString()); piTaskHistoryQuery.setPunchInDateTo(endDate.toString()); List piTaskHistoryList = piTaskHistoryService.queryByCondition(piTaskHistoryQuery); Map> piTaskHistoryMap = piTaskHistoryList.stream().collect(Collectors.groupingBy(v -> v.getPunchInDate().substring(5))); // 总任务数 List totalTaskCountList = new ArrayList<>(lineCategories.length); // 打卡任务数 List punchInTaskCountList = new ArrayList<>(lineCategories.length); // 完成打卡任务数 List doneTaskCountList = new ArrayList<>(lineCategories.length); for (String lineCategory : lineCategories) { List piTaskHistories = piTaskHistoryMap.get(lineCategory); if (CollectionUtils.isEmpty(piTaskHistories)) { totalTaskCountList.add(0); punchInTaskCountList.add(0); doneTaskCountList.add(0); continue; } // 总任数 totalTaskCountList.add(piTaskHistories.size()); // TODO 这里要单独弄一个统计表,不然无法计算 punchInTaskCountList.add(piTaskHistories.size()); // 任务完成数 doneTaskCountList.add(piTaskHistories.stream().filter(piTaskHistory -> piTaskHistory.getPunchInResult().equals(PunchInResultEnum.DONE)).collect(Collectors.toList()).size()); } LineSeriesVO totalTaskCountLineSeriesVO = new LineSeriesVO(); totalTaskCountLineSeriesVO.setName("总任务数"); totalTaskCountLineSeriesVO.setData(totalTaskCountList.toArray(Integer[]::new)); LineSeriesVO punchInTaskCountLineSeriesVO = new LineSeriesVO(); punchInTaskCountLineSeriesVO.setName("打卡数量"); punchInTaskCountLineSeriesVO.setData(punchInTaskCountList.toArray(Integer[]::new)); LineSeriesVO doneTaskCountLineSeriesVO = new LineSeriesVO(); doneTaskCountLineSeriesVO.setName("完成数量"); doneTaskCountLineSeriesVO.setData(doneTaskCountList.toArray(Integer[]::new)); LineVO lineVO = new LineVO(); lineVO.setCategories(lineCategories); lineVO.setSeries(Arrays.asList(totalTaskCountLineSeriesVO, punchInTaskCountLineSeriesVO, doneTaskCountLineSeriesVO)); return lineVO; } @Override @Transactional(rollbackFor = Exception.class) public void statPiTaskData(IStatPiTaskService statPiTaskService, StatPeriodEnum statPeriod, List userIds, LocalDate statTime, LocalDate statFirstDay, LocalDate statLastDay) { // 查询任务数据 List piTasks = piTaskService.getActiveTask(userIds); // 没有待统计任务跳过 if (CollectionUtils.isEmpty(piTasks)) { return; } StatPiTaskQuery statPiTaskQuery = new StatPiTaskQuery(); statPiTaskQuery.setStatTime(statTime.toString()); statPiTaskQuery.setUserIds(userIds); List statPiTaskDatas = statPiTaskService.queryByCondition(statPiTaskQuery); // 任务唯一ID - 周统计数据 Map statPiTaskDataMap = statPiTaskDatas.stream().collect(Collectors.toMap(StatPiTask::getTaskUniqueId, Function.identity(), (key1, key2) -> key1)); // 查询已结算数据 SettleTaskRelaHistoryQuery settleTaskRelaHistoryQuery = new SettleTaskRelaHistoryQuery(); settleTaskRelaHistoryQuery.setUserIds(userIds); settleTaskRelaHistoryQuery.setSettleDateFrom(statFirstDay.toString()); settleTaskRelaHistoryQuery.setSettleDateTo(statLastDay.toString()); settleTaskRelaHistoryQuery.setSettleResult(SettleResultEnum.SETTLE); List settleTaskRelaHistorieList = settleTaskRelaHistoryService.queryByCondition(settleTaskRelaHistoryQuery); // 任务唯一ID - 结算任务关联记录 Map> settleTaskRelaHistoryMap = settleTaskRelaHistorieList.stream().collect(Collectors.groupingBy(SettleTaskRelaHistory::getPiTaskUniqueId)); // 新增 List addStatPiTaskWeekList = new ArrayList<>(); // 更新 List updateStatPiTaskWeekList = new ArrayList<>(); for (PiTask piTask : piTasks) { // 获取结算数据 List settleTaskRelaHistories = settleTaskRelaHistoryMap.get(piTask.getUniqueId()); // 统计数据 StatPiTask newStatPiTask = statPiTaskData(statPeriod, settleTaskRelaHistories); // 获取统计数据 StatPiTask oldStatPiTaskWeek = statPiTaskDataMap.get(piTask.getUniqueId()); // 如果没有统计数据,则创建 if (Objects.isNull(oldStatPiTaskWeek)) { newStatPiTask.setUserId(piTask.getCreatedBy()); newStatPiTask.setTaskUniqueId(piTask.getUniqueId()); newStatPiTask.setStatTime(statTime.toString()); addStatPiTaskWeekList.add(newStatPiTask); } else { newStatPiTask.setId(oldStatPiTaskWeek.getId()); updateStatPiTaskWeekList.add(newStatPiTask); } } if (!CollectionUtils.isEmpty(addStatPiTaskWeekList)) { statPiTaskWeekService.insertList(addStatPiTaskWeekList); } if (!CollectionUtils.isEmpty(updateStatPiTaskWeekList)) { statPiTaskWeekService.batchUpdate(updateStatPiTaskWeekList); } } /** * 重新统计打卡任务数据,如果不存在结算记录,则所有数据为0 * @param statPeriod 统计周期 * @param settleTaskRelaHistories 结算记录 * @return */ private StatPiTask statPiTaskData(StatPeriodEnum statPeriod, List settleTaskRelaHistories) { List list = CollectionUtils.isEmpty(settleTaskRelaHistories) ? List.of() : settleTaskRelaHistories.stream().filter(v -> SettleResultEnum.SETTLE.equals(v.getSettleResult())).toList(); // 总需打卡数 int punchInTotalCount = list.size(); // 打卡数 int punchInCount = (int) list.stream().filter(v -> Objects.nonNull(v.getPiTaskHistoryId())).count(); // 打卡完成数 int punchInDoneCount = (int) list.stream().filter(v -> Optional.ofNullable(v.getSettlePoints()).orElse(0) > 0).count(); // 总结算积分 Integer settlePoints = list.stream().map(v -> Optional.ofNullable(v.getSettlePoints()).orElse(0)).reduce(0, Integer::sum); // 打卡率 BigDecimal punchInRate = BigDecimal.ZERO; // 打卡完成率 BigDecimal punchInDoneRate = BigDecimal.ZERO; if (punchInTotalCount != 0) { BigDecimal punchInTotalCountVal = BigDecimal.valueOf(punchInTotalCount); punchInRate = BigDecimal.valueOf(punchInCount).divide(punchInTotalCountVal, 4, RoundingMode.HALF_UP).multiply(ONE_HUNDRED); punchInDoneRate = BigDecimal.valueOf(punchInDoneCount).divide(punchInTotalCountVal, 4, RoundingMode.HALF_UP).multiply(ONE_HUNDRED); } StatPiTask statPiTask = null; if (StatPeriodEnum.WEEK.equals(statPeriod)) { statPiTask = new StatPiTaskWeek(); } else if (StatPeriodEnum.MONTH.equals(statPeriod)) { statPiTask = new StatPiTaskMonth(); } else if (StatPeriodEnum.YEAR.equals(statPeriod)) { statPiTask = new StatPiTaskYear(); } statPiTask.setPunchInTotalCount(punchInTotalCount); statPiTask.setPunchInCount(punchInCount); statPiTask.setPunchInDoneCount(punchInDoneCount); statPiTask.setPunchInRate(punchInRate); statPiTask.setPunchInDoneRate(punchInDoneRate); statPiTask.setPoints(settlePoints); return statPiTask; } @Override @Transactional(rollbackFor = Exception.class) public void statNewUser(String statTime) { Assert.isNullInBusiness(statTime, "统计日期不能为空"); // 构建查询的时间范围 String creationTimeFrom = String.format("%s 00:00:00.000", statTime); String creationTimeTo = String.format("%s 23:59:59.999", statTime); // 统计 int newUserCount = userService.countByCondition(creationTimeFrom, creationTimeTo); // 数据入库 StatNewUser addStatNewUser = new StatNewUser(); addStatNewUser.setStatTime(statTime.toString()); addStatNewUser.setNewUserCount(newUserCount); statNewUserService.insert(addStatNewUser); } }