WorkCalendarDateHandler.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. package com.persagy.calendar.handle;
  2. import java.util.ArrayList;
  3. import java.util.Date;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import java.util.stream.Collectors;
  10. import org.springframework.beans.BeanUtils;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  15. import com.google.common.collect.Lists;
  16. import com.persagy.calendar.constant.WorkCalendarConstant;
  17. import com.persagy.calendar.pojo.dto.WorkCalendar;
  18. import com.persagy.calendar.pojo.dto.WorkCalendarDate;
  19. import com.persagy.calendar.pojo.dto.WorkCalendarRule;
  20. import com.persagy.calendar.pojo.vo.CalendarDateResult;
  21. import com.persagy.calendar.pojo.vo.CustomCalendarVO;
  22. import com.persagy.calendar.pojo.vo.WorkCalendarDateCreateVO;
  23. import com.persagy.calendar.pojo.vo.WorkCalendarDateQueryVO;
  24. import com.persagy.calendar.pojo.vo.WorkCalendarDateUpdateVO;
  25. import com.persagy.calendar.pojo.vo.WorkCalendarMoreDateCreateVO;
  26. import com.persagy.calendar.service.IWorkCalendarDateService;
  27. import com.persagy.calendar.service.IWorkCalendarDictService;
  28. import com.persagy.calendar.service.IWorkCalendarRuleService;
  29. import com.persagy.calendar.service.IWorkCalendarService;
  30. import com.persagy.common.enums.ResponseCode;
  31. import com.persagy.common.exception.BusinessException;
  32. import com.persagy.common.utils.DateUtil;
  33. import com.persagy.common.utils.IdGenerator;
  34. import com.persagy.common.utils.StringUtil;
  35. import cn.hutool.core.collection.CollectionUtil;
  36. import cn.hutool.core.date.DateField;
  37. import cn.hutool.core.date.DateTime;
  38. import cn.hutool.core.date.DateUnit;
  39. import lombok.extern.slf4j.Slf4j;
  40. /**
  41. * @version
  42. * @description
  43. * @company persagy
  44. * @author zhangqiankun
  45. * @since 2020年10月3日: 下午1:03:19
  46. */
  47. @Slf4j
  48. @Component
  49. public class WorkCalendarDateHandler {
  50. @Autowired
  51. private IWorkCalendarService workCalendarService;
  52. @Autowired
  53. private IWorkCalendarRuleService workCalendarRuleService;
  54. @Autowired
  55. private IWorkCalendarDateService workCalendarDateService;
  56. @Autowired
  57. private IWorkCalendarDictService workCalendarDictService;
  58. /**
  59. * 创建工作历作息时间信息,先删除在创建
  60. *
  61. * @param workCalendarDates
  62. * @param workDateStart 不能为空
  63. * @param workDateEnd 不能为空
  64. * @return
  65. */
  66. @Transactional
  67. public boolean createCalendarDateByDelete(List<WorkCalendarDate> workCalendarDates, String workDateStart, String workDateEnd) {
  68. // 1.防止重复执行,优先删除从今日开始的非自定义的作息时间信息
  69. QueryWrapper<WorkCalendarDate> queryWrapper = new WorkCalendarDate.Builder().createQueryWrapper()
  70. .customFlagEq(WorkCalendarConstant.CUSTOM_CALENDAR_DATE_NO)
  71. .workDateGe(workDateStart).workDateLe(workDateEnd).builderQueryWrapper();
  72. this.workCalendarDateService.remove(queryWrapper);
  73. // 2.信息插入
  74. boolean result = this.workCalendarDateService.batchCreateCalendarDate(workCalendarDates);
  75. return result;
  76. }
  77. /**
  78. * 实时查询作息时间信息,包括自定义的,当日的作息时间,走规则解析
  79. *
  80. * @param queryVO
  81. * @return
  82. */
  83. public List<CalendarDateResult> queryWorkCalendarDateList(WorkCalendarDateQueryVO queryVO) {
  84. // 优先进行时间调整解析,所有的时间都必须是 YYYYMMDD 格式
  85. String dateStartStr = queryVO.getWorkDateStart();
  86. String dateEndStr = queryVO.getWorkDateEnd();
  87. // 中间时间
  88. String now = DateUtil.format(new Date(), DateUtil.FORMAT_DATE_YYYYMMDD);
  89. DateTime dateStart = DateUtil.parse(dateStartStr, DateUtil.FORMAT_DATE_YYYYMMDD);
  90. DateTime dateEnd = DateUtil.parse(dateEndStr, DateUtil.FORMAT_DATE_YYYYMMDD);
  91. Map<String, DateTime> parseWorkDate = this.parseWorkDate(dateStart, dateEnd, now);
  92. DateTime historyStart = parseWorkDate.get("historyStart");
  93. DateTime historyEnd = parseWorkDate.get("historyEnd");
  94. DateTime parseRuleStart = parseWorkDate.get("parseRuleStart");
  95. DateTime parseRuleEnd = parseWorkDate.get("parseRuleEnd");
  96. // 判断字典类型,TODO 这里该不该判断??后续再做修改
  97. int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(null, queryVO.getDictType());
  98. // 1.取历史作息时间记录
  99. Map<String, List<WorkCalendarDate>> calendarDateMap = new HashMap<String, List<WorkCalendarDate>>();
  100. if (historyStart != null && historyEnd != null) {
  101. queryVO.setWorkDateStart(DateUtil.format(historyStart, DateUtil.FORMAT_DATE_YYYYMMDD));
  102. queryVO.setWorkDateEnd(DateUtil.format(historyEnd, DateUtil.FORMAT_DATE_YYYYMMDD));
  103. List<WorkCalendarDate> historyDateList = this.selectWorkCalendarDateList(queryVO, null, dictTypeLevel);
  104. if (CollectionUtil.isNotEmpty(historyDateList)) {
  105. for (WorkCalendarDate historyDate : historyDateList) {
  106. if (calendarDateMap.containsKey(historyDate.getWorkDate())) {
  107. calendarDateMap.get(historyDate.getWorkDate()).add(historyDate);
  108. } else {
  109. calendarDateMap.put(historyDate.getWorkDate(), Lists.newArrayList(historyDate));
  110. }
  111. }
  112. }
  113. }
  114. // 2.取当日往后的自定义作息时间
  115. List<WorkCalendarDate> customDateList = null;
  116. if (parseRuleStart != null && parseRuleEnd != null) {
  117. queryVO.setWorkDateStart(DateUtil.format(parseRuleStart, DateUtil.FORMAT_DATE_YYYYMMDD));
  118. queryVO.setWorkDateEnd(DateUtil.format(parseRuleEnd, DateUtil.FORMAT_DATE_YYYYMMDD));
  119. customDateList = this.selectWorkCalendarDateList(queryVO, WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES, dictTypeLevel);
  120. }
  121. // 3.获取当日往后的规则信息中的作息时间
  122. if (parseRuleStart != null && parseRuleEnd != null) {
  123. List<WorkCalendarRule> ruleList = this.selectWorkCalendarRuleList(queryVO, dictTypeLevel);
  124. long betweenDay = DateUtil.between(parseRuleStart, parseRuleEnd, DateUnit.DAY) + 1;
  125. this.paserWorkCalendarRule(calendarDateMap, ruleList, parseRuleStart, betweenDay);
  126. }
  127. // 获取start~end之间的天数,+1 包括首尾
  128. dateStart = DateUtil.parse(dateStartStr, DateUtil.FORMAT_DATE_YYYYMMDD);
  129. long betweenDay = DateUtil.between(dateStart, dateEnd, DateUnit.DAY) + 1;
  130. // 4.结果加工处理返回
  131. return this.setResultList(calendarDateMap, customDateList, dateStart, betweenDay);
  132. }
  133. /**
  134. * 响应数据,最后处理加工,自定义的将会覆盖掉非自定义的
  135. *
  136. * @param calendarDateMap 包含历史与实时规则中的作息时间集合
  137. * @param customDateList
  138. * @param dateStart
  139. * @param betweenDay
  140. * @return
  141. */
  142. private List<CalendarDateResult> setResultList(Map<String, List<WorkCalendarDate>> calendarDateMap, List<WorkCalendarDate> customDateList,
  143. DateTime dateStart, long betweenDay) {
  144. // 结果集
  145. List<CalendarDateResult> resultList = new ArrayList<CalendarDateResult>();
  146. // 自定义作息时间 当日~end时间内的
  147. Map<String, List<WorkCalendarDate>> customDateMap = new HashMap<String, List<WorkCalendarDate>>();
  148. if (CollectionUtil.isNotEmpty(customDateList)) {
  149. for (WorkCalendarDate customDate : customDateList) {
  150. if (customDateMap.containsKey(customDate.getWorkDate())) {
  151. customDateMap.get(customDate.getWorkDate()).add(customDate);
  152. } else {
  153. customDateMap.put(customDate.getWorkDate(), Lists.newArrayList(customDate));
  154. }
  155. }
  156. }
  157. String workDate = null;
  158. for (int i = 0; i < betweenDay; i++) {
  159. workDate = dateStart.toString(DateUtil.FORMAT_DATE_YYYYMMDD);
  160. List<WorkCalendarDate> historyOrRuleDates = calendarDateMap.get(workDate);
  161. List<WorkCalendarDate> customDates = customDateMap.get(workDate);
  162. CalendarDateResult result = new CalendarDateResult();
  163. int dayOfWeek = DateUtil.dayOfWeek(dateStart);
  164. if (dayOfWeek == 1 || dayOfWeek == 7) {
  165. result.setWorkday(false);
  166. }
  167. result.setWorkDate(workDate);
  168. // 判断是否需要覆盖掉非自定义的作息时间信息
  169. if (CollectionUtil.isNotEmpty(customDates)) {
  170. historyOrRuleDates = this.coverCalendarDate(historyOrRuleDates, customDates, true);
  171. }
  172. result.setDate(DateUtil.dayOfMonth(dateStart));
  173. result.setWeek(WorkCalendarConstant.DAY_OF_WEEK.get(dayOfWeek));
  174. if (CollectionUtil.isNotEmpty(historyOrRuleDates)) {
  175. for (int j = historyOrRuleDates.size() - 1; j >= 0; j--) {
  176. WorkCalendarDate calendarDate = historyOrRuleDates.get(j);
  177. // 是工作日,剔除非工作日的数据,目前仅有OpenTime-1,工作日,OpenTime-2,休息日
  178. if (result.isWorkday() && "OpenTime-2".equals(calendarDate.getDictCode())) {
  179. historyOrRuleDates.remove(j);
  180. } else if (!result.isWorkday() && "OpenTime-1".equals(calendarDate.getDictCode())) {
  181. historyOrRuleDates.remove(j);
  182. }
  183. }
  184. }
  185. result.setTimeTable(CollectionUtil.isEmpty(historyOrRuleDates) ? Lists.newArrayList() : historyOrRuleDates);
  186. resultList.add(result);
  187. dateStart.offset(DateField.DAY_OF_YEAR, 1); // 下一天
  188. }
  189. customDateMap.clear(); //释放
  190. calendarDateMap.clear();
  191. return resultList;
  192. }
  193. /**
  194. * 自定义与规则的进行判断,存在的自定义的将不再进行插入
  195. *
  196. * @param queryVO
  197. * @param customDateList
  198. * @param calendarDateMap
  199. * @return
  200. */
  201. public List<WorkCalendarDate> handleParseCalendarRule(WorkCalendarDateQueryVO queryVO, List<WorkCalendarDate> customDateList,
  202. Map<String, List<WorkCalendarDate>> calendarDateMap) {
  203. List<WorkCalendarDate> resultList = new ArrayList<WorkCalendarDate>();
  204. Set<String> keySet = calendarDateMap.keySet();
  205. if (CollectionUtil.isEmpty(customDateList)) {
  206. for (String key : keySet) {
  207. resultList.addAll(calendarDateMap.get(key));
  208. }
  209. } else {
  210. for (String key : keySet) {
  211. List<WorkCalendarDate> ruleDates = calendarDateMap.get(key);
  212. ruleDates = this.coverCalendarDate(ruleDates, customDateList, false);
  213. resultList.addAll(ruleDates);
  214. }
  215. }
  216. resultList.forEach(calendarDate -> calendarDate.setId(IdGenerator.getSnowIdStr(WorkCalendarConstant.WORK_CALENDAR_DATE_ID_PREFIX)));
  217. return resultList;
  218. }
  219. /**
  220. * 是否需要覆盖掉非自定义的作息时间,自定义覆盖掉规则的
  221. *
  222. * @param ruleDates 允许为空
  223. * @param customDates 不能为空
  224. * @param isCover true-自定义的覆盖非自定义的,false-当天存在自定义的信息时,非自定义的丢弃
  225. * @return 不会返回null
  226. */
  227. private List<WorkCalendarDate> coverCalendarDate(List<WorkCalendarDate> ruleDates, List<WorkCalendarDate> customDates, boolean isCover) {
  228. if (CollectionUtil.isEmpty(ruleDates)) {
  229. return customDates;
  230. }
  231. // 开始验证
  232. List<WorkCalendarDate> result = new ArrayList<WorkCalendarDate>();
  233. Map<String, Integer> dictTypeLevelMap = new HashMap<String, Integer>();
  234. for (int i = ruleDates.size() - 1; i >= 0; i--) {
  235. WorkCalendarDate ruleDate = ruleDates.get(i);
  236. // 判断当前字典类型级别
  237. Integer dictTypeLevel = dictTypeLevelMap.get(ruleDate.getDictType());
  238. if (dictTypeLevel == null) {
  239. dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(ruleDate.getGroupCode(), ruleDate.getDictType());
  240. dictTypeLevelMap.put(ruleDate.getDictType(), dictTypeLevel);
  241. }
  242. // 项目ID和工作历ID转换
  243. if (dictTypeLevel == 0) {
  244. ruleDate.setProjectId("0");
  245. ruleDate.setCalendarId("0");
  246. } else if (dictTypeLevel == 1) {
  247. ruleDate.setCalendarId("0");
  248. }
  249. boolean isCustom = false;
  250. for (WorkCalendarDate customDate : customDates) {
  251. // 项目ID和工作历ID转换
  252. if (dictTypeLevel == 0) {
  253. customDate.setProjectId("0");
  254. customDate.setCalendarId("0");
  255. } else if (dictTypeLevel == 1) {
  256. customDate.setCalendarId("0");
  257. }
  258. // 相等,证明非自定义的需要被覆盖
  259. if (customDate.equals(ruleDate)) {
  260. isCustom = true;
  261. if (isCover) { // 是否需要将自定义的加入到结果集中
  262. result.add(customDate);
  263. } else {
  264. // 移除这一作息时间
  265. ruleDates.remove(i);
  266. }
  267. break;
  268. }
  269. }
  270. // 如果没有找到自定义的,加入到结果集
  271. if (!isCustom) {
  272. result.add(ruleDate);
  273. }
  274. }
  275. return result;
  276. }
  277. /**
  278. * 解析此段时间的规则
  279. *
  280. * @param ruleList 规则信息
  281. * @param dateStart 开始时间
  282. * @param betweenDay 时间范围
  283. * @return 这里返回的结果,一定要在后续判断当日是否存在自定义的作息时间,才能继续使用,此处不会返回null
  284. */
  285. public void paserWorkCalendarRule(Map<String, List<WorkCalendarDate>> calendarDateMap, List<WorkCalendarRule> ruleList,
  286. DateTime dateStart, long betweenDay) {
  287. Map<String, WorkCalendar> calendarMap = new HashMap<String, WorkCalendar>();
  288. for(int i = 0; i < betweenDay; i++) {
  289. // 判断此天有没有对应的规则信息,每次都要循环全部,生成多条信息
  290. for (WorkCalendarRule rule : ruleList) {
  291. // 验证对应的工作历类型是否存,假若为工作历级别的规则配置
  292. WorkCalendar calendar = null;
  293. if (!"0".equals(rule.getCalendarId())) {
  294. calendar = this.workCalendarService.getById(rule.getCalendarId(), rule.getGroupCode(), rule.getProjectId());
  295. if (calendar == null) {
  296. log.warn("规则ID为[{}]的规则,所对应的工作历信息不存在", rule.getId());
  297. continue;
  298. }
  299. }
  300. // 判断当前规则是否生效
  301. WorkCalendarDate calendarDate = this.parseWorkCalendarRule(rule, calendar, dateStart);
  302. if (calendarDate == null) {
  303. continue;
  304. }
  305. // 数据添加
  306. String workDate = dateStart.toString(DateUtil.FORMAT_DATE_YYYYMMDD);
  307. if (calendarDateMap.containsKey(workDate)) {
  308. calendarDateMap.get(workDate).add(calendarDate);
  309. } else {
  310. calendarDateMap.put(workDate, Lists.newArrayList(calendarDate));
  311. }
  312. }
  313. dateStart.offset(DateField.DAY_OF_YEAR, 1);
  314. }
  315. calendarMap.clear();
  316. }
  317. /**
  318. * 解析规则信息,生成对应作息时间记录
  319. *
  320. * @param rule
  321. * @param workCalendar 为null, 代表非工作历级别的规则配置
  322. * @param dateStart
  323. */
  324. private WorkCalendarDate parseWorkCalendarRule(WorkCalendarRule rule, WorkCalendar workCalendar, DateTime currentDate) {
  325. String workDate = currentDate.toString(DateUtil.FORMAT_DATE_YYYYMMDD);
  326. String dictCode = StringUtil.isBlank(rule.getDictCode()) ? "" : rule.getDictCode();
  327. int indexOf = dictCode.indexOf(WorkCalendarConstant.WORK_CALENDAR_D_OF_W);
  328. if (indexOf <= 0) {
  329. indexOf = dictCode.indexOf(WorkCalendarConstant.WORK_CALENDAR_D_OF_M);
  330. if (indexOf <= 0) {
  331. indexOf = dictCode.indexOf(WorkCalendarConstant.WORK_CALENDAR_M_AND_D);
  332. }
  333. }
  334. if (indexOf > 0) {
  335. dictCode = dictCode.substring(0, indexOf - 1);
  336. }
  337. // 解析规则,生成数据
  338. int work = Integer.valueOf(workDate);
  339. String ruleStart = rule.getRuleDateStart();
  340. String ruleEnd = rule.getRuleDateEnd();
  341. if (StringUtil.isNotBlank(ruleStart) && StringUtil.isNotBlank(ruleEnd)) {
  342. int start = Integer.valueOf(ruleStart);
  343. int end = Integer.valueOf(ruleEnd);
  344. if (work >= start && work <= end) {
  345. // 当日期生效
  346. if (indexOf > 0) {
  347. // 验证value是否存在,存在按周 月 解析,判断是否生效
  348. boolean result = this.parseRuleValue(rule, currentDate, workDate);
  349. if (!result) {
  350. return null;
  351. }
  352. }
  353. // 组合数据
  354. WorkCalendarDate calendarDate = this.comWorkCalendarOrRule(rule, workDate, dictCode, workCalendar);
  355. return calendarDate;
  356. }
  357. }
  358. return null;
  359. }
  360. /**
  361. * 获取满足条件的所有新版规则信息
  362. * TODO 这里工作历和项目ID不传参,则无影响,若传参了,目前仅支持工作历级别的规则解析
  363. *
  364. * @param workCalendarDateQueryVO
  365. * @param dictTypeLevel 0-集团级别,1-项目级别,2-工作历级别,null-默认工作历级别
  366. * @return
  367. */
  368. public List<WorkCalendarRule> selectWorkCalendarRuleList(WorkCalendarDateQueryVO queryVO, Integer dictTypeLevel) {
  369. QueryWrapper<WorkCalendarRule> queryWrapper = new WorkCalendarRule.Builder().createQueryWrapper().projectIdEq(queryVO.getProjectId(), dictTypeLevel)
  370. .calendarIdEq(queryVO.getCalendarId(), dictTypeLevel).ruleDateBetween(queryVO.getWorkDateStart(), queryVO.getWorkDateEnd())
  371. .dictTypeEq(queryVO.getDictType()).dictCodeIn(queryVO.getDictCode()).dictTypeIn(queryVO.getDictTypes())
  372. .idEq(queryVO.getRuleId()).delFlagEq(WorkCalendarConstant.WORK_CALENDAR_DEL_FLAG_NOR)
  373. .ruleFlagEq(WorkCalendarConstant.WORK_CALENDAR_RULE_FLAG_NEW).builderQueryWrapper();
  374. return this.workCalendarRuleService.list(queryWrapper);
  375. }
  376. /**
  377. * 获取满足条件的所有已存在的作息时间信息,包括自定义的
  378. * @param workCalendarDateQueryVO
  379. * @return
  380. */
  381. public List<WorkCalendarDate> selectWorkCalendarDateList(WorkCalendarDateQueryVO workCalendarDateQueryVO, String customFlag, Integer dictTypeLevel) {
  382. QueryWrapper<WorkCalendarDate> queryWrapper = new WorkCalendarDate.Builder().createQueryWrapper()
  383. .projectIdEq(workCalendarDateQueryVO.getProjectId(), dictTypeLevel).idEq(workCalendarDateQueryVO.getId())
  384. .ruleIdEq(workCalendarDateQueryVO.getRuleId()).calendarIdEq(workCalendarDateQueryVO.getCalendarId(), dictTypeLevel)
  385. .workDateGe(workCalendarDateQueryVO.getWorkDateStart()).workDateLe(workCalendarDateQueryVO.getWorkDateEnd())
  386. .dictTypeEq(workCalendarDateQueryVO.getDictType()).dictTypeIn(workCalendarDateQueryVO.getDictTypes())
  387. .dictCodeIn(workCalendarDateQueryVO.getDictCode()).calendarNameLike(workCalendarDateQueryVO.getCalendarName())
  388. .customFlagEq(customFlag).builderQueryWrapper();
  389. queryWrapper.select("ID, GROUP_CODE, RULE_ID, CALENDAR_ID, PROJECT_ID, CALENDAR_NAME, CALENDAR_DESC, DICT_TYPE, "
  390. + "DICT_CODE, WORK_DATE, WORK_TIME, CUSTOM_FLAG, UPDATE_USER");
  391. List<WorkCalendarDate> list = workCalendarDateService.list(queryWrapper);
  392. return list == null ? new ArrayList<WorkCalendarDate>() : list;
  393. }
  394. /**
  395. * 组合出作息时间对象
  396. *
  397. * @param rule
  398. * @param workDate
  399. * @param dictCode
  400. * @param workCalendar
  401. * @return
  402. */
  403. public WorkCalendarDate comWorkCalendarOrRule(WorkCalendarRule rule, String workDate, String dictCode, WorkCalendar workCalendar) {
  404. // 首先判断工作历是否还存在
  405. WorkCalendarDate workCalendarDate = new WorkCalendarDate();
  406. workCalendarDate.setGroupCode(rule.getGroupCode());
  407. workCalendarDate.setCalendarId(workCalendar == null ? "0" : workCalendar.getId());
  408. workCalendarDate.setRuleId(rule.getId());
  409. workCalendarDate.setCalendarName(workCalendar == null ? "" : workCalendar.getCalendarName());
  410. workCalendarDate.setCalendarDesc(workCalendar == null ? "" : workCalendar.getCalendarDesc());
  411. workCalendarDate.setProjectId(rule.getProjectId());
  412. workCalendarDate.setDictType(rule.getDictType());
  413. workCalendarDate.setDictCode(dictCode);
  414. workCalendarDate.setWorkDateStart(rule.getRuleDateStart());
  415. workCalendarDate.setWorkDateEnd(rule.getRuleDateEnd());
  416. workCalendarDate.setWorkDate(workDate);
  417. workCalendarDate.setWorkTime(StringUtil.isBlank(rule.getRuleTime()) ?
  418. (CollectionUtil.isEmpty(rule.getValue()) ? null : rule.getValue().stream().collect(Collectors.joining(",")))
  419. : rule.getRuleTime());
  420. // 自定义的一定是查出来的,封装的一定是非自定义添加的
  421. workCalendarDate.setCustomFlag(WorkCalendarConstant.CUSTOM_CALENDAR_DATE_NO);
  422. workCalendarDate.setUpdateUser(rule.getUpdateUser());
  423. return workCalendarDate;
  424. }
  425. /**
  426. * 批量自定义作息时间信息
  427. * @return
  428. */
  429. public void batchCreateCalendarDate(CustomCalendarVO<WorkCalendarDateCreateVO> createVO) {
  430. boolean result = false;
  431. List<WorkCalendarDateCreateVO> batchInfos = createVO.getBatchInfo();
  432. // 判断日期是否合法
  433. DateTime now = DateUtil.date();
  434. //now = now.offset(DateField.DAY_OF_YEAR, 1); //明天
  435. String format = DateUtil.format(now, DateUtil.FORMAT_DATE_YYYYMMDD);
  436. Integer current = Integer.valueOf(format);
  437. for (WorkCalendarDateCreateVO batchInfo : batchInfos) {
  438. int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(createVO.getGroupCode(), batchInfo.getDictType());
  439. Integer workDate = Integer.valueOf(batchInfo.getWorkDate());
  440. if (workDate < current) {
  441. throw new BusinessException("作息日期不合法,必须大于等于当日");
  442. }
  443. // 判断工作历类型是否存在
  444. WorkCalendar calendar = null;
  445. if (dictTypeLevel == 2) {
  446. calendar = this.workCalendarService.getById(batchInfo.getCalendarId(), createVO.getGroupCode(), createVO.getProjectId());
  447. if (calendar == null) {
  448. throw new BusinessException("工作历类型不存在");
  449. }
  450. }
  451. WorkCalendarDate calendarDate = new WorkCalendarDate();
  452. BeanUtils.copyProperties(batchInfo, calendarDate);
  453. // 验证数据有效性
  454. result = this.workCalendarDateService.checkWorkDateValid(calendarDate, dictTypeLevel);
  455. if (!result) {
  456. throw new BusinessException("当天已存在相同类型额定义,不可重复添加");
  457. }
  458. calendarDate.setGroupCode(createVO.getGroupCode());
  459. calendarDate.setCalendarName(calendar == null ? null : calendar.getCalendarName());
  460. calendarDate.setCalendarDesc(calendar == null ? null : calendar.getCalendarDesc());
  461. calendarDate.setProjectId(dictTypeLevel == 0 ? "0" : createVO.getProjectId());
  462. if (dictTypeLevel != 2) {
  463. calendarDate.setCalendarId("0");
  464. }
  465. calendarDate.setUpdateUser(createVO.getUserId());
  466. calendarDate.setCustomFlag(WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES);
  467. result = this.workCalendarDateService.save(calendarDate);
  468. if (!result) {
  469. throw new BusinessException("自定义工作历作息时间添加失败");
  470. }
  471. }
  472. }
  473. /**
  474. * 解析rule 的 value属性,判断其是否生效
  475. * @param rule
  476. * @param workDate
  477. * @return value为空,默认返回true
  478. */
  479. private boolean parseRuleValue(WorkCalendarRule rule, DateTime currentDate, String workDate) {
  480. if (CollectionUtil.isEmpty(rule.getValue())) {
  481. return true;
  482. }
  483. // 获取月日
  484. String mmddStr = workDate.substring(4, 8);
  485. Integer mmdd = Integer.valueOf(mmddStr);
  486. Set<String> values = rule.getValue();
  487. String dictCode = rule.getDictCode();
  488. String[] split = dictCode.split("-");
  489. dictCode = split[0]; // 真实的code编码
  490. String type = split[1]; // 类型
  491. switch (type) {
  492. case WorkCalendarConstant.WORK_CALENDAR_D_OF_W: //周
  493. // 1表示周日,2表示周一
  494. int dayOfWeek = DateUtil.dayOfWeek(currentDate);
  495. dayOfWeek = dayOfWeek - 1;
  496. if (dayOfWeek == 0) {
  497. dayOfWeek = 7;
  498. }
  499. String num = String.valueOf(dayOfWeek);
  500. if (values.contains(num)) {
  501. return true;
  502. }
  503. break;
  504. case WorkCalendarConstant.WORK_CALENDAR_D_OF_M: //月
  505. // 当前日期为此月的第几天
  506. int ofMonth = DateUtil.dayOfMonth(currentDate);
  507. Iterator<String> iterator = values.iterator();
  508. while(iterator.hasNext()) {
  509. String next = iterator.next();
  510. if (next.contains("-")) {
  511. String[] split2 = next.split("-");
  512. int start = Integer.parseInt(split2[0]);
  513. int end = Integer.parseInt(split2[1]);
  514. if (ofMonth >= start && ofMonth <= end) {
  515. return true;
  516. }
  517. } else if (next.equals(String.valueOf(ofMonth))) {
  518. return true;
  519. }
  520. }
  521. break;
  522. case WorkCalendarConstant.WORK_CALENDAR_M_AND_D: //年
  523. iterator = values.iterator();
  524. while(iterator.hasNext()) {
  525. String next = iterator.next();
  526. if (next.contains("-")) {
  527. String[] split2 = next.split("-");
  528. Integer startInt = Integer.valueOf(split2[0]);
  529. Integer endInt = Integer.valueOf(split2[1]);
  530. if (mmdd >= startInt && mmdd <= endInt) {
  531. return true;
  532. }
  533. } else if (mmddStr.equals(next)) {
  534. return true;
  535. }
  536. }
  537. break;
  538. default:
  539. break;
  540. }
  541. return false;
  542. }
  543. /**
  544. * 批量自定义作息时间信息
  545. * @return
  546. */
  547. public <T> void batchUpdateCalendarDate(CustomCalendarVO<WorkCalendarDateUpdateVO> updateVO) {
  548. boolean result = false;
  549. List<WorkCalendarDateUpdateVO> batchInfos = updateVO.getBatchInfo();
  550. // 判断日期是否合法
  551. DateTime now = DateUtil.date();
  552. //now = now.offset(DateField.DAY_OF_YEAR, 1); //明天
  553. String format = DateUtil.format(now, DateUtil.FORMAT_DATE_YYYYMMDD);
  554. Integer current = Integer.valueOf(format);
  555. for (WorkCalendarDateUpdateVO batchInfo : batchInfos) {
  556. Integer workDate = Integer.valueOf(batchInfo.getWorkDate());
  557. if (workDate < current) {
  558. throw new BusinessException("作息日期不合法,必须大于等于当日");
  559. }
  560. // 验证是否存在
  561. WorkCalendarDate temp = this.workCalendarDateService.getById(batchInfo.getId());
  562. if (temp == null || !WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES.equals(temp.getCustomFlag())) {
  563. throw new BusinessException("当天不存在自定义的作息时间信息");
  564. }
  565. int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(updateVO.getGroupCode(), batchInfo.getDictType());
  566. // 判断工作历类型是否存在
  567. WorkCalendar calendar = null;
  568. if (dictTypeLevel == 2) {
  569. calendar = this.workCalendarService.getById(batchInfo.getCalendarId(), updateVO.getGroupCode(), updateVO.getProjectId());
  570. if (calendar == null) {
  571. throw new BusinessException("工作历类型不存在");
  572. }
  573. }
  574. WorkCalendarDate calendarDate = new WorkCalendarDate();
  575. BeanUtils.copyProperties(batchInfo, calendarDate);
  576. calendarDate.setCustomFlag(WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES);
  577. calendarDate.setGroupCode(updateVO.getGroupCode());
  578. calendarDate.setCalendarName(calendar == null ? null : calendar.getCalendarName());
  579. calendarDate.setCalendarDesc(calendar == null ? null : calendar.getCalendarDesc());
  580. calendarDate.setProjectId(dictTypeLevel == 0 ? "0" : updateVO.getProjectId());
  581. if (dictTypeLevel != 2) {
  582. calendarDate.setCalendarId("0");
  583. }
  584. calendarDate.setUpdateUser(updateVO.getUserId());
  585. result = this.workCalendarDateService.updateById(calendarDate);
  586. if (!result) {
  587. throw new BusinessException("自定义工作历作息时间添加失败");
  588. }
  589. }
  590. }
  591. /**
  592. * 多时间段添加工作历作息时间信息,先删除此dictType下数据,再添加
  593. *
  594. * @param createVO
  595. * @param calendar
  596. */
  597. @Transactional
  598. public void batchCreateCalendarMoreDate(WorkCalendarMoreDateCreateVO createVO, WorkCalendar calendar) {
  599. int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(createVO.getGroupCode(), createVO.getDictType());
  600. String calendarId = createVO.getCalendarId();
  601. String projectId = createVO.getProjectId();
  602. String now = DateUtil.format(new Date(), DateUtil.FORMAT_DATE_YYYYMMDDHHMMSS);
  603. DateTime dateStart = DateUtil.parse(createVO.getWorkDateStart(), DateUtil.FORMAT_DATE_YYYYMMDD);
  604. DateTime dateEnd = DateUtil.parse(createVO.getWorkDateEnd(), DateUtil.FORMAT_DATE_YYYYMMDD);
  605. long betweenDay = DateUtil.between(dateStart, dateEnd, DateUnit.DAY) + 1;
  606. Set<String> timetables = createVO.getTimetable();
  607. List<WorkCalendarDate> datas = new ArrayList<WorkCalendarDate>();
  608. for (int i = 0; i < betweenDay; i++) {
  609. String currentDay = DateUtil.format(dateStart, DateUtil.FORMAT_DATE_YYYYMMDD);
  610. if (CollectionUtil.isEmpty(timetables)) {
  611. WorkCalendarDate calendarDate = this.setCalendarDate(createVO, calendar, currentDay, null, now);
  612. datas.add(calendarDate);
  613. } else {
  614. for (String timetable : timetables) {
  615. // 数据库数据组装
  616. WorkCalendarDate calendarDate = this.setCalendarDate(createVO, calendar, currentDay, timetable, now);
  617. datas.add(calendarDate);
  618. }
  619. }
  620. // 偏移天数
  621. dateStart.offset(DateField.DAY_OF_YEAR, 1);
  622. }
  623. // 先删除
  624. QueryWrapper<WorkCalendarDate> queryWrapper = new WorkCalendarDate.Builder().createQueryWrapper()
  625. .calendarIdEq(calendarId, dictTypeLevel).projectIdEq(projectId, dictTypeLevel).dictTypeEq(createVO.getDictType())
  626. .workDateGe(createVO.getWorkDateStart()).workDateLe(createVO.getWorkDateEnd())
  627. .customFlagEq(createVO.getCustomFlag()).builderQueryWrapper();
  628. this.workCalendarDateService.remove(queryWrapper);
  629. // 批量添加数据
  630. this.workCalendarDateService.batchCreateCalendarDate(datas);
  631. }
  632. /**
  633. * 参数赋值
  634. * @param createVO
  635. * @param calendar
  636. * @param currentDay
  637. * @param timetable
  638. * @param now
  639. * @return
  640. */
  641. private WorkCalendarDate setCalendarDate(WorkCalendarMoreDateCreateVO createVO, WorkCalendar calendar,
  642. String currentDay, String timetable, String now) {
  643. WorkCalendarDate calendarDate = new WorkCalendarDate();
  644. calendarDate.setId(IdGenerator.getSnowIdStr(WorkCalendarConstant.WORK_CALENDAR_DATE_ID_PREFIX));
  645. calendarDate.setGroupCode(createVO.getGroupCode());
  646. calendarDate.setProjectId(createVO.getProjectId());
  647. calendarDate.setCalendarId(createVO.getCalendarId());
  648. calendarDate.setCustomFlag(WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES);
  649. calendarDate.setCalendarName(calendar == null ? "" : calendar.getCalendarName());
  650. calendarDate.setCalendarDesc(calendar == null ? "" : calendar.getCalendarDesc());
  651. calendarDate.setDictType(createVO.getDictType());
  652. calendarDate.setDictCode(createVO.getDictCode());
  653. calendarDate.setWorkDate(currentDay);
  654. calendarDate.setWorkTime(timetable);
  655. calendarDate.setCreateTime(now);
  656. calendarDate.setUpdateTime(now);
  657. calendarDate.setUpdateUser(createVO.getUserId());
  658. return calendarDate;
  659. }
  660. /**
  661. * 检查参数日期是否大于当前日期
  662. * @param workDate 工作日期 yyyyMMdd
  663. * @return
  664. */
  665. public static boolean isMoreThanToday(String workDate) {
  666. DateTime now = DateUtil.date();
  667. //now = now.offset(DateField.DAY_OF_YEAR, 1); //明天
  668. String format = DateUtil.format(now, DateUtil.FORMAT_DATE_YYYYMMDD);
  669. Integer current = Integer.valueOf(format);
  670. Integer dateInt = Integer.valueOf(workDate);
  671. if (dateInt < current) {
  672. return false;
  673. }
  674. return true;
  675. }
  676. /**
  677. * 解析起止时间,判读当前时间位于哪里
  678. * @param dateStart yyyyMMdd
  679. * @param dateEnd yyyyMMdd
  680. * @param now yyyyMMdd
  681. * @return
  682. */
  683. private Map<String, DateTime> parseWorkDate(DateTime dateStart, DateTime dateEnd, String now) {
  684. if (dateEnd.before(dateStart)) {
  685. throw new BusinessException(ResponseCode.A0400.getCode(), "结束日期不能早于开始日期");
  686. }
  687. DateTime historyStart = null;
  688. DateTime historyEnd = null;
  689. DateTime parseRuleStart = null;
  690. DateTime parseRuleEnd = null;
  691. Map<String, DateTime> result = new HashMap<String, DateTime>();
  692. DateTime currentDate = DateUtil.parse(now, DateUtil.FORMAT_DATE_YYYYMMDD);
  693. // 第一种情况,当日在start之前
  694. if (!dateStart.before(currentDate)) {
  695. // 开始日期,不早于当日,全部走解析
  696. parseRuleStart = dateStart;
  697. parseRuleEnd = dateEnd;
  698. } else if (dateStart.before(currentDate) && !dateEnd.before(currentDate)) {
  699. // 第二种情况,当日在start~end之间,包含等于dateEnd 的情况
  700. historyStart = dateStart;
  701. historyEnd = currentDate.offset(DateField.DAY_OF_YEAR, -1);
  702. parseRuleStart = DateUtil.parse(now, DateUtil.FORMAT_DATE_YYYYMMDD);
  703. parseRuleEnd = dateEnd;
  704. } else {
  705. // 第三种情况,当日在dataEnd之后,全部查历史即可
  706. historyStart = dateStart;
  707. historyEnd = dateEnd;
  708. }
  709. result.put("historyStart", historyStart);
  710. result.put("historyEnd", historyEnd);
  711. result.put("parseRuleStart", parseRuleStart);
  712. result.put("parseRuleEnd", parseRuleEnd);
  713. return result;
  714. }
  715. /**
  716. * 判断是否存在自动作息时间的依据key
  717. *
  718. * @param calendarId 工作历类型ID
  719. * @param workDate 当日
  720. * @param dictType 字典类型,不可为空
  721. * @param dictCode 字典类型下的编码,允许为空
  722. * @return
  723. */
  724. public String getCustomCalendarDateTempKey(String calendarId, String workDate, String dictType, String dictCode) {
  725. return workDate + dictType + (StringUtil.isBlank(dictCode) ? "" : dictCode);
  726. }
  727. /**
  728. * 判断是否存在自动作息时间的依据key
  729. *
  730. * @param rule
  731. * @param workDate
  732. * @param dictCode
  733. * @return
  734. */
  735. public String getCustomKey(WorkCalendarRule rule, String workDate, String dictCode) {
  736. String customKey = rule.getCalendarId() + rule.getProjectId() + workDate + rule.getDictType() + (StringUtil.isBlank(dictCode) ? "" : dictCode);
  737. return customKey;
  738. }
  739. }