CRM 全称 客户关系管理 (Customer Relationship Management),指企业用于优化客户互动并最大化利用数据的战略与技术手段的综合体。总体而言,CRM 将销售、营销和服务整合于统一的系统或战略之下,旨在帮助团队协同工作,以实现组织的整体目标。一套完善的 CRM 战略应覆盖客户旅程的全生命周期——从潜在客户首次知晓品牌,到完成购买并成为忠诚的回头客。本文将基于年最新技术栈,从需求分析到部署上线,详细讲解如何构建一个企业级CRM系统,涵盖前端交互、后端架构、数据库设计、DevOps全流程,适合有一定编程基础的开发者学习实践。

源码及演示:zxkfym.top

技术栈选型:企业级CRM的最优解

CRM系统的技术选型需兼顾开发效率、性能、可维护性和扩展性。经过对主流框架的对比测试,我们选择以下技术栈组合:

1. 前端技术栈:React 18 + TypeScript + Ant Design Pro

  • React 18:采用并发渲染(Concurrent Rendering)和自动批处理(Automatic Batching)特性,提升复杂表单和大数据列表的交互流畅度。配合react-hook-form处理表单验证,@tanstack/react-table实现动态表格,满足CRM中客户列表、商机管理等高频交互场景。
  • TypeScript:强类型系统减少运行时错误,尤其在CRM这类业务逻辑复杂的系统中,类型定义能显著提升代码可维护性。例如,为客户实体定义Customer接口:
    interface Customer {
      id: string;
      name: string;
      contact: {
        phone: string;
        email: string;
        address?: string; // 可选字段
      };
      tags: string[]; // 客户标签
      status: 'active' | 'inactive' | 'potential'; // 状态枚举
      createdAt: Date;
    }
    
  • Ant Design Pro:基于Ant Design的企业级中后台框架,内置路由、权限、国际化等功能,减少重复开发。其ProTable组件支持分页、筛选、排序,完美适配CRM的客户列表页;ProForm组件提供丰富的表单控件(如日期选择器、级联选择器),简化商机跟进记录的录入流程。

2. 后端技术栈:Node.js + NestJS + Prisma

  • Node.js + NestJS:NestJS是基于TypeScript的Node.js框架,采用模块化架构(Module/Controller/Service)和依赖注入(DI)模式,代码结构清晰,适合大型项目。相比Express,NestJS内置拦截器、守卫、管道等特性,可快速实现权限控制(如JWT认证)和请求日志。
  • Prisma ORM:下一代ORM工具,支持TypeScript类型安全,自动生成数据库迁移文件,避免手写SQL。通过Prisma Schema定义数据模型,例如客户表:
    model Customer {
      id        String   @id @default(uuid())
      name      String
      contact   Json     // 存储联系方式(JSON格式)
      tags      String[] // 标签数组
      status    Status   @default(POTENTIAL)
      createdAt DateTime @default(now())
      updatedAt DateTime @updatedAt
      orders    Order[]  // 关联订单表
    }
    
    enum Status {
      ACTIVE
      INACTIVE
      POTENTIAL
    }
    
  • Redis:用于缓存高频访问数据(如客户列表、权限配置),减轻数据库压力。通过ioredis库连接Redis,设置缓存策略:客户详情缓存30分钟,权限配置缓存1小时。

3. 数据库:PostgreSQL + Redis

  • PostgreSQL:关系型数据库的首选,支持JSONB类型(存储动态字段)、全文搜索(客户名称模糊查询)和事务(确保订单创建与库存扣减的原子性)。相比MySQL,PostgreSQL的JSONB性能更优,适合CRM中灵活的自定义字段需求。
  • Redis:除缓存外,还用于存储临时会话(如登录验证码)和分布式锁(防止并发操作冲突)。

4. DevOps工具链:Docker + GitHub Actions + AWS

  • Docker:容器化部署,确保开发、测试、生产环境一致性。编写Dockerfiledocker-compose.yml,一键启动前后端服务及数据库。
  • GitHub Actions:自动化CI/CD流程,提交代码后自动运行单元测试、构建镜像、推送到Docker Hub,并部署到AWS EC2。
  • AWS EC2 + RDS:生产环境部署在AWS,EC2运行应用服务,RDS托管PostgreSQL数据库,搭配S3存储客户附件(如合同PDF)。

需求分析与系统设计

1. 核心功能模块

CRM系统需覆盖客户全生命周期管理,主要功能模块如下:

  • 客户管理:客户信息录入、编辑、批量导入导出、标签分类、跟进记录。
  • 商机管理:商机阶段划分(初步接触→需求分析→方案报价→谈判签约)、金额预测、负责人分配。
  • 任务协作:待办事项(如跟进提醒)、日程安排、团队任务分配。
  • 数据分析:销售漏斗图表、客户转化率统计、业绩报表。
  • 权限控制:角色-based访问控制(RBAC),区分管理员、销售经理、普通销售员权限。

2. 数据库设计

基于ER模型设计核心表结构,重点关注关联关系和索引优化:

  • 用户表(users):存储账号信息,关联角色表(roles)。
  • 客户表(customers):核心实体,包含基础信息和动态字段(custom_fields JSONB)。
  • 商机表(opportunities):关联客户ID、负责人ID,记录阶段和金额。
  • 跟进记录表(follow_ups):记录客户沟通历史,关联客户ID和商机ID。
  • 权限表(permissions):定义操作权限(如customer:createopportunity:delete)。

索引优化:为客户表的namestatus字段创建联合索引,加速列表查询;商机表的customer_idowner_id字段创建索引,优化关联查询。

3. API设计规范

采用RESTful风格,统一响应格式:

{
  "code": 200, // 状态码:200成功,400参数错误,401未授权,500服务器错误
  "data": {}, // 响应数据
  "message": "success" // 提示信息
}

核心API示例:

  • GET /api/customers:分页查询客户列表(支持筛选、排序)。
  • POST /api/customers:创建客户(校验必填字段,如姓名、电话)。
  • PUT /api/customers/:id:更新客户信息(权限校验:仅负责人或管理员可修改)。
  • GET /api/analytics/sales-funnel:获取销售漏斗数据(按商机阶段统计数量)。

开发实战:从0到1构建CRM

1. 环境搭建

前端环境

# 安装Node.js 18+,创建React项目
npx create-react-app crm-frontend --template typescript
cd crm-frontend
npm install antd @ant-design/pro-components react-hook-form @tanstack/react-table axios

后端环境

# 创建NestJS项目
npm i -g @nestjs/cli
nest new crm-backend
cd crm-backend
npm install prisma @prisma/client @nestjs/config @nestjs/jwt passport-jwt bcrypt
npx prisma init # 初始化Prisma,生成schema.prisma文件

2. 核心模块开发

(1)客户管理模块(前端)

使用Ant Design Pro的ProTable组件实现客户列表,支持分页、筛选和批量操作:

// src/pages/CustomerList/index.tsx
import { ProTable } from '@ant-design/pro-components';
import { request } from 'umi';

const CustomerList = () => {
  const columns = [
    { title: '客户名称', dataIndex: 'name', key: 'name' },
    { title: '联系电话', dataIndex: 'contact.phone', key: 'phone' },
    { title: '状态', dataIndex: 'status', valueEnum: {
      active: { text: '活跃', status: 'Success' },
      inactive: { text: ' inactive', status: 'Error' },
      potential: { text: '潜在', status: 'Warning' },
    }},
    { title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime' },
  ];

  return (
    <ProTable
      columns={columns}
      request={async (params) => {
        const res = await request('/api/customers', { params });
        return { data: res.data, success: true, total: res.total };
      }}
      rowSelection={{}} // 启用批量选择
      toolBarRender={() => [<Button type="primary">新增客户</Button>]}
    />
  );
};

(2)商机管理模块(后端)

NestJS控制器实现商机CRUD,通过Prisma操作数据库:

// src/opportunities/opportunities.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { OpportunitiesService } from './opportunities.service';
import { CreateOpportunityDto } from './dto/create-opportunity.dto';

@Controller('opportunities')
export class OpportunitiesController {
  constructor(private readonly opportunitiesService: OpportunitiesService) {}

  @Post()
  async create(@Body() createOpportunityDto: CreateOpportunityDto) {
    return this.opportunitiesService.create(createOpportunityDto);
  }

  @Get(':id')
  async findOne(@Param('id') id: string) {
    return this.opportunitiesService.findOne(id);
  }

  // 其他接口:更新、删除、分页查询...
}

// src/opportunities/opportunities.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateOpportunityDto } from './dto/create-opportunity.dto';

@Injectable()
export class OpportunitiesService {
  constructor(private prisma: PrismaService) {}

  async create(dto: CreateOpportunityDto) {
    return this.prisma.opportunity.create({
      data: {
        name: dto.name,
        amount: dto.amount,
        stage: dto.stage,
        customerId: dto.customerId,
        ownerId: dto.ownerId,
      },
    });
  }
}

3. 权限控制实现

采用RBAC模型,通过NestJS守卫(Guard)和装饰器实现接口级权限校验:

// src/auth/guards/roles.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!requiredRoles) return true; // 无角色要求,放行

    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.some((role) => user.roles.includes(role)); // 校验用户角色
  }
}

// 使用示例:仅管理员可删除客户
@Delete(':id')
@Roles('admin')
async remove(@Param('id') id: string) {
  return this.customersService.remove(id);
}

4. 数据分析模块

使用ECharts生成销售漏斗图表,后端通过Prisma聚合查询数据:

// 后端:获取商机阶段统计数据
async getSalesFunnel() {
  const stages = ['初步接触', '需求分析', '方案报价', '谈判签约'];
  const result = await this.prisma.opportunity.groupBy({
    by: ['stage'],
    _count: { id: true },
    where: { status: 'active' },
  });

  // 转换为ECharts所需格式
  return stages.map(stage => ({
    name: stage,
    value: result.find(r => r.stage === stage)?._count.id || 0,
  }));
}

// 前端:ECharts渲染
import * as echarts from 'echarts';

const option = {
  title: { text: '销售漏斗' },
  series: [{
    type: 'funnel',
    data: salesFunnelData, // 后端返回的数据
    label: { show: true, position: 'inside' },
  }],
};
```![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d49110ac4bab470595955d3310ed8662.jpeg#pic_center)

# 测试与部署
## 1. 测试策略
- **单元测试**:使用Jest测试后端服务,覆盖率目标80%以上。例如测试客户创建接口:
  ```typescript
  describe('CustomersService', () => {
    it('should create a customer', async () => {
      const dto = { name: '测试客户', contact: { phone: '13800138000' } };
      const result = await service.create(dto);
      expect(result.name).toBe(dto.name);
    });
  });
  • E2E测试:使用Cypress模拟用户操作,测试关键流程(如客户创建→商机添加→跟进记录)。

2. Docker容器化

编写docker-compose.yml一键启动服务:

version: '3'
services:
  frontend:
    build: ./crm-frontend
    ports: ['3000:3000']
    depends_on: [backend]
  backend:
    build: ./crm-backend
    ports: ['3001:3001']
    environment:
      DATABASE_URL: postgresql://postgres:password@db:5432/crm
      REDIS_URL: redis://redis:6379
    depends_on: [db, redis]
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: password
    volumes: ['pgdata:/var/lib/postgresql/data']
  redis:
    image: redis:7
volumes:
  pgdata:

3. CI/CD流程

GitHub Actions配置自动部署:

# .github/workflows/deploy.yml
name: Deploy CRM
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 18 }
      - run: npm ci
      - run: npm test # 运行测试
      - run: docker build -t crm-backend .
      - run: docker push username/crm-backend # 推送镜像
      - run: ssh -i key.pem ec2-user@aws-ip "docker-compose pull && docker-compose up -d"

总结

客户关系管理(CRM)系统是企业数字化转型的核心工具,但市面上的商业CRM往往存在定制成本高、数据私有化困难等问题。从零开发开源CRM不仅能完全掌控代码和数据,还能根据业务需求灵活扩展功能。本教程详细阐述了基于 React 18、NestJS 与 PostgreSQL 等前沿技术栈,从零构建开源 CRM 系统的全过程。我们不仅要关注代码的实现,更要理解其背后的架构哲学:通过前后端分离与微服务化设计,打造一个高内聚、低耦合的企业级应用,以应对复杂多变的商业逻辑。开发 CRM 不仅仅是编写一个管理软件,更是对企业业务流程的一次数字化重塑。从精准的销售漏斗分析到高效的跨团队协作,技术的落地最终是为了释放生产力。随着 AI 智能化与移动化趋势的深入,未来的 CRM 将更加主动与智能。希望本教程能成为你探索企业级应用开发的基石,不仅掌握最新的技术栈,更能构建出真正服务于业务、驱动增长的开源产品,在技术变革的浪潮中把握先机。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐