package cn.fw.valhalla.service.bus.follow.strategy.impl; import cn.fw.shirasawa.sdk.enums.BusinessTypeEnum; import cn.fw.shirasawa.sdk.enums.DataTypeEnum; import cn.fw.shirasawa.sdk.enums.TerminationReason; import cn.fw.valhalla.common.utils.DateUtil; import cn.fw.valhalla.domain.db.OriginalData; import cn.fw.valhalla.domain.db.follow.ClueTask; import cn.fw.valhalla.domain.db.follow.FollowClue; import cn.fw.valhalla.domain.db.pub.PubCluePool; import cn.fw.valhalla.domain.dto.CustomerDetailDto; import cn.fw.valhalla.domain.enums.*; import cn.fw.valhalla.domain.vo.setting.SettingVO; import cn.fw.valhalla.rpc.ehr.EhrRpcService; import cn.fw.valhalla.rpc.ehr.dto.StaffInfoDTO; import cn.fw.valhalla.rpc.oop.OopService; import cn.fw.valhalla.rpc.oop.dto.ShopDTO; import cn.fw.valhalla.rpc.shirasawa.ShirasawaRpcService; import cn.fw.valhalla.rpc.shirasawa.dto.ClueStopDTO; import cn.fw.valhalla.rpc.shirasawa.dto.FollowInitDTO; import cn.fw.valhalla.service.bus.cust.CustomerBizService; import cn.fw.valhalla.service.bus.follow.strategy.FollowStrategy; import cn.fw.valhalla.service.bus.setting.SettingBizService; import cn.fw.valhalla.service.data.ClueTaskService; import cn.fw.valhalla.service.data.PubCluePoolService; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import static cn.fw.common.businessvalidator.Validator.BV; import static cn.fw.valhalla.service.bus.setting.strategy.SettingStrategy.COMMON_BRAND_ID; /** * 公共池线索跟进 * * @author : kurisu * @version : 1.0 * @className : PubFollowStrategy * @description : 公共池线索跟进 * @date : 2023-03-21 16:52 */ @Slf4j @Component public class PubFollowStrategy implements FollowStrategy { private final PubCluePoolService pubCluePoolService; private final ClueTaskService clueTaskService; private final CustomerBizService customerBizService; private final ShirasawaRpcService shirasawaRpcService; private final OopService oopService; private final SettingBizService settingBizService; private final EhrRpcService ehrRpcService; private final StringRedisTemplate redisTemplate; @Value("${spring.cache.custom.global-prefix}:follow:clue:change:STOP") @Getter private String clueChangeKey; @Autowired public PubFollowStrategy(final PubCluePoolService pubCluePoolService, final ClueTaskService clueTaskService, final CustomerBizService customerBizService, final ShirasawaRpcService shirasawaRpcService, final OopService oopService, final SettingBizService settingBizService, final EhrRpcService ehrRpcService, final StringRedisTemplate redisTemplate) { this.pubCluePoolService = pubCluePoolService; this.clueTaskService = clueTaskService; this.customerBizService = customerBizService; this.shirasawaRpcService = shirasawaRpcService; this.oopService = oopService; this.settingBizService = settingBizService; this.ehrRpcService = ehrRpcService; this.redisTemplate = redisTemplate; } @Override public FollowTypeEnum getFollowType() { return FollowTypeEnum.PL; } /** * 公共池线索跟进此方法无效 * * @param originalData * @return */ @Override public boolean origin2task(final OriginalData originalData) { // 公共池线索不存在这种场景 return false; } /** * 公共池线索跟进此方法无效 * * @param followClue * @return */ @Override public void startClue(final FollowClue followClue) { // 公共池线索不存在这种场景 } @Override public void settingChanged(List setting, Long groupId) { // 更改设置不影响公共池线索跟进 } @Override public void forceStopClue(final FollowClue clue, final boolean fromTask) { // 公共池线索不存在这种场景 } @Transactional(rollbackFor = Exception.class) public void startClue(final PubCluePool pubClue) { ClueTask clueTask = new ClueTask(); clueTask.setClueId(pubClue.getId()); clueTask.setType(getFollowType()); clueTask.setBeginTime(pubClue.getStartTime().atStartOfDay()); clueTask.setRedistribution(Boolean.FALSE); clueTask.setDeadline(pubClue.getDeadline().atStartOfDay()); clueTask.setState(TaskStateEnum.ONGOING); clueTask.setGroupId(pubClue.getGroupId()); clueTask.setFollowShop(pubClue.getShopId()); clueTask.setFollowUser(pubClue.getAdviserId()); clueTask.setFollowUserName(pubClue.getAdviserName()); clueTask.setVersion(2); clueTaskService.save(clueTask); final FollowInitDTO followInitDTO = creteFollowInitDTO(pubClue, clueTask); shirasawaRpcService.createFollowData(followInitDTO); } @Transactional(rollbackFor = Exception.class) public void clueConvertSuccess(final PubCluePool pubClue) { pubClue.setState(PublicClueStateEnum.COMPLETE); pubClue.setCloseTime(LocalDateTime.now()); ClueTask clueTask = clueTaskService.queryOngoingTaskByClueId(pubClue.getId(), FollowTypeEnum.PL); if (Objects.nonNull(clueTask)) { clueTask.setCloseTime(LocalDateTime.now()); clueTask.setFinishUser(pubClue.getAdviserId()); StaffInfoDTO infoDTO = ehrRpcService.queryStaffInfo(pubClue.getAdviserId()); if (Objects.nonNull(infoDTO)) { clueTask.setFinishUserName(infoDTO.getName()); } clueTask.setState(TaskStateEnum.COMPLETE); clueTask.setFinishShop(pubClue.getShopId()); boolean rpcSucess = rpcStopTask(clueTask); clueTask.setRpcSuccess(rpcSucess); clueTaskService.updateById(clueTask); redisTemplate.opsForSet().add(getClueChangeKey(), String.valueOf(clueTask.getId())); } pubCluePoolService.updateById(pubClue); } @Transactional(rollbackFor = Exception.class) public void clueConvertFailed(final PubCluePool pubClue, Long userId, Long shopId) { pubClue.setState(PublicClueStateEnum.DEFEAT); pubClue.setCloseTime(LocalDateTime.now()); pubClue.setDefeatReason(TaskDefeatTypeEnum.F); ClueTask clueTask = clueTaskService.queryOngoingTaskByClueId(pubClue.getId(), FollowTypeEnum.PL); if (Objects.nonNull(clueTask)) { clueTask.setCloseTime(LocalDateTime.now()); clueTask.setFinishUser(pubClue.getAdviserId()); StaffInfoDTO infoDTO = ehrRpcService.queryStaffInfo(userId); if (Objects.nonNull(infoDTO)) { clueTask.setFinishUserName(infoDTO.getName()); } clueTask.setState(TaskStateEnum.DEFEAT); clueTask.setReason(TaskDefeatTypeEnum.F); clueTask.setFinishShop(shopId); boolean rpcSucess = rpcStopTask(clueTask); clueTask.setRpcSuccess(rpcSucess); clueTaskService.updateById(clueTask); redisTemplate.opsForSet().add(getClueChangeKey(), String.valueOf(clueTask.getId())); } pubCluePoolService.updateById(pubClue); } /** * 审批而来的数据 * * @param vin * @param groupId */ @Transactional(rollbackFor = Exception.class) public void clueConvertFailedFromFlow(final String vin, final Long groupId) { PubCluePool cluePool = pubCluePoolService.getOne(Wrappers.lambdaQuery() .eq(PubCluePool::getVin, vin) .eq(PubCluePool::getGroupId, groupId) , false); if (Objects.isNull(cluePool)) { return; } Long clueId = cluePool.getId(); ClueTask clueTask = clueTaskService.queryOngoingTaskByClueId(clueId, FollowTypeEnum.PL); if (Objects.nonNull(clueTask)) { clueTask.setCloseTime(LocalDateTime.now()); clueTask.setState(TaskStateEnum.DEFEAT); clueTask.setReason(TaskDefeatTypeEnum.A); boolean rpcSucess = rpcStopTask(clueTask); clueTask.setRpcSuccess(rpcSucess); clueTaskService.updateById(clueTask); redisTemplate.opsForSet().add(getClueChangeKey(), String.valueOf(clueTask.getId())); } if (PublicClueStateEnum.ONGOING.equals(cluePool.getState())) { cluePool.setState(PublicClueStateEnum.DEFEAT); cluePool.setCloseTime(LocalDateTime.now()); cluePool.setDefeatReason(TaskDefeatTypeEnum.A); pubCluePoolService.updateById(cluePool); } } @Override @Transactional(rollbackFor = Exception.class) public void onRoleChangeCloseTask(final ClueTask task) { task.setState(TaskStateEnum.DEFEAT); task.setCloseTime(LocalDateTime.now()); task.setReason(TaskDefeatTypeEnum.D); boolean rpcSucess = rpcStopTask(task); if (!rpcSucess) { log.info("跟进系统终止任务失败"); } task.setRpcSuccess(rpcSucess); clueTaskService.updateById(task); redisTemplate.opsForSet().add(getClueChangeKey(), String.valueOf(task.getId())); } @Override public void forceStopTask(final ClueTask task) { // 公共池线索不存在这种场景 } /** * 维护跟进次数 * * @param clue 虚拟的跟进线索[只有id 和类型 其他信息没有] * @param overdue */ @Override public void onFollowComplete(final FollowClue clue, final boolean overdue) { ClueTask task = clueTaskService.queryOngoingTaskByClueId(clue.getId(), FollowTypeEnum.PL); if (!overdue && Objects.nonNull(task)) { Integer times = Optional.ofNullable(task.getTimes()).orElse(0); task.setTimes(times + 1); clueTaskService.updateById(task); } } @Override public void syncEndTask(final ClueTask task) { if (TaskStateEnum.ONGOING.equals(task.getState())) { return; } boolean res = rpcStopTask(task); task.setRpcSuccess(res); clueTaskService.updateById(task); } @Override @Transactional(rollbackFor = Exception.class) public void closeTask(final ClueTask task) { final Long clueId = task.getClueId(); PubCluePool pubCluePool = pubCluePoolService.getById(clueId); BV.notNull(pubCluePool, () -> "公共池跟进线索不存在: " + 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); clueTaskService.updateById(task); pubCluePool.setCloseTime(task.getCloseTime()); pubCluePool.setState(PublicClueStateEnum.DEFEAT); pubCluePool.setDefeatReason(task.getReason()); pubCluePoolService.updateById(pubCluePool); customerBizService.pubTaskEndAbandon(pubCluePool); redisTemplate.opsForSet().add(getClueChangeKey(), String.valueOf(task.getId())); } private boolean rpcStopTask(ClueTask clueTask) { ClueStopDTO clueStopDTO = new ClueStopDTO(); clueStopDTO.setType(DataTypeEnum.ofValue(clueTask.getType().getValue())); clueStopDTO.setBusinessType(BusinessTypeEnum.AS); clueStopDTO.setDetailId(String.valueOf(clueTask.getClueId())); clueStopDTO.setShopId(clueTask.getFinishShop()); ShopDTO shop = oopService.shop(clueTask.getFinishShop()); if (Objects.nonNull(shop)) { clueStopDTO.setShopName(shop.getShortName()); } clueStopDTO.setCompleteTime(DateUtil.localDateTime2Date(clueTask.getCloseTime())); clueStopDTO.setGroupId(clueTask.getGroupId()); clueStopDTO.setUserId(clueTask.getFollowUser()); clueStopDTO.setUserName(clueTask.getFollowUserName()); clueStopDTO.setReason(TerminationReason.ABANDON); if (TaskStateEnum.DEFEAT.equals(clueTask.getState())) { clueStopDTO.setTermination(Boolean.TRUE); if (TaskDefeatTypeEnum.A.equals(clueTask.getReason())) { clueStopDTO.setReason(TerminationReason.ABANDON); } if (TaskDefeatTypeEnum.F.equals(clueTask.getReason())) { clueStopDTO.setReason(TerminationReason.ANOTHER); } if (TaskDefeatTypeEnum.D.equals(clueTask.getReason())) { clueStopDTO.setReason(TerminationReason.ROLE_CHANGE); } } return shirasawaRpcService.stopTask(clueStopDTO); } private FollowInitDTO creteFollowInitDTO(PubCluePool pubClue, ClueTask clueTask) { CustomerDetailDto customerDetailDto = customerBizService.queryByFrameNo(pubClue.getVin(), pubClue.getGroupId()); BV.notNull(customerDetailDto, () -> "档案信息有误"); final FollowInitDTO followInitDTO = FollowInitDTO.builder() .businessType(BusinessTypeEnum.AS) .type(DataTypeEnum.ofValue(getFollowType().getValue())) .customerId(customerDetailDto.getId()) .customerName(customerDetailDto.getName()) .memberId(customerDetailDto.getMemberId()) .plateNo(customerDetailDto.getPlateNo()) .frameNo(pubClue.getVin()) .contacts(customerDetailDto.getMobile()) .detailId(String.valueOf(clueTask.getClueId())) .generateTime(DateUtil.localDateTime2Date(clueTask.getBeginTime())) .deadline(DateUtil.localDateTime2Date(clueTask.getDeadline())) .groupId(clueTask.getGroupId()) .shopId(clueTask.getFollowShop()) .userId(clueTask.getFollowUser()) .userName(clueTask.getFollowUserName()) .bizId(pubClue.getVin()) .followDuration(Duration.ofHours(36L)) .build(); followInitDTO.setShopName(pubClue.getShopName()); Optional setting = settingBizService.querySettingByType(FollowTypeEnum.OT, SettingTypeEnum.EFFECTIVE_TIME, clueTask.getGroupId(), COMMON_BRAND_ID); setting.ifPresent(r -> { Integer unit = r.getUnit(); int detailValue = Optional.ofNullable(r.getDetailValue()).orElse(0); Duration duration = transferDuration(detailValue, unit); BV.notNull(duration, () -> "设置有误,周期计算不成功"); followInitDTO.setFollowDuration(duration); }); Map noteMap = createNoteMap(pubClue, customerDetailDto); followInitDTO.setNoteMap(noteMap); return followInitDTO; } private Map createNoteMap(PubCluePool pubClue, CustomerDetailDto detailDto) { Map dynamicMap = new HashMap<>(); if (Objects.nonNull(pubClue)) { int count = Optional.ofNullable(detailDto.getArrivalCount()).orElse(0); Date buyDate = detailDto.getBuyDate(); Date expires = detailDto.getInsuranceExpires(); String name = detailDto.getName(); String vin = pubClue.getVin(); String plate = detailDto.getPlateNo(); String arrivalCount = String.valueOf(count); String defeatCount = "0"; String vehicleAge = String.valueOf(Math.abs(DateUtil.sub(buyDate, new Date(), "y"))); String _buyDate = DateUtil.getStringDateShort(buyDate); String insuranceExpires = DateUtil.getStringDateShort(expires); dynamicMap.put("name", name); dynamicMap.put("vin", vin); dynamicMap.put("plate", plate); dynamicMap.put("arrivalCount", arrivalCount); dynamicMap.put("defeatCount", defeatCount); dynamicMap.put("vehicleAge", vehicleAge + "年"); dynamicMap.put("buyDate", _buyDate); dynamicMap.put("insuranceExpires", insuranceExpires); } return dynamicMap; } }