liyong
2026-04-17 544bfa4c9c8b5777d1440b93e12a5e51ae916156
feat(basic): 客户管理模块重构及数据库配置更新

- 更新数据库连接配置为本地环境设置
- 将模块从 staff 切换为 basic 模块
- 在 Customer 实体类中新增使用用户和使用状态字段
- 添加 CustomerDto 和 CustomerPrivateDto 数据传输对象
- 引入 CustomerPrivate 和 CustomerPrivatePool 实体类
- 创建 CustomerPrivateController 控制器实现增删改查功能
- 实现 CustomerPrivateMapper 数据访问层接口
- 更新 CustomerFollowUp 表关联字段从 customerId 到 customerPrivatePoolId
- 修改 CustomerReturnVisit 表关联字段为 customerPrivatePoolId
- 重构 CustomerServiceImpl 中的列表查询逻辑
- 添加 CustomerMapper 的 listPage 分页查询方法
- 创建 CustomerMapper.xml 映射文件实现复杂查询逻辑
- 更新 InvoiceLedgerMapper.xml 的分组查询条件
- 修改回访提醒任务中的客户ID参数传递
- 添加销售台账中客户私海池的相关处理逻辑
- 更新忽略表配置增加产品相关表单
已添加15个文件
已修改16个文件
1341 ■■■■■ 文件已修改
src/main/java/com/ruoyi/CodeGenerator.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerController.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerFollowUpController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerPrivateController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerPrivatePoolController.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/CustomerDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/CustomerPrivateDto.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/CustomerPrivatePoolDto.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerPrivateMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerPrivatePoolMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/Customer.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/CustomerFollowUp.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/CustomerPrivate.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/CustomerPrivatePool.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/CustomerReturnVisit.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/CustomerPrivatePoolService.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/CustomerPrivateService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ICustomerService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerFollowUpServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerPrivatePoolServiceImpl.java 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerPrivateServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerReturnVisitServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/task/ReturnVisitReminderTask.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerMapper.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerPrivateMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerPrivatePoolMapper.xml 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/InvoiceLedgerMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/CodeGenerator.java
@@ -19,11 +19,11 @@
// æ¼”示例子,执行 main æ–¹æ³•控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
    public static String database_url = "jdbc:mysql://1.15.17.182:9999/product-inventory-management-new";
    public static String database_url = "jdbc:mysql://localhost:3300/product-inventory-management-new";
    public static String database_username = "root";
    public static String database_password= "xd@123456..";
    public static String database_password= "root";
    public static String author = "芯导软件(江苏)有限公司";
    public static String model = "staff"; // æ¨¡å—
    public static String model = "basic"; // æ¨¡å—
    public static String setParent = "com.ruoyi."+ model; // åŒ…路径
    public static String tablePrefix = ""; // è®¾ç½®è¿‡æ»¤è¡¨å‰ç¼€
    public static void main(String[] args) {
src/main/java/com/ruoyi/basic/controller/CustomerController.java
@@ -2,6 +2,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.pojo.Customer;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -9,6 +10,7 @@
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -32,8 +34,9 @@
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆåˆ—表
     */
    @GetMapping("/list")
    public IPage<Customer> list(Page<Customer> page, Customer customer) {
        return customerService.selectCustomerList(page, customer);
    public R list(Page<CustomerDto> page, CustomerDto customer) {
        IPage<CustomerDto> customerDtoIPage = customerService.selectCustomerList(page, customer);
        return R.ok(customerDtoIPage);
    }
    /**
@@ -116,4 +119,6 @@
    public List customerList(Customer customer) {
        return customerService.customerList(customer);
    }
}
src/main/java/com/ruoyi/basic/controller/CustomerFollowUpController.java
@@ -46,7 +46,7 @@
    @ApiOperation("查询客户跟进列表")
    public IPage<CustomerFollowUp> list(Page<CustomerFollowUp> page, CustomerFollowUp customerFollowUp) {
        LambdaQueryWrapper<CustomerFollowUp> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(customerFollowUp.getCustomerId() != null, CustomerFollowUp::getCustomerId, customerFollowUp.getCustomerId())
        queryWrapper.eq(customerFollowUp.getCustomerPrivatePoolId() != null, CustomerFollowUp::getCustomerPrivatePoolId, customerFollowUp.getCustomerPrivatePoolId())
                .like(customerFollowUp.getFollowerUserName() != null, CustomerFollowUp::getFollowerUserName, customerFollowUp.getFollowerUserName())
                .orderByDesc(CustomerFollowUp::getFollowUpTime);
        return customerFollowUpService.page(page, queryWrapper);
src/main/java/com/ruoyi/basic/controller/CustomerPrivateController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.basic.controller;
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.CustomerPrivateDto;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerPrivate;
import com.ruoyi.basic.service.CustomerPrivatePoolService;
import com.ruoyi.basic.service.CustomerPrivateService;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import static com.ruoyi.framework.web.domain.AjaxResult.success;
/**
 * <p>
 * å®¢æˆ·æ¡£æ¡ˆ å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-17 10:39:09
 */
@RestController
@RequestMapping("/customerPrivate")
public class CustomerPrivateController {
    @Autowired
    private CustomerPrivateService customerPrivateService;
    @Autowired
    private CustomerPrivatePoolService customerPrivatePoolService;
    @PostMapping("/add")
    public R add(@RequestBody CustomerPrivateDto customerPrivateDto) {
        return R.ok(customerPrivateService.add(customerPrivateDto));
    }
    @DeleteMapping("/delete")
    public R delete(@RequestBody List<Long> ids) {
        return R.ok(customerPrivateService.delete(ids));
    }
    /**
     * å¯¼å…¥å®¢æˆ·æ¡£æ¡ˆ
     */
    @Log(title = "客户档案", businessType = BusinessType.IMPORT)
    @PostMapping("/importData")
    public R importData(MultipartFile file) throws Exception {
        return customerPrivateService.importData(file);
    }
    /**
     * å¯¼å‡ºå®¢æˆ·æ¡£æ¡ˆåˆ—表
     */
    @Log(title = "客户档案", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, CustomerPrivatePoolDto customerPrivatePoolDto) {
        List<Long> ids = customerPrivatePoolDto.getIds();
        List<CustomerPrivatePoolDto> list;
        if (ids != null && ids.size() > 0) {
            list = customerPrivatePoolService.selectCustomerPrivatePoolDtoListByIds(ids);
        } else {
            list = customerPrivatePoolService.selectCustomerPrivatePoolDtoLists(customerPrivatePoolDto);
        }
        ExcelUtil<CustomerPrivatePoolDto> util = new ExcelUtil<CustomerPrivatePoolDto>(CustomerPrivatePoolDto.class);
        util.exportExcel(response, list, "客户档案数据");
    }
    @PostMapping("/downloadTemplate")
    @Log(title = "客户档案-下载模板", businessType = BusinessType.EXPORT)
    public void downloadTemplate(HttpServletResponse response) {
        ExcelUtil<CustomerPrivatePoolDto> util = new ExcelUtil<CustomerPrivatePoolDto>(CustomerPrivatePoolDto.class);
        util.importTemplateExcel(response, "客户档案模板");
    }
}
src/main/java/com/ruoyi/basic/controller/CustomerPrivatePoolController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package com.ruoyi.basic.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.ruoyi.basic.service.CustomerPrivatePoolService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 *  å‰ç«¯æŽ§åˆ¶å™¨ å®¢æˆ·ï¼ˆç§æµ·ï¼‰
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-16 04:43:00
 */
@RestController
@Api(tags = "客户(私海)")
@RequestMapping("/customerPrivatePool")
public class CustomerPrivatePoolController {
    @Autowired
    private CustomerPrivatePoolService customerPrivatePoolService;
    @GetMapping("/listPage")
    @ApiOperation("客户(私海)列表")
    public R listPage(CustomerPrivatePoolDto customerPrivatePoolDto, Page<CustomerPrivatePoolDto> page){
        //查询当前用户的客户信息
        customerPrivatePoolDto.setBoundId(SecurityUtils.getUserId());
        IPage<CustomerPrivatePoolDto> listPage = customerPrivatePoolService.listPage(page, customerPrivatePoolDto);
        return R.ok(listPage);
    }
    @PostMapping("/add")
    @ApiOperation("分配客户(私海)")
    public R add(@RequestBody CustomerPrivatePoolDto customerPrivatePool){
        boolean result = customerPrivatePoolService.add(customerPrivatePool);
        return R.ok(result);
    }
    @PutMapping("/update")
    public R update(@RequestBody CustomerPrivatePoolDto customerPrivatePoolDto) {
        return R.ok(customerPrivatePoolService.updateCustomerPrivatePoolDto(customerPrivatePoolDto));
    }
    @DeleteMapping("/delete/{id}")
    @ApiOperation("删除客户(私海)")
    public R delete(@PathVariable Long id){
        boolean result = customerPrivatePoolService.deleteCustomerPrivatePool(id);
        return R.ok(result);
    }
    @PostMapping("/together")
    @ApiOperation("共享")
    public R together( @RequestBody CustomerPrivatePoolDto customerPrivatePool){
        boolean result = customerPrivatePoolService.together(customerPrivatePool);
        return R.ok(result);
    }
    @GetMapping("/info/{id}")
    @ApiOperation("详情")
    public R getInfo(@PathVariable Long id){
        CustomerPrivatePoolDto customerPrivatePool = customerPrivatePoolService.getInfo(id);
        return R.ok(customerPrivatePool);
    }
    @GetMapping("/getbyId/{id}")
    @ApiOperation("详情")
    public R getbyId(@PathVariable Long id){
        CustomerPrivatePoolDto customerPrivatePool = customerPrivatePoolService.getbyId(id);
        return R.ok(customerPrivatePool);
    }
}
src/main/java/com/ruoyi/basic/dto/CustomerDto.java
@@ -21,4 +21,8 @@
    private List<CustomerFollowUpDto> followUpList;
    private String usageUserName;
    private String togetherUserNames;
}
src/main/java/com/ruoyi/basic/dto/CustomerPrivateDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.basic.dto;
import com.ruoyi.basic.pojo.CustomerPrivate;
import lombok.Data;
import java.util.List;
@Data
public class CustomerPrivateDto extends CustomerPrivate {
    private List<CustomerFollowUpDto> followUpList;
}
src/main/java/com/ruoyi/basic/dto/CustomerPrivatePoolDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package com.ruoyi.basic.dto;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.basic.pojo.CustomerFollowUp;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
@Data
public class CustomerPrivatePoolDto extends CustomerPrivatePool {
    /**
     * å®¢æˆ·åç§°
     */
    @Excel(name = "客户名称")
    private String customerName;
    /** å®¢æˆ·åˆ†ç±»ï¼šé›¶å”®å®¢æˆ·ï¼Œè¿›é”€å•†å®¢æˆ· */
    @ApiModelProperty(value = "客户要id")
    private Long customerId;
    /**
     * è·Ÿè¿›ç¨‹åº¦
     */
//    @Excel(name = "跟进程度")
    @TableField(exist = false)
    private String followUpLevel;
    /**
     * è·Ÿè¿›æ—¶é—´
     */
//    @Excel(name = "跟进时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @TableField(exist = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date followUpTime;
    @Excel(name = "客户分类")
    private String customerType;
    private  String addressPhone;
    /**
     * çº³ç¨Žäººè¯†åˆ«å·
     */
    @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;
    /**
     * ç»´æŠ¤æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "维护时间" , width = 30, dateFormat = "yyyy-MM-dd")
    private Date maintenanceTime;
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @ApiModelProperty(value = "银行基本户")
    @Excel(name = "银行基本户")
    private String basicBankAccount;
    @ApiModelProperty(value = "银行账号")
    @Excel(name = "银行账号")
    private String bankAccount;
    @ApiModelProperty(value = "开户行号")
    @Excel(name = "开户行号")
    private String bankCode;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty(value = "跟进记录")
    private List<CustomerFollowUpDto> followUpList;
    @ApiModelProperty(value = "绑定人ids")
    private List< Long> boundIds;
    @Excel(isExport = false)
    private List<Long> ids;
}
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
@@ -1,7 +1,12 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
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.pojo.Customer;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -11,6 +16,7 @@
 * @author ruoyi
 * @date 2025-05-07
 */
@Mapper
public interface CustomerMapper extends BaseMapper<Customer>
{
    /**
@@ -60,4 +66,6 @@
     * @return ç»“æžœ
     */
    int deleteCustomerByIds(Long[] ids);
    IPage<CustomerDto> listPage(Page<CustomerDto> page, @Param("c") CustomerDto customer);
}
src/main/java/com/ruoyi/basic/mapper/CustomerPrivateMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.basic.mapper;
import com.ruoyi.basic.pojo.CustomerPrivate;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * å®¢æˆ·æ¡£æ¡ˆ Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-17 10:39:09
 */
@Mapper
public interface CustomerPrivateMapper extends BaseMapper<CustomerPrivate> {
}
src/main/java/com/ruoyi/basic/mapper/CustomerPrivatePoolMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 *  Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-16 04:43:00
 */
@Mapper
public interface CustomerPrivatePoolMapper extends BaseMapper<CustomerPrivatePool> {
    IPage<CustomerPrivatePoolDto> listPage(IPage<CustomerPrivatePoolDto> page, @Param("c") CustomerPrivatePoolDto customerPrivatePoolDto);
    CustomerPrivatePoolDto selectInfo(Long id);
    List<CustomerPrivatePoolDto> selectInfos();
}
src/main/java/com/ruoyi/basic/pojo/Customer.java
@@ -10,6 +10,7 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import org.checkerframework.checker.units.qual.A;
/**
 * å®¢æˆ·æ¡£æ¡ˆå¯¹è±¡ customer
@@ -123,4 +124,9 @@
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @ApiModelProperty(value = "使用用户")
    private Long usageUser;
    @ApiModelProperty(value = "使用状态")
    private Long usageStatus;
}
src/main/java/com/ruoyi/basic/pojo/CustomerFollowUp.java
@@ -8,6 +8,7 @@
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
@@ -34,9 +35,9 @@
    private Integer id;
    /**
     * å…³è”的客户ID
     * å…³è”的私海id
     */
    private Integer customerId;
    private Long customerPrivatePoolId;
    /**
     * è·Ÿè¿›æ–¹å¼
@@ -52,6 +53,7 @@
     * è·Ÿè¿›æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime followUpTime;
    /**
src/main/java/com/ruoyi/basic/pojo/CustomerPrivate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Date;
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.Getter;
import lombok.Setter;
/**
 * <p>
 * å®¢æˆ·æ¡£æ¡ˆ
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-17 10:39:09
 */
@Getter
@Setter
@TableName("customer_private")
@ApiModel(value = "CustomerPrivate对象", description = "客户档案")
public class CustomerPrivate implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty("客户名称")
    @Excel(name = "客户名称")
    private String customerName;
    @ApiModelProperty("纳税人识别号")
    @Excel(name = "纳税人识别号")
    private String taxpayerIdentificationNumber;
    @ApiModelProperty("公司地址")
    @Excel(name = "公司地址")
    private String companyAddress;
    @ApiModelProperty("公司电话")
    @Excel(name = "公司电话")
    private String companyPhone;
    @ApiModelProperty("联系人")
    @Excel(name = "联系人")
    private String contactPerson;
    @ApiModelProperty("联系电话")
    @Excel(name = "联系电话", cellType = Excel.ColumnType.STRING)
    private String contactPhone;
    @ApiModelProperty("维护人")
    @Excel(name = "维护人")
    private String maintainer;
    @ApiModelProperty("维护时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "维护时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date maintenanceTime;
    @ApiModelProperty("租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @ApiModelProperty("银行基本户")
    @Excel(name = "银行基本户")
    private String basicBankAccount;
    @ApiModelProperty("银行账号")
    @Excel(name = "银行账号")
    private String bankAccount;
    @ApiModelProperty("开户行号")
    @Excel(name = "开户行号")
    private String bankCode;
    @ApiModelProperty("客户分类:零售客户,进销商客户")
    @Excel(name = "客户分类")
    private String customerType;
    @ApiModelProperty("创建人ID")
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @ApiModelProperty("部门ID")
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @ApiModelProperty("跟进程度")
    @TableField(exist = false)
    private String followUpLevel;
    @ApiModelProperty("跟进时间")
    @TableField(exist = false)
    private Date followUpTime;
    @TableField(exist = false)
    private Long[] ids;
    @TableField(exist = false)
    private String addressPhone;
}
src/main/java/com/ruoyi/basic/pojo/CustomerPrivatePool.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
 * <p>
 *
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-16 04:43:00
 */
@Data
@TableName("customer_private_pool")
@ApiModel(value = "CustomerPrivatePool对象", description = "")
public class CustomerPrivatePool implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty("客户id")
    private Long customerId;
    @ApiModelProperty("绑定人id")
    private Long boundId;
    @ApiModelProperty("创建人")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty("更新人")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @ApiModelProperty("创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty("类型区分公海客户和私海客户 é»˜è®¤æ˜¯ç§æµ·å®¢æˆ·0 å…¬æµ·1")
    private Long type;
    @ApiModelProperty("部门id")
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @ApiModelProperty("删除标识 é»˜è®¤0 1已经删除")
    private Integer DeleteFlag;
}
src/main/java/com/ruoyi/basic/pojo/CustomerReturnVisit.java
@@ -36,7 +36,7 @@
    /**
     * å…³è”客户ID
     */
    private Integer customerId;
    private Integer customerPrivatePoolId;
    /**
     * æé†’开关 (0:关闭, 1:开启)
src/main/java/com/ruoyi/basic/service/CustomerPrivatePoolService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
 * <p>
 *  æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-16 04:43:00
 */
public interface CustomerPrivatePoolService extends IService<CustomerPrivatePool> {
    IPage<CustomerPrivatePoolDto> listPage(Page<CustomerPrivatePoolDto> page, CustomerPrivatePoolDto customerPrivatePoolDto);
    boolean deleteCustomerPrivatePool(Long id);
    boolean together(CustomerPrivatePoolDto customerPrivatePool);
    boolean add(CustomerPrivatePoolDto customerPrivatePool);
    CustomerPrivatePoolDto getInfo(Long id);
    Boolean updateCustomerPrivatePoolDto(CustomerPrivatePoolDto customerPrivatePoolDto);
    CustomerPrivatePoolDto getbyId(Long id);
    List<CustomerPrivatePoolDto> selectCustomerPrivatePoolDtoListByIds(List<Long> ids);
    List<CustomerPrivatePoolDto> selectCustomerPrivatePoolDtoLists(CustomerPrivatePoolDto customerPrivatePoolDto);
}
src/main/java/com/ruoyi/basic/service/CustomerPrivateService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerPrivateDto;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerPrivate;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
 * <p>
 * å®¢æˆ·æ¡£æ¡ˆ æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-17 10:39:09
 */
public interface CustomerPrivateService extends IService<CustomerPrivate> {
    Boolean add(CustomerPrivateDto customerPrivateDto);
    Integer delete(List<Long> id);
    R importData(MultipartFile file);
}
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.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.framework.web.domain.AjaxResult;
import org.springframework.web.multipart.MultipartFile;
@@ -32,7 +33,7 @@
     * @param id å®¢æˆ·æ¡£æ¡ˆä¸»é”®
     * @return å®¢æˆ·è¯¦æƒ…DTO
     */
    CustomerDto selectCustomerDetailById(Long id);
    Customer selectCustomerDetailById(Long id);
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆåˆ—表
@@ -40,7 +41,6 @@
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return å®¢æˆ·æ¡£æ¡ˆé›†åˆ
     */
    IPage<Customer> selectCustomerList(Page<Customer> page, Customer customer);
    /**
     * æ–°å¢žå®¢æˆ·æ¡£æ¡ˆ
@@ -78,4 +78,6 @@
    List<Customer> selectCustomerLists(Customer customer);
    AjaxResult importData(MultipartFile file);
    IPage<CustomerDto> selectCustomerList(Page<CustomerDto> page, CustomerDto customer);
}
src/main/java/com/ruoyi/basic/service/impl/CustomerFollowUpServiceImpl.java
@@ -132,7 +132,7 @@
        }
        List<CustomerFollowUp> followUps = list(new LambdaQueryWrapper<CustomerFollowUp>()
                .eq(CustomerFollowUp::getCustomerId, customerId));
                .eq(CustomerFollowUp::getCustomerPrivatePoolId, customerId));
        if (followUps != null && !followUps.isEmpty()) {
            for (CustomerFollowUp followUp : followUps) {
src/main/java/com/ruoyi/basic/service/impl/CustomerPrivatePoolServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,234 @@
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.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.dto.CustomerFollowUpDto;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.mapper.CustomerPrivateMapper;
import com.ruoyi.basic.pojo.*;
import com.ruoyi.basic.mapper.CustomerPrivatePoolMapper;
import com.ruoyi.basic.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
 * æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-16 04:43:00
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class CustomerPrivatePoolServiceImpl extends ServiceImpl<CustomerPrivatePoolMapper, CustomerPrivatePool> implements CustomerPrivatePoolService {
    @Autowired
    private CustomerPrivatePoolMapper customerPrivatePoolMapper;
    @Autowired
    private CustomerFollowUpService customerFollowUpService;
    @Autowired
    private CustomerReturnVisitService customerReturnVisitService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private CustomerFollowUpFileService customerFollowUpFileService;
    @Autowired
    private CustomerPrivateMapper customerPrivateMapper;
    @Override
    public IPage<CustomerPrivatePoolDto> listPage(Page<CustomerPrivatePoolDto> page, CustomerPrivatePoolDto customerPrivatePoolDto) {
        IPage<CustomerPrivatePoolDto> customerPrivatePoolDtoIPage = customerPrivatePoolMapper.listPage(page, customerPrivatePoolDto);
        List<Long> customerIds = customerPrivatePoolDtoIPage.getRecords().stream()
                .map(CustomerPrivatePoolDto::getId )
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!org.springframework.util.CollectionUtils.isEmpty(customerIds)) {
            Map<Long, CustomerFollowUp> latestFollowUpMap = getLatestFollowUpMap(customerIds);
            customerPrivatePoolDtoIPage.getRecords().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()
                    ));
                }
            });
        }
        return customerPrivatePoolDtoIPage;
    }
    private Map<Long, CustomerFollowUp> getLatestFollowUpMap(List<Long> customerIds) {
        List<CustomerFollowUp> followUps = customerFollowUpService.list(
                new LambdaQueryWrapper<CustomerFollowUp>()
                        .in(CustomerFollowUp::getCustomerPrivatePoolId, customerIds)
                        .orderByDesc(CustomerFollowUp::getFollowUpTime)
        );
        return followUps.stream()
                .collect(Collectors.toMap(
                        CustomerFollowUp::getCustomerPrivatePoolId,
                        followUp -> followUp,
                        (existing, replacement) -> existing
                ));
    }
    @Override
    public boolean deleteCustomerPrivatePool(Long id) {
        List<CustomerPrivatePool> list = this.list(new QueryWrapper<CustomerPrivatePool>().lambda().eq(CustomerPrivatePool::getCustomerId, id));
        for (CustomerPrivatePool customerPrivatePool : list) {
            customerFollowUpService.remove(new QueryWrapper<CustomerFollowUp>().lambda().eq(CustomerFollowUp::getCustomerPrivatePoolId, customerPrivatePool.getId()));
            customerReturnVisitService.remove(new QueryWrapper<CustomerReturnVisit>().lambda().eq(CustomerReturnVisit::getCustomerPrivatePoolId, customerPrivatePool.getId()));
        }
        Customer byId = customerService.getById(id);
        byId.setUsageStatus(0L);
        byId.setUsageUser(0L);
        customerService.updateById(byId);
        list.stream().forEach(customerPrivatePool -> {
            customerPrivatePool.setDeleteFlag(1);
            customerPrivatePoolMapper.updateById(customerPrivatePool);
        });
        return true;
    }
    @Override
    public boolean together(CustomerPrivatePoolDto customerPrivatePoolDto) {
        List<CustomerPrivatePool> existingPools = this.list(new QueryWrapper<CustomerPrivatePool>().lambda().eq(CustomerPrivatePool::getCustomerId, customerPrivatePoolDto.getCustomerId()));
        List<Long> existingBoundIds = existingPools.stream()
                .map(CustomerPrivatePool::getBoundId)
                .collect(Collectors.toList());
        List<Long> newBoundIds = customerPrivatePoolDto.getBoundIds().stream()
                .filter(boundId -> !existingBoundIds.contains(boundId))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(newBoundIds)) {
            return true;
        }
        for (Long id : customerPrivatePoolDto.getBoundIds()) {
            CustomerPrivatePool customerPrivatePool = new CustomerPrivatePool();
            customerPrivatePool.setCustomerId(customerPrivatePoolDto.getCustomerId());
            customerPrivatePool.setBoundId(id);
            customerPrivatePool.setType(1L);
            this.save(customerPrivatePool);
        }
        return true;
    }
    @Override
    public boolean add(CustomerPrivatePoolDto customerPrivatePool) {
        customerPrivatePool.setType(1L);
        this.save(customerPrivatePool);
        Customer byId = customerService.getById(customerPrivatePool.getCustomerId());
        if (byId != null) {
            byId.setUsageStatus(1L);
            byId.setUsageUser(customerPrivatePool.getBoundId());
            return customerService.updateById(byId);
        }
        throw new RuntimeException("客户不存在");
    }
    @Override
    public CustomerPrivatePoolDto getInfo(Long id) {
        CustomerPrivatePoolDto customerPrivatePool = customerPrivatePoolMapper.selectInfo(id);
        if (customerPrivatePool == null) {
            return null;
        }
        // æŸ¥è¯¢è·Ÿè¿›è®°å½•
        List<CustomerFollowUp> followUpList = customerFollowUpService.list(
                new LambdaQueryWrapper<CustomerFollowUp>()
                        .eq(CustomerFollowUp::getCustomerPrivatePoolId, id)
                        .orderByDesc(CustomerFollowUp::getFollowUpTime)
        );
        if (!CollectionUtils.isEmpty(followUpList)) {
            List<CustomerFollowUpDto> followUpDtoList = followUpList.stream().map(followUp -> {
                CustomerFollowUpDto followUpDto = new CustomerFollowUpDto();
                BeanUtils.copyProperties(followUp, followUpDto);
                // æŸ¥è¯¢é™„ä»¶
                List<CustomerFollowUpFile> fileList = customerFollowUpFileService.list(
                        new LambdaQueryWrapper<CustomerFollowUpFile>()
                                .eq(CustomerFollowUpFile::getFollowUpId, followUp.getId())
                );
                followUpDto.setFileList(fileList);
                return followUpDto;
            }).collect(Collectors.toList());
            customerPrivatePool.setFollowUpList(followUpDtoList);
        }
        return customerPrivatePool;
    }
    @Override
    public Boolean updateCustomerPrivatePoolDto(CustomerPrivatePoolDto customerPrivatePoolDto) {
        if (customerPrivatePoolDto.getType() == 0L) {
            CustomerPrivate byId = customerPrivateMapper.selectById(customerPrivatePoolDto.getCustomerId());
            BeanUtils.copyProperties(customerPrivatePoolDto, byId);
            byId.setId(customerPrivatePoolDto.getCustomerId());
            customerPrivateMapper.updateById(byId);
        } else if (customerPrivatePoolDto.getType() == 1L) {
            Customer customer = customerService.getById(customerPrivatePoolDto.getCustomerId());
            BeanUtils.copyProperties(customerPrivatePoolDto, customer);
            customer.setId(customerPrivatePoolDto.getCustomerId());
            customerService.updateById(customer);
        }
        return true;
    }
    @Override
    public CustomerPrivatePoolDto getbyId(Long id) {
        CustomerPrivatePoolDto customerPrivatePool = customerPrivatePoolMapper.selectInfo(id);
        return customerPrivatePool;
    }
    @Override
    public List<CustomerPrivatePoolDto> selectCustomerPrivatePoolDtoListByIds(List<Long> ids) {
        ArrayList<CustomerPrivatePoolDto> customerPrivatePoolDtos = new ArrayList<>();
        for (Long id : ids) {
            CustomerPrivatePoolDto customerPrivatePoolDto = customerPrivatePoolMapper.selectInfo(id);
            customerPrivatePoolDtos.add(customerPrivatePoolDto);
        }
        return customerPrivatePoolDtos;
    }
    @Override
    public List<CustomerPrivatePoolDto> selectCustomerPrivatePoolDtoLists(CustomerPrivatePoolDto customerPrivatePoolDto) {
        return customerPrivatePoolMapper.selectInfos();
    }
}
src/main/java/com/ruoyi/basic/service/impl/CustomerPrivateServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.CustomerPrivateDto;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.CustomerPrivatePoolMapper;
import com.ruoyi.basic.pojo.*;
import com.ruoyi.basic.mapper.CustomerPrivateMapper;
import com.ruoyi.basic.service.CustomerFollowUpService;
import com.ruoyi.basic.service.CustomerPrivateService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.service.CustomerReturnVisitService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
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.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
 * <p>
 * å®¢æˆ·æ¡£æ¡ˆ æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-17 10:39:09
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class CustomerPrivateServiceImpl extends ServiceImpl<CustomerPrivateMapper, CustomerPrivate> implements CustomerPrivateService {
    @Autowired
    private CustomerPrivatePoolMapper customerPrivatePoolMapper;
    @Autowired
    private CustomerFollowUpService customerFollowUpService;
    @Autowired
    private CustomerReturnVisitService customerReturnVisitService;
    @Autowired
    private SalesLedgerMapper salesLedgerMapper;
    @Override
    public Boolean add(CustomerPrivateDto customerPrivateDto) {
        //新增私海 å®¢æˆ·
        this.save(customerPrivateDto);
        //新增私海记录
        CustomerPrivatePool customerPrivatePool = new CustomerPrivatePool();
        customerPrivatePool.setCustomerId(customerPrivateDto.getId());
        customerPrivatePool.setBoundId(SecurityUtils.getLoginUser().getUserId());
        customerPrivatePoolMapper.insert(customerPrivatePool);
        return true;
    }
    @Override
    public Integer delete(List<Long> ids) {
        List<CustomerPrivatePool> customerPrivatePools = customerPrivatePoolMapper.selectList(new QueryWrapper<CustomerPrivatePool>().lambda().in(CustomerPrivatePool::getId, ids));
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new QueryWrapper<SalesLedger>().lambda().in(SalesLedger::getCustomerId, customerPrivatePools.stream().map(CustomerPrivatePool::getCustomerId).collect(Collectors.toList())));
        if (!CollectionUtils.isEmpty(salesLedgers)) {
            throw new RuntimeException("客户有销售合同,请先删除销售合同");
        }
        if (CollectionUtils.isEmpty(customerPrivatePools)) {
            throw new RuntimeException("客户不存在");
        }
        customerFollowUpService.remove(new QueryWrapper<CustomerFollowUp>().lambda().in(CustomerFollowUp::getCustomerPrivatePoolId, customerPrivatePools.stream().map(CustomerPrivatePool::getCustomerId).collect(Collectors.toList())));
        customerReturnVisitService.remove(new QueryWrapper<CustomerReturnVisit>().lambda().in(CustomerReturnVisit::getCustomerPrivatePoolId, customerPrivatePools.stream().map(CustomerPrivatePool::getCustomerId).collect(Collectors.toList())));
        customerPrivatePools.stream().forEach(customerPrivatePool -> {
            customerPrivatePool.setDeleteFlag(1);
            customerPrivatePoolMapper.updateById(customerPrivatePool);
        });
        return 1;
    }
    @Override
    public R importData(MultipartFile file) {
        try {
            ExcelUtil<CustomerPrivate> util = new ExcelUtil<CustomerPrivate>(CustomerPrivate.class);
            List<CustomerPrivate> userList = util.importExcel(file.getInputStream());
            if (CollectionUtils.isEmpty(userList)) {
                return R.fail("模板错误或导入数据为空");
            }
            for (CustomerPrivate user : userList) {
                CustomerPrivateDto customerPrivateDto = new CustomerPrivateDto();
                BeanUtils.copyProperties(user, customerPrivateDto);
                this.add(customerPrivateDto);
            }
            return R.ok(true);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("导入失败");
        }
    }
}
src/main/java/com/ruoyi/basic/service/impl/CustomerReturnVisitServiceImpl.java
@@ -75,7 +75,7 @@
            throw new ServiceException("客户ID不能为空");
        }
        LambdaQueryWrapper<CustomerReturnVisit> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CustomerReturnVisit::getCustomerId, customerId);
        queryWrapper.eq(CustomerReturnVisit::getCustomerPrivatePoolId, customerId);
        CustomerReturnVisit returnVisit = baseMapper.selectOne(queryWrapper);
        if (returnVisit == null) {
@@ -94,7 +94,7 @@
            throw new ServiceException("客户ID不能为空");
        }
        LambdaQueryWrapper<CustomerReturnVisit> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CustomerReturnVisit::getCustomerId, customerId);
        queryWrapper.eq(CustomerReturnVisit::getCustomerPrivatePoolId, customerId);
        List<CustomerReturnVisit> returnVisits = baseMapper.selectList(queryWrapper);
        for (CustomerReturnVisit returnVisit : returnVisits) {
@@ -124,7 +124,7 @@
        if (returnVisit == null) {
            throw new ServiceException("回访提醒数据不能为空");
        }
        if (returnVisit.getCustomerId() == null) {
        if (returnVisit.getCustomerPrivatePoolId() == null) {
            throw new ServiceException("客户ID不能为空");
        }
        if (returnVisit.getReminderTime() == null) {
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,10 +9,13 @@
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.CustomerPrivatePoolDto;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.CustomerPrivatePoolMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerFollowUp;
import com.ruoyi.basic.pojo.CustomerFollowUpFile;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.ruoyi.basic.service.CustomerFollowUpFileService;
import com.ruoyi.basic.service.CustomerFollowUpService;
import com.ruoyi.basic.service.CustomerReturnVisitService;
@@ -30,6 +31,7 @@
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;
@@ -51,13 +53,18 @@
@AllArgsConstructor
@Slf4j
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService {
    private final SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private  SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private CustomerPrivatePoolMapper customerPrivatePoolMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private CustomerFollowUpService customerFollowUpService;
    @Autowired
    private CustomerFollowUpFileService customerFollowUpFileService;
    @Autowired
    private CustomerReturnVisitService customerReturnVisitService;
    /**
@@ -78,41 +85,8 @@
     * @return å®¢æˆ·è¯¦æƒ…DTO
     */
    @Override
    public CustomerDto selectCustomerDetailById(Long id) {
        Customer customer = customerMapper.selectById(id);
        if (customer == null) {
            return null;
        }
        CustomerDto dto = new CustomerDto();
        BeanUtils.copyProperties(customer, dto);
        // æŸ¥è¯¢è·Ÿè¿›è®°å½•
        List<CustomerFollowUp> followUpList = customerFollowUpService.list(
                new LambdaQueryWrapper<CustomerFollowUp>()
                        .eq(CustomerFollowUp::getCustomerId, id)
                        .orderByDesc(CustomerFollowUp::getFollowUpTime)
        );
        if (!CollectionUtils.isEmpty(followUpList)) {
            List<CustomerFollowUpDto> followUpDtoList = followUpList.stream().map(followUp -> {
                CustomerFollowUpDto followUpDto = new CustomerFollowUpDto();
                BeanUtils.copyProperties(followUp, followUpDto);
                // æŸ¥è¯¢é™„ä»¶
                List<CustomerFollowUpFile> fileList = customerFollowUpFileService.list(
                        new LambdaQueryWrapper<CustomerFollowUpFile>()
                                .eq(CustomerFollowUpFile::getFollowUpId, followUp.getId())
                );
                followUpDto.setFileList(fileList);
                return followUpDto;
            }).collect(Collectors.toList());
            dto.setFollowUpList(followUpDtoList);
        }
        return dto;
    public Customer selectCustomerDetailById(Long id) {
        return this.getById( id);
    }
    /**
@@ -122,62 +96,53 @@
     * @return å®¢æˆ·æ¡£æ¡ˆ
     */
    @Override
    public IPage<Customer> selectCustomerList(Page<Customer> page, Customer customer) {
        // 1. å¤„理空值场景(参数校验)
        if (page == null) {
            page = Page.of(1, 10); // é»˜è®¤ç¬¬1页,每页10条数据
        }
        if (customer == null) {
            customer = new Customer(); // é¿å…ç©ºå¯¹è±¡å¯¼è‡´çš„NPE
    public IPage<CustomerDto> selectCustomerList(Page<CustomerDto> page, CustomerDto customer) {
        IPage<CustomerDto> customerPage = customerMapper.listPage(page, customer);
        List<CustomerDto> records = customerPage.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return customerPage;
        }
        // 2. æž„建查询条件(增强空值安全)
        LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
        String customerName = customer.getCustomerName();
        String customerType = customer.getCustomerType();
        if (StringUtils.isNotBlank(customerName)) {
            queryWrapper.like(Customer::getCustomerName, customerName);
        }
        if (StringUtils.isNotBlank(customerType)) {
            queryWrapper.like(Customer::getCustomerType, customerType);
        }
        // 3. æ‰§è¡Œåˆ†é¡µæŸ¥è¯¢ï¼ˆä¿ç•™åˆ†é¡µå…ƒæ•°æ®ï¼‰
        IPage<Customer> customerPage = customerMapper.selectPage(page, queryWrapper);
        // 4. æ•°æ®å¤„理(增强空值安全 & ä»£ç å¯è¯»æ€§ï¼‰
        List<Customer> processedList = customerPage.getRecords().stream()
                .filter(Objects::nonNull) // è¿‡æ»¤ç©ºå¯¹è±¡ï¼ˆé¿å…åŽç»­æ“ä½œNPE)
                .peek(c -> {
                    // å®‰å…¨èŽ·å–å­—æ®µï¼Œé¿å…null值拼接
                    String address = StringUtils.defaultString(c.getCompanyAddress(), "");
                    String phone = StringUtils.defaultString(c.getCompanyPhone(), "");
                    c.setAddressPhone(address + "(" + phone + ")");
                    // æŸ¥è¯¢æœ€æ–°çš„跟进记录
                    CustomerFollowUp followUp = customerFollowUpService.getOne(
                            new LambdaQueryWrapper<CustomerFollowUp>()
                                    .eq(CustomerFollowUp::getCustomerId, c.getId())
                                    .orderByDesc(CustomerFollowUp::getFollowUpTime)
                                    .last("LIMIT 1")
                    );
                    if (followUp != null) {
                        c.setFollowUpLevel(followUp.getFollowUpLevel());
                        c.setFollowUpTime(
                                Date.from(
                                        followUp.getFollowUpTime().atZone(ZoneId.systemDefault()).toInstant()
                                )
                        );
                    }
                })
        List<Long> customerIds = records.stream()
                .map(CustomerDto::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        // 5. æ›´æ–°åˆ†é¡µç»“果中的数据(保持分页信息完整)
        IPage<Customer> resultPage = new Page<>(customerPage.getCurrent(), customerPage.getSize(), customerPage.getTotal());
        resultPage.setRecords(processedList);
        if (!CollectionUtils.isEmpty(customerIds)) {
            Map<Long, CustomerFollowUp> latestFollowUpMap = getLatestFollowUpMap(customerIds);
        return customerPage; // è¿”回包含分页信息的IPage对象
            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()
                    ));
                }
            });
        }
        return customerPage;
    }
    private Map<Long, CustomerFollowUp> getLatestFollowUpMap(List<Long> customerIds) {
        List<CustomerFollowUp> followUps = customerFollowUpService.list(
                new LambdaQueryWrapper<CustomerFollowUp>()
                        .in(CustomerFollowUp::getCustomerPrivatePoolId, customerIds)
                        .orderByDesc(CustomerFollowUp::getFollowUpTime)
        );
        return followUps.stream()
                .collect(Collectors.toMap(
                        CustomerFollowUp::getCustomerPrivatePoolId,
                        followUp -> followUp,
                        (existing, replacement) -> existing
                ));
    }
    /**
src/main/java/com/ruoyi/basic/task/ReturnVisitReminderTask.java
@@ -76,7 +76,7 @@
        }
        try {
            unipushService.sendReturnVisitReminder(returnVisitId, client.getCid(), returnVisit.getContent(), returnVisit.getCustomerId());
            unipushService.sendReturnVisitReminder(returnVisitId, client.getCid(), returnVisit.getContent(), returnVisit.getCustomerPrivatePoolId());
            CustomerReturnVisit updateObj = new CustomerReturnVisit();
            updateObj.setId(returnVisitId);
            updateObj.setIsCompleted(1);
src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java
@@ -36,6 +36,8 @@
        IGNORE_TABLES.add("gen_table");
        IGNORE_TABLES.add("sys_notice");
        IGNORE_TABLES.add("sys_user_client");
        IGNORE_TABLES.add("gen_table_column");
        IGNORE_TABLES.add("product_model");
        IGNORE_TABLES.add("product");
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -9,10 +9,13 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.service.AccountIncomeService;
import com.ruoyi.basic.dto.CustomerPrivatePoolDto;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.CustomerPrivatePoolMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.CustomerPrivatePool;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.exception.base.BaseException;
@@ -128,6 +131,8 @@
    ;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private CustomerPrivatePoolMapper customerPrivatePoolMapper;
    @Override
    public List<SalesLedger> selectSalesLedgerList(SalesLedgerDto salesLedgerDto) {
@@ -594,7 +599,7 @@
    public int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto) {
        try {
            // 1. æ ¡éªŒå®¢æˆ·ä¿¡æ¯
            Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId());
            CustomerPrivatePoolDto customer = customerPrivatePoolMapper.selectInfo(salesLedgerDto.getCustomerId());
            if (customer == null) {
                throw new BaseException("客户不存在");
            }
src/main/resources/mapper/basic/CustomerMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
<?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">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.Customer">
        <id column="id" property="id" />
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.basic.dto.CustomerDto">
        select
        c.*,
        u.user_name usage_user_name,
        (
        select group_concat(u2.user_name separator ', ')
        from customer_private_pool cpp2
        left join sys_user u2 on cpp2.bound_id = u2.user_id
        where cpp2.customer_id = c.id
        and cpp2.bound_id != c.usage_user
        ) as together_user_names
        from customer c
        left join sys_user u on c.usage_user = u.user_id
        <where>
            <if test="c.customerName != null and c.customerName != ''">
                and customer_name like concat('%', #{c.customerName}, '%')
            </if>
            <if test="c.customerType != null and c.customerType != ''">
                and customer_type = #{c.customerType}
            </if>
        </where>
    </select>
</mapper>
src/main/resources/mapper/basic/CustomerPrivateMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
<?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.CustomerPrivateMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.CustomerPrivate">
        <id column="id" property="id" />
        <result column="customer_name" property="customerName" />
        <result column="taxpayer_identification_number" property="taxpayerIdentificationNumber" />
        <result column="company_address" property="companyAddress" />
        <result column="company_phone" property="companyPhone" />
        <result column="contact_person" property="contactPerson" />
        <result column="contact_phone" property="contactPhone" />
        <result column="maintainer" property="maintainer" />
        <result column="maintenance_time" property="maintenanceTime" />
        <result column="tenant_id" property="tenantId" />
        <result column="basic_bank_account" property="basicBankAccount" />
        <result column="bank_account" property="bankAccount" />
        <result column="bank_code" property="bankCode" />
        <result column="customer_type" property="customerType" />
        <result column="create_user" property="createUser" />
        <result column="dept_id" property="deptId" />
    </resultMap>
</mapper>
src/main/resources/mapper/basic/CustomerPrivatePoolMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
<?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.CustomerPrivatePoolMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.CustomerPrivatePool">
        <id column="id" property="id" />
        <result column="customer_id" property="customerId" />
        <result column="bound_id" property="boundId" />
        <result column="create_user" property="createUser" />
        <result column="update_user" property="updateUser" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.basic.dto.CustomerPrivatePoolDto">
        select cpp.id,
        cpp.bound_id,
        cpp.type,
        coalesce(c.id, cp.id) as customer_id,
        coalesce(c.customer_name, cp.customer_name) as customer_name,
        coalesce(c.customer_type, cp.customer_type) as customer_type,
        coalesce(c.taxpayer_identification_number, cp.taxpayer_identification_number) as taxpayer_identification_number,
        coalesce(c.company_address, cp.company_address) as company_address,
        coalesce(c.company_phone, cp.company_phone) as company_phone,
        coalesce(c.contact_person, cp.contact_person) as contact_person,
        coalesce(c.contact_phone, cp.contact_phone) as contact_phone,
        coalesce(c.maintainer, cp.maintainer) as maintainer,
        coalesce(c.maintenance_time, cp.maintenance_time) as maintenance_time,
        coalesce(c.tenant_id, cp.tenant_id) as tenant_id,
        coalesce(c.basic_bank_account, cp.basic_bank_account) as basic_bank_account,
        coalesce(c.bank_account, cp.bank_account) as bank_account,
        coalesce(c.bank_code, cp.bank_code) as bank_code
        from customer_private_pool cpp
        left join customer c on c.id = cpp.customer_id and cpp.type = 1
        left join customer_private cp on cp.id = cpp.customer_id and cpp.type = 0
        <where>
            cpp.delete_flag = 0
            <if test="c.customerName != null and c.customerName != ''">
                and c.customer_name like concat('%', #{c.customerName}, '%')
            </if>
            <if test="c.boundId != null">
                and cpp.bound_id = #{c.boundId}
            </if>
        </where>
        order by cpp.id desc
    </select>
    <select id="selectInfo" resultType="com.ruoyi.basic.dto.CustomerPrivatePoolDto">
        select cpp.id,
               cpp.bound_id,
               cpp.type,
               coalesce(c.id, cp.id) as customer_id,
               coalesce(c.customer_name, cp.customer_name) as customer_name,
               coalesce(c.customer_type, cp.customer_type) as customer_type,
               coalesce(c.taxpayer_identification_number, cp.taxpayer_identification_number) as taxpayer_identification_number,
               coalesce(c.company_address, cp.company_address) as company_address,
               coalesce(c.company_phone, cp.company_phone) as company_phone,
               coalesce(c.contact_person, cp.contact_person) as contact_person,
               coalesce(c.contact_phone, cp.contact_phone) as contact_phone,
               coalesce(c.maintainer, cp.maintainer) as maintainer,
               coalesce(c.maintenance_time, cp.maintenance_time) as maintenance_time,
               coalesce(c.tenant_id, cp.tenant_id) as tenant_id,
               coalesce(c.basic_bank_account, cp.basic_bank_account) as basic_bank_account,
               coalesce(c.bank_account, cp.bank_account) as bank_account,
               coalesce(c.bank_code, cp.bank_code) as bank_code
        from customer_private_pool cpp
                 left join customer c on c.id = cpp.customer_id and cpp.type = 1
                 left join customer_private cp on cp.id = cpp.customer_id and cpp.type = 0
        <where>
            <if test="id != null">
                and cpp.id = #{id}
            </if>
        </where>
    </select>
    <select id="selectInfos" resultType="com.ruoyi.basic.dto.CustomerPrivatePoolDto">
        select cpp.id,
               cpp.bound_id,
               cpp.type,
               coalesce(c.id, cp.id) as customer_id,
               coalesce(c.customer_name, cp.customer_name) as customer_name,
               coalesce(c.customer_type, cp.customer_type) as customer_type,
               coalesce(c.taxpayer_identification_number, cp.taxpayer_identification_number) as taxpayer_identification_number,
               coalesce(c.company_address, cp.company_address) as company_address,
               coalesce(c.company_phone, cp.company_phone) as company_phone,
               coalesce(c.contact_person, cp.contact_person) as contact_person,
               coalesce(c.contact_phone, cp.contact_phone) as contact_phone,
               coalesce(c.maintainer, cp.maintainer) as maintainer,
               coalesce(c.maintenance_time, cp.maintenance_time) as maintenance_time,
               coalesce(c.tenant_id, cp.tenant_id) as tenant_id,
               coalesce(c.basic_bank_account, cp.basic_bank_account) as basic_bank_account,
               coalesce(c.bank_account, cp.bank_account) as bank_account,
               coalesce(c.bank_code, cp.bank_code) as bank_code
        from customer_private_pool cpp
                 left join customer c on c.id = cpp.customer_id and cpp.type = 1
                 left join customer_private cp on cp.id = cpp.customer_id and cpp.type = 0
    </select>
</mapper>
src/main/resources/mapper/sales/InvoiceLedgerMapper.xml
@@ -123,7 +123,7 @@
                T1.customer_name LIKE CONCAT ('%',#{invoiceLedgerDto.searchText},'%')
            </if>
        </where>
        GROUP BY T1.customer_name
        GROUP BY T1.customer_name,t1.customer_id
    </select>
    <select id="invoiceLedgerProductInfo" resultType="com.ruoyi.sales.dto.InvoiceRegistrationProductDto">