关注:CodingTechWork
引言
在企业级应用中,客户关系管理(CRM)是核心功能之一。客户树是一种用于表示客户之间层级关系的结构,例如企业客户与子公司、经销商与下级经销商等。本文将详细介绍如何设计客户树及其关联关系,通过三张表来实现:客户表、客户树表和客户关联关系表。这种设计可以清晰地分离客户的基本信息、树的结构信息以及客户之间的层级关系。
一、客户树及其关联关系的定义
客户树是一种层级结构,用于表示客户之间的上下级关系。每个客户可以有多个子客户,而每个子客户又可以有自己的子客户,形成一个树状结构。客户树通常用于以下场景:
- 企业与子公司:表示企业集团的层级结构。
- 经销商与下级经销商:表示销售渠道的层级关系。
- 客户与联系人:表示客户内部的组织架构。
客户树的特点包括:
- 层级性:每个客户都有一个层级,顶级客户为第1层,其子客户为第2层,依此类推。
- 递归性:客户树的结构是递归的,每个子客户可以有自己的子客户。
- 关联性:客户之间通过父子关系关联。
二、设计方案
2.1 数据库设计
为了实现客户树及其关联关系,我们需要设计三张表:customer
表(客户表)、customer_tree
表(客户树表)和 customer_relationship
表(客户关联关系表)。
2.1.1 客户表(customer
)
字段名 | 数据类型 | 描述 |
---|---|---|
id |
INT | 客户唯一标识(主键) |
name |
VARCHAR(100) | 客户名称 |
code |
VARCHAR(50) | 客户编码 |
created_at |
DATETIME | 创建时间 |
updated_at |
DATETIME | 更新时间 |
2.1.2 客户树表(customer_tree
)
字段名 | 数据类型 | 描述 |
---|---|---|
id |
INT | 树唯一标识(主键) |
tree_name |
VARCHAR(100) | 树名称 |
root_id |
INT | 根节点ID(外键,指向customer 表) |
max_level |
INT | 树的最大层级 |
created_at |
DATETIME | 创建时间 |
updated_at |
DATETIME | 更新时间 |
2.1.3 客户关联关系表(customer_relationship
)
字段名 | 数据类型 | 描述 |
---|---|---|
id |
INT | 关系唯一标识(主键) |
tree_id |
INT | 所属树ID(外键,指向customer_tree 表) |
customer_id |
INT | 客户ID(外键,指向customer 表) |
parent_id |
INT | 父节点ID(外键,指向customer 表) |
level |
INT | 当前层级 |
created_at |
DATETIME | 创建时间 |
updated_at |
DATETIME | 更新时间 |
2.2 功能设计
- 查询客户树:通过递归查询或存储过程,获取完整的客户树结构。
- 新增客户:支持添加顶级客户或子客户。
- 删除客户:删除客户时,需要考虑其子客户是否需要同时删除。
- 更新客户信息:更新客户的基本信息或层级关系。
- 查询客户层级:查询某个客户的层级关系。
三、SQL实现
3.1 创建表
创建客户表
sql
CREATE TABLE `customer` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL,
`code` VARCHAR(50) NOT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
创建客户树表
sql
CREATE TABLE `customer_tree` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`tree_name` VARCHAR(100) NOT NULL,
`root_id` INT,
`max_level` INT DEFAULT 1,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`root_id`) REFERENCES `customer` (`id`)
);
创建客户关联关系表
sql
CREATE TABLE `customer_relationship` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`tree_id` INT,
`customer_id` INT,
`parent_id` INT,
`level` INT DEFAULT 1,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`tree_id`) REFERENCES `customer_tree` (`id`),
FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`),
FOREIGN KEY (`parent_id`) REFERENCES `customer` (`id`)
);
3.2 插入测试数据
插入客户数据
sql
INSERT INTO `customer` (`name`, `code`) VALUES
('顶级客户A', 'A'),
('子客户A1', 'A1'),
('子客户A2', 'A2'),
('子客户A1-1', 'A1-1'),
('顶级客户B', 'B'),
('子客户B1', 'B1');
插入客户树数据
sql
INSERT INTO `customer_tree` (`tree_name`, `root_id`, `max_level`) VALUES
('客户树A', 1, 3),
('客户树B', 5, 2);
插入客户关联关系数据
sql
INSERT INTO `customer_relationship` (`tree_id`, `customer_id`, `parent_id`, `level`) VALUES
(1, 1, NULL, 1), -- 顶级客户A
(1, 2, 1, 2), -- 子客户A1
(1, 3, 1, 2), -- 子客户A2
(1, 4, 2, 3), -- 子客户A1-1
(2, 5, NULL, 1), -- 顶级客户B
(2, 6, 5, 2); -- 子客户B1
3.3 查询客户树
使用递归查询(MySQL 8.0+):
sql
WITH RECURSIVE CustomerTree AS (
SELECT cr.id, cr.customer_id, cr.parent_id, cr.level
FROM customer_relationship cr
WHERE cr.parent_id IS NULL
UNION ALL
SELECT cr.id, cr.customer_id, cr.parent_id, cr.level
FROM customer_relationship cr
JOIN CustomerTree cte ON cr.parent_id = cte.customer_id
)
SELECT * FROM CustomerTree ORDER BY level, id;
3.4 删除客户
删除客户及其所有子客户:
sql
-- 删除客户及其所有子客户
WITH RECURSIVE CustomerTree AS (
SELECT id FROM customer_relationship WHERE customer_id = ?
UNION ALL
SELECT cr.id FROM customer_relationship cr JOIN CustomerTree cte ON cr.parent_id = cte.customer_id
)
DELETE FROM customer_relationship WHERE id IN (SELECT id FROM CustomerTree);
四、Java代码实现
4.1 项目依赖
使用Spring Boot和MyBatis:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
4.2 实体类
客户实体类
java
package com.example.demo.model;
import java.util.Date;
public class Customer {
private Integer id;
private String name;
private String code;
private Date createdAt;
private Date updatedAt;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
客户树实体类
java
package com.example.demo.model;
import java.util.Date;
public class CustomerTree {
private Integer id;
private String treeName;
private Integer rootId;
private Integer maxLevel;
private Date createdAt;
private Date updatedAt;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTreeName() {
return treeName;
}
public void setTreeName(String treeName) {
this.treeName = treeName;
}
public Integer getRootId() {
return rootId;
}
public void setRootId(Integer rootId) {
this.rootId = rootId;
}
public Integer getMaxLevel() {
return maxLevel;
}
public void setMaxLevel(Integer maxLevel) {
this.maxLevel = maxLevel;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
客户关联关系实体类
java
package com.example.demo.model;
import java.util.Date;
public class CustomerRelationship {
private Integer id;
private Integer treeId;
private Integer customerId;
private Integer parentId;
private Integer level;
private Date createdAt;
private Date updatedAt;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getTreeId() {
return treeId;
}
public void setTreeId(Integer treeId) {
this.treeId = treeId;
}
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
4.3 Mapper接口
客户Mapper
java
package com.example.demo.mapper;
import com.example.demo.model.Customer;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CustomerMapper {
@Select("SELECT * FROM customer")
List<Customer> getAllCustomers();
@Insert("INSERT INTO customer (name, code) VALUES (#{name}, #{code})")
void insertCustomer(Customer customer);
@Delete("DELETE FROM customer WHERE id = #{id}")
void deleteCustomerById(Integer id);
@Update("UPDATE customer SET name = #{name}, code = #{code} WHERE id = #{id}")
void updateCustomer(Customer customer);
}
客户树Mapper
java
package com.example.demo.mapper;
import com.example.demo.model.CustomerTree;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CustomerTreeMapper {
@Select("SELECT * FROM customer_tree")
List<CustomerTree> getAllCustomerTrees();
@Insert("INSERT INTO customer_tree (tree_name, root_id, max_level) VALUES (#{treeName}, #{rootId}, #{maxLevel})")
void insertCustomerTree(CustomerTree customerTree);
@Delete("DELETE FROM customer_tree WHERE id = #{id}")
void deleteCustomerTreeById(Integer id);
@Update("UPDATE customer_tree SET tree_name = #{treeName}, root_id = #{rootId}, max_level = #{maxLevel} WHERE id = #{id}")
void updateCustomerTree(CustomerTree customerTree);
}
客户关联关系Mapper
java
package com.example.demo.mapper;
import com.example.demo.model.CustomerRelationship;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CustomerRelationshipMapper {
@Select("SELECT * FROM customer_relationship")
List<CustomerRelationship> getAllCustomerRelationships();
@Insert("INSERT INTO customer_relationship (tree_id, customer_id, parent_id, level) VALUES (#{treeId}, #{customerId}, #{parentId}, #{level})")
void insertCustomerRelationship(CustomerRelationship customerRelationship);
@Delete("DELETE FROM customer_relationship WHERE id = #{id}")
void deleteCustomerRelationshipById(Integer id);
@Update("UPDATE customer_relationship SET tree_id = #{treeId}, customer_id = #{customerId}, parent_id = #{parentId}, level = #{level} WHERE id = #{id}")
void updateCustomerRelationship(CustomerRelationship customerRelationship);
}
4.4 服务层
客户服务类
java
package com.example.demo.service;
import com.example.demo.mapper.CustomerMapper;
import com.example.demo.model.Customer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CustomerService {
@Autowired
private CustomerMapper customerMapper;
public List<Customer> getAllCustomers() {
return customerMapper.getAllCustomers();
}
public void addCustomer(Customer customer) {
customerMapper.insertCustomer(customer);
}
public void deleteCustomer(Integer id) {
customerMapper.deleteCustomerById(id);
}
public void updateCustomer(Customer customer) {
customerMapper.updateCustomer(customer);
}
}
客户树服务类
java
package com.example.demo.service;
import com.example.demo.mapper.CustomerTreeMapper;
import com.example.demo.model.CustomerTree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CustomerTreeService {
@Autowired
private CustomerTreeMapper customerTreeMapper;
public List<CustomerTree> getAllCustomerTrees() {
return customerTreeMapper.getAllCustomerTrees();
}
public void addCustomerTree(CustomerTree customerTree) {
customerTreeMapper.insertCustomerTree(customerTree);
}
public void deleteCustomerTree(Integer id) {
customerTreeMapper.deleteCustomerTreeById(id);
}
public void updateCustomerTree(CustomerTree customerTree) {
customerTreeMapper.updateCustomerTree(customerTree);
}
}
客户关联关系服务类
java
package com.example.demo.service;
import com.example.demo.mapper.CustomerRelationshipMapper;
import com.example.demo.model.CustomerRelationship;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CustomerRelationshipService {
@Autowired
private CustomerRelationshipMapper customerRelationshipMapper;
public List<CustomerRelationship> getAllCustomerRelationships() {
return customerRelationshipMapper.getAllCustomerRelationships();
}
public void addCustomerRelationship(CustomerRelationship customerRelationship) {
customerRelationshipMapper.insertCustomerRelationship(customerRelationship);
}
public void deleteCustomerRelationship(Integer id) {
customerRelationshipMapper.deleteCustomerRelationshipById(id);
}
public void updateCustomerRelationship(CustomerRelationship customerRelationship) {
customerRelationshipMapper.updateCustomerRelationship(customerRelationship);
}
}
4.5 控制器
客户控制器
java
package com.example.demo.controller;
import com.example.demo.model.Customer;
import com.example.demo.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/customers")
public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping
public List<Customer> getAllCustomers() {
return customerService.getAllCustomers();
}
@PostMapping
public void addCustomer(@RequestBody Customer customer) {
customerService.addCustomer(customer);
}
@DeleteMapping("/{id}")
public void deleteCustomer(@PathVariable Integer id) {
customerService.deleteCustomer(id);
}
@PutMapping
public void updateCustomer(@RequestBody Customer customer) {
customerService.updateCustomer(customer);
}
}
客户树控制器
java
package com.example.demo.controller;
import com.example.demo.model.CustomerTree;
import com.example.demo.service.CustomerTreeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/customer-trees")
public class CustomerTreeController {
@Autowired
private CustomerTreeService customerTreeService;
@GetMapping
public List<CustomerTree> getAllCustomerTrees() {
return customerTreeService.getAllCustomerTrees();
}
@PostMapping
public void addCustomerTree(@RequestBody CustomerTree customerTree) {
customerTreeService.addCustomerTree(customerTree);
}
@DeleteMapping("/{id}")
public void deleteCustomerTree(@PathVariable Integer id) {
customerTreeService.deleteCustomerTree(id);
}
@PutMapping
public void updateCustomerTree(@RequestBody CustomerTree customerTree) {
customerTreeService.updateCustomerTree(customerTree);
}
}
客户关联关系控制器
java
package com.example.demo.controller;
import com.example.demo.model.CustomerRelationship;
import com.example.demo.service.CustomerRelationshipService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/customer-relationships")
public class CustomerRelationshipController {
@Autowired
private CustomerRelationshipService customerRelationshipService;
@GetMapping
public List<CustomerRelationship> getAllCustomerRelationships() {
return customerRelationshipService.getAllCustomerRelationships();
}
@PostMapping
public void addCustomerRelationship(@RequestBody CustomerRelationship customerRelationship) {
customerRelationshipService.addCustomerRelationship(customerRelationship);
}
@DeleteMapping("/{id}")
public void deleteCustomerRelationship(@PathVariable Integer id) {
customerRelationshipService.deleteCustomerRelationship(id);
}
@PutMapping
public void updateCustomerRelationship(@RequestBody CustomerRelationship customerRelationship) {
customerRelationshipService.updateCustomerRelationship(customerRelationship);
}
}
五、总结
本文通过设计客户表、客户树表和客户关联关系表,展示了如何构建和管理客户树及其关联关系。这种设计可以满足企业级应用中对客户关系管理的需求,同时提供了灵活的查询和操作功能。希望本文能够为需要实现类似功能的开发者提供参考。如果对本文有任何疑问或建议,欢迎在评论区留言。