package cn.fw.valhalla.service.bus.follow.strategy.impl; import cn.fw.common.exception.BusinessException; import cn.fw.valhalla.common.constant.RoleCode; import cn.fw.valhalla.common.utils.DateUtil; import cn.fw.valhalla.common.utils.MobileUtil; import cn.fw.valhalla.domain.db.OriginalData; import cn.fw.valhalla.domain.db.customer.Customer; import cn.fw.valhalla.domain.db.follow.FollowNoticeRecord; import cn.fw.valhalla.domain.db.follow.FollowRecord; import cn.fw.valhalla.domain.db.follow.FollowRecordLog; import cn.fw.valhalla.domain.db.follow.FollowTask; import cn.fw.valhalla.domain.db.pool.CustomerCluePool; import cn.fw.valhalla.domain.dto.CustomerDetailDto; import cn.fw.valhalla.domain.dto.FollowAttachmentDTO; import cn.fw.valhalla.domain.enums.*; import cn.fw.valhalla.domain.vo.follow.*; import cn.fw.valhalla.domain.vo.setting.SettingVO; import cn.fw.valhalla.rpc.angel.dto.InsuranceDTO; import cn.fw.valhalla.rpc.erp.dto.PostUserDTO; import cn.fw.valhalla.rpc.erp.dto.UserInfoDTO; import cn.fw.valhalla.rpc.oop.dto.ShopDTO; import cn.fw.valhalla.service.bus.follow.strategy.AbstractFollowStrategy; import cn.fw.valhalla.service.event.CancelApproveEvent; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import static cn.fw.common.businessvalidator.Validator.BV; /** * @author : kurisu * @className : IRFollowStrategy * @description : 续保策略 * @date: 2020-08-17 10:48 */ @Slf4j @Component @SuppressWarnings("Duplicates") public class IRFollowStrategy extends AbstractFollowStrategy { @Override public FollowTypeEnum getFollowType() { return FollowTypeEnum.IR; } @Override public List getList(Integer startIndex, Integer pageSize, Long userId) { List list = list(startIndex, pageSize, userId, getFollowType()); if (CollectionUtils.isEmpty(list)) { return new ArrayList<>(); } List voList = new ArrayList<>(); for (FollowRecord followRecord : list) { IRTodoListVO vo = new IRTodoListVO(); vo.setId(followRecord.getId()); vo.setTaskId(followRecord.getTaskId()); vo.setCustomerId(followRecord.getCustomerId()); vo.setDeadline(followRecord.getDeadline()); CustomerDetailDto customerDetailDto = customerBizService.queryById(followRecord.getCustomerId()); vo.setName(customerDetailDto.getName()); vo.setCarImage(customerDetailDto.getCarImage()); vo.setPlateNo(customerDetailDto.getPlateNo()); Optional insuranceDTO = queryInsuInfo(followRecord.getCustomerId()); insuranceDTO.ifPresent(ins -> { vo.setInsComName(ins.getInsurerName()); vo.setInsExpiration(ins.getExpiryDate()); }); voList.add(vo); } return voList; } @Override public FollowDetailVO getDetail(Long id) { FollowRecord followRecord = followRecordService.getById(id); BV.notNull(followRecord, "跟进记录不存在"); IRDetailVO vo = assemble(followRecord.getCustomerId()); int count = followRecordLogService.count(Wrappers.lambdaQuery() .eq(FollowRecordLog::getRecordId, followRecord.getId()) .eq(FollowRecordLog::getAttType, AttTypeEnum.SMART_PHONE) ); vo.setHadCall(count > 0); vo.setId(followRecord.getId()); vo.setTaskId(followRecord.getTaskId()); vo.setDeadline(followRecord.getDeadline()); return vo; } @Override public List getRecordList(Long taskId) { return super.getRecordList(taskId); } @Override public void uploadAtt(FollowAttachmentDTO dto, Long userId) { super.uploadAtt(dto, userId); } @Override @Transactional(rollbackFor = Exception.class) public void cancelFollowTask(Long customerId, Date time, Long groupId) { CustomerCluePool cluePool = customerCluePoolService.queryByRefererId(customerId, groupId, getFollowType()); if (Objects.isNull(cluePool)) { return; } if (ClueStatusEnum.WAITING.equals(cluePool.getClueStatus())) { customerCluePoolService.removeById(cluePool.getId()); followNoticeRecordService.removeByClueId(cluePool.getId()); } if (ClueStatusEnum.ONGOING.equals(cluePool.getClueStatus())) { customerCluePoolService.removeById(cluePool.getId()); followNoticeRecordService.removeByClueId(cluePool.getId()); cancelFollowTodo(cluePool.getRefererId(), getFollowType()); } //暂不考虑退单回滚的情况 } @Override @Transactional(rollbackFor = Exception.class) public void settingChanged(List setting, Long groupId) { List poolList = customerCluePoolService.list(Wrappers.lambdaQuery() .eq(CustomerCluePool::getClueType, getFollowType()) .eq(CustomerCluePool::getGroupId, groupId) .eq(CustomerCluePool::getClueStatus, ClueStatusEnum.WAITING) ); if (CollectionUtils.isEmpty(poolList)) { return; } this.updateClue(poolList, setting); SettingVO noticeSetting = setting.stream().filter(r -> SettingTypeEnum.FIRST_NOTICE_TIME.getValue().equals(r.getType())).findFirst().orElse(new SettingVO()); this.updateNoticeRecord(poolList, noticeSetting); } @Override protected void updateClue(List list, List setting) { for (SettingVO vo : setting) { final Integer unit = vo.getUnit(); final int value = Optional.ofNullable(vo.getDetailValue()).orElse(0); SettingUnitEnum unitEnum = SettingUnitEnum.ofValue(unit); if (SettingTypeEnum.FIRST_TRIGGER_TIME.getValue().equals(vo.getType())) { if (Objects.nonNull(unitEnum) && value > 0) { final int calendarType = getCalendarType(unitEnum); list.forEach(clue -> { Date originTime = clue.getAddTime(); Timestamp timestamp = DateUtil.getExpired(originTime, -1 * value, calendarType); clue.setStartTime(timestamp); }); } } } customerCluePoolService.updateBatchById(list); } /** * 更新服务号消息记录 * * @param list * @param vo */ @Override protected void updateNoticeRecord(List list, SettingVO vo) { List idList = list.stream().map(CustomerCluePool::getId).collect(Collectors.toList()); List noticeList = followNoticeRecordService.list(Wrappers.lambdaQuery() .eq(FollowNoticeRecord::getStatus, SendStatusEnum.NOT_SENT) .in(FollowNoticeRecord::getClueId, idList) ); Integer unit = vo.getUnit(); int value = Optional.ofNullable(vo.getDetailValue()).orElse(0); SettingUnitEnum unitEnum = SettingUnitEnum.ofValue(unit); if (Objects.isNull(unitEnum) || value <= 0) { return; } final int calendarType = getCalendarType(unitEnum); for (CustomerCluePool clue : list) { for (FollowNoticeRecord noticeRecord : noticeList) { if (clue.getId().equals(noticeRecord.getClueId())) { Date originTime = clue.getAddTime(); Timestamp timestamp = DateUtil.getExpired(originTime, -1 * value, calendarType); noticeRecord.setSendTime(timestamp); } } } followNoticeRecordService.updateBatchById(noticeList); } @Override @Transactional(rollbackFor = Exception.class) public boolean origin2task(OriginalData originalData) throws Exception { Customer customer = customerService.queryById(originalData.getCustomerId()); BV.notNull(customer, () -> "档案不存在"); CustomerCluePool cluePool = customerCluePoolService.queryByRefererId(customer.getId(), customer.getGroupId(), getFollowType()); completeClue(cluePool, originalData); CustomerCluePool clue = this.createClueInfo(originalData, getFollowType(), customer); BV.notNull(clue, () -> "生成跟进线索失败"); customerCluePoolService.save(clue); this.createFirstNotice(clue, originalData.getGenerateTime()); return true; } @Override @Transactional(rollbackFor = Exception.class) public void updateTask(Long customerId) { //续保跟进与服务顾问无关 } @Override @Transactional(rollbackFor = Exception.class) public void closeTask(FollowTask task) throws BusinessException { task.setState(TaskStateEnum.DEFEAT); task.setCloseTime(new Date()); if (Boolean.TRUE.equals(task.getRedistribution())) { task.setReason(TaskDefeatTypeEnum.C); } else { task.setReason(TaskDefeatTypeEnum.C); final Long clueId = task.getClueId(); CustomerCluePool cluePool = customerCluePoolService.getById(clueId); if (Objects.nonNull(cluePool)) { if (Boolean.FALSE.equals(cluePool.getRedistribution()) && LocalDateTime.now().isBefore((DateUtil.date2LocalDateTime(cluePool.getDeadline())))) { task.setReason(TaskDefeatTypeEnum.B); PostUserDTO followUser = customerChangeBizService.changeInsFollowUser(task); if (Objects.isNull(followUser)) { task.setReason(null); task.setState(TaskStateEnum.ONGOING); task.setCloseTime(null); task.setDeadline(cluePool.getDeadline()); followTaskService.updateById(task); return; } cluePool.setRedistribution(Boolean.TRUE); customerCluePoolService.updateById(cluePool); FollowTask followTask = createTask(cluePool, true); followTask.setFollowUser(followUser.getUserId()); followTask.setFollowUserName(followUser.getUserName()); followTask.setFollowShop(task.getFollowShop()); followTaskService.save(followTask); startTask(followTask, true); } else { cluePool.setCloseTime(new Date()); cluePool.setClueStatus(ClueStatusEnum.FAILURE); customerCluePoolService.updateById(cluePool); } } } followTaskService.updateById(task); CancelApproveEvent event = new CancelApproveEvent(task.getId(), ApproveTypeEnum.FOLLOW_DEFEAT); eventPublisher.publishEvent(event); } @Override @Transactional(rollbackFor = Exception.class) public void overdueProcessing(FollowRecord record) { super.overdueProcessing(record); } @Override public FollowDetailVO followPoolDetail(FollowTask task) { IRDetailVO vo = assemble(task.getCustomerId()); vo.setTaskId(task.getId()); return vo; } /** * 生成消息推送 * * @param cluePool * @param time */ @Override @Transactional(rollbackFor = Exception.class) protected void createFirstNotice(final CustomerCluePool cluePool, final Date time) { if (!cluePool.getDeadline().after(cluePool.getStartTime()) || !cluePool.getDeadline().after(DateUtil.localDateTime2Date(LocalDateTime.now()))) { return; } final FollowNoticeRecord record = new FollowNoticeRecord(); record.setClueId(cluePool.getId()); record.setCustomerId(cluePool.getRefererId()); record.setStatus(SendStatusEnum.NOT_SENT); settingBizService.querySettingByType(getFollowType(), SettingTypeEnum.FIRST_NOTICE_TIME, cluePool.getGroupId()) .ifPresent(r -> record.setSendTime(calDate(r, time, true))); record.setCreateTime(new Date()); record.setFollowType(getFollowType()); followNoticeRecordService.save(record); } @Override protected CustomerCluePool createClueInfo(final OriginalData originalData, FollowTypeEnum followType, Customer customer) { final CustomerCluePool pool = new CustomerCluePool(); pool.setClueType(followType); pool.setAddTime(originalData.getGenerateTime()); pool.setClueStatus(ClueStatusEnum.WAITING); pool.setRedistribution(Boolean.FALSE); pool.setGroupId(originalData.getGroupId()); pool.setCreateTime(new Date()); settingBizService.querySettingByType(followType, SettingTypeEnum.FIRST_TRIGGER_TIME, originalData.getGroupId()) .ifPresent(r -> pool.setStartTime(calDate(r, originalData.getGenerateTime(), true))); pool.setDeadline(originalData.getGenerateTime()); pool.setRefererId(customer.getId()); pool.setFrameNo(customer.getFrameNo()); pool.setPlateNo(customer.getPlateNo()); ShopDTO shop = oopService.shop(customer.getShopId()); if (Objects.nonNull(shop)) { pool.setOriginalShopName(shop.getShortName()); } pool.setOriginalShopId(customer.getShopId()); List userByRole = userService.getUserByRole(customer.getShopId(), RoleCode.XBGJ); BV.isFalse(CollectionUtils.isEmpty(userByRole), () -> "该门店没有续保跟进人员"); Collections.shuffle(userByRole); PostUserDTO userDTO = userByRole.get(0); pool.setOriginalUserId(userDTO.getUserId()); pool.setOriginalUserName(userDTO.getUserName()); if (shop.getId().equals(customer.getShopId())) { Optional dto = userByRole.stream().filter(u -> u.getUserId().equals(customer.getAdviserId())).findFirst(); dto.ifPresent(d -> { pool.setOriginalUserId(d.getUserId()); pool.setOriginalUserName(d.getUserName()); }); } return pool; } @Transactional(rollbackFor = Exception.class) @Override protected void completeClue(CustomerCluePool cluePool, OriginalData originalData) { if (Objects.isNull(cluePool)) { return; } if (ClueStatusEnum.COMPLETE.equals(cluePool.getClueStatus()) || ClueStatusEnum.FAILURE.equals(cluePool.getClueStatus())) { return; } if (ClueStatusEnum.WAITING.equals(cluePool.getClueStatus())) { customerCluePoolService.removeById(cluePool.getId()); return; } LocalDateTime deadline = DateUtil.date2LocalDateTime(cluePool.getDeadline()); if (deadline.isAfter(DateUtil.date2LocalDateTime(DateUtil.getExpiredYear(originalData.getGenerateTime(), -1)))) { if (cluePool.getOriginalShopId().equals(originalData.getShopId())) { cluePool.setFinishUserId(cluePool.getOriginalUserId()); cluePool.setFinishUserName(cluePool.getOriginalUserName()); cluePool.setFinishShopId(cluePool.getOriginalShopId()); cluePool.setFinishShopName(cluePool.getOriginalShopName()); } else { UserInfoDTO user = userService.user(originalData.getUserId()); cluePool.setFinishUserId(originalData.getUserId()); if (Objects.nonNull(user)) { cluePool.setFinishUserName(user.getUserName()); } ShopDTO shop = oopService.shop(originalData.getShopId()); cluePool.setFinishShopId(originalData.getShopId()); if (Objects.nonNull(shop)) { cluePool.setFinishShopName(shop.getShortName()); } } cluePool.setCloseTime(DateUtil.getExpiredYear(originalData.getGenerateTime(), -1)); cluePool.setClueStatus(ClueStatusEnum.COMPLETE); customerCluePoolService.updateById(cluePool); this.completeTask(cluePool); } } /** * 处理进行中的跟进任务 * * @param cluePool * @return */ @Override @Transactional(rollbackFor = Exception.class) public void completeTask(CustomerCluePool cluePool) { FollowTask followTask = followTaskService.queryOngoingTaskByClueId(cluePool.getId()); if (Objects.isNull(followTask)) { return; } followTask.setFinishUser(cluePool.getFinishUserId()); followTask.setFinishUserName(cluePool.getFinishUserName()); followTask.setFinishShop(cluePool.getFinishShopId()); followTask.setCloseTime(cluePool.getCloseTime()); boolean equals = followTask.getFollowShop().equals(cluePool.getFinishShopId()); followTask.setState(equals ? TaskStateEnum.COMPLETE : TaskStateEnum.DEFEAT); if (!equals) { followTask.setReason(TaskDefeatTypeEnum.F); } boolean succeed = followTaskService.updateById(followTask); if (succeed) { completeTodo(followTask.getId()); followNoticeRecordService.removeByClueId(followTask.getId()); CancelApproveEvent event = new CancelApproveEvent(followTask.getId(), ApproveTypeEnum.FOLLOW_DEFEAT); eventPublisher.publishEvent(event); } } @Override public IRDetailVO assemble(Long customerId) { CustomerDetailDto detailDto = customerBizService.queryById(customerId); IRDetailVO vo = new IRDetailVO(); vo.setCustomerId(customerId); vo.setAdviserId(detailDto.getAdviserId()); vo.setAdviserName(detailDto.getAdviserName()); vo.setVin(detailDto.getFrameNo()); vo.setName(detailDto.getName()); vo.setMobile(detailDto.getMobile()); vo.setRegion(MobileUtil.attribution(detailDto.getMobile())); vo.setRealMobile(detailDto.getMobile()); vo.setPlateNo(detailDto.getPlateNo()); vo.setCarModel(detailDto.getBrandName() + " " + detailDto.getSeriesName()); vo.setExpires(detailDto.getExpires()); vo.setPeriods(detailDto.getPeriods()); vo.setLoanCustomer(detailDto.isLoanCustomer()); Optional insuranceDTO = queryInsuInfo(customerId); insuranceDTO.ifPresent(ins -> { vo.setTclInsComName(ins.getTciInsurerName()); vo.setTclInsExpiration(ins.getTciExpiryDate()); vo.setBusInsComName(ins.getInsurerName()); vo.setBusInsExpiration(ins.getExpiryDate()); }); return vo; } }