IRFollowStrategy.java 15 KB
package cn.fw.valhalla.service.bus.follow.strategy.impl;

import cn.fw.valhalla.common.constant.RoleCode;
import cn.fw.valhalla.common.utils.StringUtils;
import cn.fw.valhalla.domain.db.OriginalData;
import cn.fw.valhalla.domain.db.customer.Customer;
import cn.fw.valhalla.domain.db.customer.CustomerReachLog;
import cn.fw.valhalla.domain.db.follow.ClueTask;
import cn.fw.valhalla.domain.db.follow.FollowClue;
import cn.fw.valhalla.domain.dto.CustomerDetailDto;
import cn.fw.valhalla.domain.enums.*;
import cn.fw.valhalla.domain.vo.customer.CustomerDetailVO;
import cn.fw.valhalla.domain.vo.setting.SettingVO;
import cn.fw.valhalla.rpc.erp.dto.PostUserDTO;
import cn.fw.valhalla.rpc.oop.dto.ShopDTO;
import cn.fw.valhalla.rpc.shirasawa.dto.FollowInitDTO;
import cn.fw.valhalla.service.bus.follow.strategy.AbstractFollowStrategy;
import cn.fw.valhalla.service.data.CustomerReachLogService;
import cn.fw.valhalla.service.event.ClueFromPublicEvent;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static cn.fw.common.businessvalidator.Validator.BV;
import static cn.fw.valhalla.service.bus.setting.strategy.SettingStrategy.COMMON_BRAND_ID;

/**
 * @author : kurisu
 * @className : IRFollowStrategy
 * @description : 续保策略
 * @date: 2020-08-17 10:48
 */
@Slf4j
@Component
@SuppressWarnings("Duplicates")
public class IRFollowStrategy extends AbstractFollowStrategy {
    private final CustomerReachLogService customerReachLogService;
    private final ApplicationEventPublisher eventPublisher;

    @Autowired
    public IRFollowStrategy(final CustomerReachLogService customerReachLogService,
                            final ApplicationEventPublisher eventPublisher) {
        this.customerReachLogService = customerReachLogService;
        this.eventPublisher = eventPublisher;
    }

    @Override
    public FollowTypeEnum getFollowType() {
        return FollowTypeEnum.IR;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean origin2task(OriginalData originalData) {
        Customer customer = null;
        if (StringUtils.isValid(originalData.getFrameNo())) {
            customer = customerService.queryByFrameNo(originalData.getFrameNo(), originalData.getGroupId());
        }
        if (Objects.isNull(customer)) {
            customer = customerService.queryById(originalData.getCustomerId());
        }
        BV.notNull(customer, () -> "档案不存在");
        FollowClue completeClue = followClueService.getOne(Wrappers.<FollowClue>lambdaQuery()
                        .eq(FollowClue::getVin, customer.getFrameNo())
                        .eq(FollowClue::getClueType, getFollowType())
                        .eq(FollowClue::getClueState, ClueStatusEnum.ONGOING)
                , Boolean.FALSE);
        finishClue(originalData, completeClue);

        FollowClue wattingClue = followClueService.getOne(Wrappers.<FollowClue>lambdaQuery()
                        .eq(FollowClue::getVin, customer.getFrameNo())
                        .eq(FollowClue::getClueType, getFollowType())
                        .eq(FollowClue::getClueState, ClueStatusEnum.WAITING)
                , Boolean.FALSE);
        if (Objects.nonNull(wattingClue)) {
            followClueService.removeById(wattingClue.getId());
        }
        FollowClue clue = createClueInfo(originalData, getFollowType(), customer);
        followClueService.save(clue);
        createFirstNotice(clue, customer.getId(), customer.getBrandId());
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void startClue(FollowClue followClue) {
        if (!ClueStatusEnum.WAITING.equals(followClue.getClueState())) {
            return;
        }
        Customer customer = customerService.queryByFrameNo(followClue.getVin(), followClue.getGroupId());
        if (Objects.isNull(customer)) {
            followClueService.removeById(followClue.getId());
            return;
        }
        Optional<SettingVO> settingVO = settingBizService.querySettingByType(getFollowType(), SettingTypeEnum.MODE, followClue.getGroupId(), COMMON_BRAND_ID);
        // 模式 1、续保角色 2、续保角色+服务接待/新车销售
        final int mode = settingVO.map(SettingVO::getDetailValue).orElse(1);
        followClue.setPlateNo(customer.getPlateNo());
        final ClueTask clueTask = createNewTask(followClue);
        List<PostUserDTO> userByRole;
        if (mode == 1) {
            userByRole = userService.getShopRolesUser(clueTask.getFollowShop(), RoleCode.XBGJ);
        } else {
            userByRole = userService.getShopRolesUser(clueTask.getFollowShop(), RoleCode.XBGJ, RoleCode.FWGW);
        }
        BV.isNotEmpty(userByRole, () -> String.format("该门店[mode: %s]没有【跟进】人员", mode));
        fillTaskUser(clueTask, userByRole);
        if (mode == 2 && Objects.isNull(customer.getAdviserId())) {
            final ClueFromPublicEvent poolEvent = new ClueFromPublicEvent();
            poolEvent.setVin(customer.getFrameNo());
            poolEvent.setShopId(clueTask.getFollowShop());
            poolEvent.setUserId(clueTask.getFollowUser());
            poolEvent.setType(PubStandType.IR);
            poolEvent.setGroupId(clueTask.getGroupId());
            CompletableFuture.runAsync(() -> eventPublisher.publishEvent(poolEvent));
        }
        clueTaskService.save(clueTask);
        followClue.setClueState(ClueStatusEnum.ONGOING);
        final FollowInitDTO followInitDTO = creteFollowInitDTO(followClue, clueTask);
        shirasawaRpcService.createFollowData(followInitDTO);
        followClueService.updateById(followClue);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeTask(ClueTask task) {
        final Long clueId = task.getClueId();
        FollowClue clue = followClueService.getById(clueId);
        BV.notNull(clue, () -> "跟进线索不存在: " + clueId);
        task.setState(TaskStateEnum.DEFEAT);
        task.setCloseTime(task.getDeadline().minusSeconds(1L));
        task.setReason(TaskDefeatTypeEnum.C);
        boolean rpcSucess = rpcStopTask(task);
        if (!rpcSucess) {
            log.info("跟进系统终止任务失败");
        }
        task.setRpcSuccess(rpcSucess);
        if (Objects.nonNull(clue)) {
            clue.setClueState(ClueStatusEnum.FAILURE);
            clue.setCloseTime(task.getCloseTime());
            followClueService.updateById(clue);
            redisTemplate.opsForSet().add(generateStopKey(), String.valueOf(task.getId()));
            afterStopClue(clue);
        }
        clueTaskService.updateById(task);
    }

    /**
     * [角色变动]结束任务
     *
     * @param task
     */
    @Transactional(rollbackFor = Exception.class)
    public void onRoleChangeCloseTask(ClueTask task) {
        Long clueId = task.getClueId();
        FollowClue clue = followClueService.getById(clueId);
        BV.notNull(clue, () -> "跟进线索不存在: " + clueId);
        Customer customer = customerService.queryByFrameNo(clue.getVin(), clue.getGroupId());
        Long adviserId = Optional.ofNullable(customer).map(Customer::getAdviserId).orElse(null);

        Optional<SettingVO> settingVO = settingBizService.querySettingByType(getFollowType(), SettingTypeEnum.MODE, clue.getGroupId(), COMMON_BRAND_ID);
        // 模式 1、续保角色 2、续保角色+服务接待/新车销售
        final int mode = settingVO.map(SettingVO::getDetailValue).orElse(1);
        task.setState(TaskStateEnum.DEFEAT);
        task.setCloseTime(LocalDateTime.now());
        task.setReason(TaskDefeatTypeEnum.D);
        if (mode == 1) {
            task.setRpcSuccess(true);
            clueTaskService.updateById(task);
            List<PostUserDTO> userByRole = userService.getShopRolesUser(task.getFollowShop(), RoleCode.XBGJ);
            BV.isNotEmpty(userByRole, () -> String.format("该门店[mode: %s]没有【跟进】人员", mode));
            int randomIndex = new Random().nextInt(userByRole.size());
            PostUserDTO userDTO = userByRole.get(randomIndex);
            createSecondaryTask(clue, userDTO.getUserId());
        } else {
            if (Objects.isNull(adviserId)) {
                List<PostUserDTO> userByRole = userService.getShopRolesUser(task.getFollowShop(), RoleCode.XBGJ, RoleCode.FWGW);
                BV.isNotEmpty(userByRole, () -> String.format("该门店[mode: %s]没有【跟进】人员", mode));
                int randomIndex = new Random().nextInt(userByRole.size());
                PostUserDTO userDTO = userByRole.get(randomIndex);
                adviserId = userDTO.getUserId();
            } else {
                if (task.getFollowUser().equals(adviserId)) {
                    return;
                }
            }

            boolean rpcSucess = rpcStopTask(task);
            if (!rpcSucess) {
                log.info("跟进系统终止任务失败");
            }
            task.setRpcSuccess(rpcSucess);
            clueTaskService.updateById(task);
            this.createSecondaryTask(clue, adviserId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void forceStopTask(ClueTask task) {
        Long clueId = task.getClueId();
        FollowClue clue = followClueService.getById(clueId);
        task.setState(TaskStateEnum.DEFEAT);
        task.setCloseTime(LocalDateTime.now());
        task.setReason(TaskDefeatTypeEnum.A);
        boolean rpcSucess = rpcStopTask(task);
        task.setRpcSuccess(rpcSucess);
        clueTaskService.updateById(task);
        if (Objects.nonNull(clue)) {
            clue.setClueState(ClueStatusEnum.FAILURE);
            clue.setCloseTime(LocalDateTime.now());
            followClueService.updateById(clue);
            afterStopClue(clue);
            redisTemplate.opsForSet().add(generateStopKey(), String.valueOf(task.getId()));
        }
        Optional<SettingVO> settingVO = settingBizService.querySettingByType(getFollowType(), SettingTypeEnum.MODE, clue.getGroupId(), COMMON_BRAND_ID);
        // 模式 1、续保角色 2、续保角色+服务接待/新车销售
        final int mode = settingVO.map(SettingVO::getDetailValue).orElse(1);
        if (mode != 1) {
            CustomerDetailVO detailByVin = customerBizService.getDetailByVin(clue.getVin(), clue.getGroupId());
            Long aLong = Optional.ofNullable(detailByVin).map(CustomerDetailVO::getAdviserId).orElse(null);
            if (task.getFollowUser().equals(aLong)) {
                customerBizService.abandon(detailByVin.getId(), "主动放弃");
            }
        }
    }

    @Override
    protected ClueTask createNewTask(FollowClue followClue) {
        final FollowTypeEnum followTypeEnum = followClue.getClueType();
        final ClueTask clueTask = new ClueTask();
        clueTask.setClueId(followClue.getId());
        clueTask.setType(followTypeEnum);
        clueTask.setBeginTime(followClue.getStartTime());
        clueTask.setRedistribution(Boolean.FALSE);
        clueTask.setDeadline(followClue.getEndTime());
        clueTask.setState(TaskStateEnum.ONGOING);
        clueTask.setGroupId(followClue.getGroupId());
        Long userId = null;
        Long shopId = null;
        Customer customer = customerService.queryByFrameNo(followClue.getVin(), followClue.getGroupId());
        if (Objects.nonNull(customer)) {
            userId = customer.getAdviserId();
            shopId = customer.getShopId();
            ShopDTO shop = oopService.shop(shopId);
            if (Objects.isNull(shop)) {
                userId = null;
                shopId = null;
            }
        }
        if (Objects.isNull(shopId)) {
            CustomerReachLog lastReach = customerReachLogService.getOne(Wrappers.<CustomerReachLog>lambdaQuery()
                            .eq(CustomerReachLog::getFrameNo, followClue.getVin())
                            .eq(CustomerReachLog::getGroupId, followClue.getGroupId())
                            .orderByDesc(CustomerReachLog::getArrivalTime)
                    , Boolean.FALSE);
            if (Objects.nonNull(lastReach)) {
                shopId = lastReach.getShopId();
            }

            if (Objects.isNull(shopId)) {
                shopId = followClue.getSuggestShopId();
            }
        }
        if (shopId == 146L) {
            ArrayList<Long> arrayList = ListUtil.toList(67L, 65L, 66L);
            int randomIndex = new Random().nextInt(arrayList.size());
            shopId = arrayList.get(randomIndex);
        }
        clueTask.setFollowShop(shopId);
        clueTask.setFollowUser(userId);
        clueTask.setVersion(2);
        return clueTask;
    }

    private void afterStopClue(FollowClue clue) {
        String vin = clue.getVin();
        Long groupId = clue.getGroupId();
        LocalDate originTime = clue.getOriginTime().plusYears(1L).toLocalDate();
        CustomerDetailDto customer = customerBizService.queryByFrameNo(vin, groupId);
        if (Objects.isNull(customer)) {
            return;
        }
        final FollowClue newClue = new FollowClue();
        newClue.setVin(vin);
        newClue.setPlateNo(customer.getPlateNo());
        newClue.setOriginTime(clue.getOriginTime().plusYears(1L));
        newClue.setClueState(ClueStatusEnum.WAITING);
        newClue.setClueType(clue.getClueType());
        newClue.setGroupId(groupId);
        newClue.setSuggestShopId(Objects.isNull(customer.getShopId()) ? clue.getSuggestShopId() : customer.getShopId());
        newClue.setSuggestMobile(StringUtils.isEmpty(customer.getMobile()) ? clue.getSuggestMobile() : customer.getMobile());

        settingBizService.querySettingByType(clue.getClueType(), SettingTypeEnum.FIRST_TRIGGER_TIME, groupId, customer.getBrandId())
                .ifPresent(r -> {
                    int detailValue = Optional.ofNullable(r.getDetailValue()).orElse(0);
                    SettingUnitEnum unitEnum = Objects.requireNonNull(SettingUnitEnum.ofValue(r.getUnit()), "时间单位缺失");
                    LocalDateTime localDateTime = calTime(originTime.atStartOfDay(), unitEnum, detailValue * -1);
                    newClue.setStartTime(localDateTime);
                });

        settingBizService.querySettingByType(clue.getClueType(), SettingTypeEnum.FAIL_TIME, groupId, customer.getBrandId())
                .ifPresent(r -> {
                    int detailValue = Optional.ofNullable(r.getDetailValue()).orElse(0);
                    SettingUnitEnum unitEnum = Objects.requireNonNull(SettingUnitEnum.ofValue(r.getUnit()), "时间单位缺失");
                    LocalDateTime localDateTime = calTime(originTime.atStartOfDay(), unitEnum, detailValue);
                    newClue.setEndTime(localDateTime);
                });
        followClueService.save(newClue);
        createFirstNotice(newClue, customer.getId(), customer.getBrandId());
    }
}