PunchInManagerImpl.java 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  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.time.LocalTime;
  6. import java.util.Arrays;
  7. import java.util.Comparator;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Objects;
  11. import java.util.Optional;
  12. import java.util.Set;
  13. import java.util.function.Function;
  14. import java.util.stream.Collectors;
  15. import org.springframework.beans.BeanUtils;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.cache.annotation.Cacheable;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import org.springframework.util.CollectionUtils;
  21. import org.springframework.util.StringUtils;
  22. import com.punchsettle.server.atomic.StatPiTask;
  23. import com.punchsettle.server.atomic.entity.PiStatus;
  24. import com.punchsettle.server.atomic.entity.PiTask;
  25. import com.punchsettle.server.atomic.entity.PiTaskExt;
  26. import com.punchsettle.server.atomic.entity.PiTaskHistory;
  27. import com.punchsettle.server.atomic.entity.SettleTaskRelaHistory;
  28. import com.punchsettle.server.atomic.service.IPiStatusService;
  29. import com.punchsettle.server.atomic.service.IPiTaskExtService;
  30. import com.punchsettle.server.atomic.service.IPiTaskHistoryService;
  31. import com.punchsettle.server.atomic.service.IPiTaskService;
  32. import com.punchsettle.server.atomic.service.ISettleTaskRelaHistoryService;
  33. import com.punchsettle.server.atomic.service.IStatPiTaskMonthService;
  34. import com.punchsettle.server.atomic.service.IStatPiTaskYearService;
  35. import com.punchsettle.server.common.constant.CommonEnableStatusEnum;
  36. import com.punchsettle.server.common.exception.BusinessException;
  37. import com.punchsettle.server.common.utils.Assert;
  38. import com.punchsettle.server.constant.ArchiveStatusEnum;
  39. import com.punchsettle.server.constant.CacheNameConstant;
  40. import com.punchsettle.server.constant.ContinueStatusEnum;
  41. import com.punchsettle.server.constant.PunchInDimensionEnum;
  42. import com.punchsettle.server.constant.PunchInExtraMethodEnum;
  43. import com.punchsettle.server.constant.PunchInMethodEnum;
  44. import com.punchsettle.server.constant.PunchInResultEnum;
  45. import com.punchsettle.server.constant.PunchInResultViewEnum;
  46. import com.punchsettle.server.constant.RepeatCategoryEnum;
  47. import com.punchsettle.server.constant.SettleResultEnum;
  48. import com.punchsettle.server.constant.StatPeriodEnum;
  49. import com.punchsettle.server.constant.VersionStatusEnum;
  50. import com.punchsettle.server.pojo.punchIn.PiTaskExtDto;
  51. import com.punchsettle.server.pojo.punchIn.PiTaskExtQuery;
  52. import com.punchsettle.server.pojo.punchIn.PiTaskHistoryQuery;
  53. import com.punchsettle.server.pojo.punchIn.PiTaskHistorySimpleVO;
  54. import com.punchsettle.server.pojo.punchIn.PiTaskHistoryVO;
  55. import com.punchsettle.server.pojo.punchIn.PiTaskQuery;
  56. import com.punchsettle.server.pojo.punchIn.PiTaskRequest;
  57. import com.punchsettle.server.pojo.punchIn.PiTaskSimpleVO;
  58. import com.punchsettle.server.pojo.punchIn.PiTaskStatQuery;
  59. import com.punchsettle.server.pojo.punchIn.PiTaskStatVO;
  60. import com.punchsettle.server.pojo.punchIn.PiTaskToDoVO;
  61. import com.punchsettle.server.pojo.punchIn.PiTaskVO;
  62. import com.punchsettle.server.pojo.punchIn.PunchInHistoryVO;
  63. import com.punchsettle.server.pojo.punchIn.PunchInRequest;
  64. import com.punchsettle.server.pojo.settle.SettleTaskRelaHistoryQuery;
  65. import com.punchsettle.server.pojo.stat.StatPiTaskQuery;
  66. import com.punchsettle.server.pojo.ucharts.LineSeriesVO;
  67. import com.punchsettle.server.pojo.ucharts.LineVO;
  68. import com.punchsettle.server.pojo.uniapp.CalendarSelectedVO;
  69. import com.punchsettle.server.service.manager.ICacheManager;
  70. import com.punchsettle.server.service.manager.ICalendarManager;
  71. import com.punchsettle.server.service.manager.IPunchInCoreManager;
  72. import com.punchsettle.server.service.manager.IPunchInManager;
  73. import com.punchsettle.server.utiis.DateUtils;
  74. import com.punchsettle.server.utiis.SpringUtils;
  75. import com.punchsettle.server.utiis.UserUtils;
  76. import lombok.extern.slf4j.Slf4j;
  77. /**
  78. * @author tyuio
  79. * @version 1.0.0
  80. * @description 打卡结算任务 服务类
  81. * @date 2024/11/25 15:12
  82. */
  83. @Slf4j
  84. @Service
  85. public class PunchInManagerImpl implements IPunchInManager {
  86. @Autowired
  87. private IPiTaskService piTaskService;
  88. @Autowired
  89. private IPiTaskHistoryService piTaskHistoryService;
  90. @Autowired
  91. private IPunchInCoreManager punchInCoreManager;
  92. @Autowired
  93. private IStatPiTaskYearService statPiTaskYearService;
  94. @Autowired
  95. private IStatPiTaskMonthService statPiTaskMonthService;
  96. @Autowired
  97. private ICalendarManager calendarManager;
  98. @Autowired
  99. private ICacheManager cacheManager;
  100. @Autowired
  101. private ISettleTaskRelaHistoryService settleTaskRelaHistoryService;
  102. @Autowired
  103. private IPiTaskExtService piTaskExtService;
  104. /**
  105. * 默认时间00:00:00.000
  106. */
  107. private static final LocalTime ZERO_TIME = LocalTime.parse("00:00:00.000");
  108. /**
  109. * 数值60
  110. */
  111. private static final BigDecimal SIXTY = new BigDecimal("60");
  112. @Override
  113. public List<PiTaskToDoVO> queryToDoList(Long currentUserId) {
  114. Assert.isNull(currentUserId);
  115. // 查询打卡任务
  116. List<PiTask> piTasks = SpringUtils.getBean(IPunchInManager.class).getTaskListInCache(currentUserId);
  117. if (CollectionUtils.isEmpty(piTasks)) {
  118. return List.of();
  119. }
  120. // 当前日期
  121. LocalDate today = LocalDate.now();
  122. String todayStr = DateUtils.YYYY_MM_DD_FORMATTER.format(today);
  123. // 当前时间
  124. LocalTime nowTime = LocalTime.now();
  125. // 当前是否节假日
  126. boolean holidayFlag = calendarManager.judgeHoliday(todayStr);
  127. // 查询打卡记录
  128. List<PiTaskHistory> piTaskHistories = SpringUtils.getBean(IPunchInManager.class).getTaskHistoryListInCache(currentUserId, todayStr);
  129. // 打卡任务唯一ID-打卡记录关联
  130. Map<Long, PiTaskHistory> piTaskHistoryMap = piTaskHistories.stream().collect(Collectors.toMap(PiTaskHistory::getTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  131. // 1.判断重复频率,只显示今天待打卡的任务
  132. // 2.判断显示时间,还没到点显示的暂时不显示
  133. // 3.根据displayOrder进行排序,按自然顺序排列
  134. return piTasks.stream().filter(piTask -> punchInCoreManager.judgeRepeat(piTask, today))
  135. .filter(piTask -> {
  136. LocalTime displayTime = Optional.ofNullable(piTask.getDisplayTime()).orElse(ZERO_TIME);
  137. return nowTime.compareTo(displayTime) > -1;
  138. })
  139. .sorted(Comparator.comparing(piTask -> Optional.ofNullable(piTask.getDisplayOrder()).orElse(0)))
  140. .map(piTask -> {
  141. PiTaskToDoVO piTaskToDoVO = new PiTaskToDoVO();
  142. BeanUtils.copyProperties(piTask, piTaskToDoVO);
  143. piTaskToDoVO.setPunchInResult(PunchInResultEnum.UNDONE);
  144. piTaskToDoVO.setHolidayFlag(CommonEnableStatusEnum.ENABLED.equals(piTask.getHolidayStatus()) && holidayFlag);
  145. // 获取并设置已打卡记录
  146. PiTaskHistory piTaskHistory = piTaskHistoryMap.get(piTask.getUniqueId());
  147. if (Objects.nonNull(piTaskHistory)) {
  148. piTaskToDoVO.setPunchInResult(piTaskHistory.getPunchInResult());
  149. piTaskToDoVO.setCurrentCountTrack(piTaskHistory.getCountTrack());
  150. piTaskToDoVO.setCurrentTimeTrack(piTaskHistory.getTimeTrack());
  151. }
  152. return piTaskToDoVO;
  153. }).collect(Collectors.toList());
  154. }
  155. @Override
  156. @Cacheable(cacheNames = CacheNameConstant.TASK_LIST_VO, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()")
  157. public List<PiTaskSimpleVO> queryTaskList() {
  158. Long currentUserId = UserUtils.getCurrentUserId();
  159. // 查询打卡任务
  160. List<PiTask> piTasks = piTaskService.getActiveTask(Arrays.asList(currentUserId));
  161. if (CollectionUtils.isEmpty(piTasks)) {
  162. return List.of();
  163. }
  164. // 当前日期
  165. LocalDate today = LocalDate.now();
  166. LocalDate beforeToady = today.minusDays(5);
  167. LocalDate afterToady = today.plusDays(4);
  168. // 获取日期范围
  169. List<LocalDate> weeklyDateRange = DateUtils.getDateRange(beforeToady, afterToady);
  170. // 打卡任务唯一ID
  171. Set<Long> taskUniqueIds = piTasks.stream().map(PiTask::getUniqueId).collect(Collectors.toSet());
  172. // 找出范围内的打卡记录
  173. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  174. piTaskHistoryQuery.setTaskUniqueIds(taskUniqueIds);
  175. piTaskHistoryQuery.setUserIds(Arrays.asList(currentUserId));
  176. piTaskHistoryQuery.setPunchInDateFrom(weeklyDateRange.getFirst().toString());
  177. piTaskHistoryQuery.setPunchInDateTo(weeklyDateRange.getLast().toString());
  178. List<PiTaskHistory> piTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  179. // 打卡任务唯一ID-打卡记录关联
  180. Map<Long, List<PiTaskHistory>> piTaskHistoryMap = piTaskHistories.stream().collect(Collectors.groupingBy(PiTaskHistory::getTaskUniqueId));
  181. // 按顺序排列
  182. return piTasks.stream()
  183. .sorted(Comparator.comparing(piTask -> Optional.ofNullable(piTask.getDisplayOrder()).orElse(0)))
  184. .map(piTask -> {
  185. PiTaskSimpleVO piTaskSimpleVO = new PiTaskSimpleVO();
  186. BeanUtils.copyProperties(piTask, piTaskSimpleVO);
  187. // 获取对应的打卡记录
  188. List<PiTaskHistory> tempPiTaskHistories = Optional.ofNullable(piTaskHistoryMap.get(piTask.getUniqueId())).orElse(List.of());
  189. // 任务创建日期
  190. LocalDate creationDate = piTask.getCreationTime().toLocalDateTime().toLocalDate();
  191. // 打卡日期-打卡记录关联
  192. Map<String, PiTaskHistory> tempPiTaskHistoryMap = tempPiTaskHistories.stream().collect(Collectors.toMap(PiTaskHistory::getPunchInDate, Function.identity(), (key1, key2) -> key1));
  193. // 按时间范围遍历,构造页面打卡结果
  194. List<PiTaskHistorySimpleVO> piTaskHistorySimpleVOS = weeklyDateRange.stream().map(punchInDate -> {
  195. // 打卡日期
  196. String punchInDateStr = punchInDate.toString();
  197. // 判断页面打卡结果
  198. PiTaskHistory piTaskHistory = tempPiTaskHistoryMap.get(punchInDateStr);
  199. PunchInResultViewEnum punchInResultView = judgePunchInResultView(piTask, piTaskHistory, punchInDate, today, creationDate);
  200. // 构建页面打卡结果
  201. PiTaskHistorySimpleVO piTaskHistorySimpleVO = new PiTaskHistorySimpleVO();
  202. piTaskHistorySimpleVO.setPunchInDate(punchInDateStr);
  203. piTaskHistorySimpleVO.setPunchInResult(punchInResultView);
  204. return piTaskHistorySimpleVO;
  205. }).collect(Collectors.toList());
  206. piTaskSimpleVO.setPiTaskHistorySimpleVOS(piTaskHistorySimpleVOS);
  207. return piTaskSimpleVO;
  208. }).collect(Collectors.toList());
  209. }
  210. /**
  211. * 判断页面打卡结果
  212. * @return
  213. */
  214. private PunchInResultViewEnum judgePunchInResultView(PiTask piTask, PiTaskHistory piTaskHistory, LocalDate punchInDate, LocalDate today, LocalDate creationDate) {
  215. // 未到打卡时间
  216. if (punchInDate.isAfter(today)) {
  217. return PunchInResultViewEnum.FUTURE;
  218. }
  219. // 任务未创建,不需要打卡
  220. if (punchInDate.isBefore(creationDate)) {
  221. return PunchInResultViewEnum.NOT_NEED;
  222. }
  223. // 打卡日期在今天之前且存在打卡记录,则简单判断打卡结果即可
  224. if (punchInDate.isBefore(today) && Objects.nonNull(piTaskHistory)) {
  225. return PunchInResultEnum.DONE.equals(piTaskHistory.getPunchInResult()) ? PunchInResultViewEnum.DONE : PunchInResultViewEnum.UNDONE;
  226. }
  227. // 打卡日期在今天之前且不存在打卡记录,则有两种情况:1.那天不用打卡;2.那天需要打卡但是没打卡
  228. // TODO 这里应该要有打卡任务的每天记录才能精准判断,现在先简化一下用当前任务的重复频率进行判断
  229. if (punchInDate.isBefore(today) && Objects.isNull(piTaskHistory)) {
  230. // 判断是否需要打卡
  231. boolean repeatResult = punchInCoreManager.judgeRepeat(piTask, today);
  232. return repeatResult ? PunchInResultViewEnum.UNDONE : PunchInResultViewEnum.NOT_NEED;
  233. }
  234. // 打卡日期是今天,且不存在打卡记录,则有两种情况:1.今天不用打卡;2.今天需要打卡但是还没打卡
  235. if (punchInDate.isEqual(today)) {
  236. // 判断是否需要打卡
  237. boolean repeatResult = punchInCoreManager.judgeRepeat(piTask, today);
  238. // 今天不用无须打卡
  239. if (!repeatResult) {
  240. return PunchInResultViewEnum.NOT_NEED;
  241. }
  242. // 今天还没有打卡记录无法判断打卡结果
  243. if (Objects.isNull(piTaskHistory)) {
  244. return PunchInResultViewEnum.TODAY_UNKNOWN;
  245. }
  246. return PunchInResultEnum.DONE.equals(piTaskHistory.getPunchInResult()) ? PunchInResultViewEnum.DONE : PunchInResultViewEnum.TODAY_UNKNOWN;
  247. }
  248. // 不符合任何情况则是无需打卡
  249. return PunchInResultViewEnum.NOT_NEED;
  250. }
  251. @Override
  252. public PiTaskVO queryTask(Long id) {
  253. Assert.isNullInBusiness(id, "请传入待查询的任务ID");
  254. PiTaskVO piTaskVO = new PiTaskVO();
  255. // 获取打卡任务
  256. PiTask piTask = Optional.ofNullable(piTaskService.getById(id)).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
  257. BeanUtils.copyProperties(piTask, piTaskVO);
  258. if (RepeatCategoryEnum.CUSTOM.equals(piTask.getRepeatCategory()) && StringUtils.hasText(piTask.getRepeatCustomDay())) {
  259. piTaskVO.setRepeatCustomDayList(Arrays.stream(piTask.getRepeatCustomDay().split(",")).toList());
  260. }
  261. // 获取拓展信息
  262. PiTaskExtQuery piTaskExtQuery = new PiTaskExtQuery();
  263. piTaskExtQuery.setPunchInTaskIds(Arrays.asList(piTask.getId()));
  264. List<PiTaskExt> piTaskExtList = piTaskExtService.queryByCondition(piTaskExtQuery);
  265. // 没有拓展信息直接返回
  266. if (CollectionUtils.isEmpty(piTaskExtList)) {
  267. return piTaskVO;
  268. }
  269. // 拓展信息(当天打卡)
  270. List<PiTaskExtDto> taskExtList = piTaskExtList.stream().filter(piTaskExt -> PunchInDimensionEnum.ONE_DAY.equals(piTaskExt.getDimension())).map(piTaskExt -> {
  271. PiTaskExtDto piTaskExtDto = new PiTaskExtDto();
  272. BeanUtils.copyProperties(piTaskExt, piTaskExtDto);
  273. return piTaskExtDto;
  274. }).toList();
  275. piTaskVO.setTaskExtList(taskExtList);
  276. // 拓展信息(连续打卡)
  277. List<PiTaskExtDto> continueTaskExtList = piTaskExtList.stream().filter(piTaskExt -> PunchInDimensionEnum.MULTI_DAY.equals(piTaskExt.getDimension())).map(piTaskExt -> {
  278. PiTaskExtDto piTaskExtDto = new PiTaskExtDto();
  279. BeanUtils.copyProperties(piTaskExt, piTaskExtDto);
  280. return piTaskExtDto;
  281. }).toList();
  282. piTaskVO.setContinueTaskExtList(continueTaskExtList);
  283. return piTaskVO;
  284. }
  285. @Override
  286. @Transactional(rollbackFor = Exception.class)
  287. public void saveTask(PiTaskRequest request) {
  288. Assert.isNullInBusiness(request, "请传入任务信息");
  289. // 今天
  290. LocalDate today = LocalDate.now();
  291. // 当前用户ID
  292. Long currentUserId = UserUtils.getCurrentUserId();
  293. // 数据校验
  294. if (PunchInMethodEnum.TIMING.equals(request.getPunchInMethod())) {
  295. if (Objects.isNull(request.getCompareRule()) || Objects.isNull(request.getTimeTrack())) {
  296. throw BusinessException.fail("打卡类型:计时,比较规则和时间不能为空");
  297. }
  298. if (CommonEnableStatusEnum.ENABLED.equals(request.getHolidayStatus()) && Objects.isNull(request.getHolidayTimeTrack())) {
  299. throw BusinessException.fail("打卡类型:计时,启用了节假日奖励,节假日的时间不能为空");
  300. }
  301. }
  302. if (PunchInMethodEnum.COUNT.equals(request.getPunchInMethod())) {
  303. if (Objects.isNull(request.getCompareRule()) || Objects.isNull(request.getCountTrack())) {
  304. throw BusinessException.fail("打卡类型:计数,比较规则和次数不能为空");
  305. }
  306. if (CommonEnableStatusEnum.ENABLED.equals(request.getHolidayStatus()) && Objects.isNull(request.getHolidayCountTrack())) {
  307. throw BusinessException.fail("打卡类型:计数,启用了节假日奖励,节假日的次数不能为空");
  308. }
  309. }
  310. if (CommonEnableStatusEnum.ENABLED.equals(request.getFullAttendanceStatus()) && (Objects.isNull(request.getFullAttendancePeriod()) || Objects.isNull(request.getFullAttendanceFaultToleranceCnt()))) {
  311. throw BusinessException.fail("启用了全勤奖励,全勤周期和全勤容错次数不能为空");
  312. }
  313. if (RepeatCategoryEnum.CUSTOM.equals(request.getRepeatCategory()) && CollectionUtils.isEmpty(request.getRepeatCustomDayList())) {
  314. throw BusinessException.fail("自定义重复周期,自定义重复日不能为空");
  315. }
  316. if (PunchInExtraMethodEnum.FIXED.equals(request.getExtraMethod()) || PunchInExtraMethodEnum.INTERVAL.equals(request.getExtraMethod())) {
  317. if (PunchInMethodEnum.TIMING.equals(request.getPunchInMethod()) && Objects.isNull(request.getExtraTimeStep())) {
  318. throw BusinessException.fail("打卡类型:计时,额外奖励方式为固定或区间,额外奖励时间间隔不能为空");
  319. }
  320. if (PunchInExtraMethodEnum.FIXED.equals(request.getExtraMethod()) && Objects.isNull(request.getExtraPoints())) {
  321. throw BusinessException.fail("额外奖励方式为固定,额外奖励积分不能为空");
  322. }
  323. if (PunchInExtraMethodEnum.INTERVAL.equals(request.getExtraMethod())) {
  324. if (CollectionUtils.isEmpty(request.getTaskExtList())) {
  325. throw BusinessException.fail("额外奖励方式为区间,积分区间信息不能为空");
  326. }
  327. Map<Integer, List<PiTaskExtDto>> initialValueMap = request.getTaskExtList().stream().collect(Collectors.groupingBy(PiTaskExtDto::getInitialValue));
  328. for (List<PiTaskExtDto> value : initialValueMap.values()) {
  329. if (value.size() > 1) {
  330. throw BusinessException.fail("任务积分区间不能有重复的起始值");
  331. }
  332. }
  333. }
  334. }
  335. if (CommonEnableStatusEnum.ENABLED.equals(request.getContinueStatus()) && (Objects.isNull(request.getGraceDay()) || Objects.isNull(request.getContinueInterruptedCount()) || Objects.isNull(request.getPenaltyDay()))) {
  336. throw BusinessException.fail("启用了连续规则,宽限期、连续中断次数、惩罚天数不能为空");
  337. }
  338. if (CommonEnableStatusEnum.ENABLED.equals(request.getTaskPointsStatus())) {
  339. if (CollectionUtils.isEmpty(request.getContinueTaskExtList())) {
  340. throw BusinessException.fail("启用了任务积分计算,连续任务积分区间信息不能为空");
  341. }
  342. Map<Integer, List<PiTaskExtDto>> initialValueMap = request.getContinueTaskExtList().stream().collect(Collectors.groupingBy(PiTaskExtDto::getInitialValue));
  343. for (List<PiTaskExtDto> value : initialValueMap.values()) {
  344. if (value.size() > 1) {
  345. throw BusinessException.fail("连续任务积分区间不能有重复的起始值");
  346. }
  347. }
  348. }
  349. if (Objects.nonNull(request.getAutoArchiveDate()) && request.getAutoArchiveDate().isBefore(today)) {
  350. throw BusinessException.fail("任务自动归档时间无效,必需大于等于当前时间");
  351. }
  352. // 新增打卡任务
  353. PiTask addPiTask = new PiTask();
  354. BeanUtils.copyProperties(request, addPiTask);
  355. addPiTask.setId(null);
  356. addPiTask.setTaskStatus(VersionStatusEnum.ACTIVE);
  357. addPiTask.setTaskVersion(0);
  358. addPiTask.setArchiveStatus(ArchiveStatusEnum.ACTIVE);
  359. if (!CollectionUtils.isEmpty(request.getRepeatCustomDayList())) {
  360. addPiTask.setRepeatCustomDay(String.join(",", request.getRepeatCustomDayList()));
  361. }
  362. // 拓展信息(当天打卡)
  363. List<PiTaskExt> taskExtList = Optional.ofNullable(request.getTaskExtList()).orElse(List.of()).stream()
  364. .sorted(Comparator.comparing(PiTaskExtDto::getInitialValue))
  365. .map(piTaskExtDto -> {
  366. PiTaskExt piTaskExt = new PiTaskExt();
  367. BeanUtils.copyProperties(piTaskExtDto, piTaskExt);
  368. piTaskExt.setUserId(currentUserId);
  369. piTaskExt.setDimension(PunchInDimensionEnum.ONE_DAY);
  370. return piTaskExt;
  371. }).toList();
  372. // 拓展信息(连续打卡)
  373. List<PiTaskExt> continueTaskExtList = Optional.ofNullable(request.getContinueTaskExtList()).orElse(List.of()).stream()
  374. .sorted(Comparator.comparing(PiTaskExtDto::getInitialValue))
  375. .map(piTaskExtDto -> {
  376. PiTaskExt piTaskExt = new PiTaskExt();
  377. BeanUtils.copyProperties(piTaskExtDto, piTaskExt);
  378. piTaskExt.setUserId(currentUserId);
  379. piTaskExt.setDimension(PunchInDimensionEnum.MULTI_DAY);
  380. return piTaskExt;
  381. }).toList();
  382. // 如果任务已存在,则更新任务版本号、设置唯一ID、旧数据归档
  383. if (Objects.nonNull(request.getId())) {
  384. PiTask piTask = Optional.ofNullable(piTaskService.getById(request.getId())).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
  385. addPiTask.setTaskVersion(piTask.getTaskVersion() + 1);
  386. addPiTask.setUniqueId(piTask.getUniqueId());
  387. // 旧数据归档
  388. PiTask updatePiTask = new PiTask();
  389. updatePiTask.setId(piTask.getId());
  390. updatePiTask.setTaskStatus(VersionStatusEnum.ARCHIVE);
  391. piTaskService.update(updatePiTask);
  392. // 切换打卡模式删除当前旧打卡数据
  393. if (!piTask.getPunchInMethod().equals(addPiTask.getPunchInMethod())) {
  394. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  395. piTaskHistoryQuery.setTaskUniqueIds(Arrays.asList(piTask.getUniqueId()));
  396. piTaskHistoryQuery.setPunchInDate(LocalDate.now().toString());
  397. List<PiTaskHistory> piTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  398. if (!CollectionUtils.isEmpty(piTaskHistories)) {
  399. Set<Long> piTaskHistoryIds = piTaskHistories.stream().map(PiTaskHistory::getId).collect(Collectors.toSet());
  400. piTaskHistoryService.deleteByIds(piTaskHistoryIds);
  401. }
  402. }
  403. }
  404. // 保存数据
  405. piTaskService.insert(addPiTask);
  406. if (!CollectionUtils.isEmpty(taskExtList)) {
  407. taskExtList.forEach(v -> v.setTaskId(addPiTask.getId()));
  408. piTaskExtService.batchAdd(taskExtList);
  409. }
  410. if (!CollectionUtils.isEmpty(continueTaskExtList)) {
  411. continueTaskExtList.forEach(v -> v.setTaskId(addPiTask.getId()));
  412. piTaskExtService.batchAdd(continueTaskExtList);
  413. }
  414. // 如果首次保存,需要设置唯一ID
  415. if (Objects.isNull(addPiTask.getUniqueId())) {
  416. PiTask updatePiTask = new PiTask();
  417. updatePiTask.setId(addPiTask.getId());
  418. updatePiTask.setUniqueId(addPiTask.getId());
  419. piTaskService.update(updatePiTask);
  420. }
  421. // 不是新增打卡任务 更新打卡记录
  422. if (Objects.nonNull(addPiTask.getUniqueId())) {
  423. // 查询今天的打卡记录
  424. PiTaskHistory oldPiTaskHistory = piTaskHistoryService.getByTaskUniqueIdAndPunchInDate(addPiTask.getUniqueId(), today.toString());
  425. // 已打卡则重新判断打卡状态
  426. if (Objects.nonNull(oldPiTaskHistory)) {
  427. // 新编辑的打卡任务+旧的打卡记录判断打卡结果
  428. PunchInResultEnum punchInResult = punchInCoreManager.judgePunchInResultInTask(addPiTask, oldPiTaskHistory);
  429. // 更新
  430. PiTaskHistory updatePiTaskHistory = new PiTaskHistory();
  431. updatePiTaskHistory.setId(oldPiTaskHistory.getId());
  432. updatePiTaskHistory.setPunchInResult(punchInResult);
  433. piTaskHistoryService.update(updatePiTaskHistory);
  434. }
  435. }
  436. // 清除缓存
  437. clearCache(currentUserId, true, true);
  438. }
  439. @Override
  440. @Transactional(rollbackFor = Exception.class)
  441. public void deleteTask(Long id) {
  442. Assert.isNullInBusiness(id, "请传入待删除的任务");
  443. // 新增打卡任务记录
  444. PiTask addPiTask = copyAndArchivePiTask(id);
  445. addPiTask.setTaskStatus(VersionStatusEnum.DELETE);
  446. piTaskService.insert(addPiTask);
  447. // 清除缓存
  448. clearCache(UserUtils.getCurrentUserId(), false, false);
  449. }
  450. @Override
  451. @Transactional(rollbackFor = Exception.class)
  452. public void doPunchIn(PunchInRequest request) {
  453. if (Objects.isNull(request) || Objects.isNull(request.getId())) {
  454. BusinessException.throwFail("请传入待打卡的任务");
  455. }
  456. // 当前用户ID
  457. Long currentUserId = UserUtils.getCurrentUserId();
  458. PiTask piTask = Optional.ofNullable(piTaskService.getById(request.getId())).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
  459. if (PunchInMethodEnum.TIMING.equals(piTask.getPunchInMethod()) && Objects.isNull(request.getTimeTrack())) {
  460. BusinessException.throwFail("打卡类型:计时,请传入时间记录");
  461. }
  462. // 打卡日期
  463. String punchInDate = LocalDate.now().toString();
  464. // 查询今天的打卡记录
  465. PiTaskHistory oldPiTaskHistory = piTaskHistoryService.getByTaskUniqueIdAndPunchInDate(piTask.getUniqueId(), punchInDate);
  466. // 待更新/新增的打卡记录
  467. PiTaskHistory tempPiTaskHistory = new PiTaskHistory();
  468. tempPiTaskHistory.setTaskId(piTask.getId());
  469. // 不存在则新增,存在则更新
  470. if (Objects.isNull(oldPiTaskHistory)) {
  471. tempPiTaskHistory.setTaskUniqueId(piTask.getUniqueId());
  472. tempPiTaskHistory.setPunchInDate(punchInDate);
  473. tempPiTaskHistory.setUserId(currentUserId);
  474. } else {
  475. tempPiTaskHistory.setId(oldPiTaskHistory.getId());
  476. tempPiTaskHistory.setPunchInDate(oldPiTaskHistory.getPunchInDate());
  477. }
  478. // 打卡类型:单次打卡,
  479. if (PunchInMethodEnum.SINGLE.equals(piTask.getPunchInMethod())) {
  480. // 需要判断是否重复打卡
  481. if (Objects.nonNull(oldPiTaskHistory) && Optional.ofNullable(oldPiTaskHistory.getCountTrack()).orElse(0) > 0) {
  482. BusinessException.throwFail("已打卡,无须重复打卡");
  483. }
  484. // 默认打卡次数是1,是计数打卡的特列
  485. tempPiTaskHistory.setCountTrack(1);
  486. }
  487. // 打卡类型:计数,需要累加打卡次数
  488. if (PunchInMethodEnum.COUNT.equals(piTask.getPunchInMethod())) {
  489. if (Objects.isNull(oldPiTaskHistory)) {
  490. tempPiTaskHistory.setCountTrack(1);
  491. } else {
  492. tempPiTaskHistory.setCountTrack(Optional.ofNullable(oldPiTaskHistory.getCountTrack()).orElse(0) + 1);
  493. }
  494. }
  495. // 打卡类型:计时,需要记录最新打卡时长
  496. if (PunchInMethodEnum.TIMING.equals(piTask.getPunchInMethod())) {
  497. tempPiTaskHistory.setTimeTrack(request.getTimeTrack());
  498. }
  499. // 设置打卡结果
  500. PunchInResultEnum punchInResult = punchInCoreManager.judgePunchInResultInTask(piTask, tempPiTaskHistory);
  501. tempPiTaskHistory.setPunchInResult(punchInResult);
  502. // 新增或更新
  503. if (Objects.isNull(oldPiTaskHistory)) {
  504. piTaskHistoryService.insert(tempPiTaskHistory);
  505. } else {
  506. piTaskHistoryService.update(tempPiTaskHistory);
  507. }
  508. // 清楚缓存
  509. clearCache(currentUserId, true, true);
  510. }
  511. @Override
  512. @Transactional(rollbackFor = Exception.class)
  513. public void archiveTask(Long id) {
  514. Assert.isNullInBusiness(id, "请传入待删除的任务");
  515. // 归档
  516. PiTask addPiTask = copyAndArchivePiTask(id);
  517. addPiTask.setArchiveStatus(ArchiveStatusEnum.ARCHIVE);
  518. addPiTask.setManualArchiveDate(LocalDate.now());
  519. piTaskService.insert(addPiTask);
  520. // 清楚缓存
  521. clearCache(UserUtils.getCurrentUserId(), false, false);
  522. }
  523. @Override
  524. public void revokePunchIn(Long id) {
  525. // 获取打卡任务
  526. PiTask piTask = Optional.ofNullable(piTaskService.getById(id)).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
  527. // 获取打卡记录
  528. PiTaskHistory piTaskHistory = piTaskHistoryService.getByTaskUniqueIdAndPunchInDate(piTask.getUniqueId(), LocalDate.now().toString());
  529. if (Objects.isNull(piTaskHistory)) {
  530. return;
  531. }
  532. PiTaskHistory updatePiTaskHistory = new PiTaskHistory();
  533. updatePiTaskHistory.setId(piTaskHistory.getId());
  534. updatePiTaskHistory.setTaskId(piTask.getId());
  535. updatePiTaskHistory.setPunchInDate(piTaskHistory.getPunchInDate());
  536. // 单次打卡,次数置为0
  537. if (PunchInMethodEnum.SINGLE.equals(piTask.getPunchInMethod())) {
  538. updatePiTaskHistory.setCountTrack(0);
  539. }
  540. // 计时打卡 时间置为00:00:00.000
  541. if (PunchInMethodEnum.TIMING.equals(piTask.getPunchInMethod())) {
  542. updatePiTaskHistory.setTimeTrack(ZERO_TIME);
  543. }
  544. // 计数打卡,当前打卡次数减1
  545. if (PunchInMethodEnum.COUNT.equals(piTask.getPunchInMethod())) {
  546. Integer countTrack = Optional.ofNullable(piTaskHistory.getCountTrack()).orElse(0);
  547. if (countTrack == 0) {
  548. return;
  549. }
  550. updatePiTaskHistory.setCountTrack(countTrack - 1);
  551. }
  552. // 设置打卡结果
  553. PunchInResultEnum punchInResult = punchInCoreManager.judgePunchInResultInTask(piTask, updatePiTaskHistory);
  554. updatePiTaskHistory.setPunchInResult(punchInResult);
  555. // 更新
  556. piTaskHistoryService.update(updatePiTaskHistory);
  557. // 清楚缓存
  558. clearCache(UserUtils.getCurrentUserId(), true, true);
  559. }
  560. @Override
  561. @Cacheable(cacheNames = CacheNameConstant.STAT_TASK, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()+'_'+#query.taskId+'_'+#query.statTime", condition = "#query.taskId != null && #query.statTime != null && !#query.statTime.isBlank()")
  562. public PiTaskStatVO queryStat(PiTaskStatQuery query) {
  563. // 当前用户ID
  564. Long currentUserId = UserUtils.getCurrentUserId();
  565. // 查找打卡任务
  566. PiTask piTask = Optional.ofNullable(piTaskService.getById(query.getTaskId())).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
  567. // 查询统计数据
  568. StatPiTask statPiTask = null;
  569. // 统计范围的第一天
  570. LocalDate statFirstDate = null;
  571. // 统计范围的最后一天
  572. LocalDate statLastDay = null;
  573. // 今天
  574. LocalDate today = LocalDate.now();
  575. // 构造查询条件
  576. StatPiTaskQuery statPiTaskQuery = new StatPiTaskQuery();
  577. statPiTaskQuery.setUserId(currentUserId);
  578. statPiTaskQuery.setTaskUniqueId(piTask.getUniqueId());
  579. statPiTaskQuery.setStatTime(query.getStatTime());
  580. if (StatPeriodEnum.YEAR.equals(query.getStatPeriod())) {
  581. statPiTask = statPiTaskYearService.queryOneByCondition(statPiTaskQuery);
  582. statFirstDate = LocalDate.of(Integer.valueOf(query.getStatTime()), 1, 1);
  583. statLastDay = DateUtils.getLastDayOfYear(statFirstDate);
  584. } else if (StatPeriodEnum.MONTH.equals(query.getStatPeriod())) {
  585. statPiTask = statPiTaskMonthService.queryOneByCondition(statPiTaskQuery);
  586. String[] split = query.getStatTime().split("-");
  587. statFirstDate = LocalDate.of(Integer.valueOf(split[0]), Integer.valueOf(split[1]), 1);
  588. statLastDay = DateUtils.getLastDayOfYear(statFirstDate);
  589. }
  590. // 查询打卡记录
  591. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  592. piTaskHistoryQuery.setUserIds(Arrays.asList(currentUserId));
  593. piTaskHistoryQuery.setPunchInDateLike(query.getStatTime());
  594. piTaskHistoryQuery.setTaskUniqueIds(Arrays.asList(piTask.getUniqueId()));
  595. List<PiTaskHistory> piTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  596. // 构造折线图数据
  597. List<LocalDate> dateRangeInMonth = null;
  598. if (today.isBefore(statLastDay)) {
  599. dateRangeInMonth = DateUtils.getDateRange(statFirstDate, today);
  600. } else {
  601. dateRangeInMonth = DateUtils.getDateRange(statFirstDate, statLastDay);
  602. }
  603. // 横坐标
  604. String[] categories = dateRangeInMonth.stream().map(v -> DateUtils.MM_DD_FORMATTER.format(v)).toArray(String[]::new);
  605. // 打卡日期-打卡记录 关联
  606. Map<String, PiTaskHistory> piTaskHistoryMap = piTaskHistories.stream().collect(Collectors.toMap(piTaskHistory -> piTaskHistory.getPunchInDate().substring(5), Function.identity(), (key1, key2) -> key1));
  607. Object[] seriesData = Arrays.stream(categories).map(category -> {
  608. PiTaskHistory piTaskHistory = piTaskHistoryMap.get(category);
  609. if (Objects.isNull(piTaskHistory)) {
  610. return null;
  611. }
  612. if (PunchInMethodEnum.SINGLE.equals(piTask.getPunchInMethod())) {
  613. return PunchInResultEnum.DONE.equals(piTaskHistory.getPunchInResult()) ? 1 : 0;
  614. }
  615. if (PunchInMethodEnum.COUNT.equals(piTask.getPunchInMethod())) {
  616. return Optional.ofNullable(piTaskHistory.getCountTrack()).orElse(0);
  617. }
  618. if (PunchInMethodEnum.TIMING.equals(piTask.getPunchInMethod())) {
  619. LocalTime timeTrack = Optional.ofNullable(piTaskHistory.getTimeTrack()).orElse(ZERO_TIME);
  620. BigDecimal hourVal = BigDecimal.valueOf(timeTrack.getHour());
  621. BigDecimal minVal = BigDecimal.valueOf(timeTrack.getMinute()).divide(SIXTY, 2, RoundingMode.HALF_UP);
  622. return hourVal.add(minVal);
  623. }
  624. return null;
  625. }).toArray();
  626. // 构建折线图数据
  627. LineSeriesVO lineSeriesVO = new LineSeriesVO();
  628. lineSeriesVO.setName("每日打卡数据");
  629. lineSeriesVO.setData(seriesData);
  630. LineVO lineVO = new LineVO();
  631. lineVO.setCategories(categories);
  632. lineVO.setSeries(Arrays.asList(lineSeriesVO));
  633. // 查询打卡任务
  634. Set<Long> piTaskIds = piTaskHistories.stream().map(PiTaskHistory::getTaskId).collect(Collectors.toSet());
  635. PiTaskQuery piTaskQuery = new PiTaskQuery();
  636. piTaskQuery.setIds(piTaskIds);
  637. List<PiTask> piTasks = piTaskService.queryByCondition(piTaskQuery);
  638. // 打卡任务ID-打卡任务对象关联
  639. Map<Long, PiTask> piTaskMap = piTasks.stream().collect(Collectors.toMap(PiTask::getId, Function.identity(), (key1, key2) -> key1));
  640. // 获取结算数据
  641. SettleTaskRelaHistoryQuery settleTaskRelaHistoryQuery = new SettleTaskRelaHistoryQuery();
  642. settleTaskRelaHistoryQuery.setSettleDateFrom(statFirstDate.toString());
  643. settleTaskRelaHistoryQuery.setSettleDateTo(statLastDay.toString());
  644. settleTaskRelaHistoryQuery.setUserIds(Arrays.asList(UserUtils.getCurrentUserId()));
  645. settleTaskRelaHistoryQuery.setSettleResults(Arrays.asList(SettleResultEnum.SETTLED, SettleResultEnum.NO_SETTLED));
  646. settleTaskRelaHistoryQuery.setTaskUniqueId(piTask.getUniqueId());
  647. List<SettleTaskRelaHistory> settleTaskRelaHistories = settleTaskRelaHistoryService.queryByCondition(settleTaskRelaHistoryQuery);
  648. // 结算日期 - 结算信息 关联
  649. Map<String, SettleTaskRelaHistory> settleTaskRelaHistoryMap = settleTaskRelaHistories.stream().collect(Collectors.toMap(SettleTaskRelaHistory::getSettleDate, Function.identity(), (key1, key2) -> key1));
  650. // 组装数据
  651. List<PiTaskHistoryVO> piTaskHistoryVOS = piTaskHistories.stream().map(piTaskHistory -> {
  652. PiTask tempPiTask = piTaskMap.get(piTaskHistory.getTaskId());
  653. SettleTaskRelaHistory settleTaskRelaHistory = settleTaskRelaHistoryMap.get(piTaskHistory.getPunchInDate());
  654. PiTaskHistoryVO piTaskHistoryVO = new PiTaskHistoryVO();
  655. piTaskHistoryVO.setPunchInDate(piTaskHistory.getPunchInDate());
  656. if (Objects.nonNull(tempPiTask)) {
  657. piTaskHistoryVO.setPunchInMethod(tempPiTask.getPunchInMethod());
  658. }
  659. if (Objects.nonNull(settleTaskRelaHistory)) {
  660. piTaskHistoryVO.setSettleResult(settleTaskRelaHistory.getSettleResult());
  661. piTaskHistoryVO.setSettlePoints(Optional.ofNullable(settleTaskRelaHistory.getSettlePoints()).orElse(0));
  662. }
  663. piTaskHistoryVO.setPunchInResult(piTaskHistory.getPunchInResult());
  664. piTaskHistoryVO.setCountTrack(piTaskHistory.getCountTrack());
  665. piTaskHistoryVO.setTimeTrack(piTaskHistory.getTimeTrack());
  666. return piTaskHistoryVO;
  667. }).collect(Collectors.toList());
  668. PiTaskStatVO piTaskStatVO = new PiTaskStatVO();
  669. if (Objects.nonNull(statPiTask)) {
  670. BeanUtils.copyProperties(statPiTask, piTaskStatVO);
  671. }
  672. piTaskStatVO.setPiTaskHistoryVOS(piTaskHistoryVOS);
  673. piTaskStatVO.setLineVO(lineVO);
  674. return piTaskStatVO;
  675. }
  676. /**
  677. * 复制打卡任务信息并把旧的信息归档(审计信息不复制、任务版本号自动加1,任务状态:活跃)
  678. * @param id 打卡任务id
  679. * @return
  680. */
  681. private PiTask copyAndArchivePiTask(Long id) {
  682. // 获取旧的打卡任务信息
  683. PiTask oldPiTask = Optional.ofNullable(piTaskService.getById(id)).orElseThrow(() -> BusinessException.fail("打卡任务不存在"));
  684. // 旧的打卡任务信息归档
  685. PiTask updatePiTask = new PiTask();
  686. updatePiTask.setId(oldPiTask.getId());
  687. updatePiTask.setTaskStatus(VersionStatusEnum.ARCHIVE);
  688. piTaskService.update(updatePiTask);
  689. // 新的打卡任务信息
  690. PiTask piTask = new PiTask();
  691. BeanUtils.copyProperties(oldPiTask, piTask);
  692. piTask.setId(null);
  693. piTask.setTaskStatus(VersionStatusEnum.ACTIVE);
  694. piTask.setTaskVersion(Optional.ofNullable(oldPiTask.getTaskVersion()).orElse(0) + 1);
  695. return piTask;
  696. }
  697. @Override
  698. @Cacheable(cacheNames = CacheNameConstant.TASK_PUNCH_IN_HISTORY, key = "T(com.punchsettle.server.utiis.UserUtils).getCurrentUserId()+'_'+#punchInDate", condition = "#punchInDate != null && !#punchInDate.isBlank()")
  699. public PunchInHistoryVO queryPunchInHistory(String punchInDate) {
  700. Assert.isNullInBusiness(punchInDate, "打卡日期不能为空");
  701. // 查询打卡记录
  702. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  703. piTaskHistoryQuery.setUserIds(Arrays.asList(UserUtils.getCurrentUserId()));
  704. piTaskHistoryQuery.setPunchInDateLike(punchInDate.substring(0, 7));
  705. List<PiTaskHistory> piTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  706. // 打卡数据直接返回
  707. if (CollectionUtils.isEmpty(piTaskHistories)) {
  708. return new PunchInHistoryVO();
  709. }
  710. // 打卡日期 - 打卡记录关联
  711. Map<String, List<PiTaskHistory>> piTaskHistoryMap = piTaskHistories.stream().collect(Collectors.groupingBy(PiTaskHistory::getPunchInDate));
  712. PunchInHistoryVO punchInHistoryVO = new PunchInHistoryVO();
  713. // 日历打点信息
  714. List<CalendarSelectedVO> calendarSelectedVOS = piTaskHistoryMap.keySet().stream().map(tempPunchInDate -> {
  715. // 获取打卡记录
  716. List<PiTaskHistory> tempPiTaskHistories = piTaskHistoryMap.get(tempPunchInDate);
  717. // 打卡完成数
  718. long punchInDoneCount = tempPiTaskHistories.stream().filter(piTaskHistory -> PunchInResultEnum.DONE.equals(piTaskHistory.getPunchInResult())).count();
  719. CalendarSelectedVO calendarSelectedVO = new CalendarSelectedVO();
  720. calendarSelectedVO.setDate(tempPunchInDate);
  721. calendarSelectedVO.setInfo(String.valueOf(punchInDoneCount));
  722. return calendarSelectedVO;
  723. }).toList();
  724. // 设置日历打点信息
  725. punchInHistoryVO.setCalendarSelectedVOS(calendarSelectedVOS);
  726. // 获取任务信息
  727. List<PiTaskHistory> tempPiTaskHistories = piTaskHistoryMap.get(punchInDate);
  728. // 当天没有直接返回
  729. if (CollectionUtils.isEmpty(tempPiTaskHistories)) {
  730. return punchInHistoryVO;
  731. }
  732. Set<Long> taskIds = tempPiTaskHistories.stream().map(PiTaskHistory::getTaskId).collect(Collectors.toSet());
  733. PiTaskQuery piTaskQuery = new PiTaskQuery();
  734. piTaskQuery.setIds(taskIds);
  735. List<PiTask> piTasks = piTaskService.queryByCondition(piTaskQuery);
  736. // 任务ID - 任务信息
  737. Map<Long, PiTask> piTaskMap = piTasks.stream().collect(Collectors.toMap(PiTask::getId, Function.identity(), (key1, key2) -> key1));
  738. // 获取结算数据
  739. SettleTaskRelaHistoryQuery settleTaskRelaHistoryQuery = new SettleTaskRelaHistoryQuery();
  740. settleTaskRelaHistoryQuery.setSettleDate(punchInDate);
  741. settleTaskRelaHistoryQuery.setUserIds(Arrays.asList(UserUtils.getCurrentUserId()));
  742. settleTaskRelaHistoryQuery.setSettleResults(Arrays.asList(SettleResultEnum.SETTLED, SettleResultEnum.NO_SETTLED));
  743. List<SettleTaskRelaHistory> settleTaskRelaHistories = settleTaskRelaHistoryService.queryByCondition(settleTaskRelaHistoryQuery);
  744. // 任务唯一ID - 结算信息 关联
  745. Map<Long, SettleTaskRelaHistory> settleTaskRelaHistoryMap = settleTaskRelaHistories.stream().collect(Collectors.toMap(SettleTaskRelaHistory::getPiTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  746. // 打卡记录
  747. List<PiTaskHistoryVO> piTaskHistoryVOS = tempPiTaskHistories.stream().map(piTaskHistory -> {
  748. // 获取打卡任务
  749. PiTask piTask = piTaskMap.get(piTaskHistory.getTaskId());
  750. // 获取结算信息
  751. SettleTaskRelaHistory settleTaskRelaHistory = settleTaskRelaHistoryMap.get(piTaskHistory.getTaskUniqueId());
  752. PiTaskHistoryVO piTaskHistoryVO = new PiTaskHistoryVO();
  753. if (Objects.nonNull(piTask)) {
  754. piTaskHistoryVO.setTaskName(piTask.getTaskName());
  755. piTaskHistoryVO.setPunchInMethod(piTask.getPunchInMethod());
  756. }
  757. if (Objects.nonNull(settleTaskRelaHistory)) {
  758. piTaskHistoryVO.setSettleResult(settleTaskRelaHistory.getSettleResult());
  759. piTaskHistoryVO.setSettlePoints(settleTaskRelaHistory.getSettlePoints());
  760. }
  761. piTaskHistoryVO.setTimeTrack(piTaskHistory.getTimeTrack());
  762. piTaskHistoryVO.setCountTrack(piTaskHistory.getCountTrack());
  763. piTaskHistoryVO.setPunchInResult(piTaskHistory.getPunchInResult());
  764. return piTaskHistoryVO;
  765. }).collect(Collectors.toList());
  766. // 设置打卡记录
  767. punchInHistoryVO.setPiTaskHistoryVOS(piTaskHistoryVOS);
  768. return punchInHistoryVO;
  769. }
  770. @Override
  771. @Cacheable(cacheNames = CacheNameConstant.TASK_LIST_FOR_USER, key = "#userId", condition = "#userId != null")
  772. public List<PiTask> getTaskListInCache(Long userId) {
  773. Assert.isNullInBusiness(userId, "用户ID不能为空");
  774. return piTaskService.getActiveTask(Arrays.asList(userId));
  775. }
  776. @Override
  777. @Cacheable(cacheNames = CacheNameConstant.TASK_HISTORY_LIST_FOR_USER, key = "#userId+'_'+#punchInDate", condition = "#userId != null && #punchInDate != null && !#punchInDate.isBlank()")
  778. public List<PiTaskHistory> getTaskHistoryListInCache(Long userId, String punchInDate) {
  779. Assert.isNullInBusiness(userId, "用户ID不能为空");
  780. Assert.isNullInBusiness(punchInDate, "打卡日期不能为空");
  781. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  782. piTaskHistoryQuery.setPunchInDate(punchInDate);
  783. piTaskHistoryQuery.setUserIds(Arrays.asList(userId));
  784. List<PiTaskHistory> piTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  785. return piTaskHistories;
  786. }
  787. @Override
  788. public void clearCache(Long userId, boolean deleteHistory, boolean deleteStat) {
  789. cacheManager.batchEvict(Arrays.asList(CacheNameConstant.TASK_LIST_FOR_USER, CacheNameConstant.TASK_LIST_VO), userId);
  790. if (deleteHistory) {
  791. cacheManager.batchEvictLike(Arrays.asList(CacheNameConstant.TASK_PUNCH_IN_HISTORY, CacheNameConstant.TASK_HISTORY_LIST_FOR_USER), String.valueOf(userId));
  792. }
  793. if (deleteStat) {
  794. cacheManager.batchEvictLike(Arrays.asList(CacheNameConstant.STAT_TASK), String.valueOf(userId));
  795. }
  796. }
  797. }