作为一名经历过三次企业CRM系统改造的老开发,我深刻理解中小企业在客户管理上的困境。去年帮一家本地商贸公司做系统升级时,发现他们还在用Excel表格记录客户信息,员工离职带走了300多个客户联系方式,老板只能苦笑着重新跑市场。这正是当前中小企业客户管理的典型缩影——数据分散、流程断裂、工具落后。
这个毕设项目瞄准的正是这个被大厂忽略的细分市场:20-200人规模的中小企业。与大企业动辄百万级的Salesforce部署不同,我们需要解决的是三个具体痛点:
在技术预研阶段,我们对比了三种主流方案:
最终选择SSM(Spring+SpringMVC+MyBatis)的原因有三:
实测数据:在阿里云2核4G环境下,SSM处理并发请求的能力是Django的3.2倍
Vue.js胜出的关键因素是:
特别说明没有选择React的原因:中小企业技术团队往往前端能力有限,Vue的模板语法更接近传统HTML,学习成本更低。
采用改良版RBAC模型,核心表结构设计:
sql复制CREATE TABLE `sys_role` (
`role_id` int NOT NULL COMMENT '员工/客户经理/管理员',
`data_scope` int NOT NULL COMMENT '1本人 2部门 3全部'
);
CREATE TABLE `sys_user_role` (
`user_id` varchar(32) NOT NULL,
`role_id` int NOT NULL
);
权限拦截关键代码:
java复制@PreAuthorize("@ss.hasDataScope('customer:view')")
public List<Customer> selectCustomerList(Customer customer) {
// 自动注入数据权限SQL
String dataScope = " AND " + dataScopeFilter("u", "dept");
return mapper.selectList(dataScope);
}
采用MinIO分布式存储方案,上传逻辑优化:
java复制String objectName = DigestUtils.md5Hex(file.getInputStream())
+ "." + FilenameUtils.getExtension(filename);
基于SM2国密算法的签名流程:
javascript复制const { privateKey, publicKey } = generateSM2KeyPair()
const signature = sm2.doSignature(JSON.stringify(report), privateKey)
java复制boolean verify = SM2Util.verify(
report.getOriginalText(),
report.getSignature(),
publicKey
);
在客户列表查询中,通过EXPLAIN分析发现未使用索引:
code复制type: ALL
rows: 12548
Extra: Using where
优化措施:
sql复制ALTER TABLE crm_customer
ADD INDEX idx_owner_status (owner_id, status);
sql复制SELECT * FROM crm_customer
WHERE owner_id = ? AND status = ? -- 确保最左匹配
ORDER BY last_contact_time DESC
LIMIT 10
优化后性能提升17倍(2300ms→135ms)
针对高频访问的客户详情页,设计二级缓存:
java复制public Customer getCustomer(Long id) {
String key = "customer:" + id;
return redisTemplate.execute(new RedisCallback<Customer>() {
@Override
public Customer doInRedis(RedisConnection connection) {
// 双重检查锁
synchronized (this) {
if (connection.exists(key.getBytes())) {
return deserialize(connection.get(key.getBytes()));
}
Customer customer = mapper.selectById(id);
connection.setEx(key.getBytes(), 1800, serialize(customer));
return customer;
}
}
});
}
开发初期遇到的典型跨域错误:
code复制Access-Control-Allow-Origin missing
最终采用的解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
配合前端axios配置:
javascript复制axios.defaults.withCredentials = true
初期使用Spring默认文件上传导致OOM:
code复制java.lang.OutOfMemoryError: Java heap space
优化方案:
yaml复制spring:
servlet:
multipart:
location: /tmp/uploads
file-size-threshold: 1MB
nginx复制location /upload {
client_max_body_size 50M;
proxy_pass http://minio:9000;
}
完整部署文件示例:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
volumes:
- ./mysql:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
minio:
image: minio/minio
command: server /data
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: admin123
ports:
- "9000:9000"
app:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
- minio
Prometheus监控关键指标:
yaml复制- job_name: 'spring'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8080']
Grafana监控看板包含:
经过3个月的实际运行测试,在合作企业取得的数据:
几个值得注意的教训:
这个项目的代码我已经整理成完整可运行的版本,包含详细的部署文档和数据库初始化脚本。对于想学习企业级CRM开发的同学,建议重点关注权限控制和数据一致性这两个模块的实现。