经销商管理系统
Go to file
andy ea8f8390da chore: 从 git 中移除 backend/target 目录
- 使用 git rm --cached 从版本控制中删除 target 目录
- .gitignore 已配置忽略 target 和 node_modules
- 本地文件保留,仅从 git 索引中删除

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 16:12:04 +08:00
backend chore: 从 git 中移除 backend/target 目录 2026-01-26 16:12:04 +08:00
docs 初始化项目 2026-01-26 15:59:48 +08:00
frontend chore: 从 git 中移除 backend/target 目录 2026-01-26 16:12:04 +08:00
sql 初始化项目 2026-01-23 17:20:52 +08:00
.gitignore 初始化项目 2026-01-23 17:20:52 +08:00
CLAUDE.md 初始化项目 2026-01-23 17:20:52 +08:00
tmpclaude-62fc-cwd chore: 从 git 中移除 backend/target 目录 2026-01-26 16:12:04 +08:00

经销商管理系统

项目简介

经销商管理系统是一款面向企业渠道管理的 CRM 系统,旨在解决渠道管理中的核心问题——客户报备冲突保护期归属。系统通过唯一有效报备机制与自动到期释放机制,确保同一客户在同一时间只能被一个经销商跟进,并在保护期结束后自动回归可报备池,避免长期占坑,提升渠道效率与公平性。

核心价值

  • 防撞单机制:同一客户同一时间只能有一个有效报备
  • 保护期管理:自动计算和跟踪保护期,保护期到期自动释放
  • 公平竞争:避免经销商长期占用客户资源
  • 全程可追溯:所有报备记录永久保留,支持审计

技术架构

后端技术栈

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

前端技术栈

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

架构模式

  • 前后端分离: RESTful API 架构
  • MVC 分层: Controller → Service → Mapper → Entity
  • 数据库设计: 遵循范式,合理使用索引
  • 安全设计: 前端 SHA-256 哈希 + 后端 BCrypt 双重加密

用户角色与权限

1. 管理员

角色标识: role = 0

权限范围:

  • 客户管理(全部客户的查看、新增、编辑、删除)
  • 报备管理(全部报备的查看、审核、撤回)
  • 经销商管理(经销商的增删改查、重置密码)
  • 系统配置(保护期天数等参数配置)
  • 数据报表(经销商活跃度、客户转化率等)
  • 查看所有运营数据

主要职责:

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

2. 经销商用户

角色标识: role = 1

权限范围:

  • 查看自己的客户列表
  • 提交报备申请(选择客户进行报备)
  • 查看自己的报备记录
  • 撤回待审核的报备
  • 查看报备审核状态
  • 修改个人密码

主要职责:

  • 报备意向客户
  • 跟进保护期内的客户
  • 维护客户信息
  • 管理报备记录

权限矩阵

功能模块 管理员 经销商
客户管理 全部 仅查看
报备管理 全部 自己的报备
报备审核
经销商管理
系统配置
数据报表
密码管理 自己的 自己的

功能清单

1. 认证与授权

1.1 用户登录

  • 路径: POST /api/auth/login
  • 描述: 用户登录系统,获取 JWT Token
  • 安全: 前端 SHA-256 哈希 + 后端 BCrypt 加密
  • 返回: Token + 用户基本信息

默认账号:

  • 管理员: admin / admin123
  • 经销商1: user001 / admin123
  • 经销商2: user002 / admin123
  • 经销商3: user003 / admin123

1.2 修改密码

  • 路径: POST /api/auth/change-password
  • 权限: 所有用户
  • 功能: 用户修改自己的登录密码
  • 验证: 需要输入原密码验证
  • 安全: 密码 6-20 位,传输前哈希加密

1.3 重置密码

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

1.4 退出登录

  • 路径: POST /api/auth/logout
  • 功能: 清除客户端 Token

2. 客户管理

2.1 客户列表

  • 路径: GET /api/customer/page
  • 权限: 管理员查看全部,经销商查看全部
  • 功能: 分页查询客户信息
  • 搜索字段: 客户名称、所属行业、状态
  • 状态说明:
    • 0 - 可报备: 未被保护,可报备
    • 1 - 保护中: 正在保护期内
    • 2 - 已失效: 保护期已过

2.2 新增客户

  • 路径: POST /api/customer
  • 权限: 仅管理员
  • 功能: 创建新客户信息
  • 必填字段: 客户名称
  • 可选字段: 联系电话、地址、所属行业
  • 学校联动: 支持从学校库自动完成选择

2.3 编辑客户

  • 路径: PUT /api/customer/{id}
  • 权限: 仅管理员
  • 功能: 修改客户信息

2.4 删除客户

  • 路径: DELETE /api/customer/{id}
  • 权限: 仅管理员
  • 功能: 删除客户记录
  • 限制: 有报备记录的客户需谨慎处理

2.5 客户搜索

  • 路径: GET /api/customer/search
  • 功能: 模糊搜索客户名称(用于报备时的客户选择)

3. 报备管理

3.1 报备列表

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

3.2 提交报备

  • 路径: POST /api/report
  • 权限: 经销商
  • 功能: 经销商报备意向客户
  • 必填字段: 客户ID、报备说明
  • 限制:
    • 同一客户同一时间只能有一个有效报备
    • 已保护的客户无法再次报备
    • 待审核的报备可撤回

3.3 撤回报备

  • 路径: DELETE /api/report/{id}
  • 权限: 报备所属经销商
  • 功能: 撤回待审核的报备申请
  • 限制: 已通过和已驳回的报备无法撤回

3.4 审核报备

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

3.5 报备详情

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

4. 经销商管理

4.1 经销商列表

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

4.2 新增经销商

  • 路径: POST /api/dealer
  • 权限: 仅管理员
  • 必填字段: 经销商名称、经销商账号(用作登录用户名)、联系人、联系电话
  • 可选字段: 邮箱、初始密码
  • 自动创建:
    • 创建经销商记录
    • 创建对应的登录用户
    • 用户名 = 经销商账号
    • 默认密码 = 123456可自定义

4.3 编辑经销商

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

4.4 删除经销商

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

4.5 重置密码

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

5. 学校管理

5.1 学校列表

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

5.2 学校搜索

  • 路径: GET /api/school/search?keyword=xxx
  • 权限: 所有用户
  • 功能: 模糊搜索学校名称(用于客户名称自动完成)
  • 应用场景: 新增客户时,输入学校名称,自动匹配学校库

5.3 批量导入学校

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

5.4 从 docs 目录导入

  • 路径: POST /api/school/import-from-docs
  • 权限: 仅管理员
  • 功能: 直接导入项目 docs 目录下的 school.xls 文件

学校数据结构:

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

6. 系统配置

6.1 保护期配置

  • 配置项: report.protect.days
  • 默认值: 90
  • 说明: 控制报备通过后的保护期时长
  • 影响:
    • 保护期计算
    • 自动失效定时任务

6.2 报备冲突控制

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

业务流程

报备生命周期

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

1. 经销商提交报备
   ├─ 选择客户(只能选择"可报备"状态的客户)
   ├─ 填写报备说明
   └─ 提交审核
   ↓
   状态:待审核(0)
   客户状态:可报备(0)

2. 管理员审核
   ├─ 通过 → 设置保护期如90天
   │         ├─ 报备状态:已通过(1)
   │         └─ 客户状态:保护中(1)
   │
   └─ 驳回 → 填写驳回原因
             ├─ 报备状态:已驳回(2)
             └─ 客户状态:可报备(0)

3. 保护期内
   ├─ 客户被锁定,其他经销商无法报备
   ├─ 原报备经销商可跟进客户
   └─ 剩余保护天数倒计时

4. 保护期到期(定时任务)
   └─ 每天凌晨1点自动执行
       ├─ 报备状态:已通过 → 已失效(3)
       └─ 客户状态:保护中 → 可报备(0)

5. 客户释放
   └─ 所有经销商可再次报备该客户

客户状态流转

可报备(0) ──[提交报备]──> 锁定(其他经销商不可报备)
                                │
                           [管理员审核通过]
                                ↓
                          保护中(1) ──[90天后]──> 可报备(0)
                                ↑
                           [报备被驳回]
                                │
                           [管理员驳回]
                                ↓
                          可报备(0)

数据库设计

核心表结构

1. 客户表 (crm_customer)

字段 类型 说明
id BIGINT 客户ID主键
name VARCHAR(200) 客户名称
phone VARCHAR(20) 联系电话
address VARCHAR(500) 地址
industry VARCHAR(50) 所属行业
status TINYINT 状态0-可报备 1-保护中 2-已失效
current_dealer_id BIGINT 当前报备经销商ID
protect_end_date DATE 保护期结束日期
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

2. 报备表 (crm_report)

字段 类型 说明
id BIGINT 报备ID主键
dealer_id BIGINT 经销商ID
customer_id BIGINT 客户ID
description VARCHAR(500) 报备说明
status TINYINT 状态0-待审核 1-已通过 2-已驳回 3-已失效
reject_reason VARCHAR(255) 驳回原因
protect_start_date DATE 保护期开始日期
protect_end_date DATE 保护期结束日期
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

唯一索引: uk_customer_valid (customer_id, status) - 同一客户只能有一个有效报备

3. 经销商表 (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 更新时间

4. 用户表 (crm_user)

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

5. 学校表 (crm_school)

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

定时任务

报备过期处理

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

功能:

  1. 查询所有保护期已到的报备(protect_end_date <= 今天status = 1
  2. 批量更新报备状态为"已失效"status = 3
  3. 批量更新关联客户状态为"可报备"status = 0
  4. 保证渠道资源公平分配

安全机制

1. 认证与授权

JWT Token 认证

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

权限拦截

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

2. 密码安全

双重加密

用户输入 → [前端 SHA-256 哈希] → HTTPS 传输 → [后端 BCrypt 加密] → 存储

密码规则

  • 长度6-20 位
  • 加密BCrypt自动加盐
  • 传输SHA-256 哈希后传输
  • 修改密码:需验证原密码

3. 数据安全

SQL 注入防护

  • 使用 MyBatis 参数化查询
  • 所有用户输入都经过参数绑定

XSS 防护

  • 前端输出自动转义
  • 后端返回 JSON 格式

CSRF 防护

  • 使用 JWT 无状态认证
  • 不依赖 Session Cookie

项目结构

by-crm/
├── backend/                    # 后端工程
│   ├── src/main/java/com/bycrm/
│   │   ├── annotations/       # 自定义注解(权限等)
│   │   ├── common/            # 公共类Result、Constants等
│   │   ├── config/            # 配置类CORS、MyBatis、Swagger等
│   │   ├── controller/        # 控制器层
│   │   │   ├── AuthController.java
│   │   │   ├── CustomerController.java
│   │   │   ├── ReportController.java
│   │   │   ├── DealerController.java
│   │   │   └── SchoolController.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  # 首页
│   │   │   ├── Customer.vue   # 客户管理
│   │   │   ├── Report.vue     # 报备管理
│   │   │   └── Dealer.vue     # 经销商管理
│   │   ├── App.vue           # 根组件
│   │   └── main.ts           # 入口文件
│   ├── vite.config.ts         # Vite 配置
│   ├── package.json           # 依赖配置
│   └── tsconfig.json          # TS 配置
│
├── sql/                       # 数据库脚本
│   ├── init.sql               # 初始化脚本
│   ├── school_table.sql      # 学校表
│   └── *.sql                  # 其他 SQL 脚本
│
└── docs/                      # 文档目录
    ├── api.md                  # API 文档Swagger 导出)
    └── password-security.md   # 密码安全说明

环境配置

后端配置 (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:
    ttl-days: 90           # 保护期天数默认90天
    allow-overlap: false    # 是否允许报备重叠测试用生产必须false

前端配置

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

// Token 存储
const TOKEN_KEY = 'token'

// 路由模式history 或 hash
const routerMode = 'history'

部署说明

开发环境启动

后端:

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/customer/page - 分页查询客户
  • GET /api/customer/{id} - 获取客户详情
  • POST /api/customer - 创建客户
  • PUT /api/customer/{id} - 更新客户
  • DELETE /api/customer/{id} - 删除客户
  • GET /api/customer/search?keyword=xxx - 搜索客户

报备接口

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

经销商接口(仅管理员)

  • 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 - 文件上传导入
  • POST /api/school/import-from-docs - 从 docs 目录导入

版本历史

v1.0.0 (当前版本)

新增功能:

  • 客户管理(增删改查)
  • 报备管理(提交、审核、撤回)
  • 经销商管理(增删改查、重置密码)
  • 学校管理(查询、搜索、导入)
  • 用户认证(登录、密码管理)
  • 自动保护期到期处理
  • 学校数据自动完成功能
  • 密码传输加密SHA-256 + BCrypt 双重加密)

安全特性:

  • JWT Token 认证
  • BCrypt 密码加密存储
  • SHA-256 前端哈希传输
  • 权限注解拦截
  • SQL 注入防护
  • XSS 防护

常见问题

1. 默认密码是什么?

  • 新增经销商时,如果不设置初始密码,默认为 123456
  • 管理员账号:admin / admin123
  • 经销商账号:user001~003 / admin123

2. 保护期可以修改吗?

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

3. 同一客户可以同时被多个经销商报备吗?

  • 不可以,这是系统的核心规则
  • 同一时间只能有一个"已通过"的报备
  • 其他经销商报备该客户会被拒绝

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

  • 定时任务每天凌晨1点自动执行
  • 将过期报备状态改为"已失效"
  • 客户状态自动变为"可报备"
  • 其他经销商可以再次报备

5. 如何确保传输安全?

  • 前端:使用 Web Crypto API 进行 SHA-256 哈希
  • 传输HTTPS + 哈希后的密码
  • 后端BCrypt 二次加密存储
  • 三重安全保障

6. 管理员可以重置其他管理员密码吗?

  • 不可以,只能重置经销商用户的密码
  • 管理员修改自己的密码通过"修改密码"功能

7. 学校数据从哪里来?

  • 存放在 docs/school.xls 文件中
  • 包含:学校标识码、学校名称、所在地
  • 可通过 API 导入到数据库

技术支持

开发团队

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

联系方式

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

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