1. 项目背景与核心价值
这个基于SpringBoot+Vue的小型企业客户关系管理系统(CRM)平台,是我在指导Java Web方向毕业设计时反复打磨的一个实战项目。不同于市面上那些花哨的商业系统,它精准抓住了小微企业客户管理的三个核心痛点:轻量易部署、功能模块完整、二次开发友好。
在实际教学和企业咨询中,我发现很多5-20人规模的小微企业,根本用不起Salesforce这类重型CRM,但又确实需要管理客户资料、跟进记录和商机流程。这个项目就是针对这个细分场景设计的——后端采用SpringBoot 2.7 + MyBatis-Plus的组合保证开发效率,前端用Vue 3 + Element Plus实现响应式界面,整套系统在2核4G的云服务器上就能流畅运行。
关键设计原则:所有功能模块都经过真实小微企业验证,去掉了冗余功能,保留客户管理、联系记录、销售漏斗、数据看板等刚需模块,SQL脚本包含完整的测试数据,接上电源就能演示业务流程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构解析
2.1 后端技术栈设计
SpringBoot的选择绝非偶然:相比传统的SSM框架,它的自动配置特性让毕业生能快速搭建可运行的Web服务。我特别加入了以下关键配置:
- 多环境配置分离(application-dev.yml/prod.yml)
- 统一异常处理(@ControllerAdvice + 自定义BusinessException)
- 接口幂等性设计(通过@RepeatSubmit注解+Redis实现)
- 数据权限控制(基于AOP的动态SQL改写)
数据库选用MySQL 8.0,SQL脚本里包含几个精妙设计:
sql复制-- 客户跟进记录表的分区设计
CREATE TABLE `crm_contact_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`client_id` bigint NOT NULL COMMENT '关联客户ID',
`contact_type` tinyint NOT NULL COMMENT '1电话 2拜访 3线上',
`contact_time` datetime NOT NULL,
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`, `contact_time`),
KEY `idx_client` (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
PARTITION BY RANGE (YEAR(contact_time)*100 + MONTH(contact_time)) (
PARTITION p202301 VALUES LESS THAN (202302),
PARTITION p202302 VALUES LESS THAN (202303)
);
2.2 前端工程化实践
Vue 3的组合式API比Options API更适合业务逻辑封装,项目中体现为:
- 基于Pinia的状态管理方案
- 动态路由权限方案(对应后端接口/menu/list)
- 自定义Hooks复用(如useTable封装了分页查询逻辑)
- ECharts可视化看板的按需加载
特别值得一提的是表格组件的封装技巧:
javascript复制// src/components/ProTable/index.vue
const loadData = async (params) => {
loading.value = true;
try {
const { data } = await props.request({
...params,
...searchParams.value
});
tableData.value = data.records;
pagination.total = data.total;
} finally {
loading.value = false;
}
};
3. 核心功能实现细节
3.1 客户360°视图
这个模块的难点在于关联数据的聚合展示。后端采用CQRS模式分离查询和命令:
java复制@GetMapping("/clients/{id}/detail")
public R<ClientDetailVO> getClientDetail(@PathVariable Long id) {
// 主线程获取基础信息
ClientBasicVO basic = clientService.getBasicInfo(id);
// 并行获取其他信息
CompletableFuture<List<ContactRecordVO>> contactFuture = CompletableFuture
.supplyAsync(() -> contactService.listByClientId(id), taskExecutor);
CompletableFuture<List<OrderVO>> orderFuture = CompletableFuture
.supplyAsync(() -> orderService.listByClientId(id), taskExecutor);
return R.success(ClientDetailVO.builder()
.basic(basic)
.contacts(contactFuture.join())
.orders(orderFuture.join())
.build());
}
前端则使用Tab页+缓存策略优化体验:
vue复制<el-tabs v-model="activeTab" @tab-click="handleTabChange">
<el-tab-pane label="基础信息" name="basic" :lazy="true">
<BasicInfo :data="detail.basic" />
</el-tab-pane>
<el-tab-pane label="跟进记录" name="contact" :lazy="true">
<ContactTable :data="detail.contacts" />
</el-tab-pane>
</el-tabs>
3.2 销售漏斗分析
这个功能的业务逻辑值得深挖:
- 使用状态机模式定义商机阶段
java复制public enum OpportunityStage {
LEAD("线索", 1),
QUALIFIED("已确认", 2),
PROPOSAL("方案中", 3),
NEGOTIATION("谈判中", 4),
CLOSED_WON("已成交", 5),
CLOSED_LOST("已丢失", 5);
private final String desc;
private final int order;
// 省略构造方法...
}
- 漏斗计算使用窗口函数优化
sql复制SELECT
stage,
COUNT(*) as count,
ROUND(COUNT(*) * 100.0 / FIRST_VALUE(COUNT(*)) OVER (), 2) as percentage
FROM crm_opportunity
WHERE deleted = 0
GROUP BY stage
ORDER BY stage_order;
4. 项目部署与二次开发
4.1 快速启动方案
为避免环境配置的麻烦,项目内置了Docker Compose文件:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: crm
ports:
- "3306:3306"
volumes:
- ./sql:/docker-entrypoint-initdb.d
redis:
image: redis:6
ports:
- "6379:6379"
后端启动时有个易错点:如果本地同时装了MySQL5和MySQL8,需要显式指定驱动版本:
properties复制spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4.2 接口文档规范
采用Swagger + OpenAPI 3.0规范,但做了两项增强:
- 接口分组(通过@Tag注解)
- 字段级注释(通过@Schema注解示例)
java复制@Operation(summary = "创建客户")
@PostMapping("/clients")
public R<Long> createClient(
@RequestBody @Valid ClientCreateDTO dto) {
return R.success(clientService.createClient(dto));
}
@Data
public class ClientCreateDTO {
@Schema(description = "客户名称", example = "阿里巴巴")
@NotBlank(message = "名称不能为空")
private String name;
@Schema(description = "客户等级", example = "1")
private Integer level;
}
5. 毕设常见问题解决方案
在指导过程中,我发现学生最容易卡在三个地方:
- 跨域问题:虽然SpringBoot配置了CORS,但前端开发时还是建议配置代理
javascript复制// vue.config.js
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
- 日期序列化:前端收到的日期是Long类型时间戳,需要统一处理
javascript复制// src/utils/date.js
export function parseDateTime(timestamp) {
return dayjs(typeof timestamp === 'string'
? parseInt(timestamp)
: timestamp).format('YYYY-MM-DD HH:mm')
}
- 权限按钮显隐:不要直接v-if判断权限码,应该封装指令
javascript复制// src/directives/permission.js
app.directive('permission', {
mounted(el, binding) {
if (!hasPermission(binding.value)) {
el.parentNode?.removeChild(el);
}
}
})
这个项目经过三次大版本迭代,最新版特别强化了以下特性:
- 前端采用Vite构建速度提升70%
- 后端接口响应时间平均控制在200ms内
- SQL脚本包含压力测试数据(约1万条客户记录)
- 提供Postman接口测试集合
对于想深入研究的同学,建议从销售漏斗模块入手,尝试添加"预计成交概率"算法,这是个能体现技术深度的改进方向。我在代码中预留了// TODO注释作为扩展提示点,比如在OpportunityServiceImpl类中就有一个基于历史数据的概率预测算法空实现。
