package cn.fw.valhalla.service.bus.cust; import cn.fw.common.cache.locker.DistributedLocker; import cn.fw.common.constant.CommonConstant; import cn.fw.common.web.annotation.DisLock; import cn.fw.common.web.auth.LoginAuthBean; import cn.fw.passport.sdk.api.param.WxBCodeParam; import cn.fw.valhalla.common.constant.RoleCode; import cn.fw.valhalla.common.utils.DateUtil; import cn.fw.valhalla.common.utils.MessageFormatUtil; import cn.fw.valhalla.common.utils.QRCodeKit; import cn.fw.valhalla.common.utils.StringUtils; import cn.fw.valhalla.domain.db.customer.AffiliationRecord; import cn.fw.valhalla.domain.db.customer.Customer; import cn.fw.valhalla.domain.db.customer.CustomerBaseInfo; import cn.fw.valhalla.domain.db.follow.FollowRecord; import cn.fw.valhalla.domain.db.follow.FollowRecordLog; import cn.fw.valhalla.domain.db.pool.PublicCluePool; import cn.fw.valhalla.domain.db.pool.StammkundePool; import cn.fw.valhalla.domain.dto.*; import cn.fw.valhalla.domain.enums.*; import cn.fw.valhalla.domain.vo.customer.CustomerChangeInfoVO; import cn.fw.valhalla.domain.vo.customer.CustomerChangeQrCodeVO; import cn.fw.valhalla.rpc.angel.dto.InsuranceDTO; import cn.fw.valhalla.rpc.erp.dto.UserInfoDTO; import cn.fw.valhalla.rpc.erp.dto.UserRoleDataRangeDTO; import cn.fw.valhalla.rpc.member.MemberRpcService; import cn.fw.valhalla.rpc.member.dto.MemberUserDTO; import cn.fw.valhalla.rpc.oop.dto.ShopDTO; import cn.fw.valhalla.rpc.passport.PassportService; import cn.fw.valhalla.sdk.enums.*; import cn.fw.valhalla.sdk.param.ChangeAdviserReq; import cn.fw.valhalla.sdk.result.CustomerInfoDto; import cn.fw.valhalla.service.bus.CommonService; import cn.fw.valhalla.service.data.AffiliationRecordService; import cn.fw.valhalla.service.data.FollowRecordLogService; import cn.fw.valhalla.service.data.FollowRecordService; import cn.fw.valhalla.service.data.PublicPoolService; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; import org.redisson.api.RLock; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static cn.fw.common.businessvalidator.Validator.BV; /** * @author : kurisu * @className : CustomerChangeBizService * @description : 档案变更服务 * @date: 2020-08-12 11:23 */ @Slf4j @Service @RequiredArgsConstructor public class CustomerChangeBizService extends AbstractCustomerService { public final String CUSTOMER_CHANGE_CODE = "valhalla:customer:change:key:{0}:{1}"; private final DistributedLocker distributedLocker; private final StringRedisTemplate stringRedisTemplate; /** * 会员服务 */ private final PassportService passportService; private final MemberRpcService memberRpcService; private final AffiliationRecordService affiliationRecordService; private final PublicPoolService publicPoolService; private final FollowRecordService followRecordService; private final FollowRecordLogService followRecordLogService; private final CommonService commonService; @Value("${spring.cache.custom.global-prefix}:pub-clue:complete") @Getter(AccessLevel.PRIVATE) private String pubClueCompleteKey; /** * 生成档案变更二维码 * * @param user * @param customerChangeDto * @return */ public CustomerChangeQrCodeVO createQrCode(LoginAuthBean user, CustomerChangeDto customerChangeDto) { String plateNo = customerChangeDto.getPlateNo(); String vin = customerChangeDto.getFrameNo(); ShopDTO shopDTO = queryDealId(user.getUserId(), RoleCode.FWGW); BV.notNull(shopDTO, () -> "用户权限不正确"); if (customerChangeDto.getId() == null) { customerChangeDto.setGroupId(shopDTO.getGroupId()); customerChangeDto.setDealerId(shopDTO.getDealerId()); customerChangeDto.setShopId(shopDTO.getId()); customerChangeDto.setAdviserId(user.getUserId()); } else { Customer cust = customerService.queryById(customerChangeDto.getId()); customerChangeDto.setGroupId(cust.getGroupId()); customerChangeDto.setDealerId(shopDTO.getDealerId()); customerChangeDto.setShopId(cust.getShopId()); customerChangeDto.setAdviserId(cust.getAdviserId()); customerChangeDto.setModelCode(cust.getSpecCode()); } String str = plateNo + vin + user.getUserId(); if (Objects.isNull(customerChangeDto.getShopId())) { customerChangeDto.setShopId(shopDTO.getId()); customerChangeDto.setAdviserId(user.getUserId()); } String key = MessageFormatUtil.MessageFormatTransfer(CUSTOMER_CHANGE_CODE, customerChangeDto.getShopId(), str); String customerString = JSON.toJSONString(customerChangeDto); Pair lockPair = distributedLocker.tryLock(key, TimeUnit.SECONDS, 0, 1000 * 15); RLock rLock = lockPair.getRight(); boolean flag = rLock.isLocked(); BV.isTrue(flag, "重复操作,请稍后重试"); int expires = 60 * 30; stringRedisTemplate.opsForValue().set(key, customerString, expires, TimeUnit.SECONDS); String code = createCode(key, expires, customerChangeDto.getDealerId()); distributedLocker.unlock(key); return new CustomerChangeQrCodeVO(code, key); } /** * 根据主键查询档案信息 * * @param customerId * @return */ public CustomerChangeInfoVO mpGetById(Long customerId) { final CustomerChangeInfoVO detailVO = new CustomerChangeInfoVO(); Customer customer = customerService.queryById(customerId); if (customer == null) { return null; } CustomerDetailDto customerDetailDto = renderDto(customer); BeanUtils.copyProperties(customerDetailDto, detailVO); detailVO.setId(customer.getId()); Optional dtoOptional = queryInsuInfo(customerId); dtoOptional.ifPresent(t -> { detailVO.setInsurCompanyId(t.getInsurerId()); detailVO.setInsurCompanyName(t.getInsurerName()); detailVO.setInsurExpireDate(t.getExpiryDate()); }); return detailVO; } /** * C端 档案新增或者变更 * * @param dto */ @Transactional(rollbackFor = Exception.class) @DisLock(prefix = "'save-customer:'", key = "#dto.memberId + ':' + #dto.frameNo", message = "系统繁忙,请5秒后再试") public Long saveCustomer(NewCustomerDTO dto) { Long memberId = dto.getMemberId(); // 客户类型 Integer cusType = dto.getCusType(); MemberUserDTO memberDTO = memberRpcService.user(memberId); BV.notNull(memberDTO, () -> "用户信息不存在"); ShopDTO shop = oopService.shop(dto.getShopId()); BV.notNull(shop, () -> "服务站信息不存在"); Long groupId = shop.getGroupId(); dto.setGroupId(groupId); // 处理基础信息 Long[] baseIds = updateBaseInfo(memberDTO, groupId, dto.getName(), cusType); Long customerId = dto.getId(); Long oldBaseId = baseIds[1]; Long baseId = baseIds[0]; String plateNo = dto.getPlateNo(); String frameNo = dto.getFrameNo(); Customer customerByFrameNo = customerService.queryByFrameNo(frameNo, groupId); if (Objects.isNull(customerId)) { customerId = addCustomer(customerByFrameNo, dto, baseId, memberId); } else { Customer customer = customerService.queryById(customerId); if (Objects.isNull(customer)) { customerId = addCustomer(customerByFrameNo, dto, baseId, memberId); } else { final CustomerInfoDto customerInfoDto = new CustomerInfoDto(); if (Objects.nonNull(customerByFrameNo)) { if (!customerByFrameNo.getId().equals(customerId)) { if (customer.getGroupId().equals(groupId)) { stammkundePoolService.reject(customerId, groupId, DefeatReasonEnum.CI); publicCluePoolService.removeClue(customerId, customer.getAdviserId()); customerService.forbidden(customerId); } } packageCustomer(customerByFrameNo, dto, baseId); BeanUtils.copyProperties(customerByFrameNo, customerInfoDto); customerInfoDto.setConsultantId(customerByFrameNo.getAdviserId()); customerInfoDto.setMemberId(memberId); customerService.updateById(customerByFrameNo); } else { packageCustomer(customer, dto, baseId); BeanUtils.copyProperties(customer, customerInfoDto); customerInfoDto.setConsultantId(customer.getAdviserId()); customerInfoDto.setMemberId(memberId); customerService.updateById(customer); } this.customerChangeProducer.send(customerInfoDto); } } Customer customerByPlateNo = customerService.queryByPlateNo(plateNo, groupId); if (Objects.nonNull(customerByPlateNo)) { if (!customerByPlateNo.getFrameNo().equals(frameNo)) { customerByPlateNo.setPlateNo("临" + plateNo); customerService.updateById(customerByPlateNo); } } List list = customerService.queryByBaseId(oldBaseId); list.forEach(r -> { r.setBaseId(baseId); if (!StringUtils.isEmpty(r.getTags())) { Set tagSet = new HashSet<>(Arrays.asList(r.getTags().split(","))); //质保卡 tagSet.remove(CusTagEnum.QUA_ASS_CARD.getValue()); //套餐卡 tagSet.remove(CusTagEnum.MAINTAIN_CARD.getValue()); String newTags = String.join(",", tagSet); r.setTags(newTags); } }); customerService.updateBatchById(list); return customerId; } /** * 变更地址信息 * * @param dto */ @Transactional(rollbackFor = Exception.class) @DisLock(prefix = "'change-address-customer:'", key = "#dto.id", message = "系统繁忙,请5秒后再试") public void updateCustomerAddress(CustomerAddressChangeDTO dto) { Long id = dto.getId(); Customer customer = customerService.getById(id); BV.notNull(customer, () -> "档案信息不存在"); Long baseId = customer.getBaseId(); CustomerBaseInfo baseInfo = customerBaseInfoService.queryById(baseId); BV.notNull(baseInfo, () -> "车主信息不存在"); baseInfo.setAddress(dto.getAddress()); baseInfo.setLat(dto.getLat()); baseInfo.setLng(dto.getLng()); baseInfo.setCityCode(dto.getCityCode()); baseInfo.setCityName(dto.getCityName()); baseInfo.setHobby(dto.getHobby()); baseInfo.setJob(dto.getJob()); baseInfo.setCompanyName(dto.getCompanyName()); customerBaseInfoService.updateById(baseInfo); } /** * 变更档案标签 * * @param cusId * @param tags * @return */ @Transactional(rollbackFor = Exception.class) public int updateTagById(Long cusId, String tags) { Customer customer = customerService.queryById(cusId); BV.notNull(customer, "档案信息异常"); customer.setTags(tags); customer.setUpdateTime(new Date()); int updateCount = customerService.getBaseMapper().updateById(customer); if (updateCount > 0) { customerChangeHandler(customer); } return updateCount; } /** * 更新保有客专属服务顾问 * * @param changeAdviserReq 新的服务顾问 * @return 是否成功 */ @Transactional(rollbackFor = Exception.class) public Boolean changeAdviser(ChangeAdviserReq changeAdviserReq) { Customer customer = customerService.queryById(changeAdviserReq.getCusId()); if (Objects.isNull(customer)) { return true; } boolean isCard = ReasonEnum.MC.getValue().equals(changeAdviserReq.getReason()) || ReasonEnum.WC.getValue().equals(changeAdviserReq.getReason()); if (isCard) { return true; } Long adviserId = changeAdviserReq.getAdviserId(); List dataRange = userService.getUserRoleDataRange(adviserId, RoleCode.FWGW); if (CollectionUtils.isEmpty(dataRange) || Objects.isNull(dataRange.get(0).getRangeValue())) { return true; } String sb = customer.getFrameNo() + CommonConstant.Symbol.AT + adviserId + CommonConstant.Symbol.AT + customer.getGroupId() + CommonConstant.Symbol.AT + dataRange.get(0).getRangeValue(); stringRedisTemplate.opsForList().rightPush(getPubClueCompleteKey(), sb); if (adviserId.equals(customer.getAdviserId())) { StammkundePool stammkundePool = stammkundePoolService.queryAktivAble(customer.getId(), customer.getGroupId(), customer.getShopId()); if (Objects.nonNull(stammkundePool)) { stammkundePool.setAktiv(Boolean.TRUE); stammkundePool.setActivationTime(new Date()); stammkundePool.setPoolStatus(StammkundeStatusEnum.KUNDE); Date createTime = DateUtil.getBeginInTime(stammkundePool.getCreateTime()); Date expiration = DateUtil.getBeginInTime(new Date()); boolean flag = expiration.after(createTime); if (StammkundeSourcesEnum.PUBLIC_POOL.equals(stammkundePool.getSources())) { PublicCluePool cluePool = publicCluePoolService.queryByAdviserAndCust(customer.getId(), adviserId); if (Objects.nonNull(cluePool)) { flag = flag || expiration.after(cluePool.getCreateTime()); List recordList = followRecordService.list(Wrappers.lambdaQuery() .eq(FollowRecord::getTaskId, cluePool.getId()) .eq(FollowRecord::getType, FollowTypeEnum.OT) .eq(FollowRecord::getAddTodo, Boolean.TRUE) .orderByAsc(FollowRecord::getId) ); if (!CollectionUtils.isEmpty(recordList)) { FollowRecord followRecord = recordList.get(0); int count = followRecordLogService.count(Wrappers.lambdaQuery().eq(FollowRecordLog::getRecordId, followRecord.getId())); flag = flag && (count > 0); } } if (flag) { publicCluePoolService.closeClue(customer.getId(), adviserId); } else { stammkundePool.setCreateTime(new Date()); stammkundePool.setSources(StammkundeSourcesEnum.NATURAL); publicCluePoolService.removeClue(customer.getId(), adviserId); } } customer.setTemporary(Boolean.FALSE); customerService.updateById(customer); stammkundePoolService.updateById(stammkundePool); } return true; } Long shopId = dataRange.get(0).getRangeValue(); boolean updated = customerService.update(Wrappers.lambdaUpdate() .set(Customer::getAdviserId, adviserId) .set(Customer::getShopId, shopId) .set(Customer::getTemporary, Boolean.FALSE) .eq(Customer::getId, changeAdviserReq.getCusId())); BV.isTrue(updated, () -> "变更档案服务顾问失败,请重试"); if (Objects.nonNull(customer.getAdviserId()) && Objects.nonNull(customer.getShopId())) { Integer reason = changeAdviserReq.getReason(); DefeatReasonEnum reasonEnum = DefeatReasonEnum.ofValue(reason); BV.notNull(reasonEnum, () -> "档案变更原因不正确"); AffiliationRecord entity = createEntity(customer, adviserId, shopId); entity.setReason(reasonEnum.getName()); affiliationRecordService.save(entity); //处理保有客池数据 StammkundeDto stammkundeDto = new StammkundeDto(); stammkundeDto.setCustomerId(customer.getId()); stammkundeDto.setReason(reasonEnum); stammkundeDto.setShopId(customer.getShopId()); UserInfoDTO user = ehrRpcService.user(adviserId); stammkundeDto.setNewUserId(adviserId); if (Objects.nonNull(user)) { stammkundeDto.setNewUserName(user.getUserName()); } ShopDTO shop = oopService.shop(shopId); stammkundeDto.setNewShopId(shopId); if (Objects.nonNull(shop)) { stammkundeDto.setNewShopName(shop.getShortName()); } stammkundeDto.setGroupId(customer.getGroupId()); stammkundePoolService.reject(stammkundeDto); publicCluePoolService.removeClue(customer.getId(), adviserId); create(customer, adviserId, shopId); } if (Objects.isNull(customer.getAdviserId()) && Objects.isNull(customer.getShopId())) { publicPoolService.removeByCustomerId(customer.getId(), customer.getGroupId()); create(customer, adviserId, shopId, r -> r.setFromPublic(Boolean.TRUE)); } return updated; } private String createCode(String key, int expires, Long dealerId) { final WxBCodeParam param = new WxBCodeParam(); final Map map = new HashMap<>(1); map.put("customerKey", key); param.setParam(map); param.setPage("pgCas/CarArchives/index"); param.setExpires(expires); param.setDealerId(dealerId); final byte[] bytes = passportService.getWxBCode(param); if (Objects.isNull(bytes)) { return null; } return QRCodeKit.byte2Base64String(bytes); } private void create(Customer customer, Long adviserId, Long shopId) { create(customer, adviserId, shopId, null); } private void create(Customer customer, Long adviserId, Long shopId, Consumer cs) { StammkundePool stammkundePool = createSimpleInfo(customer, adviserId); stammkundePool.setSources(StammkundeSourcesEnum.NATURAL); stammkundePool.setShopId(shopId); stammkundePool.setPoolStatus(StammkundeStatusEnum.KUNDE); stammkundePool.setAktiv(Boolean.TRUE); stammkundePool.setActivationTime(new Date()); stammkundePool.setCreateTime(new Date()); if (cs != null) { cs.accept(stammkundePool); } stammkundePoolService.save(stammkundePool); } private AffiliationRecord createEntity(Customer customer, Long adviserId, Long shopId) { AffiliationRecord record = new AffiliationRecord(); record.setCustomerId(customer.getId()); record.setType(CustomerChangeTypeEnum.DEFEAT); record.setDefeatTime(DateUtil.localDateTime2Date(LocalDateTime.now())); record.setOriginUserId(customer.getAdviserId()); record.setOriginShopId(customer.getShopId()); record.setCurUserId(adviserId); record.setCurShopId(shopId); record.setCreateTime(DateUtil.localDateTime2Date(LocalDateTime.now())); return record; } private Long[] updateBaseInfo(MemberUserDTO memberDTO, Long groupId, String name, Integer cusType) { CustomerBaseInfo baseInfo = customerBaseInfoService.queryByMemberId(memberDTO.getUserId(), groupId); CusTypeEnum cusTypeEnum = CusTypeEnum.ofValue(cusType); if (!CusTypeEnum.COMPANY.equals(cusTypeEnum)) { BV.isTrue(Objects.equals(memberDTO.getRealName(), name), () -> "本人姓名与行驶证不符"); } CustomerBaseInfo mobileBaseInfo = customerBaseInfoService.queryByMobile(memberDTO.getPhone(), groupId); if (Objects.nonNull(baseInfo)) { if (Objects.nonNull(mobileBaseInfo)) { if (!baseInfo.getId().equals(mobileBaseInfo.getId())) { mobileBaseInfo.setYn(Boolean.FALSE); customerBaseInfoService.updateById(mobileBaseInfo); } } } else { if (Objects.nonNull(mobileBaseInfo)) { mobileBaseInfo.setYn(Boolean.FALSE); customerBaseInfoService.updateById(mobileBaseInfo); } baseInfo = new CustomerBaseInfo(); } baseInfo.setName(name); baseInfo.setMemberId(memberDTO.getUserId()); baseInfo.setContact(memberDTO.getRealName()); baseInfo.setMobile(memberDTO.getPhone()); baseInfo.setGender(GenderEnum.ofValue(memberDTO.getSex())); baseInfo.setBirthday(memberDTO.getBirthday()); baseInfo.setIdCode(memberDTO.getCredentNo()); baseInfo.setCusType(cusTypeEnum); baseInfo.setGroupId(groupId); baseInfo.setYn(Boolean.TRUE); customerBaseInfoService.saveOrUpdate(baseInfo); return new Long[]{baseInfo.getId(), Objects.isNull(mobileBaseInfo) ? null : mobileBaseInfo.getId()}; } private Long addCustomer(Customer customer, NewCustomerDTO dto, Long baseId, Long memberId) { if (Objects.nonNull(customer)) { packageCustomer(customer, dto, baseId); customerService.updateById(customer); } else { customer = new Customer(); packageCustomer(customer, dto, baseId); customer.setAdviserId(dto.getAdviserId()); customer.setShopId(dto.getShopId()); customerService.save(customer); StammkundePool simpleInfo = createSimpleInfo(customer, dto.getAdviserId()); simpleInfo.setSources(StammkundeSourcesEnum.NATURAL); simpleInfo.setPoolStatus(StammkundeStatusEnum.KUNDE); simpleInfo.setAktiv(Boolean.TRUE); simpleInfo.setActivationTime(new Date()); stammkundePoolService.save(simpleInfo); commonService.createClue(customer); } customer.setTemporary(Boolean.FALSE); final CustomerInfoDto customerInfoDto = new CustomerInfoDto(); BeanUtils.copyProperties(customer, customerInfoDto); customerInfoDto.setConsultantId(customer.getAdviserId()); customerInfoDto.setMemberId(memberId); this.customerChangeProducer.send(customerInfoDto); return customer.getId(); } private void packageCustomer(Customer customer, NewCustomerDTO dto, Long baseId) { if (Objects.nonNull(customer.getBaseId()) && !customer.getBaseId().equals(baseId)) { if (StringUtils.isValid(customer.getTags())) { Set tagSet = new HashSet<>(Arrays.asList(customer.getTags().split(","))); //质保卡 tagSet.remove(CusTagEnum.QUA_ASS_CARD.getValue()); //套餐卡 tagSet.remove(CusTagEnum.MAINTAIN_CARD.getValue()); String newTags = String.join(",", tagSet); customer.setTags(newTags); } } customer.setBaseId(baseId); customer.setPlateNo(dto.getPlateNo()); customer.setFrameNo(dto.getFrameNo()); customer.setRegDate(dto.getRegDate()); customer.setEngineNo(dto.getEngineNo()); customer.setBrandId(dto.getBrandId()); customer.setBrandName(dto.getBrandName()); customer.setSeriesId(dto.getSeriesId()); customer.setSeriesName(dto.getSeriesName()); customer.setSpecId(dto.getSpecId()); customer.setSpecName(dto.getSpecName()); customer.setSpecCode(dto.getSpecCode()); customer.setUseType(CarUseTypeEnum.ofValue(dto.getUseType())); customer.setSource(SourceTypeEnum.TO_SHOP); customer.setYn(Boolean.TRUE); customer.setGroupId(dto.getGroupId()); if (Objects.isNull(customer.getBuyDate())) { customer.setBuyDate(dto.getRegDate()); } } }