by-crm/docs
2026-02-03 15:58:03 +08:00
..
docker-deployment.md 优化代码 2026-01-28 08:50:26 +08:00
README.md 提交初始化及项目说明 2026-02-03 15:58:03 +08:00
school.xls .. 2026-01-26 17:12:47 +08:00

泊云智销通 CRM 系统

项目简介

泊云智销通是一款面向企业渠道管理的 CRM 系统,核心解决报备冲突保护期归属问题。系统通过唯一有效报备机制自动到期释放机制,确保同一学校的同一项目在同一时间只能被一个经销商跟进,并在保护期结束后自动释放资源,提升渠道效率与公平性。

核心价值

  • 防撞单机制:基于"学校+产品+项目类型"组合的唯一性校验,避免恶意抢占
  • 保护期管理:自动计算保护期,到期自动释放,无需人工干预
  • 公平竞争:防止资源长期占用,所有经销商公平竞争
  • 全程可追溯:报备记录永久保留,支持进展记录追踪
  • 灵活配置:支持手动输入学校名称,降低报备门槛

技术架构

后端技术栈

  • 框架: Spring Boot 2.7
  • ORM: MyBatis
  • 数据库: MySQL 8
  • 安全: Spring Security + JWT + BCrypt
  • API 文档: Swagger
  • 构建工具: Maven
  • 定时任务: Spring Task Scheduler

前端技术栈

  • 框架: Vue 3 + TypeScript
  • UI 组件: Element Plus
  • 路由: Vue Router 4
  • 状态管理: Pinia
  • HTTP 客户端: Axios
  • 构建工具: Vite
  • 代码规范: ESLint + Prettier

架构模式

  • 前后端分离: RESTful API 架构
  • MVC 分层: Controller → Service → Mapper → Entity
  • 数据库设计: 遵循范式,合理使用索引
  • 安全设计: JWT 认证BCrypt 密码加密

用户角色与权限

1. 管理员

角色标识: role = 0

权限范围:

  • 报备管理(全部报备的查看、审核、编辑、作废)
  • 经销商管理(经销商的增删改查、重置密码)
  • 学校管理(查询、批量导入学校数据)
  • 系统配置(保护期天数等参数配置)
  • 查看所有运营数据

主要职责:

  • 审核报备申请
  • 管理经销商账号
  • 配置系统参数
  • 监控渠道数据

2. 经销商用户

角色标识: role = 1

权限范围:

  • 查看自己的报备记录
  • 提交报备申请
  • 撤回待审核的报备
  • 为已通过的报备添加进展记录
  • 修改个人密码

主要职责:

  • 报备意向学校项目
  • 跟进保护期内的项目
  • 维护进展记录

权限矩阵

功能模块 管理员 经销商
报备管理 全部 自己的报备
提交报备
报备审核
报备编辑/作废
进展记录 全部 自己报备的进展
经销商管理
学校管理
系统配置

功能清单

1. 认证与授权

1.1 用户登录

  • 路径: POST /api/auth/login
  • 描述: 用户登录系统,获取 JWT Token
  • 安全: BCrypt 加密存储
  • 返回: Token + 用户基本信息

默认账号:

  • 管理员: admin / admin123
  • 经销商用户: 根据经销商数据

1.2 修改密码

  • 路径: POST /api/auth/change-password
  • 权限: 所有用户
  • 功能: 用户修改自己的登录密码
  • 验证: 需要输入原密码验证

1.3 重置密码

  • 路径: POST /api/auth/reset-password
  • 权限: 仅管理员
  • 功能: 管理员重置经销商用户的密码

2. 报备管理

2.1 报备列表

  • 路径: GET /api/report/page
  • 权限: 管理员查看全部,经销商查看自己的
  • 功能: 分页查询报备记录
  • 搜索字段: 学校名称、状态
  • 状态说明:
    • 0 - 待审核: 等待管理员审核
    • 1 - 已通过: 审核通过,保护期内
    • 2 - 已驳回: 审核未通过
    • 3 - 已失效: 保护期已过
    • 4 - 已作废: 管理员主动作废

2.2 提交报备

  • 路径: POST /api/report
  • 权限: 经销商
  • 功能: 经销商报备意向学校项目
  • 必填字段: 学校名称、所属产品、项目类型
  • 可选字段: 报备说明
  • 防撞单规则:
    • 同一学校的同一产品+项目类型组合,只能有一个有效报备
    • 有效报备定义:状态为"待审核(0)"或"已通过(1)"
    • 已失效、已驳回、已作废的报备不影响新报备

2.3 撤回报备

  • 路径: DELETE /api/report/{id}
  • 权限: 报备所属经销商
  • 功能: 撤回待审核的报备申请
  • 限制: 只能撤回状态为"待审核"的报备

2.4 审核报备

  • 路径: PUT /api/report/{id}/audit
  • 权限: 仅管理员
  • 功能: 审核报备申请
  • 操作:
    • 通过设置保护期默认90天状态变为"已通过"
    • 驳回:填写驳回原因,状态变为"已驳回"

2.5 编辑报备

  • 路径: PUT /api/report/{id}
  • 权限: 仅管理员
  • 功能: 编辑已通过的报备
  • 可编辑字段: 所属产品、项目类型、报备说明、状态
  • 作废操作: 状态改为"已作废"时必须填写作废原因

2.6 报备详情

  • 路径: GET /api/report/{id}
  • 权限: 根据权限查看
  • 功能: 查看报备详细信息和进展记录

3. 进展记录管理

3.1 添加进展记录

  • 路径: POST /api/report-progress
  • 权限: 经销商只能为自己的报备添加,管理员可查看所有
  • 功能: 为已通过的报备添加进展记录
  • 限制: 只能为状态为"已通过"的报备添加进展

3.2 编辑进展记录

  • 路径: PUT /api/report-progress/{id}
  • 权限: 进展记录创建者
  • 功能: 修改进展记录内容
  • 保留历史: 不支持删除,确保数据完整性

4. 经销商管理

4.1 经销商列表

  • 路径: GET /api/dealer/list
  • 权限: 仅管理员
  • 功能: 查询所有经销商信息

4.2 新增经销商

  • 路径: POST /api/dealer
  • 权限: 仅管理员
  • 必填字段: 经销商名称、经销商账号、联系人、联系电话
  • 自动创建: 创建经销商记录的同时创建对应的登录用户

4.3 编辑经销商

  • 路径: PUT /api/dealer/{id}
  • 权限: 仅管理员
  • 功能: 修改经销商信息

4.4 删除经销商

  • 路径: DELETE /api/dealer/{id}
  • 权限: 仅管理员
  • 功能: 删除经销商及其关联用户

5. 学校管理

5.1 学校列表

  • 路径: GET /api/school/list
  • 权限: 所有用户
  • 功能: 查询所有学校信息

5.2 学校搜索

  • 路径: GET /api/school/search?keyword=xxx
  • 权限: 所有用户
  • 功能: 模糊搜索学校名称
  • 应用场景: 报备时学校名称的自动完成提示

5.3 批量导入学校

  • 路径: POST /api/school/import
  • 权限: 仅管理员
  • 功能: 上传 Excel 文件批量导入学校数据
  • 文件格式: .xls.xlsx

学校数据结构:

  • 学校标识码(唯一)
  • 学校名称
  • 所在地(省份 + 城市)

6. 系统配置

6.1 保护期配置

  • 配置项: report.protect.days
  • 默认值: 90
  • 说明: 控制报备通过后的保护期时长

6.2 报备冲突控制

  • 配置项: report.allow.overlap
  • 默认值: false
  • 说明: 是否允许重叠报备(仅用于测试)
  • 生产环境: 必须为 false

业务流程

报备完整生命周期

┌─────────────────────────────────────────────────────────────┐
│  报备完整流程                                               │
└─────────────────────────────────────────────────────────────┘

1. 经销商提交报备
   ├─ 学校名称(可从学校库选择或手动输入)
   ├─ 所属产品(必填)
   ├─ 项目类型(必填)
   ├─ 报备说明(可选)
   └─ 提交审核
   ↓
   状态:待审核(0)

2. 管理员审核
   ├─ 通过 → 设置保护期默认90天
   │         ├─ 报备状态:已通过(1)
   │         └─ 保护期倒计时开始
   │
   └─ 驳回 → 填写驳回原因
             └─ 报备状态:已驳回(2)

3. 保护期内
   ├─ 该"学校+产品+项目类型"组合被锁定
   ├─ 其他经销商无法报备相同组合
   ├─ 经销商可添加进展记录
   └─ 剩余保护天数倒计时

4. 管理员操作(可选)
   ├─ 编辑报备:修改产品、项目类型、说明
   └─ 作废报备:状态改为已作废(4),填写原因
       └─ 作废后该组合可重新报备

5. 保护期到期(定时任务)
   └─ 每天凌晨1点自动执行
       ├─ 报备状态:已通过 → 已失效(3)
       └─ 该"学校+产品+项目类型"组合可再次报备

报备状态转换规则

待审核(0)
    ↓ [管理员审核通过]
已通过(1) ←───────┐
    ↓               │
    │ [保护期到期]   │ [管理员编辑恢复]
    ↓               │
已失效(3) ─────────┘

待审核(0)
    ↓ [管理员驳回]
已驳回(2) ←─── 终态

待审核(0)
    ↓ [经销商撤回]
删除记录 ←─── 物理删除

已通过(1)
    ↓ [管理员作废]
已作废(4) ←─── 终态

数据库设计

核心表结构

1. 报备表 (crm_report)

字段 类型 说明
id BIGINT 报备ID主键
dealer_id BIGINT 经销商ID
school_id BIGINT 学校ID可为NULL手动输入时为空
school_name VARCHAR(255) 学校名称(冗余存储,方便查询)
product VARCHAR(255) 所属产品
project_type VARCHAR(255) 项目类型
description VARCHAR(500) 报备说明
status TINYINT 状态0-待审核 1-已通过 2-已驳回 3-已失效 4-已作废
reject_reason VARCHAR(255) 驳回原因
cancel_reason VARCHAR(500) 作废原因
protect_start_date DATE 保护期开始日期
protect_end_date DATE 保护期结束日期
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

索引:

  • idx_dealer_id - 经销商ID索引
  • idx_status - 状态索引
  • idx_protect_end_date - 保护期结束日期索引(定时任务使用)

2. 经销商表 (crm_dealer)

字段 类型 说明
id BIGINT 经销商ID主键
name VARCHAR(200) 经销商名称
code VARCHAR(50) 经销商账号(唯一)
contact_person VARCHAR(50) 联系人
contact_phone VARCHAR(20) 联系电话
email VARCHAR(100) 邮箱
status TINYINT 状态0-禁用 1-启用
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

3. 用户表 (crm_user)

字段 类型 说明
id BIGINT 用户ID主键
username VARCHAR(50) 用户名(登录账号)
password VARCHAR(255) 密码BCrypt加密
dealer_id BIGINT 关联经销商ID管理员为NULL
role TINYINT 角色0-管理员 1-经销商用户
status TINYINT 状态0-禁用 1-启用
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

4. 学校表 (crm_school)

字段 类型 说明
id BIGINT 学校ID主键
school_code VARCHAR(50) 学校标识码(唯一)
school_name VARCHAR(200) 学校名称
location VARCHAR(255) 所在地
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

索引:

  • uk_school_code - 学校标识码唯一索引
  • idx_school_name - 学校名称索引

5. 报备进展记录表 (crm_report_progress)

字段 类型 说明
id BIGINT 主键ID
report_id BIGINT 报备ID
progress_content VARCHAR(500) 进展内容
created_by BIGINT 创建人ID
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

索引:

  • idx_report_id - 报备ID索引
  • idx_created_by - 创建人索引

6. 系统配置表 (crm_system_config)

字段 类型 说明
id BIGINT 主键ID
config_key VARCHAR(100) 配置键(唯一)
config_value VARCHAR(500) 配置值
config_type VARCHAR(20) 配置类型string/integer/boolean
is_editable TINYINT 是否可编辑1-是 0-否)
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

定时任务

报备过期处理

执行时间: 每天凌晨 1 点 Cron 表达式: 0 0 1 * * ?

功能:

  1. 查询所有保护期已到的报备(protect_end_date <= 今天status = 1
  2. 批量更新报备状态为"已失效"status = 3
  3. 释放资源,允许相同"学校+产品+项目类型"组合重新报备

核心代码: ReportExpireTask.java

@Scheduled(cron = "0 0 1 * * ?")
public void handleExpiredReports() {
    log.info("开始处理过期报备...");
    try {
        reportService.handleExpiredReports();
        log.info("过期报备处理完成");
    } catch (Exception e) {
        log.error("处理过期报备失败", e);
    }
}

防撞单机制详解

核心规则

唯一性维度: 学校 + 产品 + 项目类型

有效报备定义: 状态为"待审核(0)"或"已通过(1)"的报备

校验逻辑

// 1. 如果选择了学校(有 school_id
// 按 school_id + product + project_type 精确匹配
WHERE school_id = #{schoolId}
  AND product = #{product}
  AND project_type = #{projectType}
  AND status IN (0, 1)

// 2. 如果手动输入学校(无 school_id
// 按 school_name + product + project_type 匹配
WHERE school_name = #{schoolName}
  AND product = #{product}
  AND project_type = #{projectType}
  AND status IN (0, 1)

业务意义

  • 防止撞单: 同一项目不能被多个经销商同时报备
  • 公平竞争: 保护期内锁定,到期后自动释放
  • 灵活性: 支持手动输入学校名称,降低报备门槛

学校选择机制

学校数据来源

学校数据存储在 crm_school 表中,包含:

  • 学校标识码(唯一)
  • 学校名称
  • 所在地

前端交互

  1. 自动完成搜索

    • 用户输入学校名称
    • 前端调用 /api/school/search?keyword=xxx 接口
    • 后端模糊匹配 school_name
    • 返回匹配列表供用户选择
  2. 手动输入

    • 用户可以直接输入学校名称
    • 如果不从下拉列表选择,school_id 为 NULL
    • school_name 保存用户输入的文本
    • 防撞单校验时按 school_name 进行匹配

设计优势

  • 降低门槛: 学校不在库中也能报备,不影响业务
  • 数据规范化: 优先使用学校库数据,保证数据质量
  • 灵活扩展: 支持批量导入学校数据

安全机制

1. 认证与授权

JWT Token 认证

  • 用户登录后获取 JWT Token
  • Token 包含用户ID、用户名、角色、经销商ID
  • Token 存储在客户端 localStorage
  • 每次请求在 Header 中携带:Authorization: Bearer {token}

权限拦截

  • 前端路由守卫:未登录重定向到登录页
  • 后端拦截器:验证 Token 有效性
  • 数据权限:经销商只能查看自己的报备

2. 密码安全

密码规则

  • 长度6-20 位
  • 加密BCrypt自动加盐
  • 修改密码:需验证原密码

项目结构

by-crm/
├── backend/                    # 后端工程
│   ├── src/main/java/com/bycrm/
│   │   ├── annotations/       # 自定义注解(权限等)
│   │   ├── common/            # 公共类Result、Constants等
│   │   ├── config/            # 配置类CORS、MyBatis、Swagger等
│   │   ├── controller/        # 控制器层
│   │   │   ├── AuthController.java
│   │   │   ├── ReportController.java
│   │   │   ├── DealerController.java
│   │   │   ├── SchoolController.java
│   │   │   └── ReportProgressController.java
│   │   ├── dto/               # 数据传输对象
│   │   ├── entity/            # 实体类
│   │   ├── exception/         # 异常处理
│   │   ├── mapper/            # MyBatis Mapper 接口
│   │   ├── service/           # 业务逻辑层
│   │   ├── task/              # 定时任务
│   │   ├── util/              # 工具类JWT、加密等
│   │   ├── vo/                # 视图对象
│   │   └── ByCrmApplication.java
│   ├── src/main/resources/
│   │   ├── mapper/*.xml        # MyBatis SQL 映射
│   │   └── application.yml    # 配置文件
│   └── pom.xml                # Maven 依赖
│
├── frontend/                   # 前端工程
│   ├── src/
│   │   ├── api/               # API 接口封装
│   │   ├── assets/            # 静态资源
│   │   ├── components/        # 通用组件
│   │   ├── router/            # Vue Router 配置
│   │   ├── stores/            # Pinia 状态管理
│   │   ├── types/             # TypeScript 类型定义
│   │   ├── utils/             # 工具函数(请求、加密等)
│   │   ├── views/             # 页面组件
│   │   │   ├── Login.vue      # 登录页
│   │   │   ├── Layout.vue     # 主布局
│   │   │   ├── Dashboard.vue  # 首页
│   │   │   ├── Report.vue     # 报备管理
│   │   │   └── Dealer.vue     # 经销商管理
│   │   ├── App.vue           # 根组件
│   │   └── main.ts           # 入口文件
│   ├── vite.config.ts         # Vite 配置
│   ├── package.json           # 依赖配置
│   └── tsconfig.json          # TS 配置
│
├── docs/                      # 文档目录
│   └── README.md              # 项目文档
│
└── sql/                       # 数据库脚本
    ├── init.sql               # 初始化脚本
    └── *.sql                  # 其他 SQL 脚本

环境配置

后端配置 (application.yml)

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/by_crm
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.bycrm.entity

# JWT 配置
jwt:
  secret: your-secret-key
  expiration: 86400000  # 24小时毫秒

# 保护期配置
crm:
  report:
    protect-days: 90        # 保护期天数默认90天
    allow-overlap: false    # 是否允许报备重叠测试用生产必须false

前端配置

// API 基础地址
const BASE_URL = 'http://localhost:8080/api'

// Token 存储
const TOKEN_KEY = 'token'

部署说明

开发环境启动

后端:

cd backend
mvn spring-boot:run
# 访问: http://localhost:8080
# Swagger: http://localhost:8080/swagger-ui.html

前端:

cd frontend
pnpm install
pnpm dev
# 访问: http://localhost:5173

生产环境部署

后端:

# 编译打包
mvn clean package

# 运行 JAR
java -jar target/bycrm-0.0.1-SNAPSHOT.jar

前端:

# 编译打包
pnpm build

# 部署 dist 目录到 Web 服务器

数据库初始化

# 创建数据库
mysql -u root -p -e "CREATE DATABASE by_crm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

# 导入初始化脚本
mysql -u root -p by_crm < sql/init.sql

API 接口清单

认证接口

  • POST /api/auth/login - 用户登录
  • GET /api/auth/user/info - 获取当前用户信息
  • POST /api/auth/logout - 用户退出
  • POST /api/auth/change-password - 修改密码
  • POST /api/auth/reset-password - 重置密码

报备接口

  • GET /api/report/page - 分页查询报备
  • GET /api/report/{id} - 获取报备详情
  • POST /api/report - 创建报备
  • PUT /api/report/{id} - 更新报备
  • DELETE /api/report/{id} - 撤回报备
  • PUT /api/report/{id}/audit - 审核报备

进展记录接口

  • GET /api/report-progress/list?reportId={id} - 查询进展记录
  • POST /api/report-progress - 添加进展记录
  • PUT /api/report-progress/{id} - 编辑进展记录

经销商接口(仅管理员)

  • GET /api/dealer/list - 查询所有经销商
  • POST /api/dealer - 创建经销商
  • PUT /api/dealer/{id} - 更新经销商
  • DELETE /api/dealer/{id} - 删除经销商

学校接口

  • GET /api/school/list - 查询所有学校
  • GET /api/school/search?keyword=xxx - 搜索学校
  • POST /api/school - 创建学校
  • POST /api/school/import - 文件上传导入

版本历史

v1.0.0 (当前版本)

核心功能:

  • 报备管理(提交、审核、撤回、编辑、作废)
  • 经销商管理(增删改查、重置密码)
  • 学校管理(查询、搜索、导入)
  • 用户认证(登录、密码管理)
  • 进展记录(添加、编辑、查询)
  • 自动保护期到期处理
  • 防撞单机制(学校+产品+项目类型)
  • 灵活的学校输入(库选择+手动输入)

安全特性:

  • JWT Token 认证
  • BCrypt 密码加密存储
  • 权限注解拦截
  • SQL 注入防护
  • XSS 防护

常见问题

1. 默认账号是什么?

  • 管理员账号:admin / admin123
  • 经销商账号:根据经销商数据创建

2. 保护期可以修改吗?

  • 可以,通过修改系统配置 report.protect.days
  • 默认为 90 天,可根据业务需求调整

3. 同一学校项目可以同时被多个经销商报备吗?

  • 不可以,这是系统的核心规则
  • 同一"学校+产品+项目类型"组合只能有一个"有效"报备
  • 有效报备定义:状态为"待审核(0)"或"已通过(1)"

4. 保护期到期后会发生什么?

  • 定时任务每天凌晨1点自动执行
  • 将过期报备状态改为"已失效"
  • 该"学校+产品+项目类型"组合可重新报备

5. 学校名称可以手动输入吗?

  • 可以,支持从学校库选择或手动输入
  • 从学校库选择时会保存 school_id 和标准学校名称
  • 手动输入时 school_id 为 NULL保存用户输入的文本

6. 报备可以作废吗?

  • 可以,只有管理员可以作废已通过的报备
  • 作废时必须填写作废原因
  • 作废后该"学校+产品+项目类型"组合可重新报备

技术支持

开发团队

  • 后端开发Spring Boot + MyBatis + MySQL
  • 前端开发Vue 3 + TypeScript + Element Plus
  • 项目管理Maven + pnpm

联系方式

  • 问题反馈:提交 Issue 到项目仓库
  • 技术文档:详见 docs/ 目录

版权声明: 本系统为企业内部使用,未经授权不得复制或传播。