package com.ruoyi.basic.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.dto.CustomerContactDto; import com.ruoyi.basic.dto.CustomerDto; import com.ruoyi.basic.dto.CustomerFollowUpDto; import com.ruoyi.basic.mapper.CustomerContactMapper; import com.ruoyi.basic.mapper.CustomerMapper; import com.ruoyi.basic.pojo.*; import com.ruoyi.basic.service.*; import com.ruoyi.basic.vo.CustomerVo; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.security.LoginUser; import com.ruoyi.framework.web.domain.R; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.pojo.SalesLedger; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; /** * 客户档案Service业务层处理 * * @author ruoyi * @date 2025-05-07 */ @Service @AllArgsConstructor @Slf4j public class CustomerServiceImpl extends ServiceImpl implements ICustomerService { @Autowired private SalesLedgerMapper salesLedgerMapper; @Autowired private CustomerMapper customerMapper; @Autowired private CustomerFollowUpService customerFollowUpService; @Autowired private CustomerFollowUpFileService customerFollowUpFileService; @Autowired private CustomerReturnVisitService customerReturnVisitService; @Autowired private CustomerUserService customerUserService; @Autowired private CustomerContactMapper customerContactMapper; /** * 查询客户档案 * * @param id 客户档案主键 * @return 客户档案 */ @Override public Customer selectCustomerById(Long id) { return customerMapper.selectById(id); } /** * 查询客户详情(含跟进记录和附件) * * @param id 客户档案主键 * @return 客户详情DTO */ @Override public CustomerVo selectCustomerDetailById(Long id) { CustomerVo customerVo = new CustomerVo(); BeanUtils.copyProperties(this.getById(id), customerVo); // 查询跟进记录 List followUpList = customerFollowUpService.list( new LambdaQueryWrapper() .eq(CustomerFollowUp::getCustomerId, id) .orderByDesc(CustomerFollowUp::getFollowUpTime) ); if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(followUpList)) { List followUpDtoList = followUpList.stream().map(followUp -> { CustomerFollowUpDto followUpDto = new CustomerFollowUpDto(); BeanUtils.copyProperties(followUp, followUpDto); // 查询附件 List fileList = customerFollowUpFileService.list( new LambdaQueryWrapper() .eq(CustomerFollowUpFile::getFollowUpId, followUp.getId()) ); followUpDto.setFileList(fileList); return followUpDto; }).collect(Collectors.toList()); customerVo.setFollowUpList(followUpDtoList); } //查询联系人信息 List customerContactList = customerContactMapper.selectList( new QueryWrapper().lambda() .apply("FIND_IN_SET({0}, customer_id)", id) ); if (!CollectionUtils.isEmpty(customerContactList)) { List contactDtoList = customerContactList.stream().map(contact -> { CustomerContactDto contactDto = new CustomerContactDto(); BeanUtils.copyProperties(contact, contactDto); return contactDto; }).collect(Collectors.toList()); customerVo.setContactList(contactDtoList); } return customerVo; } /** * 查询客户档案列表 * * @param customer 客户档案 * @return 客户档案 */ @Override public IPage selectCustomerList(Page page, CustomerDto customer) { LoginUser loginUser = SecurityUtils.getLoginUser(); Long loginUserId = loginUser.getUserId(); IPage customerPage = customerMapper.listPage(page, customer, loginUserId); List records = customerPage.getRecords(); if (CollectionUtils.isEmpty(records)) { return customerPage; } List customerIds = records.stream() .map(CustomerVo::getId) .filter(Objects::nonNull) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(customerIds)) { Map latestFollowUpMap = getLatestFollowUpMap(customerIds); records.forEach(c -> { String address = StringUtils.defaultString(c.getCompanyAddress(), ""); String phone = StringUtils.defaultString(c.getCompanyPhone(), ""); c.setAddressPhone(address + "(" + phone + ")"); CustomerFollowUp followUp = latestFollowUpMap.get(c.getId()); if (followUp != null) { c.setFollowUpLevel(followUp.getFollowUpLevel()); c.setFollowUpTime(Date.from( followUp.getFollowUpTime().atZone(ZoneId.systemDefault()).toInstant() )); } // 转换共享用户ID字符串为List String userIdsStr = c.getUserIdsStr(); if (StringUtils.isNotEmpty(userIdsStr)) { List userIds = Arrays.stream(userIdsStr.split(",")) .map(String::trim) .map(Long::parseLong) .collect(Collectors.toList()); c.setUserIds(userIds); } }); } return customerPage; } private Map getLatestFollowUpMap(List customerIds) { List followUps = customerFollowUpService.list( new LambdaQueryWrapper() .in(CustomerFollowUp::getCustomerId, customerIds) .orderByDesc(CustomerFollowUp::getFollowUpTime) ); return followUps.stream() .collect(Collectors.toMap( CustomerFollowUp::getCustomerId, followUp -> followUp, (existing, replacement) -> existing )); } /** * 新增客户档案 * * @param customer 客户档案 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int insertCustomer(CustomerDto customer) { LoginUser loginUser = SecurityUtils.getLoginUser(); Long tenantId = loginUser.getTenantId(); customer.setTenantId(tenantId); customerMapper.insert(customer); syncCustomerContacts(customer.getId(), customer.getContactList()); return 1; } /** * 修改客户档案 * * @param customer 客户档案 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int updateCustomer(CustomerDto customer) { LoginUser loginUser = SecurityUtils.getLoginUser(); Long tenantId = loginUser.getTenantId(); customer.setTenantId(tenantId); int rows = customerMapper.updateById(customer); syncCustomerContacts(customer.getId(), customer.getContactList()); return rows; } private void syncCustomerContacts(Long customerId, List contactList) { List allContacts = customerContactMapper.selectList(new QueryWrapper<>()); Map submittedContactMap = new LinkedHashMap<>(); if (!CollectionUtils.isEmpty(contactList)) { for (CustomerContact contact : contactList) { if (contact == null) { continue; } submittedContactMap.putIfAbsent(buildContactKey(contact), contact); } } // 先同步本次提交的联系人,按“姓名 + 手机号”聚合客户ID。 for (CustomerContact contact : submittedContactMap.values()) { contact.setCustomerId(buildContactCustomerIds(allContacts, contact, customerId)); if (contact.getId() == null) { customerContactMapper.insert(contact); allContacts.add(contact); } else { customerContactMapper.updateById(contact); replaceLocalContact(allContacts, contact); } } Set submittedContactKeys = submittedContactMap.keySet(); // 再处理本次已删除的联系人,把当前客户从历史关联里移除。 for (CustomerContact existingContact : new ArrayList<>(allContacts)) { if (!containsCustomerId(existingContact.getCustomerId(), customerId)) { continue; } if (submittedContactKeys.contains(buildContactKey(existingContact))) { continue; } String updatedCustomerIds = removeCustomerId(existingContact.getCustomerId(), customerId); if (StringUtils.isEmpty(updatedCustomerIds)) { // 没有剩余客户关联时,直接删除联系人记录。 customerContactMapper.deleteById(existingContact.getId()); } else { existingContact.setCustomerId(updatedCustomerIds); customerContactMapper.updateById(existingContact); } } } // 按联系人姓名和手机号汇总历史客户ID,并追加当前客户ID。 private String buildContactCustomerIds(List customerContacts, CustomerContact contact, Long customerId) { LinkedHashSet customerIdSet = new LinkedHashSet<>(); if (!CollectionUtils.isEmpty(customerContacts)) { customerContacts.stream() .filter(Objects::nonNull) .filter(item -> Objects.equals(item.getContactPerson(), contact.getContactPerson()) && Objects.equals(item.getContactPhone(), contact.getContactPhone())) .map(CustomerContact::getCustomerId) .filter(StringUtils::isNotEmpty) .forEach(ids -> Arrays.stream(ids.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .forEach(customerIdSet::add)); } customerIdSet.add(String.valueOf(customerId)); return String.join(",", customerIdSet); } // 判断联系人关联列表里是否包含当前客户ID。 private boolean containsCustomerId(String customerIds, Long customerId) { if (StringUtils.isEmpty(customerIds) || customerId == null) { return false; } String currentCustomerId = String.valueOf(customerId); return Arrays.stream(customerIds.split(",")) .map(String::trim) .anyMatch(currentCustomerId::equals); } // 从联系人关联列表里移除当前客户ID,并保持逗号拼接格式。 private String removeCustomerId(String customerIds, Long customerId) { if (StringUtils.isEmpty(customerIds) || customerId == null) { return customerIds; } String currentCustomerId = String.valueOf(customerId); return Arrays.stream(customerIds.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .filter(id -> !currentCustomerId.equals(id)) .distinct() .collect(Collectors.joining(",")); } // 生成联系人业务唯一键,用于对本次提交数据去重比对。 private String buildContactKey(CustomerContact contact) { return StringUtils.defaultString(contact.getContactPerson()) + "_" + StringUtils.defaultString(contact.getContactPhone()); } // 更新内存中的联系人快照,避免后续增删判断使用旧数据。 private void replaceLocalContact(List customerContacts, CustomerContact targetContact) { for (int i = 0; i < customerContacts.size(); i++) { CustomerContact customerContact = customerContacts.get(i); if (Objects.equals(customerContact.getId(), targetContact.getId())) { customerContacts.set(i, targetContact); return; } } } /** * 批量删除客户档案 * * @param ids 需要删除的客户档案主键 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int deleteCustomerByIds(Long[] ids) { List idList = Arrays.asList(ids); List salesLedgers = salesLedgerMapper.selectList(new QueryWrapper().lambda().in(SalesLedger::getCustomerId, idList)); if (!salesLedgers.isEmpty()) { throw new RuntimeException("客户档案下有销售合同,请先删除销售合同"); } // 查询是否有已分配的公海客户 List assignedPools = customerMapper.selectList( new QueryWrapper().lambda() .in(Customer::getId, idList) .eq(Customer::getType, 1). eq(Customer::getIsAssigned, 1) // 公海客户 ); if (!assignedPools.isEmpty()) { throw new RuntimeException("客户档案下有已分配的公海客户,请先收回"); } // 删除客户的同时也需要删除对应的客户跟随、附件和回访提醒 for (Long id : ids) { customerFollowUpService.deleteByCustomerId(id); customerReturnVisitService.deleteByCustomerId(id); // 删除客户的共享关系 customerUserService.remove( new QueryWrapper().lambda() .eq(CustomerUser::getCustomerId, id) ); } // 删除客户对应的联系人关联 removeCustomerContactsByCustomerIds(idList); // 删除客户主表数据 return customerMapper.deleteBatchIds(idList); } private void removeCustomerContactsByCustomerIds(List customerIds) { if (CollectionUtils.isEmpty(customerIds)) { return; } List customerContacts = customerContactMapper.selectList(new QueryWrapper<>()); if (CollectionUtils.isEmpty(customerContacts)) { return; } Set customerIdSet = customerIds.stream() .filter(Objects::nonNull) .collect(Collectors.toSet()); for (CustomerContact customerContact : customerContacts) { String contactCustomerIds = customerContact.getCustomerId(); if (StringUtils.isEmpty(contactCustomerIds)) { continue; } String updatedCustomerIds = Arrays.stream(contactCustomerIds.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .filter(id -> { Long parsedId = parseCustomerId(id); return parsedId == null || !customerIdSet.contains(parsedId); }) .distinct() .collect(Collectors.joining(",")); if (StringUtils.isEmpty(updatedCustomerIds)) { customerContactMapper.deleteById(customerContact.getId()); } else if (!updatedCustomerIds.equals(contactCustomerIds)) { customerContact.setCustomerId(updatedCustomerIds); customerContactMapper.updateById(customerContact); } } } private Long parseCustomerId(String customerId) { try { return Long.valueOf(customerId); } catch (NumberFormatException e) { return null; } } @Override public List selectCustomerListByIds(Long[] ids) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.in(Customer::getId, Arrays.asList(ids)); return customerMapper.selectList(queryWrapper); } @Override public List selectCustomerLists(CustomerDto customer) { LoginUser loginUser = SecurityUtils.getLoginUser(); Long loginUserId = loginUser.getUserId(); return customerMapper.list(customer, loginUserId); } @Override public R importData(MultipartFile file, Integer type) { try { ExcelUtil util = new ExcelUtil(Customer.class); List userList = util.importExcel(file.getInputStream()); if (CollectionUtils.isEmpty(userList)) { return R.fail("模板错误或导入数据为空"); } // 根据 type 参数设置客户类型(私海/公海) if (type != null) { userList.forEach(customer -> { customer.setType(type); }); } this.saveOrUpdateBatch(userList); return R.ok(true); } catch (Exception e) { e.printStackTrace(); return R.fail("导入失败"); } } @Override public List> customerList(Customer customer) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(Customer::getId, Customer::getCustomerName, Customer::getTaxpayerIdentificationNumber); // 获取原始查询结果 List> result = customerMapper.selectMaps(queryWrapper); // 将下划线命名转换为驼峰命名 return result.stream().map(map -> map.entrySet().stream() .collect(Collectors.toMap( entry -> underlineToCamel(entry.getKey()), Map.Entry::getValue)) ).collect(Collectors.toList()); } // 分配公海客户给私海 @Override public void assignCustomer(CustomerDto customerDto) { Customer customer = customerMapper.selectById(customerDto.getId()); if (customer.getType() == 1 ) { // 公海且可分配 customer.setIsAssigned(1); customer.setUsageStatus(1L); customer.setUsageUser(customerDto.getUsageUser()); customerMapper.updateById(customer); } } // 回收私海客户到公海 @Override @Transactional(rollbackFor = Exception.class) public void recycleCustomer(CustomerDto customerDto) { Customer customer = customerMapper.selectById(customerDto.getId()); if (customer.getType() == 1 && customer.getIsAssigned() == 1) { // 公海且已分配 customer.setIsAssigned(0); customer.setUsageStatus(0L); customer.setUsageUser(0L); customerMapper.updateById(customer); // 删除该客户的所有共享关系 customerUserService.remove( new QueryWrapper().lambda() .eq(CustomerUser::getCustomerId, customerDto.getId()) ); } } // 客户共享 @Override public void together(CustomerDto customerDto) { // 查询现有的共享记录 List existingUsers = customerUserService.list( new QueryWrapper().lambda().eq(CustomerUser::getCustomerId, customerDto.getId()) ); // 获取已存在的用户ID列表 List existingUserIds = existingUsers.stream() .map(CustomerUser::getUserId) .collect(Collectors.toList()); // 过滤掉已存在的用户,只保留新用户 List newUserIds = customerDto.getUserIds().stream() .filter(userId -> !existingUserIds.contains(userId)) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(newUserIds)) { return; } // 获取当前租户ID LoginUser loginUser = SecurityUtils.getLoginUser(); Long tenantId = loginUser.getTenantId(); // 批量保存新的共享记录 List customerUsers = newUserIds.stream() .map(userId -> { CustomerUser customerUser = new CustomerUser(); customerUser.setCustomerId(customerDto.getId()); customerUser.setUserId(userId); customerUser.setTenantId(tenantId); return customerUser; }) .collect(Collectors.toList()); customerUserService.saveBatch(customerUsers); } @Override public Boolean back(Long id) { //将客户的type改为1 且直接分配给当前用户 Customer customer = customerMapper.selectById(id); customer.setType(1); customer.setIsAssigned(0); return this.updateById(customer); } /** * 下划线命名转驼峰命名 */ private String underlineToCamel(String param) { if (param == null || "".equals(param.trim())) { return ""; } int len = param.length(); StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { char c = param.charAt(i); if (c == '_') { if (++i < len) { sb.append(Character.toUpperCase(param.charAt(i))); } } else { sb.append(Character.toLowerCase(c)); } } return sb.toString(); } }