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.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.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 = 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 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(clueId)); 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 = 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 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 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(clueId)); } Optional 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.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 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()); } }