FollowRecordTask.java 9.38 KB
package cn.fw.shirasawa.server.controller.task;

import cn.fw.common.cache.locker.DistributedLocker;
import cn.fw.shirasawa.common.utils.DateUtil;
import cn.fw.shirasawa.domain.db.follow.FollowRecord;
import cn.fw.shirasawa.domain.db.follow.FollowTask;
import cn.fw.shirasawa.domain.enums.FollowTypeEnum;
import cn.fw.shirasawa.domain.enums.OutTimeEnum;
import cn.fw.shirasawa.domain.enums.TaskStateEnum;
import cn.fw.shirasawa.rpc.backlog.TodoRpcService;
import cn.fw.shirasawa.rpc.backlog.dto.BackLogItemDTO;
import cn.fw.shirasawa.rpc.ehr.EhrRpcService;
import cn.fw.shirasawa.rpc.ehr.dto.StaffInfoDTO;
import cn.fw.shirasawa.service.bus.follow.FollowBizService;
import cn.fw.shirasawa.service.data.FollowRecordService;
import cn.fw.shirasawa.service.data.FollowTaskService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.Lock;

/**
 * @author : kurisu
 * @className : FollowRecordTask
 * @description : 跟进任务定时任务
 * @date: 2020-08-25 16:56
 */
@Component
@Slf4j
@ConditionalOnProperty(prefix = "task", name = "switch", havingValue = "on")
public class FollowRecordTask {
    private final FollowRecordService followRecordService;
    private final FollowBizService followBizService;
    private final TodoRpcService todoRpcService;
    private final FollowTaskService followTaskService;
    private final EhrRpcService ehrRpcService;
    private final DistributedLocker distributedLocker;

    @Value("${spring.cache.custom.global-prefix}:task")
    @Getter
    private String prefix;

    @Autowired
    public FollowRecordTask(final FollowRecordService followRecordService,
                            final FollowBizService followBizService,
                            final TodoRpcService todoRpcService,
                            final FollowTaskService followTaskService,
                            final EhrRpcService ehrRpcService,
                            final DistributedLocker distributedLocker) {
        this.followRecordService = followRecordService;
        this.followBizService = followBizService;
        this.todoRpcService = todoRpcService;
        this.followTaskService = followTaskService;
        this.ehrRpcService = ehrRpcService;
        this.distributedLocker = distributedLocker;
    }

    /**
     * 处理跟进逾期
     */
    @Scheduled(initialDelay = 1000 * 15, fixedRate = 1000 * 5)
    public void endTaskRecord() {
        final String cacheName = getCacheName("end-task");
        Lock lock = distributedLocker.lock(cacheName);
        boolean locked = ((RLock) lock).isLocked();
        if (!locked) {
            return;
        }
        try {
            List<FollowRecord> list = followRecordService.list(Wrappers.<FollowRecord>lambdaQuery()
                    .eq(FollowRecord::getOutTime, OutTimeEnum.ONGOING)
                    .le(FollowRecord::getDeadline, LocalDateTime.now())
                    .eq(FollowRecord::getYn, Boolean.TRUE)
                    .last("limit 0, 500")
            );
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            for (FollowRecord record : list) {
                followBizService.overdueProcessing(record);
            }
        } finally {
            distributedLocker.unlock(lock);
        }
    }

    /**
     * 普通待办指定时间才推送
     * 推送到待办系统
     */
    @Scheduled(cron = "0/8 * 8-18 * * *")
    public void push2NorTodo() {
        final String cacheName = getCacheName("push-task:normal");
        Lock lock = distributedLocker.lock(cacheName);
        boolean locked = ((RLock) lock).isLocked();
        if (!locked) {
            return;
        }
        try {
            List<FollowRecord> list = followRecordService.list(Wrappers.<FollowRecord>lambdaQuery()
                    .eq(FollowRecord::getOutTime, OutTimeEnum.ONGOING)
                    .eq(FollowRecord::getAddTodo, Boolean.FALSE)
                    .notIn(FollowRecord::getType, FollowTypeEnum.AC, FollowTypeEnum.CF, FollowTypeEnum.PF)
                    .le(FollowRecord::getPlanTime, LocalDateTime.now())
                    .eq(FollowRecord::getYn, Boolean.TRUE)
                    .last("limit 0,500")
            );
            execute(list);
        } finally {
            distributedLocker.unlock(lock);
        }
    }

    /**
     * 事故车待办随时推送
     * 推送到待办系统
     */
    @Scheduled(initialDelay = 1500, fixedRate = 1000 * 3)
    public void push2AccTodo() {
        final String cacheName = getCacheName("push-task:acc");
        Lock lock = distributedLocker.lock(cacheName);
        boolean locked = ((RLock) lock).isLocked();
        if (!locked) {
            return;
        }
        try {
            List<FollowRecord> list = followRecordService.list(Wrappers.<FollowRecord>lambdaQuery()
                    .eq(FollowRecord::getOutTime, OutTimeEnum.ONGOING)
                    .eq(FollowRecord::getAddTodo, Boolean.FALSE)
                    .eq(FollowRecord::getYn, Boolean.TRUE)
                    .in(FollowRecord::getType, FollowTypeEnum.AC, FollowTypeEnum.CF, FollowTypeEnum.PF)
                    .le(FollowRecord::getPlanTime, LocalDateTime.now())
                    .last("limit 0,50")
            );
            execute(list);
        } finally {
            distributedLocker.unlock(lock);
        }
    }


    /**
     * 完成待办的重试
     */
    @Scheduled(initialDelay = 1500, fixedRate = 15 * 1000)
    public void retryCompleteTodoItem() {
        final String cacheName = getCacheName("retry:complete");
        Lock lock = distributedLocker.lock(cacheName);
        boolean locked = ((RLock) lock).isLocked();
        if (!locked) {
            return;
        }
        try {
            String key = todoRpcService.generateKey(TodoRpcService.TodoOperationEnum.COMPLETE);
            Collection<BackLogItemDTO> all = todoRpcService.getAllFromCache(key);
            all.forEach(todoRpcService::complete);
        } finally {
            distributedLocker.unlock(lock);
        }
    }

    /**
     * 取消待办的重试
     */
    @Scheduled(initialDelay = 1000, fixedRate = 10 * 1000)
    public void retryCancelTodoItem() {
        final String cacheName = getCacheName("retry:cancel");
        Lock lock = distributedLocker.lock(cacheName);
        boolean locked = ((RLock) lock).isLocked();
        if (!locked) {
            return;
        }
        try {
            String key = todoRpcService.generateKey(TodoRpcService.TodoOperationEnum.CANCEL);
            Collection<BackLogItemDTO> all = todoRpcService.getAllFromCache(key);
            all.forEach(todoRpcService::cancel);
        } finally {
            distributedLocker.unlock(lock);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public void execute(List<FollowRecord> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<Long> idList = new ArrayList<>();
        List<Long> failIdList = new ArrayList<>();
        for (FollowRecord record : list) {
            Long userId = record.getUserId();
            StaffInfoDTO infoDTO = ehrRpcService.queryStaffInfo(userId);
            if (Objects.isNull(infoDTO) || !Integer.valueOf(2).equals(infoDTO.getStaffStatus())) {
                continue;
            }
            FollowTask task = followTaskService.getById(record.getTaskId());
            if (Objects.nonNull(task) && TaskStateEnum.ONGOING.equals(task.getState())) {
                if (execute(record)) {
                    idList.add(record.getId());
                }
            } else {
                failIdList.add(record.getId());
            }
        }
        followRecordService.addTodoBatchById(idList);
        if (!CollectionUtils.isEmpty(failIdList)) {
            followRecordService.removeByIds(failIdList);
        }
    }

    private boolean execute(FollowRecord record) {
        try {
            String note = record.getNote();
            HashMap<String, String> dynamicMap = Optional.ofNullable(JSONObject.<HashMap<String, String>>parseObject(note, HashMap.class)).orElse(new HashMap<>(4));
            dynamicMap.put("type", record.getType().getValue().toString());
            dynamicMap.put("taskId", record.getTaskId().toString());
            dynamicMap.put("id", record.getId().toString());
            BackLogItemDTO dto = new BackLogItemDTO(record.getUserId(), record.getTodoCode(), String.valueOf(record.getId()), DateUtil.toDate(record.getPlanTime()), record.getShopId());
            dto.setExpireTime(DateUtil.toDate(record.getDeadline()));
            dto.setDynamicMap(dynamicMap);
            return todoRpcService.push(dto);
        } catch (Exception e) {
            log.error("推送待办失败 record: [{}]", record, e);
            return false;
        }
    }

    private String getCacheName(@NonNull String name) {
        return getPrefix() + ":" + name;
    }
}