From af6c73841b54f0c9002ce85b8c0d1679e25d5d7c Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期三, 01 七月 2026 18:06:02 +0800
Subject: [PATCH] 【景宁】任务组开发
---
smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java | 356 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 356 insertions(+), 0 deletions(-)
diff --git a/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java
new file mode 100644
index 0000000..4f0a035
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java
@@ -0,0 +1,356 @@
+package com.smartor.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.smartor.domain.ServiceSubtask;
+import com.smartor.domain.ServiceSubtaskPreachform;
+import com.smartor.domain.ServiceTaskSchedule;
+import com.smartor.domain.ServiceTaskScheduleDetail;
+import com.smartor.domain.ServiceTaskScheduleVO;
+import com.smartor.mapper.ServiceSubtaskMapper;
+import com.smartor.mapper.ServiceSubtaskPreachformMapper;
+import com.smartor.mapper.ServiceTaskScheduleDetailMapper;
+import com.smartor.mapper.ServiceTaskScheduleMapper;
+import com.smartor.service.IServiceTaskScheduleService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝 Service 瀹炵幇
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Slf4j
+@Service
+public class ServiceTaskScheduleServiceImpl implements IServiceTaskScheduleService {
+
+ @Autowired
+ private ServiceTaskScheduleMapper serviceTaskScheduleMapper;
+
+ @Autowired
+ private ServiceTaskScheduleDetailMapper serviceTaskScheduleDetailMapper;
+
+ @Autowired
+ private ServiceSubtaskMapper serviceSubtaskMapper;
+
+ @Autowired
+ private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+ /** 鍗曟鎵弿鐨勬渶澶ц鍒掓暟锛岄伩鍏嶉暱浜嬪姟銆� */
+ private static final int ROLL_BATCH_LIMIT = 200;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public ServiceTaskSchedule saveScheduleWithDetail(ServiceTaskScheduleVO vo) {
+ if (vo == null || vo.getTaskid() == null) {
+ throw new BaseException("浠诲姟璁″垝鍏ュ弬闈炴硶锛歵askid 涓嶈兘涓虹┖");
+ }
+ if (CollectionUtils.isEmpty(vo.getDetailList())) {
+ throw new BaseException("浠诲姟璁″垝鏄庣粏涓嶈兘涓虹┖锛堣嚦灏戜竴鏉� detail锛�");
+ }
+
+ // 1. 鏃ц鍒掕蒋鍒�
+ serviceTaskScheduleMapper.deleteByTaskid(vo.getTaskid());
+ serviceTaskScheduleDetailMapper.deleteByTaskid(vo.getTaskid());
+
+ // 2. 涓昏鍒掕惤搴�
+ Date now = DateUtils.getNowDate();
+ ServiceTaskSchedule schedule = new ServiceTaskSchedule();
+ // 鎷疯礉鐖剁被锛圫erviceTaskSchedule锛夊瓧娈�
+ schedule.setTaskid(vo.getTaskid());
+ schedule.setMaxLoopCount(vo.getMaxLoopCount());
+ schedule.setCurrentLoopCount(vo.getCurrentLoopCount() == null ? 0L : vo.getCurrentLoopCount());
+ schedule.setCurrentSeq(vo.getCurrentSeq() == null ? 0 : vo.getCurrentSeq());
+ schedule.setNextSeq(vo.getNextSeq() == null ? 1 : vo.getNextSeq());
+ schedule.setScheduleStyle(vo.getScheduleStyle() == null ? 0 : vo.getScheduleStyle());
+ schedule.setScheduleTimeNum(vo.getScheduleTimeNum() == null ? 0 : vo.getScheduleTimeNum());
+ schedule.setScheduleTimeUnit(vo.getScheduleTimeUnit() == null ? 0 : vo.getScheduleTimeUnit());
+ schedule.setNextExecuteTime(vo.getNextExecuteTime());
+ schedule.setScheduleBenchmark(vo.getScheduleBenchmark() == null ? 0 : vo.getScheduleBenchmark());
+ schedule.setScheduleBenchmarkCustomTime(vo.getScheduleBenchmarkCustomTime());
+ schedule.setRemark(vo.getRemark());
+ schedule.setDelFlag("0");
+ schedule.setCreateBy(vo.getCreateBy());
+ schedule.setUpdateBy(vo.getCreateBy());
+ schedule.setCreateTime(now);
+ schedule.setUpdateTime(now);
+ schedule.setOrgid(vo.getOrgid());
+ schedule.setCampusid(vo.getCampusid());
+ serviceTaskScheduleMapper.insertServiceTaskSchedule(schedule);
+
+ // 3. 鏄庣粏钀藉簱
+ List<ServiceTaskScheduleDetail> detailList = vo.getDetailList();
+ int seq = 1;
+ for (ServiceTaskScheduleDetail detail : detailList) {
+ detail.setTaskid(vo.getTaskid());
+ detail.setScheduleid(schedule.getId());
+ if (detail.getSeq() == null) {
+ detail.setSeq(seq);
+ }
+ if (detail.getDayOffset() == null) {
+ throw new BaseException("璁″垝鏄庣粏 seq=" + detail.getSeq() + " 鐨� dayOffset 涓嶈兘涓虹┖");
+ }
+ detail.setDelFlag("0");
+ detail.setCreateBy(vo.getCreateBy());
+ detail.setUpdateBy(vo.getCreateBy());
+ detail.setCreateTime(now);
+ detail.setUpdateTime(now);
+ detail.setOrgid(vo.getOrgid());
+ detail.setCampusid(vo.getCampusid());
+ seq++;
+ }
+ serviceTaskScheduleDetailMapper.batchInsertServiceTaskScheduleDetail(detailList);
+
+ log.info("[浠诲姟缁刔 taskid={} 璁″垝淇濆瓨瀹屾垚, scheduleId={}, detail 鏉℃暟={}",
+ vo.getTaskid(), schedule.getId(), detailList.size());
+ return schedule;
+ }
+
+ @Override
+ public ServiceTaskScheduleVO getScheduleByTaskid(Long taskid) {
+ ServiceTaskSchedule schedule = serviceTaskScheduleMapper.selectByTaskid(taskid);
+ if (schedule == null) {
+ return null;
+ }
+ ServiceTaskScheduleVO vo = new ServiceTaskScheduleVO();
+ vo.setId(schedule.getId());
+ vo.setTaskid(schedule.getTaskid());
+ vo.setMaxLoopCount(schedule.getMaxLoopCount());
+ vo.setCurrentLoopCount(schedule.getCurrentLoopCount());
+ vo.setCurrentSeq(schedule.getCurrentSeq());
+ vo.setNextSeq(schedule.getNextSeq());
+ vo.setScheduleStyle(schedule.getScheduleStyle());
+ vo.setScheduleTimeNum(schedule.getScheduleTimeNum());
+ vo.setScheduleTimeUnit(schedule.getScheduleTimeUnit());
+ vo.setNextExecuteTime(schedule.getNextExecuteTime());
+ vo.setScheduleBenchmark(schedule.getScheduleBenchmark());
+ vo.setScheduleBenchmarkCustomTime(schedule.getScheduleBenchmarkCustomTime());
+ vo.setRemark(schedule.getRemark());
+ vo.setOrgid(schedule.getOrgid());
+ vo.setCampusid(schedule.getCampusid());
+ vo.setDetailList(serviceTaskScheduleDetailMapper.selectByScheduleid(schedule.getId()));
+ return vo;
+ }
+
+ @Override
+ public ServiceTaskSchedule selectByTaskid(Long taskid) {
+ return serviceTaskScheduleMapper.selectByTaskid(taskid);
+ }
+
+ @Override
+ public List<ServiceTaskScheduleDetail> selectDetailByScheduleid(Long scheduleid) {
+ return serviceTaskScheduleDetailMapper.selectByScheduleid(scheduleid);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int removeByTaskid(Long taskid) {
+ int n = serviceTaskScheduleMapper.deleteByTaskid(taskid);
+ serviceTaskScheduleDetailMapper.deleteByTaskid(taskid);
+ return n;
+ }
+
+ @Override
+ public int rollCycleTasks() {
+ List<ServiceTaskSchedule> schedules = serviceTaskScheduleMapper.selectActiveCycleSchedules(ROLL_BATCH_LIMIT);
+ if (CollectionUtils.isEmpty(schedules)) {
+ return 0;
+ }
+ log.info("[浠诲姟缁�-婊氬姩] 鎵弿鍒� {} 涓緟璇勪及鐨勫惊鐜鍒�", schedules.size());
+ int rolled = 0;
+ for (ServiceTaskSchedule schedule : schedules) {
+ try {
+ // 閫氳繃 AOP 浠g悊璋冪敤锛岀‘淇� @Transactional 鐢熸晥
+ if (SpringUtils.getAopProxy(this).rollOneSchedule(schedule)) {
+ rolled++;
+ }
+ } catch (Exception e) {
+ // 鍗曟潯澶辫触涓嶅奖鍝嶅叾瀹冭鍒掞紝璁版棩蹇楃户缁�
+ log.error("[浠诲姟缁�-婊氬姩] scheduleId={} 婊氬姩澶辫触锛歿}", schedule.getId(), e.getMessage(), e);
+ }
+ }
+ log.info("[浠诲姟缁�-婊氬姩] 鏈鎺ㄨ繘 {} 涓鍒�", rolled);
+ return rolled;
+ }
+
+ /**
+ * 鍗曚釜璁″垝鐨勬粴鍔細鏈疆鍏ㄩ儴 finalize 鎵嶆帹杩涳紝姣忔浠呮帹杩� 1 杞��
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean rollOneSchedule(ServiceTaskSchedule schedule) {
+ Long scheduleId = schedule.getId();
+ Long currentLoop = schedule.getCurrentLoopCount() == null ? 0L : schedule.getCurrentLoopCount();
+
+ // 涓婇檺鏍¢獙
+ Long max = schedule.getMaxLoopCount();
+ if (max != null && max > 0 && currentLoop + 1 >= max) {
+ // 娉ㄦ剰锛氬厛鍒ゆ柇"鍐嶅紑涓�杞槸鍚︿細瓒呴檺"鈥斺�攃urrentLoop 鏄凡瀹屾垚杞锛屼笅涓�杞彿 = currentLoop+1
+ // 褰� currentLoop+1 == max 鏃舵濂藉浜庢渶鍚庝竴杞紝涓嶅啀浜х敓鏂颁竴杞�
+ if (currentLoop >= max) {
+ log.info("[浠诲姟缁�-婊氬姩] scheduleId={} 宸茶揪 maxLoopCount={}, 璺宠繃", scheduleId, max);
+ return false;
+ }
+ }
+
+ // 鏈疆鏄惁瀹屾垚
+ int active = serviceSubtaskMapper.countActiveByScheduleIdAndLoop(scheduleId, currentLoop);
+ if (active > 0) {
+ log.debug("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 浠嶆湁 {} 鏉� subtask 鏈粨鏉�, 璺宠繃", scheduleId, currentLoop, active);
+ return false;
+ }
+
+ // 鎷夋湰杞� subtask 鍋氬厠闅嗘ā鏉匡紙鍙栧幓閲嶅悗鐨勬偅鑰呴泦鍚堬級
+ List<ServiceSubtask> currentLoopSubtasks =
+ serviceSubtaskMapper.selectByScheduleIdAndLoop(scheduleId, currentLoop);
+ if (CollectionUtils.isEmpty(currentLoopSubtasks)) {
+ log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 鎵句笉鍒版湰杞� subtask, 璺宠繃", scheduleId, currentLoop);
+ return false;
+ }
+
+ // 鎷夋湰璁″垝鏄庣粏锛歴eq -> detail锛岀敤鏉ュ畾浣嶄笅涓�杞瘡鏉� subtask 鐢ㄥ摢涓ā鏉�
+ List<ServiceTaskScheduleDetail> detailList =
+ serviceTaskScheduleDetailMapper.selectByScheduleid(scheduleId);
+ if (CollectionUtils.isEmpty(detailList)) {
+ log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鏄庣粏涓虹┖, 璺宠繃", scheduleId);
+ return false;
+ }
+
+ // 鎺ㄨ繘姝ラ暱锛堣疆闂撮殧锛�
+ int stepNum = schedule.getScheduleTimeNum() == null ? 0 : schedule.getScheduleTimeNum();
+ int stepUnit = schedule.getScheduleTimeUnit() == null ? 1 : schedule.getScheduleTimeUnit();
+ if (stepNum <= 0) {
+ log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} scheduleTimeNum={} 闈炴硶, 璺宠繃", scheduleId, stepNum);
+ return false;
+ }
+
+ long nextLoop = currentLoop + 1;
+
+ // 鎸� patid+inhospid 鍘婚噸锛堝悓鎮h�呮湰杞彲鑳芥湁 N 鏉� detail锛屾ā鏉夸换閫夊叾涓�鍗冲彲鈥斺�斿厠闅嗘椂鎸� seq 澶嶇敤鏄庣粏锛�
+ // 涓虹畝鍖栵細鐩存帴鎸� schedule_seq 澶嶅埗 N 鏉★紝姣忔潯鐢ㄥ搴� detail銆�
+ Date now = DateUtils.getNowDate();
+ // 鎶� detailList 鎸� seq 绱㈠紩
+ java.util.Map<Integer, ServiceTaskScheduleDetail> detailBySeq = new java.util.HashMap<>(detailList.size());
+ for (ServiceTaskScheduleDetail d : detailList) {
+ detailBySeq.put(d.getSeq(), d);
+ }
+
+ int produced = 0;
+ for (ServiceSubtask src : currentLoopSubtasks) {
+ ServiceTaskScheduleDetail detail = detailBySeq.get(src.getScheduleSeq());
+ if (detail == null) {
+ log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鎵句笉鍒� seq={} 瀵瑰簲鐨勬槑缁�, 璺宠繃 subtask id={}",
+ scheduleId, src.getScheduleSeq(), src.getId());
+ continue;
+ }
+ ServiceSubtask copy = cloneForNextLoop(src, detail, nextLoop, stepNum, stepUnit, now);
+ serviceSubtaskMapper.insertServiceSubtask(copy);
+ // 澶嶅埗 preachform
+ copyPreachform(src.getId(), copy);
+ produced++;
+ }
+
+ // 鎺ㄨ繘 schedule 鐨� current_loop_count / next_seq / next_execute_time
+ ServiceTaskSchedule upd = new ServiceTaskSchedule();
+ upd.setId(scheduleId);
+ upd.setCurrentLoopCount(nextLoop);
+ upd.setCurrentSeq(1);
+ upd.setNextSeq(detailList.size() > 1 ? 2 : 1);
+ upd.setNextExecuteTime(advance(now, stepNum, stepUnit));
+ upd.setUpdateTime(now);
+ serviceTaskScheduleMapper.updateServiceTaskSchedule(upd);
+
+ log.info("[浠诲姟缁�-婊氬姩] scheduleId={} 鎺ㄨ繘鍒扮 {} 杞�, 鏂板缓 {} 鏉� subtask", scheduleId, nextLoop, produced);
+ return true;
+ }
+
+ /**
+ * 鍏嬮殕 subtask 鍒颁笅涓�杞細娓呬富閿�佹竻鍥炵瀛楁銆佹帹杩� visit_time銆佽缃� loop_count銆�
+ */
+ private ServiceSubtask cloneForNextLoop(ServiceSubtask src, ServiceTaskScheduleDetail detail,
+ long nextLoop, int stepNum, int stepUnit, Date now) {
+ ServiceSubtask copy = JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class);
+ copy.setId(null);
+ copy.setSenduuid(null);
+ copy.setResult(null);
+ copy.setFinishtime(null);
+ copy.setExrecallcount(null);
+ copy.setCurrentPreachform(null);
+ copy.setSendstate(2L);
+ copy.setLoopCount(nextLoop);
+ copy.setScheduleDetailId(detail.getId());
+ // 妯℃澘锛歞etail 浼樺厛
+ if (detail.getTemplateid() != null) {
+ copy.setTemplateid(detail.getTemplateid());
+ copy.setTemplatename(detail.getTemplatename());
+ }
+ if (detail.getLibtemplateid() != null) {
+ copy.setLibtemplateid(detail.getLibtemplateid());
+ }
+ Date base = src.getVisitTime() != null ? src.getVisitTime() : now;
+ Date nextVisit = advance(base, stepNum, stepUnit);
+ copy.setVisitTime(nextVisit);
+ copy.setSenddate(nextVisit);
+ copy.setLongSendTime(nextVisit);
+ copy.setCreateTime(now);
+ copy.setUpdateTime(now);
+ return copy;
+ }
+
+ /**
+ * 澶嶅埗婧� subtask 鐨勫彂閫佹柟寮忚鍒版柊 subtask銆�
+ */
+ private void copyPreachform(Long srcSubId, ServiceSubtask newSubtask) {
+ ServiceSubtaskPreachform q = new ServiceSubtaskPreachform();
+ q.setSubid(srcSubId);
+ q.setTaskid(newSubtask.getTaskid());
+ List<ServiceSubtaskPreachform> list = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(q);
+ if (CollectionUtils.isEmpty(list)) {
+ return;
+ }
+ for (ServiceSubtaskPreachform src : list) {
+ ServiceSubtaskPreachform pf = new ServiceSubtaskPreachform();
+ pf.setSort(src.getSort());
+ pf.setPreachform(src.getPreachform());
+ pf.setCompensateTime(src.getCompensateTime());
+ pf.setTaskid(newSubtask.getTaskid());
+ pf.setSubid(newSubtask.getId());
+ pf.setSendstate("1");
+ pf.setOrgid(newSubtask.getOrgid());
+ pf.setCreateTime(new Date());
+ serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf);
+ }
+ }
+
+ /**
+ * 鎸� schedule_time_unit 鎺ㄨ繘鏃堕棿锛�1-澶� 2-鍛� 3-鏈� 4-骞�
+ */
+ private Date advance(Date base, int num, int unit) {
+ Calendar c = Calendar.getInstance();
+ c.setTime(base);
+ switch (unit) {
+ case 2:
+ c.add(Calendar.WEEK_OF_YEAR, num);
+ break;
+ case 3:
+ c.add(Calendar.MONTH, num);
+ break;
+ case 4:
+ c.add(Calendar.YEAR, num);
+ break;
+ case 1:
+ default:
+ c.add(Calendar.DAY_OF_MONTH, num);
+ }
+ return c.getTime();
+ }
+}
--
Gitblit v1.9.3