gongchunyi
6 小时以前 ff41f9a13e981b543c114336058150c940110393
fix: 修复客户档案的导入、新增、编辑的的数据验证
已添加1个文件
已修改3个文件
260 ■■■■■ 文件已修改
src/main/java/com/ruoyi/basic/controller/CustomerController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/CustomerImportDto.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ICustomerService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.dto.CustomerImportDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -50,14 +51,14 @@
        } else {
            list = customerService.selectCustomerLists(customer);
        }
        ExcelUtil<Customer> util = new ExcelUtil<Customer>(Customer.class);
        ExcelUtil<Customer> util = new ExcelUtil<>(Customer.class);
        util.exportExcel(response, list, "客户档案数据");
    }
    @PostMapping("/downloadTemplate")
    @Log(title = "客户档案-下载模板", businessType = BusinessType.EXPORT)
    public void downloadTemplate(HttpServletResponse response) {
        ExcelUtil<Customer> util = new ExcelUtil<Customer>(Customer.class);
        ExcelUtil<CustomerImportDto> util = new ExcelUtil<>(CustomerImportDto.class);
        util.importTemplateExcel(response, "客户档案模板");
    }
@@ -68,8 +69,8 @@
    @Log(title = "客户档案", businessType = BusinessType.IMPORT)
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file) throws Exception {
        return customerService.importData(file);
        customerService.importData(file);
        return AjaxResult.success();
    }
    /**
src/main/java/com/ruoyi/basic/dto/CustomerImportDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
package com.ruoyi.basic.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * <br>
 * å®¢æˆ·å¯¼å…¥Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/4/17 15:15
 */
@Data
@ApiModel(value = "CustomerImportDto", description = "客户导入Dto")
public class CustomerImportDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * å®¢æˆ·åç§°
     */
    @Excel(name = "客户名称")
    @ApiModelProperty("纳税人识别号")
    private String customerName;
    /**
     * å®¢æˆ·åœ°åŒº
     */
    @Excel(name = "客户地区(多级以-分割)")
    @ApiModelProperty("客户地区")
    private String customRegions;
    /**
     * å®¢æˆ·åˆ†ç±»ï¼šé›¶å”®å®¢æˆ·ï¼Œè¿›é”€å•†å®¢æˆ·
     */
    @Excel(name = "客户分类")
    @ApiModelProperty("纳税人识别号")
    private String customerType;
    /**
     * çº³ç¨Žäººè¯†åˆ«å·
     */
    @Excel(name = "纳税人识别号")
    @ApiModelProperty("纳税人识别号")
    private String taxpayerIdentificationNumber;
    /**
     * å…¬å¸åœ°å€
     */
    @Excel(name = "公司地址")
    @ApiModelProperty("公司地址")
    private String companyAddress;
    /**
     * å…¬å¸ç”µè¯
     */
    @Excel(name = "公司电话")
    @ApiModelProperty("公司电话")
    private String companyPhone;
    /**
     * è”系人
     */
    @Excel(name = "联系人")
    @ApiModelProperty("联系人")
    private String contactPerson;
    /**
     * è”系电话
     */
    @ApiModelProperty("联系电话")
    @Excel(name = "联系电话", cellType = Excel.ColumnType.STRING)
    private String contactPhone;
    /**
     * ç»´æŠ¤äºº
     */
    @Excel(name = "维护人")
    @ApiModelProperty("维护人")
    private String maintainer;
    /**
     * ç»´æŠ¤æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "维护时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date maintenanceTime;
    @ApiModelProperty(value = "银行基本户")
    @Excel(name = "银行基本户")
    private String basicBankAccount;
    @ApiModelProperty(value = "银行账号")
    @Excel(name = "银行账号")
    private String bankAccount;
    @ApiModelProperty(value = "开户行号")
    @Excel(name = "开户行号")
    private String bankCode;
}
src/main/java/com/ruoyi/basic/service/ICustomerService.java
@@ -77,5 +77,5 @@
    List<Customer> selectCustomerLists(Customer customer);
    AjaxResult importData(MultipartFile file);
    void importData(MultipartFile file);
}
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -8,18 +8,16 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.dto.CustomerFollowUpDto;
import com.ruoyi.basic.dto.CustomerImportDto;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerFollowUp;
import com.ruoyi.basic.pojo.CustomerFollowUpFile;
import com.ruoyi.basic.pojo.CustomerRegions;
import com.ruoyi.basic.service.*;
import com.ruoyi.common.exception.ServiceException;
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.AjaxResult;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import lombok.AllArgsConstructor;
@@ -30,10 +28,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
@@ -47,6 +42,7 @@
@AllArgsConstructor
@Slf4j
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService {
    private static final Set<String> ALLOWED_CUSTOMER_TYPES = new HashSet<>(Arrays.asList("零售客户", "进销商客户"));
    private final SalesLedgerMapper salesLedgerMapper;
    private CustomerMapper customerMapper;
@@ -205,6 +201,7 @@
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long tenantId = loginUser.getTenantId();
        customer.setTenantId(tenantId);
        validateCustomerNameUnique(customer, null);
        return customerMapper.insert(customer);
    }
@@ -219,6 +216,7 @@
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long tenantId = loginUser.getTenantId();
        customer.setTenantId(tenantId);
        validateCustomerNameUnique(customer, customer.getId());
        return customerMapper.updateById(customer);
    }
@@ -258,20 +256,130 @@
    }
    @Override
    public AjaxResult importData(MultipartFile file) {
    @Transactional(rollbackFor = Exception.class)
    public void importData(MultipartFile file) {
        List<CustomerImportDto> userList;
        try {
            ExcelUtil<Customer> util = new ExcelUtil<Customer>(Customer.class);
            List<Customer> userList = util.importExcel(file.getInputStream());
            ExcelUtil<CustomerImportDto> util = new ExcelUtil<>(CustomerImportDto.class);
            userList = util.importExcel(file.getInputStream());
            if (CollectionUtils.isEmpty(userList)) {
                return AjaxResult.warn("模板错误或导入数据为空");
                throw new ServiceException("模板错误或导入数据为空");
            }
            this.saveOrUpdateBatch(userList);
            return AjaxResult.success(true);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("导入失败");
            log.error("客户导入失败: {}", e.getMessage());
            throw new ServiceException("导入失败");
        }
        List<Customer> customers = new ArrayList<>();
        Customer customer;
        int rowIndex = 1;
        Set<String> importedCustomerRegionNames = new HashSet<>();
        for (CustomerImportDto user : userList) {
            customer = new Customer();
            if (user == null) {
                throw new ServiceException("导入失败,第【" + rowIndex + "】行数据不能为空");
            }
            if (StringUtils.isEmpty(user.getCustomerName())) {
                throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户名称】不能为空");
            }
            String customerName = user.getCustomerName().trim();
            customer.setCustomerName(customerName);
            String customerType = user.getCustomerType() == null ? null : user.getCustomerType().trim();
            if (!StringUtils.isEmpty(customerType) && !ALLOWED_CUSTOMER_TYPES.contains(customerType)) {
                throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户分类】仅支持:零售客户、进销商客户");
            }
            customer.setCustomerType(customerType);
            customer.setTaxpayerIdentificationNumber(user.getTaxpayerIdentificationNumber());
            customer.setCompanyAddress(user.getCompanyAddress());
            customer.setCompanyPhone(user.getCompanyPhone());
            customer.setContactPerson(user.getContactPerson());
            customer.setContactPhone(user.getContactPhone());
            customer.setMaintainer(user.getMaintainer());
            customer.setMaintenanceTime(user.getMaintenanceTime());
            customer.setBasicBankAccount(user.getBasicBankAccount());
            customer.setBankAccount(user.getBankAccount());
            customer.setBankCode(user.getBankCode());
            if (StringUtils.isEmpty(user.getCustomRegions())) {
                throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户地区】不能为空");
            }
            Long regionsId = resolveRegionId(user.getCustomRegions(), rowIndex);
            customer.setRegionsId(regionsId);
            String importUniqueKey = customerName + "_" + (customer.getRegionsId() == null ? "NULL" : customer.getRegionsId());
            if (importedCustomerRegionNames.contains(importUniqueKey)) {
                throw new ServiceException("导入失败,第" + rowIndex + "行数据【同地区客户名称】重复");
            }
            importedCustomerRegionNames.add(importUniqueKey);
            validateCustomerNameUnique(customer, null);
            customers.add(customer);
            rowIndex++;
        }
        if (CollectionUtils.isEmpty(customers)) {
            throw new ServiceException("导入失败,未识别到可导入的数据");
        }
        this.saveBatch(customers);
    }
    /**
     * æŒ‰ â€œxx-xx-xx” å±‚级解析地区,不存在则按父级逐级创建。
     */
    private Long resolveRegionId(String regionsPath, int rowIndex) {
        if (StringUtils.isEmpty(regionsPath)) {
            throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户地区】不能为空");
        }
        String normalizedPath = regionsPath.trim().replace("—", "-").replace("-", "-");
        if (!normalizedPath.matches("^[^-]+(?:-[^-]+)*$")) {
            throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户地区】格式错误,仅支持:xxx æˆ– xxx-xxx-xxx");
        }
        String[] regionParts = normalizedPath.split("-");
        Long parentId = 0L;
        Long currentId = null;
        for (String rawPart : regionParts) {
            String regionName = rawPart == null ? null : rawPart.trim();
            if (StringUtils.isEmpty(regionName)) {
                throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户地区】格式错误");
            }
            CustomerRegions region = customerRegionsService.getOne(new LambdaQueryWrapper<CustomerRegions>()
                    .eq(CustomerRegions::getRegionsName, regionName)
                    .last("limit 1"));
            if (region == null) {
                region = new CustomerRegions();
                region.setParentId(parentId);
                region.setRegionsName(regionName);
                if (!customerRegionsService.save(region)) {
                    throw new ServiceException("导入失败,第" + rowIndex + "行数据【客户地区】创建失败");
                }
            }
            currentId = region.getId();
            parentId = currentId;
        }
        return currentId;
    }
    private void validateCustomerNameUnique(Customer customer, Long excludeId) {
        if (customer == null || StringUtils.isEmpty(customer.getCustomerName())) {
            throw new ServiceException("客户名称不能为空");
        }
        if (customer.getRegionsId() == null) {
            throw new ServiceException("客户地区不能为空");
        }
        String customerName = customer.getCustomerName().trim();
        LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<Customer>().eq(Customer::getCustomerName, customerName);
        if (customer.getRegionsId() == null) {
            queryWrapper.isNull(Customer::getRegionsId);
        } else {
            queryWrapper.eq(Customer::getRegionsId, customer.getRegionsId());
        }
        if (customer.getTenantId() != null) {
            queryWrapper.eq(Customer::getTenantId, customer.getTenantId());
        }
        if (excludeId != null) {
            queryWrapper.ne(Customer::getId, excludeId);
        }
        Long exists = customerMapper.selectCount(queryWrapper);
        if (exists != null && exists > 0) {
            throw new ServiceException("同地区下客户名称已存在,请勿重复");
        }
        customer.setCustomerName(customerName);
    }
    @Override