PunchInSettleManagerImpl.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. package com.punchsettle.server.service.manager.impl;
  2. import java.math.BigDecimal;
  3. import java.math.RoundingMode;
  4. import java.time.DayOfWeek;
  5. import java.time.LocalDate;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Objects;
  10. import java.util.Optional;
  11. import java.util.Set;
  12. import java.util.function.Function;
  13. import java.util.stream.Collectors;
  14. import com.punchsettle.server.atomic.ContinueTask;
  15. import com.punchsettle.server.atomic.entity.Account;
  16. import com.punchsettle.server.atomic.entity.AccountTransferHistory;
  17. import com.punchsettle.server.atomic.service.IAccountService;
  18. import com.punchsettle.server.atomic.service.IAccountTransferHistoryService;
  19. import com.punchsettle.server.constant.AccountCategoryEnum;
  20. import com.punchsettle.server.constant.VersionStatusEnum;
  21. import com.punchsettle.server.constant.TransferCategoryEnum;
  22. import com.punchsettle.server.pojo.punchIn.AccountQuery;
  23. import org.springframework.beans.BeanUtils;
  24. import org.springframework.beans.factory.annotation.Autowired;
  25. import org.springframework.stereotype.Component;
  26. import org.springframework.transaction.annotation.Transactional;
  27. import org.springframework.util.CollectionUtils;
  28. import com.punchsettle.server.atomic.entity.PiMultiTask;
  29. import com.punchsettle.server.atomic.entity.PiMultiTaskExt;
  30. import com.punchsettle.server.atomic.entity.PiMultiTaskHistory;
  31. import com.punchsettle.server.atomic.entity.PiMultiTaskRela;
  32. import com.punchsettle.server.atomic.entity.PiStatus;
  33. import com.punchsettle.server.atomic.entity.PiStatusHistory;
  34. import com.punchsettle.server.atomic.entity.PiTask;
  35. import com.punchsettle.server.atomic.entity.PiTaskExt;
  36. import com.punchsettle.server.atomic.entity.PiTaskHistory;
  37. import com.punchsettle.server.atomic.entity.SettlePointsHistory;
  38. import com.punchsettle.server.atomic.entity.StatPiTaskMonth;
  39. import com.punchsettle.server.atomic.entity.StatPiTaskWeek;
  40. import com.punchsettle.server.atomic.entity.User;
  41. import com.punchsettle.server.atomic.service.IPiMultiTaskExtService;
  42. import com.punchsettle.server.atomic.service.IPiMultiTaskHistoryService;
  43. import com.punchsettle.server.atomic.service.IPiMultiTaskRelaService;
  44. import com.punchsettle.server.atomic.service.IPiMultiTaskService;
  45. import com.punchsettle.server.atomic.service.IPiStatusHistoryService;
  46. import com.punchsettle.server.atomic.service.IPiStatusService;
  47. import com.punchsettle.server.atomic.service.IPiTaskExtService;
  48. import com.punchsettle.server.atomic.service.IPiTaskHistoryService;
  49. import com.punchsettle.server.atomic.service.IPiTaskService;
  50. import com.punchsettle.server.atomic.service.ISettlePointsHistoryService;
  51. import com.punchsettle.server.atomic.service.IStatPiTaskMonthService;
  52. import com.punchsettle.server.atomic.service.IStatPiTaskWeekService;
  53. import com.punchsettle.server.atomic.service.IUserService;
  54. import com.punchsettle.server.constant.ArchiveStatusEnum;
  55. import com.punchsettle.server.constant.ContinueStageEnum;
  56. import com.punchsettle.server.constant.ContinueStatusEnum;
  57. import com.punchsettle.server.constant.PunchInResultEnum;
  58. import com.punchsettle.server.constant.SettleResultEnum;
  59. import com.punchsettle.server.pojo.punchIn.PiMultiTaskExtQuery;
  60. import com.punchsettle.server.pojo.punchIn.PiMultiTaskHistoryQuery;
  61. import com.punchsettle.server.pojo.punchIn.PiMultiTaskQuery;
  62. import com.punchsettle.server.pojo.punchIn.PiStatusQuery;
  63. import com.punchsettle.server.pojo.punchIn.PiTaskData;
  64. import com.punchsettle.server.pojo.punchIn.PiTaskExtQuery;
  65. import com.punchsettle.server.pojo.punchIn.PiTaskHistoryQuery;
  66. import com.punchsettle.server.pojo.punchIn.PiTaskQuery;
  67. import com.punchsettle.server.pojo.punchIn.PunchInSettleData;
  68. import com.punchsettle.server.service.manager.ICalendarManager;
  69. import com.punchsettle.server.service.manager.IPunchInCoreManager;
  70. import com.punchsettle.server.service.manager.IPunchInSettleManager;
  71. import com.punchsettle.server.service.manager.ISettleManager;
  72. import com.punchsettle.server.utiis.DateUtils;
  73. import lombok.extern.slf4j.Slf4j;
  74. /**
  75. * @author tyuio
  76. * @version 1.0.0
  77. * @date 2025/4/18 16:39
  78. * @description 打卡结算服务类
  79. */
  80. @Slf4j
  81. @Component
  82. public class PunchInSettleManagerImpl implements IPunchInSettleManager {
  83. /**
  84. * 默认数值100
  85. */
  86. private static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
  87. /**
  88. * 默认百分比 0.00%
  89. */
  90. private static final BigDecimal ZERO_RATE = new BigDecimal("0.00");
  91. @Autowired
  92. private IPiTaskService piTaskService;
  93. @Autowired
  94. private IPiTaskExtService piTaskExtService;
  95. @Autowired
  96. private IPiTaskHistoryService piTaskHistoryService;
  97. @Autowired
  98. private IStatPiTaskMonthService statPiTaskMonthService;
  99. @Autowired
  100. private IStatPiTaskWeekService statPiTaskWeekService;
  101. @Autowired
  102. private IPiStatusService piStatusService;
  103. @Autowired
  104. private IPiStatusHistoryService piStatusHistoryService;
  105. @Autowired
  106. private IPiMultiTaskService piMultiTaskService;
  107. @Autowired
  108. private IPiMultiTaskExtService piMultiTaskExtService;
  109. @Autowired
  110. private IPiMultiTaskHistoryService piMultiTaskHistoryService;
  111. @Autowired
  112. private IPiMultiTaskRelaService piMultiTaskRelaService;
  113. @Autowired
  114. private ICalendarManager calendarManager;
  115. @Autowired
  116. private IUserService userService;
  117. @Autowired
  118. private ISettlePointsHistoryService settlePointsHistoryService;
  119. @Autowired
  120. private IPunchInCoreManager punchInManager;
  121. @Autowired
  122. private ISettleManager settleManager;
  123. @Autowired
  124. private IAccountService accountService;
  125. @Autowired
  126. private IAccountTransferHistoryService accountTransferHistoryService;
  127. @Override
  128. @Transactional(rollbackFor = Exception.class)
  129. public void punchInSettle(List<Long> userIds, String punchInDateStr, Long settleTaskHistoryId) {
  130. // 打卡日期
  131. LocalDate punchInDate = LocalDate.parse(punchInDateStr);
  132. // 是否周一
  133. boolean isMonday = DayOfWeek.MONDAY.equals(punchInDate.getDayOfWeek());
  134. // 是否周日
  135. boolean isSunday = DayOfWeek.SUNDAY.equals(punchInDate.getDayOfWeek());
  136. // 是否打卡所在月第一天
  137. boolean isFirstDayOfMonth = punchInDate.getDayOfMonth() == 1;
  138. // 是否打卡所在月最后一天
  139. boolean isLastDayOfMonth = punchInDate.getDayOfMonth() == punchInDate.lengthOfMonth();
  140. // 周期数
  141. String yearWeek = DateUtils.getYearWeek(punchInDate);
  142. // 月份数
  143. String yearMonth = DateUtils.getYearMonth(punchInDate);
  144. // 打卡任务状态记录 新增列表
  145. List<PiStatusHistory> addPiStatusHistories = new ArrayList<>();
  146. // 打卡任务周统计 新增列表
  147. List<StatPiTaskWeek> addStatPiTaskWeekList = new ArrayList<>();
  148. // 打卡任务月统计 新增列表
  149. List<StatPiTaskMonth> addStatPiTaskMonthList = new ArrayList<>();
  150. // 结算积分记录 新增列表
  151. List<SettlePointsHistory> addSettlePointsHistories = new ArrayList<>();
  152. // 转张记录 新增列表
  153. List<AccountTransferHistory> addAccountTransferHistories = new ArrayList<>();
  154. // 账户 更新列表
  155. List<Account> updateAccountList = new ArrayList<>();
  156. // 用户信息 更新列表
  157. List<User> updateUserList = new ArrayList<>();
  158. // 打卡状态 更新列表
  159. List<PiStatus> updatePiStatusList = new ArrayList<>();
  160. // 打卡任务记录 更新列表
  161. List<PiTaskHistory> updatePunchInTaskHistories = new ArrayList<>();
  162. // 打卡多任务记录 更新列表
  163. List<PiMultiTaskHistory> updatePunchInMultiTaskHistories = new ArrayList<>();
  164. // 获取结算数据
  165. List<PunchInSettleData> punchInSettleData = getPunchInSettleData(userIds, punchInDateStr);
  166. // 打卡结算逻辑
  167. for (PunchInSettleData punchInSettle : punchInSettleData) {
  168. // 结算总积分
  169. int settlePoints = 0;
  170. // 单任务处理部分
  171. for (PiTaskData piTaskData : punchInSettle.getPiTaskDataList()) {
  172. // 打卡任务
  173. PiTask piTask = piTaskData.getPiTask();
  174. // 打卡任务打卡记录
  175. PiTaskHistory piTaskHistory = piTaskData.getPiTaskHistory();
  176. // 打卡任务状态
  177. PiStatus piStatus = piTaskData.getPiStatus();
  178. // 打卡任务拖扎信息
  179. List<PiTaskExt> piTaskExtList = piTaskData.getPiTaskExtList();
  180. // 状态变更前记录历史状态
  181. PiStatusHistory piStatusHistoryInTask = new PiStatusHistory();
  182. BeanUtils.copyProperties(piStatus, piStatusHistoryInTask);
  183. addPiStatusHistories.add(piStatusHistoryInTask);
  184. // 打卡记录
  185. PiTaskHistory updatePiTaskHistory = new PiTaskHistory();
  186. updatePiTaskHistory.setId(piTaskHistory.getId());
  187. // 记录当前状态数据
  188. updatePiTaskHistory.setTaskContinueStatus(piStatus.getTaskContinueStatus());
  189. updatePiTaskHistory.setTaskContinueDay(piStatus.getTaskContinueDay());
  190. updatePiTaskHistory.setContinueStage(piStatus.getContinueStage());
  191. updatePiTaskHistory.setStageStartDate(piStatus.getStageStartDate());
  192. updatePiTaskHistory.setStageEndDate(piStatus.getStageEndDate());
  193. updatePiTaskHistory.setContinueInterruptedCount(piStatus.getContinueInterruptedCount());
  194. // 判断打卡结果
  195. PunchInResultEnum punchInResult = punchInManager.judgePunchInResultInTask(piTask, piTaskHistory);
  196. punchInSettle.getPiMultiTaskHistory().setPunchInResult(punchInResult);
  197. updatePiTaskHistory.setPunchInResult(punchInResult);
  198. // 更新打卡状态
  199. piStatus.setStatusDate(punchInDateStr);
  200. refreshPiStatus(piStatus, punchInResult, piTask, piTaskHistory, punchInDate, isMonday, isFirstDayOfMonth, yearWeek, yearMonth);
  201. // 计算积分,要先刷新打卡状态
  202. int taskPoints = settleManager.calculatePointsInTask(piTask, piTaskExtList, piTaskHistory, piStatus);
  203. settlePoints += taskPoints;
  204. // 更新打卡状态
  205. piStatus.setPointsInWeek(piStatus.getPointsInWeek() + taskPoints);
  206. piStatus.setPointsInMonth(piStatus.getPointsInMonth() + taskPoints);
  207. // 打卡记录 更新
  208. updatePiTaskHistory.setSettlePoints(taskPoints);
  209. updatePiTaskHistory.setSettleResult(SettleResultEnum.SETTLED);
  210. updatePiTaskHistory.setSettlePiTaskId(piTask.getId());
  211. updatePiTaskHistory.setSettleTaskHistoryId(settleTaskHistoryId);
  212. updatePiStatusList.add(piStatus);
  213. updatePunchInTaskHistories.add(updatePiTaskHistory);
  214. // 周日构建周统计数据
  215. if (isSunday) {
  216. StatPiTaskWeek statPiTaskWeek = buildStatPiTaskWeek(punchInSettle.getUserId(), piTask, piStatus);
  217. addStatPiTaskWeekList.add(statPiTaskWeek);
  218. }
  219. // 月末构建月统计数据
  220. if (isLastDayOfMonth) {
  221. StatPiTaskMonth statPiTaskMonth = buildStatPiTaskMonth(punchInSettle.getUserId(), piTask, piStatus);
  222. addStatPiTaskMonthList.add(statPiTaskMonth);
  223. }
  224. }
  225. // 打卡多任务
  226. PiMultiTask piMultiTask = punchInSettle.getPiMultiTask();
  227. // 打卡多任务打卡记录
  228. PiMultiTaskHistory piMultiTaskHistory = punchInSettle.getPiMultiTaskHistory();
  229. // 打卡多任务状态
  230. PiStatus piStatusInMultiTask = punchInSettle.getPiStatus();
  231. // 状态变更前记录历史状态
  232. PiStatusHistory piStatusHistoryInMultiTask = new PiStatusHistory();
  233. BeanUtils.copyProperties(piStatusInMultiTask, piStatusHistoryInMultiTask);
  234. addPiStatusHistories.add(piStatusHistoryInMultiTask);
  235. // 多任务处理部分
  236. PiMultiTaskHistory updatePiMultiTaskHistory = new PiMultiTaskHistory();
  237. // 判断多任务打卡状态并设置
  238. PunchInResultEnum punchInResult = punchInManager.judgePunchInResultInMultiTask(piMultiTask, punchInSettle.getRelaPunchInTaskHistories());
  239. piMultiTaskHistory.setPunchInResult(punchInResult);
  240. updatePiMultiTaskHistory.setPunchInResult(punchInResult);
  241. // 这里少了连续阶段的判断
  242. // 设置多任务统计时间
  243. piStatusInMultiTask.setStatusDate(punchInDateStr);
  244. // 刷新打卡状态中的连续任务数据
  245. refreshContinueData(piStatusInMultiTask, punchInResult, piMultiTask, punchInDate);
  246. // 计算多任务积分
  247. int multiTaskPoints = settleManager.calculatePointsInMultiTask(piMultiTask,
  248. punchInSettle.getPiMultiTaskExtList(), piMultiTaskHistory,
  249. piStatusInMultiTask);
  250. settlePoints += multiTaskPoints;
  251. // 设置打卡记录表信息
  252. updatePiMultiTaskHistory.setSettlePoints(multiTaskPoints);
  253. updatePiMultiTaskHistory.setSettleResult(SettleResultEnum.SETTLED);
  254. updatePiMultiTaskHistory.setSettleTaskHistoryId(settleTaskHistoryId);
  255. updatePunchInMultiTaskHistories.add(updatePiMultiTaskHistory);
  256. // 获取并设置用户信息
  257. User user = punchInSettle.getUser();
  258. Integer beforeTotalPoints = Optional.ofNullable(user.getTotalPoints()).orElse(0);
  259. Integer beforeUnusedPoints = Optional.ofNullable(user.getUnusedPoints()).orElse(0);
  260. User updateUser = new User();
  261. updateUser.setId(user.getId());
  262. updateUser.setTotalPoints(beforeTotalPoints + settlePoints);
  263. updateUser.setUnusedPoints(beforeUnusedPoints + settlePoints);
  264. updateUserList.add(user);
  265. // 更新账户信息
  266. Account account = punchInSettle.getAccount();
  267. Integer beforeAccountPoints = Optional.ofNullable(account.getPoints()).orElse(0);
  268. account.setPoints(beforeAccountPoints + settlePoints);
  269. updateAccountList.add(account);
  270. // 增加账户转账记录
  271. AccountTransferHistory accountTransferHistory = new AccountTransferHistory();
  272. accountTransferHistory.setTransferPoints(settlePoints);
  273. accountTransferHistory.setTransferCategory(TransferCategoryEnum.SETTLE);
  274. accountTransferHistory.setRecipientAccountId(account.getId());
  275. accountTransferHistory.setRaPointsAfterTransfer(beforeAccountPoints);
  276. accountTransferHistory.setRaPointsAfterTransfer(account.getPoints());
  277. addAccountTransferHistories.add(accountTransferHistory);
  278. // 设置结算积分记录
  279. SettlePointsHistory addSettlePointsHistory = new SettlePointsHistory();
  280. addSettlePointsHistory.setSettleTaskHistoryId(settleTaskHistoryId);
  281. addSettlePointsHistory.setUserId(user.getId());
  282. addSettlePointsHistory.setSettleDate(punchInDateStr);
  283. addSettlePointsHistory.setSettlePoints(settlePoints);
  284. addSettlePointsHistory.setBeforeSettlePoints(beforeTotalPoints);
  285. addSettlePointsHistory.setAfterSettlePoints(beforeTotalPoints + settlePoints);
  286. addSettlePointsHistories.add(addSettlePointsHistory);
  287. }
  288. // 数据入库
  289. if (!CollectionUtils.isEmpty(updateUserList)) {
  290. userService.batchUpdateUser(updateUserList);
  291. }
  292. if (!CollectionUtils.isEmpty(addSettlePointsHistories)) {
  293. settlePointsHistoryService.insertList(addSettlePointsHistories);
  294. }
  295. if (!CollectionUtils.isEmpty(addPiStatusHistories)) {
  296. piStatusHistoryService.insertList(addPiStatusHistories);
  297. }
  298. if (!CollectionUtils.isEmpty(addStatPiTaskWeekList)) {
  299. statPiTaskWeekService.insertList(addStatPiTaskWeekList);
  300. }
  301. if (!CollectionUtils.isEmpty(addStatPiTaskMonthList)) {
  302. statPiTaskMonthService.insertList(addStatPiTaskMonthList);
  303. }
  304. if (!CollectionUtils.isEmpty(updatePiStatusList)) {
  305. piStatusService.batchUpdate(updatePiStatusList);
  306. }
  307. if (!CollectionUtils.isEmpty(updatePunchInTaskHistories)) {
  308. piTaskHistoryService.batchUpdate(updatePunchInTaskHistories);
  309. }
  310. if (!CollectionUtils.isEmpty(updatePunchInMultiTaskHistories)) {
  311. piMultiTaskHistoryService.batchUpdate(updatePunchInMultiTaskHistories);
  312. }
  313. if (!CollectionUtils.isEmpty(updateAccountList)) {
  314. accountService.batchUpdate(updateAccountList);
  315. }
  316. if (!CollectionUtils.isEmpty(addAccountTransferHistories)) {
  317. accountTransferHistoryService.batchInsert(addAccountTransferHistories);
  318. }
  319. }
  320. /**
  321. * 获取用户ID-打卡结算数据关联
  322. *
  323. * @param userIds 设计的用户
  324. * @param punchInDateStr 打卡日期
  325. * @return
  326. */
  327. private List<PunchInSettleData> getPunchInSettleData(List<Long> userIds, String punchInDateStr) {
  328. // 用户信息, 用户ID-用户信息关联
  329. List<User> users = userService.listByIds(userIds);
  330. Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, Function.identity(), (key1, key2) -> key1));
  331. // 获取账户信息,用户ID-账户信息关联
  332. AccountQuery accountQuery = new AccountQuery();
  333. accountQuery.setAccountCategory(AccountCategoryEnum.BASIC);
  334. accountQuery.setUserIds(userIds);
  335. List<Account> accounts = accountService.getAccountByCondition(accountQuery);
  336. Map<Long, Account> accountMap = accounts.stream().collect(Collectors.toMap(Account::getUserId, Function.identity(), (key1, key2) -> key1));
  337. // 获取多任务信息,用户ID-多任务关联
  338. PiMultiTaskQuery piMultiTaskQuery = new PiMultiTaskQuery();
  339. piMultiTaskQuery.setMultiTaskTaskStatus(VersionStatusEnum.ACTIVE);
  340. piMultiTaskQuery.setUserIds(userIds);
  341. List<PiMultiTask> piMultiTasks = piMultiTaskService.queryByCondition(piMultiTaskQuery);
  342. Map<Long, PiMultiTask> piMultiTaskMap = piMultiTasks.stream()
  343. .collect(Collectors.toMap(PiMultiTask::getUserId, Function.identity(), (key1, key2) -> key1));
  344. // 多任务ID
  345. Set<Long> multiTaskIds = piMultiTasks.stream().map(PiMultiTask::getId).collect(Collectors.toSet());
  346. // 多任务唯一ID
  347. Set<Long> multiTaskUniqueIds = piMultiTasks.stream().map(PiMultiTask::getUniqueId).collect(Collectors.toSet());
  348. // 获取多任务拓展信息,多任务ID-多任务拓展关联
  349. PiMultiTaskExtQuery piMultiTaskExtQuery = new PiMultiTaskExtQuery();
  350. piMultiTaskExtQuery.setMultiTaskIds(multiTaskIds);
  351. List<PiMultiTaskExt> piMultiTaskExtList = piMultiTaskExtService.queryByCondition(piMultiTaskExtQuery);
  352. Map<Long, List<PiMultiTaskExt>> piMultiTaskExtMap =
  353. piMultiTaskExtList.stream().collect(Collectors.groupingBy(PiMultiTaskExt::getMultiTaskId));
  354. // 获取多任务打卡历史信息,多任务唯一ID-多任务打卡历史关联
  355. PiMultiTaskHistoryQuery piMultiTaskHistoryQuery = new PiMultiTaskHistoryQuery();
  356. piMultiTaskHistoryQuery.setPunchInMultiTaskUniqueIds(multiTaskUniqueIds);
  357. piMultiTaskHistoryQuery.setPunchInDate(punchInDateStr);
  358. List<PiMultiTaskHistory> piMultiTaskHistories =
  359. piMultiTaskHistoryService.queryByCondition(piMultiTaskHistoryQuery);
  360. Map<Long, PiMultiTaskHistory> piMultiTaskHistoryMap = piMultiTaskHistories.stream().collect(
  361. Collectors.toMap(PiMultiTaskHistory::getMultiTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  362. // 打卡任务状态
  363. PiStatusQuery piStatusQuery = new PiStatusQuery();
  364. piStatusQuery.setUserIds(userIds);
  365. List<PiStatus> piStatusList = piStatusService.queryByCondition(piStatusQuery);
  366. // 打卡任务状态,多任务唯一ID-打卡状态关联
  367. Map<Long, PiStatus> piStatusMapInMultiTask = piStatusList.stream().filter(v -> Objects.nonNull(v.getMultiTaskUniqueId())).collect(Collectors.toMap(PiStatus::getMultiTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  368. // 打卡任务状态,任务唯一ID-打卡状态关联
  369. Map<Long, PiStatus> piStatusMapInTask = piStatusList.stream().filter(v -> Objects.nonNull(v.getTaskUniqueId())).collect(Collectors.toMap(PiStatus::getTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  370. // 多任务关联打卡任务,多任务ID-多任务关联打卡任务关联
  371. List<PiMultiTaskRela> piMultiTaskRelaList = piMultiTaskRelaService.queryByMultiTaskId(multiTaskIds);
  372. Map<Long, List<PiMultiTaskRela>> piMultiTaskRelaMap =
  373. piMultiTaskRelaList.stream().collect(Collectors.groupingBy(PiMultiTaskRela::getMultiTaskId));
  374. // 获取打卡任务信息,打卡任务唯一ID-打卡任务关联
  375. PiTaskQuery piTaskQuery = new PiTaskQuery();
  376. piTaskQuery.setTaskStatus(VersionStatusEnum.ACTIVE);
  377. piTaskQuery.setArchiveStatus(ArchiveStatusEnum.ACTIVE);
  378. piTaskQuery.setUserIds(userIds);
  379. List<PiTask> piTasks = piTaskService.queryByCondition(piTaskQuery);
  380. Map<Long, List<PiTask>> piTaskMap =
  381. piTasks.stream().collect(Collectors.groupingBy(PiTask::getCreatedBy));
  382. // 获取打卡记录,任务唯一 ID-打卡记录关联
  383. PiTaskHistoryQuery piTaskHistoryQuery = new PiTaskHistoryQuery();
  384. piTaskHistoryQuery.setPunchInDate(punchInDateStr);
  385. piTaskHistoryQuery.setUserIds(userIds);
  386. List<PiTaskHistory> punchInTaskHistories = piTaskHistoryService.queryByCondition(piTaskHistoryQuery);
  387. Map<Long, PiTaskHistory> piTaskHistoryMap =
  388. punchInTaskHistories.stream().collect(Collectors.toMap(PiTaskHistory::getTaskUniqueId, Function.identity(), (key1, key2) -> key1));
  389. // 打卡任务ID
  390. Set<Long> punchInTaskIds = piTasks.stream().map(PiTask::getId).collect(Collectors.toSet());
  391. // 获取打卡任务拓展表,打卡任务ID-打卡任务拓展关联
  392. PiTaskExtQuery piTaskExtQuery = new PiTaskExtQuery();
  393. piTaskExtQuery.setPunchInTaskIds(punchInTaskIds);
  394. List<PiTaskExt> piTaskExtList = piTaskExtService.queryByCondition(piTaskExtQuery);
  395. Map<Long, List<PiTaskExt>> piTaskExtGroupList =
  396. piTaskExtList.stream().collect(Collectors.groupingBy(PiTaskExt::getTaskId));
  397. return userIds.stream().map(userId -> {
  398. PunchInSettleData punchInSettleData = new PunchInSettleData();
  399. // 设置用户ID
  400. punchInSettleData.setUserId(userId);
  401. // 获取并设置用户信息
  402. User user = userMap.get(userId);
  403. punchInSettleData.setUser(user);
  404. // 获取并设置账户信息
  405. Account account = accountMap.get(userId);
  406. punchInSettleData.setAccount(account);
  407. // TODO 获取并设置多任务打卡信息,这个不可能为空,为空则用户初始注册时创建时有问题
  408. PiMultiTask piMultiTask = piMultiTaskMap.get(userId);
  409. punchInSettleData.setPiMultiTask(piMultiTask);
  410. // 获取并设置对应的拓展信息
  411. List<PiMultiTaskExt> tempPiMultiTaskExtList = piMultiTaskExtMap.get(piMultiTask.getId());
  412. punchInSettleData.setPiMultiTaskExtList(tempPiMultiTaskExtList);
  413. // 获取并设置对应的多任务打卡记录
  414. PiMultiTaskHistory piMultiTaskHistory = piMultiTaskHistoryMap.get(piMultiTask.getUniqueId());
  415. punchInSettleData.setPiMultiTaskHistory(piMultiTaskHistory);
  416. // 获取多任务与打卡任务的关联信息
  417. List<PiMultiTaskRela> tempPiMultiTaskRelaList = piMultiTaskRelaMap.get(piMultiTask.getId());
  418. Set<Long> relaContainTaskIdList =
  419. tempPiMultiTaskRelaList.stream().map(PiMultiTaskRela::getTaskId).collect(Collectors.toSet());
  420. // 获取并设置多任务的状态信息
  421. PiStatus piStatusInMultiTask = piStatusMapInMultiTask.get(piMultiTask.getUniqueId());
  422. punchInSettleData.setPiStatus(piStatusInMultiTask);
  423. // 获取任务列表
  424. List<PiTask> piTasksList = piTaskMap.get(userId);
  425. // 打卡任务数据
  426. List<PiTaskData> piTaskDataList = new ArrayList<>(piTasksList.size());
  427. // 关联的打卡任务记录
  428. List<PiTaskHistory> relaPunchInTaskHistories = new ArrayList<>(relaContainTaskIdList.size());
  429. for (PiTask piTask : piTasksList) {
  430. PiTaskData piTaskData = new PiTaskData();
  431. piTaskData.setPiTask(piTask);
  432. // 获取并设置打卡任务
  433. PiTaskHistory piTaskHistory = piTaskHistoryMap.get(piTask.getUniqueId());
  434. piTaskData.setPiTaskHistory(piTaskHistory);
  435. //设置多任务与打卡记录的关联信息
  436. if (relaContainTaskIdList.contains(piTask.getId())) {
  437. relaPunchInTaskHistories.add(piTaskHistory);
  438. }
  439. // 获取并设置拓展信息
  440. List<PiTaskExt> tempPiTaskExtList = piTaskExtGroupList.get(piTask.getId());
  441. piTaskData.setPiTaskExtList(tempPiTaskExtList);
  442. // 获取并设置任务状态
  443. PiStatus piStatusInTask = piStatusMapInTask.get(piTask.getUniqueId());
  444. piTaskData.setPiStatus(piStatusInTask);
  445. piTaskDataList.add(piTaskData);
  446. }
  447. punchInSettleData.setPiTaskDataList(piTaskDataList);
  448. punchInSettleData.setRelaPunchInTaskHistories(relaPunchInTaskHistories);
  449. return punchInSettleData;
  450. }).collect(Collectors.toList());
  451. }
  452. /**
  453. * 更新打卡任务状态
  454. * @param piStatus 打卡任务状态
  455. * @param punchInResult 打卡结果
  456. * @param piTask 打卡任务
  457. * @param piTaskHistory 打卡任务记录
  458. * @param punchInDate 打卡日期
  459. * @param isMonday 是否周一
  460. * @param isFirstDayOfMonth 是否本月第一天
  461. * @param yearWeek 周数
  462. * @param yearMonth 月份数
  463. */
  464. private void refreshPiStatus(PiStatus piStatus, PunchInResultEnum punchInResult, PiTask piTask, PiTaskHistory piTaskHistory, LocalDate punchInDate, boolean isMonday, boolean isFirstDayOfMonth, String yearWeek, String yearMonth) {
  465. // 刷新打卡状态中的连续任务数据
  466. refreshContinueData(piStatus, punchInResult, piTask, punchInDate);
  467. // 周一或月初
  468. if (isMonday || isFirstDayOfMonth) {
  469. piStatus.setRepeatCategory(piTask.getRepeatCategory());
  470. piStatus.setRepeatCustomDay(piTask.getRepeatCustomDay());
  471. }
  472. // 周一
  473. if (isMonday) {
  474. piStatus.setStatTimeInWeek(yearWeek);
  475. // 设置本周总打卡次数
  476. int PunchInTotalCountInWeek = punchInManager.getPunchInTotalCountInRange(piTask.getRepeatCategory(),
  477. piTask.getRepeatCustomDay(), DateUtils.getFirstDayOfWeek(punchInDate),
  478. DateUtils.getLastDayOfWeek(punchInDate));
  479. piStatus.setPunchInTotalCountInWeek(PunchInTotalCountInWeek);
  480. // 重置数据
  481. piStatus.setPunchInCountInWeek(0);
  482. piStatus.setPunchInDoneCountInWeek(0);
  483. piStatus.setRepeatPrevTotalCountInWeek(0);
  484. piStatus.setRepeatStartDateInWeek(punchInDate);
  485. }
  486. // 打卡日所在月的第一天
  487. if (isFirstDayOfMonth) {
  488. piStatus.setStatTimeInMonth(yearMonth);
  489. // 设置本月总打卡次数
  490. int PunchInTotalCountInMonth = punchInManager.getPunchInTotalCountInRange(piTask.getRepeatCategory(),
  491. piTask.getRepeatCustomDay(), DateUtils.getFirstDayOfMonth(punchInDate),
  492. DateUtils.getLastDayOfMonth(punchInDate));
  493. piStatus.setPunchInTotalCountInMonth(PunchInTotalCountInMonth);
  494. // 重置数据
  495. piStatus.setPunchInCountInMonth(0);
  496. piStatus.setPunchInDoneCountInMonth(0);
  497. piStatus.setRepeatPrevTotalCountInMonth(0);
  498. piStatus.setRepeatStartDateInMonth(punchInDate);
  499. }
  500. // 重复类型发生变换,总打卡次数=历史真实发生的总打卡次数+变换前发生的总打卡次数+变换后可能的总打卡次数
  501. if (!piStatus.getRepeatCategory().equals(piTask.getRepeatCategory())) {
  502. // 历史真实发生的总打卡次数(周)
  503. Integer oldPrevTotalCountInWeek = Optional.ofNullable(piStatus.getRepeatPrevTotalCountInWeek()).orElse(0);
  504. // 变换前发生的总打卡次数(周)
  505. int prevTotalCountInWeek = punchInManager.getPunchInTotalCountInRange(piStatus.getRepeatCategory(),
  506. piStatus.getRepeatCustomDay(), piStatus.getRepeatStartDateInWeek(),
  507. punchInDate.minusDays(1));
  508. // 变换后可能的总打卡次数(周)
  509. int punchInTotalCountInWeek = punchInManager.getPunchInTotalCountInRange(piTask.getRepeatCategory(), piTask.getRepeatCustomDay(), punchInDate, DateUtils.getLastDayOfWeek(punchInDate));
  510. // 历史真实发生的总打卡次数(月)
  511. Integer oldPrevTotalCountInMonth = Optional.ofNullable(piStatus.getRepeatPrevTotalCountInMonth()).orElse(0);
  512. // 变换前发生的总打卡次数(月)
  513. int prevTotalCountInMonth = punchInManager.getPunchInTotalCountInRange(piStatus.getRepeatCategory(),
  514. piStatus.getRepeatCustomDay(), piStatus.getRepeatStartDateInMonth(),
  515. punchInDate.minusDays(1));
  516. // 变换后可能的总打卡次数(月 )
  517. int punchInTotalCountInMonth = punchInManager.getPunchInTotalCountInRange(piTask.getRepeatCategory(), piTask.getRepeatCustomDay(), punchInDate, DateUtils.getLastDayOfMonth(punchInDate));
  518. // 设置实时数据
  519. piStatus.setRepeatPrevTotalCountInWeek(prevTotalCountInWeek + oldPrevTotalCountInWeek);
  520. piStatus.setPunchInTotalCountInWeek(piStatus.getRepeatPrevTotalCountInWeek() + punchInTotalCountInWeek);
  521. piStatus.setRepeatPrevTotalCountInMonth(prevTotalCountInMonth + oldPrevTotalCountInMonth);
  522. piStatus.setPunchInTotalCountInMonth(piStatus.getRepeatPrevTotalCountInMonth() + punchInTotalCountInMonth);
  523. }
  524. // 存在打卡记录则已打卡数加1
  525. if (Objects.nonNull(piTaskHistory)) {
  526. piStatus.setPunchInCountInWeek(piStatus.getPunchInCountInWeek() + 1);
  527. piStatus.setPunchInCountInMonth(piStatus.getPunchInCountInMonth() + 1);
  528. }
  529. // 完成打卡则完成打卡数记录加一
  530. if (PunchInResultEnum.DONE.equals(punchInResult)) {
  531. piStatus.setPunchInDoneCountInWeek(piStatus.getPunchInDoneCountInWeek() + 1);
  532. piStatus.setPunchInDoneCountInMonth(piStatus.getPunchInDoneCountInMonth() + 1);
  533. }
  534. }
  535. /**
  536. * 刷新打卡状态中的连续任务数据
  537. * @param piStatus
  538. * @param punchInResult
  539. * @param continueTask
  540. * @param punchInDate
  541. */
  542. private void refreshContinueData(PiStatus piStatus, PunchInResultEnum punchInResult, ContinueTask continueTask, LocalDate punchInDate) {
  543. // 设置任务连续状态
  544. ContinueStatusEnum oldTaskContinueStatus = piStatus.getTaskContinueStatus();
  545. piStatus.setTaskContinueStatus(punchInResult.equals(PunchInResultEnum.DONE)
  546. ? ContinueStatusEnum.CONTINUE : ContinueStatusEnum.INTERRUPTED);
  547. piStatus.setTaskContinueDay(oldTaskContinueStatus.equals(piStatus.getTaskContinueStatus())
  548. ? piStatus.getTaskContinueDay() + 1 : 1);
  549. // 旧的连续阶段
  550. ContinueStageEnum oldContinueStage = piStatus.getContinueStage();
  551. // 判断当前处于的连续阶段
  552. ContinueStageEnum continueStageResult =
  553. punchInManager.judgeContinueStage(continueTask, punchInResult, piStatus, punchInDate);
  554. piStatus.setContinueStage(continueStageResult);
  555. // 下面3种情况不处理:宽限期 变 宽限期;正常打卡期 变 正常打卡期;惩罚期 变 惩罚期;
  556. // 宽限期 变 正常打卡期,则设置阶段开始时间,重置中断次数
  557. if (ContinueStageEnum.GRACE_STAGE.equals(oldContinueStage)
  558. && ContinueStageEnum.NORMAL_STAGE.equals(continueStageResult)) {
  559. piStatus.setStageStartDate(punchInDate);
  560. piStatus.setContinueInterruptedCount(0);
  561. }
  562. // 如果正常打卡期 变 惩罚期,则设置阶段开始时间和阶段结束时间,增加中断次数
  563. if (ContinueStageEnum.NORMAL_STAGE.equals(oldContinueStage)
  564. && ContinueStageEnum.PENALTY_STAGE.equals(continueStageResult)) {
  565. piStatus.setStageStartDate(punchInDate);
  566. piStatus.setStageEndDate(punchInDate.plusDays(continueTask.getPenaltyDay()));
  567. piStatus.setContinueInterruptedCount(piStatus.getContinueInterruptedCount() + 1);
  568. }
  569. // 如果惩罚期 变 正常打卡期,则设置阶段开始时间
  570. if (ContinueStageEnum.PENALTY_STAGE.equals(oldContinueStage)
  571. && ContinueStageEnum.NORMAL_STAGE.equals(continueStageResult)) {
  572. piStatus.setStageStartDate(punchInDate);
  573. }
  574. }
  575. /**
  576. * 构建周统计数据
  577. * @param userId 用户ID
  578. * @param piTask 打卡任务
  579. * @param piStatus 打卡任务状态
  580. * @return
  581. */
  582. private static StatPiTaskWeek buildStatPiTaskWeek(Long userId, PiTask piTask, PiStatus piStatus) {
  583. StatPiTaskWeek statPiTaskWeek = new StatPiTaskWeek();
  584. statPiTaskWeek.setUserId(userId);
  585. statPiTaskWeek.setTaskUniqueId(piTask.getUniqueId());
  586. statPiTaskWeek.setStatTime(piStatus.getStatTimeInWeek());
  587. statPiTaskWeek.setPunchInTotalCount(piStatus.getPunchInTotalCountInWeek());
  588. statPiTaskWeek.setPunchInCount(piStatus.getPunchInCountInWeek());
  589. statPiTaskWeek.setPunchInDoneCount(piStatus.getPunchInDoneCountInWeek());
  590. statPiTaskWeek.setPoints(piStatus.getPointsInWeek());
  591. Integer punchInTotalCount = Optional.ofNullable(piStatus.getPunchInTotalCountInWeek()).orElse(0);
  592. if (punchInTotalCount == 0) {
  593. statPiTaskWeek.setPunchInRate(ZERO_RATE);
  594. statPiTaskWeek.setPunchInDoneRate(ZERO_RATE);
  595. } else {
  596. Integer punchInCount = Optional.ofNullable(piStatus.getPunchInCountInWeek()).orElse(0);
  597. Integer punchInDoneCount = Optional.ofNullable(piStatus.getPunchInDoneCountInWeek()).orElse(0);
  598. BigDecimal totalCount = new BigDecimal(punchInTotalCount);
  599. BigDecimal punchInRate = BigDecimal.valueOf(punchInCount).divide(totalCount, 2, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  600. statPiTaskWeek.setPunchInRate(punchInRate);
  601. BigDecimal punchInDoneRate = BigDecimal.valueOf(punchInDoneCount).divide(totalCount, 2, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  602. statPiTaskWeek.setPunchInDoneRate(punchInDoneRate);
  603. }
  604. return statPiTaskWeek;
  605. }
  606. /**
  607. * 构建月统计数据
  608. * @param userId 用户ID
  609. * @param piTask 打卡任务
  610. * @param piStatus 打卡任务状态
  611. * @return
  612. */
  613. private static StatPiTaskMonth buildStatPiTaskMonth(Long userId, PiTask piTask, PiStatus piStatus) {
  614. StatPiTaskMonth statPiTaskMonth = new StatPiTaskMonth();
  615. statPiTaskMonth.setUserId(userId);
  616. statPiTaskMonth.setTaskUniqueId(piTask.getUniqueId());
  617. statPiTaskMonth.setStatTime(piStatus.getStatTimeInMonth());
  618. statPiTaskMonth.setPunchInTotalCount(piStatus.getPunchInTotalCountInMonth());
  619. statPiTaskMonth.setPunchInCount(piStatus.getPunchInCountInMonth());
  620. statPiTaskMonth.setPunchInDoneCount(piStatus.getPunchInDoneCountInMonth());
  621. statPiTaskMonth.setPoints(piStatus.getPointsInMonth());
  622. Integer punchInTotalCount = Optional.ofNullable(piStatus.getPunchInTotalCountInMonth()).orElse(0);
  623. if (punchInTotalCount == 0) {
  624. statPiTaskMonth.setPunchInRate(ZERO_RATE);
  625. statPiTaskMonth.setPunchInDoneRate(ZERO_RATE);
  626. } else {
  627. Integer punchInCount = Optional.ofNullable(piStatus.getPunchInCountInMonth()).orElse(0);
  628. Integer punchInDoneCount = Optional.ofNullable(piStatus.getPunchInDoneCountInMonth()).orElse(0);
  629. BigDecimal totalCount = new BigDecimal(punchInTotalCount);
  630. BigDecimal punchInRate = BigDecimal.valueOf(punchInCount).divide(totalCount, 2, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  631. statPiTaskMonth.setPunchInRate(punchInRate);
  632. BigDecimal punchInDoneRate = BigDecimal.valueOf(punchInDoneCount).divide(totalCount, 2, RoundingMode.HALF_UP).multiply(ONE_HUNDRED);
  633. statPiTaskMonth.setPunchInDoneRate(punchInDoneRate);
  634. }
  635. return statPiTaskMonth;
  636. }
  637. }