chenhj
2026-04-16 145f6c5c7a7a04eff490ae8436c0024850b78bc5
feat(customer): 新增客户跟进导出及客户转移功能

- 新增CustomerExcelDTO用于导出客户跟进记录的Excel数据结构
- 在CustomerMapper中添加selectCustomerDtoListByIds和selectCustomerDtoLists方法支持客户跟进数据查询
- 编写CustomerMapper.xml,定义客户跟进记录SQL查询语句,关联客户跟进表
- CustomerServiceImpl中实现客户跟进数据查询及客户转移业务逻辑,包括维护人存在性校验和客户存在性校验
- CustomerController添加/exportFollowUpList导出客户跟进记录接口,以及/transferCustomer实现客户转移接口
- 调整Customer实体类取消部分字段填充注解,优化导入导出相关实现
- 对客户新增和修改进行客户名称唯一性校验,防止重名冲突
已添加2个文件
已修改5个文件
239 ■■■■ 文件已修改
src/main/java/com/ruoyi/basic/controller/CustomerController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/excel/CustomerExcelDTO.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/Customer.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ICustomerService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerMapper.xml 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerController.java
@@ -2,6 +2,8 @@
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.excel.CustomerExcelDTO;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -51,6 +53,23 @@
        }
        ExcelUtil<Customer> util = new ExcelUtil<Customer>(Customer.class);
        util.exportExcel(response, list, "客户档案数据");
    }
    /**
     * å¯¼å‡ºå®¢æˆ·è·Ÿè¿›è®°å½•
     */
    @Log(title = "客户档案", businessType = BusinessType.EXPORT)
    @PostMapping("/exportFollowUpList")
    public void exportFollowUpList(HttpServletResponse response, Customer customer) {
        Long[] ids = customer.getIds();
        List<CustomerExcelDTO> list;
        if (ids != null && ids.length > 0) {
            list = customerService.selectCustomerDtoByIds(ids);
        } else {
            list = customerService.selectCustomerDtoLists();
        }
        ExcelUtil<CustomerExcelDTO> util = new ExcelUtil<CustomerExcelDTO>(CustomerExcelDTO.class);
        util.exportExcel(response, list, "客户跟进记录");
    }
    @PostMapping("/downloadTemplate")
@@ -116,4 +135,13 @@
    public List customerList(Customer customer) {
        return customerService.customerList(customer);
    }
    /**
     * è½¬ç§»å®¢æˆ·
     */
    @Log(title = "客户档案", businessType = BusinessType.UPDATE)
    @PatchMapping("/transferCustomer")
    public AjaxResult transferCustomer(@RequestBody CustomerDto customerDto) {
        return toAjax(customerService.transferCustomer(customerDto));
    }
}
src/main/java/com/ruoyi/basic/excel/CustomerExcelDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.basic.excel;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import java.time.LocalDateTime;
public class CustomerExcelDTO {
    @Excel(name = "客户名称")
    private String customerName;
    @Excel(name = "纳税人识别号")
    private String taxpayerIdentificationNumber;
    @Excel(name = "公司地址")
    private String companyAddress;
    @Excel(name = "公司电话")
    private String companyPhone;
    @Excel(name = "联系人")
    private String contactPerson;
    @Excel(name = "联系电话",cellType = Excel.ColumnType.STRING)
    private String contactPhone;
    @Excel(name = "维护人")
    private String maintainer;
    @Excel(name = "银行基本户")
    private String basicBankAccount;
    @Excel(name = "银行账号")
    private String bankAccount;
    @Excel(name = "开户行号")
    private String bankCode;
    @Excel(name = "跟进时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime followUpTime;
    @Excel(name = "跟进方式")
    private String followUpMethod;
    @Excel(name = "跟进程度")
    private String followUpLevel;
    @Excel(name = "跟进人姓名")
    private String followerUserName;
    @Excel(name = "内容")
    private String content;
}
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
@@ -1,6 +1,7 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.basic.excel.CustomerExcelDTO;
import com.ruoyi.basic.pojo.Customer;
import java.util.List;
@@ -13,51 +14,7 @@
 */
public interface CustomerMapper extends BaseMapper<Customer>
{
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param id å®¢æˆ·æ¡£æ¡ˆä¸»é”®
     * @return å®¢æˆ·æ¡£æ¡ˆ
     */
    Customer selectCustomerById(Long id);
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆåˆ—表
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return å®¢æˆ·æ¡£æ¡ˆé›†åˆ
     */
    List<Customer> selectCustomerList(Customer customer);
    /**
     * æ–°å¢žå®¢æˆ·æ¡£æ¡ˆ
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return ç»“æžœ
     */
    int insertCustomer(Customer customer);
    /**
     * ä¿®æ”¹å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return ç»“æžœ
     */
    int updateCustomer(Customer customer);
    /**
     * åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param id å®¢æˆ·æ¡£æ¡ˆä¸»é”®
     * @return ç»“æžœ
     */
    int deleteCustomerById(Long id);
    /**
     * æ‰¹é‡åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    int deleteCustomerByIds(Long[] ids);
    List<CustomerExcelDTO> selectCustomerDtoListByIds(List<Long> ids);
    List<CustomerExcelDTO> selectCustomerDtoLists();
}
src/main/java/com/ruoyi/basic/pojo/Customer.java
@@ -1,15 +1,13 @@
package com.ruoyi.basic.pojo;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import java.io.Serializable;
import java.util.Date;
/**
 * å®¢æˆ·æ¡£æ¡ˆå¯¹è±¡ customer
@@ -117,7 +115,7 @@
    @Excel(name = "开户行号")
    private String bankCode;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
//    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/basic/service/ICustomerService.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.excel.CustomerExcelDTO;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.framework.web.domain.AjaxResult;
import org.springframework.web.multipart.MultipartFile;
@@ -68,6 +69,8 @@
    List<Customer> selectCustomerListByIds(Long[] ids);
    List<CustomerExcelDTO> selectCustomerDtoByIds(Long[] ids);
    /**
     * æŸ¥è¯¢å®¢æˆ·ä¿¡æ¯
     *
@@ -76,6 +79,9 @@
    List<Map<String, Object>> customerList(Customer customer);
    List<Customer> selectCustomerLists(Customer customer);
    List<CustomerExcelDTO> selectCustomerDtoLists();
    AjaxResult importData(MultipartFile file);
    int transferCustomer(CustomerDto customer);
}
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -1,8 +1,6 @@
package com.ruoyi.basic.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -11,6 +9,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.dto.CustomerFollowUpDto;
import com.ruoyi.basic.excel.CustomerExcelDTO;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerFollowUp;
@@ -25,6 +24,7 @@
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import lombok.AllArgsConstructor;
@@ -35,7 +35,6 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
@@ -59,6 +58,8 @@
    private CustomerFollowUpFileService customerFollowUpFileService;
    private CustomerReturnVisitService customerReturnVisitService;
    private SysUserMapper sysUserMapper;
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆ
@@ -142,6 +143,7 @@
            queryWrapper.like(Customer::getCustomerType, customerType);
        }
        // 3. æ‰§è¡Œåˆ†é¡µæŸ¥è¯¢ï¼ˆä¿ç•™åˆ†é¡µå…ƒæ•°æ®ï¼‰
        IPage<Customer> customerPage = customerMapper.selectPage(page, queryWrapper);
@@ -188,9 +190,17 @@
     */
    @Override
    public int insertCustomer(Customer customer) {
        // 1. æ ¡éªŒå®¢æˆ·åç§°æ˜¯å¦å·²å­˜åœ¨
        LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Customer::getCustomerName, customer.getCustomerName());
        List<Customer> customerList = customerMapper.selectList(queryWrapper);
        if (!customerList.isEmpty()) {
            throw new RuntimeException("客户名称已存在");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long tenantId = loginUser.getTenantId();
        customer.setTenantId(tenantId);
        customer.setCreateUser(loginUser.getUserId().intValue());
        return customerMapper.insert(customer);
    }
@@ -202,6 +212,14 @@
     */
    @Override
    public int updateCustomer(Customer customer) {
        // 1. æ ¡éªŒå®¢æˆ·åç§°æ˜¯å¦å·²å­˜åœ¨
        LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Customer::getCustomerName, customer.getCustomerName());
        queryWrapper.ne(Customer::getId, customer.getId());
        List<Customer> customerList = customerMapper.selectList(queryWrapper);
        if (!customerList.isEmpty()) {
            throw new RuntimeException("客户名称已存在");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long tenantId = loginUser.getTenantId();
        customer.setTenantId(tenantId);
@@ -239,8 +257,18 @@
    }
    @Override
    public List<CustomerExcelDTO> selectCustomerDtoByIds(Long[] ids) {
        return customerMapper.selectCustomerDtoListByIds(Arrays.asList(ids));
    }
    @Override
    public List<Customer> selectCustomerLists(Customer customer) {
        return customerMapper.selectList(null);
    }
    @Override
    public List<CustomerExcelDTO> selectCustomerDtoLists() {
        return customerMapper.selectCustomerDtoLists();
    }
    @Override
@@ -297,4 +325,21 @@
        }
        return sb.toString();
    }
    @Override
    public int transferCustomer(CustomerDto customerDto) {
        // æ ¡éªŒç»´æŠ¤äººæ˜¯å¦å­˜åœ¨
        SysUser sysUser = sysUserMapper.selectUserById(customerDto.getCreateUser().longValue());
        if (sysUser == null) {
            throw new RuntimeException("维护人不存在");
        }
        // æ ¡éªŒå®¢æˆ·æ˜¯å¦å­˜åœ¨
        Customer customer = customerMapper.selectById(customerDto.getId());
        if (customer == null) {
            throw new RuntimeException("客户不存在");
        }
        customer.setMaintainer(sysUser.getNickName());
        customer.setCreateUser(customerDto.getCreateUser());
        return customerMapper.updateById(customer);
    }
}
src/main/resources/mapper/basic/CustomerMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.basic.mapper.CustomerMapper">
    <select id="selectCustomerDtoListByIds" resultType="com.ruoyi.basic.excel.CustomerExcelDTO">
        SELECT
            c.customer_name AS customerName,
            c.taxpayer_identification_number AS taxpayerIdentificationNumber,
            c.company_address AS companyAddress,
            c.company_phone AS companyPhone,
            c.contact_person AS contactPerson,
            c.contact_phone AS contactPhone,
            c.maintainer AS maintainer,
            c.basic_bank_account AS basicBankAccount,
            c.bank_account AS bankAccount,
            c.bank_code AS bankCode,
            cfu.follow_up_time AS followUpTime,
            cfu.follow_up_method AS followUpMethod,
            cfu.follow_up_level AS followUpLevel,
            cfu.follower_user_name AS followerUserName,
            cfu.content AS content
        FROM customer c
        LEFT JOIN customer_follow_up cfu ON c.id = cfu.customer_id
        WHERE c.id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
        ORDER BY c.create_user
    </select>
    <select id="selectCustomerDtoLists" resultType="com.ruoyi.basic.excel.CustomerExcelDTO">
        SELECT
            c.customer_name AS customerName,
            c.taxpayer_identification_number AS taxpayerIdentificationNumber,
            c.company_address AS companyAddress,
            c.company_phone AS companyPhone,
            c.contact_person AS contactPerson,
            c.contact_phone AS contactPhone,
            c.maintainer AS maintainer,
            c.basic_bank_account AS basicBankAccount,
            c.bank_account AS bankAccount,
            c.bank_code AS bankCode,
            cfu.follow_up_time AS followUpTime,
            cfu.follow_up_method AS followUpMethod,
            cfu.follow_up_level AS followUpLevel,
            cfu.follower_user_name AS followerUserName,
            cfu.content AS content
        FROM
            customer c
                LEFT JOIN customer_follow_up cfu ON c.id = cfu.customer_id
        ORDER BY
            c.create_user
    </select>
</mapper>