1. 项目概述
在当今企业数字化转型浪潮中,客户关系管理(CRM)系统已成为提升业务效率的核心工具。传统CRM系统普遍存在架构陈旧、扩展性差的问题,难以适应现代企业对敏捷开发和快速迭代的需求。我们团队基于SpringBoot+Vue3+MyBatis技术栈,开发了一套高性能的现代化CRM解决方案。
这个系统最显著的特点是采用了前后端分离架构,后端使用SpringBoot提供RESTful API服务,前端通过Vue3实现动态交互,数据持久层采用MyBatis框架,数据库选用MySQL 8.0。这种技术组合既保证了系统的稳定性,又提供了良好的开发体验和性能表现。
提示:系统设计时特别考虑了中小企业的实际需求,模块化设计使得客户可以根据自身业务特点灵活组合功能,避免了传统CRM系统"大而全"但实用性差的问题。
2. 技术架构解析
2.1 后端技术栈设计
SpringBoot 2.7作为后端基础框架,其自动配置特性大幅减少了XML配置工作量。我们特别优化了以下核心配置:
- 多环境配置:通过application-{profile}.yml实现开发、测试、生产环境配置隔离
- 连接池优化:采用HikariCP替代默认连接池,配置参数如下:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
- 事务管理:使用@Transactional注解实现声明式事务,特别针对客户交互记录模块配置了读写分离:
java复制@Service
public class InteractionServiceImpl implements InteractionService {
@Transactional(readOnly = true)
public Page<Interaction> queryPage(Map<String, Object> params) {
// 查询操作走从库
}
@Transactional(rollbackFor = Exception.class)
public void saveInteraction(Interaction interaction) {
// 写入操作走主库
}
}
2.2 前端架构设计
Vue3组合式API大幅提升了代码可维护性,我们采用的技术方案包括:
- 状态管理:Pinia替代Vuex作为状态管理库,模块化设计如下:
javascript复制// stores/client.js
export const useClientStore = defineStore('client', {
state: () => ({
currentClient: null,
filterConditions: {}
}),
actions: {
async fetchClientDetail(id) {
const res = await api.get(`/clients/${id}`)
this.currentClient = res.data
}
}
})
- 路由控制:基于Vue Router实现动态路由加载,配合导航守卫实现权限控制:
javascript复制router.beforeEach(async (to) => {
const authStore = useAuthStore()
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
return '/login'
}
})
- 组件封装:对Element Plus组件进行二次封装,形成统一的UI规范。
3. 数据库设计与优化
3.1 核心表结构实现
系统包含20余张业务表,其中最关键的三张表设计如下:
- 客户信息表(crm_client):
sql复制CREATE TABLE `crm_client` (
`client_id` bigint NOT NULL AUTO_INCREMENT COMMENT '客户ID',
`client_name` varchar(100) NOT NULL COMMENT '客户名称',
`industry_code` varchar(20) NOT NULL COMMENT '行业编码',
`credit_rating` tinyint DEFAULT '3' COMMENT '信用等级(1-5)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`client_id`),
KEY `idx_industry` (`industry_code`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
- 交互记录表(crm_interaction):
sql复制CREATE TABLE `crm_interaction` (
`interaction_id` bigint NOT NULL AUTO_INCREMENT,
`client_id` bigint NOT NULL,
`interaction_type` enum('CALL','MEETING','EMAIL','OTHER') NOT NULL,
`content` text,
`follow_up` tinyint(1) DEFAULT '0' COMMENT '是否需要跟进',
`create_by` bigint NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`interaction_id`),
KEY `idx_client` (`client_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
3.2 查询性能优化
针对CRM系统常见的复杂查询场景,我们实施了以下优化措施:
-
索引策略:
- 为所有外键字段创建索引
- 为高频查询条件创建组合索引
- 使用覆盖索引减少回表操作
-
SQL优化示例:
java复制@Select("<script>" +
"SELECT c.client_id, c.client_name, COUNT(i.interaction_id) as interaction_count " +
"FROM crm_client c LEFT JOIN crm_interaction i ON c.client_id = i.client_id " +
"WHERE c.industry_code IN <foreach collection='industries' item='item' open='(' separator=',' close=')'>#{item}</foreach> " +
"GROUP BY c.client_id " +
"HAVING interaction_count > #{minInteraction} " +
"ORDER BY interaction_count DESC " +
"LIMIT #{limit}" +
"</script>")
List<ClientInteractionVO> listActiveClients(@Param("industries") List<String> industries,
@Param("minInteraction") int minInteraction,
@Param("limit") int limit);
- 缓存策略:
- 使用Redis缓存热点客户数据
- 本地Caffeine缓存字典数据
- 二级缓存优化MyBatis查询
4. 核心功能实现
4.1 客户360°视图
该功能整合了客户基础信息、交互历史、交易记录等多维度数据,关键技术实现包括:
- 数据聚合服务:
java复制public ClientDetailVO getClientDetail(Long clientId) {
// 基础信息
Client client = clientMapper.selectById(clientId);
// 最近交互记录
List<Interaction> interactions = interactionMapper.selectList(
new LambdaQueryWrapper<Interaction>()
.eq(Interaction::getClientId, clientId)
.orderByDesc(Interaction::getCreateTime)
.last("LIMIT 5")
);
// 业务数据统计
Map<String, Object> stats = clientMapper.selectStats(clientId);
return new ClientDetailVO(client, interactions, stats);
}
- 前端实现:
vue复制<template>
<el-tabs v-model="activeTab">
<el-tab-pane label="基本信息" name="basic">
<client-basic :data="clientData" />
</el-tab-pane>
<el-tab-pane label="交互记录" name="interactions">
<interaction-list :client-id="clientId" />
</el-tab-pane>
</el-tabs>
</template>
<script setup>
const route = useRoute()
const clientId = computed(() => route.params.id)
const { data: clientData } = useFetchClientDetail(clientId)
</script>
4.2 智能提醒系统
基于Quartz实现的任务调度系统,主要功能点:
- 提醒规则配置:
java复制@Entity
@Table(name = "crm_reminder_rule")
public class ReminderRule {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ruleId;
@Enumerated(EnumType.STRING)
private ReminderType type; // FOLLOW_UP/BIRTHDAY/PAYMENT等
private Integer advanceDays; // 提前天数
@Column(name = "is_active")
private Boolean active;
private String templateContent; // 提醒模板
}
- 任务触发逻辑:
java复制public class FollowUpJob implements Job {
@Autowired
private InteractionService interactionService;
@Autowired
private NotificationService notificationService;
@Override
public void execute(JobExecutionContext context) {
List<Interaction> interactions = interactionService
.listNeedFollowUp(LocalDate.now());
interactions.forEach(interaction -> {
Notification notification = new Notification();
notification.setUserId(interaction.getCreateBy());
notification.setContent("客户" + interaction.getClientName() + "需要跟进");
notificationService.send(notification);
});
}
}
5. 系统部署方案
5.1 容器化部署
采用Docker Compose编排服务,典型配置:
yaml复制version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: crm
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6-alpine
ports:
- "6379:6379"
volumes:
mysql_data:
5.2 性能调优建议
- JVM参数配置:
code复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xms2g
-Xmx2g
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
- 前端性能优化:
- 路由懒加载
- 组件异步加载
- 图片压缩处理
- CDN静态资源托管
6. 开发经验分享
6.1 前后端协作实践
- API文档管理:
使用Swagger + YApi实现接口文档自动化管理,后端通过注解生成文档:
java复制@Operation(summary = "获取客户详情")
@GetMapping("/clients/{id}")
public ResponseEntity<ClientDetailVO> getClientDetail(
@Parameter(description = "客户ID") @PathVariable Long id) {
// ...
}
- Mock数据方案:
前端开发阶段使用Mock.js模拟API响应:
javascript复制Mock.mock(/\/api\/clients\/\d+/, 'get', {
'code': 200,
'data': {
'clientId': 1,
'clientName': '@cname',
'industry': '@pick(["制造","金融","IT"])',
'contactPhone': /1[3-9]\d{9}/
}
})
6.2 典型问题解决方案
- Long类型精度丢失:
前端处理Java Long类型时,使用json-bigint解析:
javascript复制const axios = createAxiosInstance({
transformResponse: [data => {
try {
return JSONbig.parse(data)
} catch (e) {
return data
}
}]
})
- MyBatis批量插入优化:
java复制@Insert("<script>" +
"INSERT INTO crm_interaction(client_id, interaction_type, content) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.clientId}, #{item.interactionType}, #{item.content})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("list") List<Interaction> interactions);
- Vue3性能优化技巧:
- 使用v-memo缓存静态组件
- 合理使用computed替代复杂模板表达式
- 对于大型列表使用虚拟滚动
这个CRM系统在实际部署后,客户数据查询响应时间从原来的平均2.3秒降低到400毫秒以内,交互记录录入操作耗时减少60%。特别值得一提的是,通过前后端分离架构,前端团队可以独立进行界面优化和功能扩展,不再受后端发版周期限制。