提交初始化及项目说明

This commit is contained in:
wanglongjie 2026-02-03 15:58:03 +08:00
parent daacd7d742
commit 8d82781bb3
2 changed files with 278 additions and 241 deletions

View File

@ -1,15 +1,16 @@
# 泊云智销通
# 泊云智销通 CRM 系统
## 项目简介
泊云智销通是一款面向企业渠道管理的 CRM 系统,旨在解决渠道管理中的核心问题——**客户报备冲突**与**保护期归属**。系统通过唯一有效报备机制与自动到期释放机制,确保同一客户在同一时间只能被一个经销商跟进,并在保护期结束后自动回归可报备池,避免长期占坑,提升渠道效率与公平性。
泊云智销通是一款面向企业渠道管理的 CRM 系统,核心解决**报备冲突**与**保护期归属**问题。系统通过**唯一有效报备机制**与**自动到期释放机制**,确保同一学校的同一项目在同一时间只能被一个经销商跟进,并在保护期结束后自动释放资源,提升渠道效率与公平性。
### 核心价值
- **防撞单机制**:同一客户同一时间只能有一个有效报备
- **保护期管理**:自动计算和跟踪保护期,保护期到期自动释放
- **公平竞争**:避免经销商长期占用客户资源
- **全程可追溯**:所有报备记录永久保留,支持审计
- **防撞单机制**:基于"学校+产品+项目类型"组合的唯一性校验,避免恶意抢占
- **保护期管理**:自动计算保护期,到期自动释放,无需人工干预
- **公平竞争**:防止资源长期占用,所有经销商公平竞争
- **全程可追溯**:报备记录永久保留,支持进展记录追踪
- **灵活配置**:支持手动输入学校名称,降低报备门槛
---
@ -23,12 +24,13 @@
- **安全**: Spring Security + JWT + BCrypt
- **API 文档**: Swagger
- **构建工具**: Maven
- **定时任务**: Spring Task Scheduler
### 前端技术栈
- **框架**: Vue 3 + TypeScript
- **UI 组件**: Element Plus
- **路由**: Vue Router
- **路由**: Vue Router 4
- **状态管理**: Pinia
- **HTTP 客户端**: Axios
- **构建工具**: Vite
@ -39,7 +41,7 @@
- **前后端分离**: RESTful API 架构
- **MVC 分层**: Controller → Service → Mapper → Entity
- **数据库设计**: 遵循范式,合理使用索引
- **安全设计**: 前端 SHA-256 哈希 + 后端 BCrypt 双重加密
- **安全设计**: JWT 认证BCrypt 密码加密
---
@ -50,15 +52,13 @@
**角色标识**: `role = 0`
**权限范围**:
- ✅ 客户管理(全部客户的查看、新增、编辑、删除)
- ✅ 报备管理(全部报备的查看、审核、撤回)
- ✅ 报备管理(全部报备的查看、审核、编辑、作废)
- ✅ 经销商管理(经销商的增删改查、重置密码)
- ✅ 学校管理(查询、批量导入学校数据)
- ✅ 系统配置(保护期天数等参数配置)
- ✅ 数据报表(经销商活跃度、客户转化率等)
- ✅ 查看所有运营数据
**主要职责**:
- 维护客户信息
- 审核报备申请
- 管理经销商账号
- 配置系统参数
@ -69,30 +69,29 @@
**角色标识**: `role = 1`
**权限范围**:
- ✅ 查看自己的客户列表
- ✅ 提交报备申请(选择客户进行报备)
- ✅ 查看自己的报备记录
- ✅ 提交报备申请
- ✅ 撤回待审核的报备
- ✅ 查看报备审核状态
- ✅ 为已通过的报备添加进展记录
- ✅ 修改个人密码
**主要职责**:
- 报备意向客户
- 跟进保护期内的客户
- 维护客户信息
- 管理报备记录
- 报备意向学校项目
- 跟进保护期内的项目
- 维护进展记录
### 权限矩阵
| 功能模块 | 管理员 | 经销商 |
|---------|--------|--------|
| 客户管理 | ✅ 全部 | ✅ 仅查看 |
| 报备管理 | ✅ 全部 | ✅ 自己的报备 |
| 报备管理 | 全部 | 自己的报备 |
| 提交报备 | ✅ | ✅ |
| 报备审核 | ✅ | ❌ |
| 报备编辑/作废 | ✅ | ❌ |
| 进展记录 | 全部 | 自己报备的进展 |
| 经销商管理 | ✅ | ❌ |
| 学校管理 | ✅ | ❌ |
| 系统配置 | ✅ | ❌ |
| 数据报表 | ✅ | ❌ |
| 密码管理 | ✅ 自己的 | ✅ 自己的 |
---
@ -103,111 +102,92 @@
#### 1.1 用户登录
- **路径**: `POST /api/auth/login`
- **描述**: 用户登录系统,获取 JWT Token
- **安全**: 前端 SHA-256 哈希 + 后端 BCrypt 加密
- **安全**: 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. 报备管理
#### 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 报备列表
#### 2.1 报备列表
- **路径**: `GET /api/report/page`
- **权限**: 管理员查看全部,经销商查看自己的
- **功能**: 分页查询报备记录
- **搜索字段**: 学校名称、状态
- **状态说明**:
- `0 - 待审核`: 等待管理员审核
- `1 - 已通过`: 审核通过,保护期内
- `2 - 已驳回`: 审核未通过
- `3 - 已失效`: 保护期已过
- `4 - 已作废`: 管理员主动作废
#### 3.2 提交报备
#### 2.2 提交报备
- **路径**: `POST /api/report`
- **权限**: 经销商
- **功能**: 经销商报备意向客户
- **必填字段**: 客户ID、报备说明
- **限制**:
- 同一客户同一时间只能有一个有效报备
- 已保护的客户无法再次报备
- 待审核的报备可撤回
- **功能**: 经销商报备意向学校项目
- **必填字段**: 学校名称、所属产品、项目类型
- **可选字段**: 报备说明
- **防撞单规则**:
- 同一学校的同一产品+项目类型组合,只能有一个有效报备
- 有效报备定义:状态为"待审核(0)"或"已通过(1)"
- 已失效、已驳回、已作废的报备不影响新报备
#### 3.3 撤回报备
#### 2.3 撤回报备
- **路径**: `DELETE /api/report/{id}`
- **权限**: 报备所属经销商
- **功能**: 撤回待审核的报备申请
- **限制**: 已通过和已驳回的报备无法撤回
- **限制**: 只能撤回状态为"待审核"的报备
#### 3.4 审核报备
#### 2.4 审核报备
- **路径**: `PUT /api/report/{id}/audit`
- **权限**: 仅管理员
- **功能**: 审核报备申请
- **操作**:
- 通过设置保护期默认90天客户状态改为"保护中"
- 驳回:填写驳回原因,客户保持可报备状态
- 通过设置保护期默认90天状态变为"已通过"
- 驳回:填写驳回原因,状态变为"已驳回"
#### 3.5 报备详情
#### 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}`
- **权限**: 进展记录创建者
- **功能**: 修改进展记录内容
- **保留历史**: 不支持删除,确保数据完整性
---
@ -217,18 +197,12 @@
- **路径**: `GET /api/dealer/list`
- **权限**: 仅管理员
- **功能**: 查询所有经销商信息
- **状态**: 启用/禁用
#### 4.2 新增经销商
- **路径**: `POST /api/dealer`
- **权限**: 仅管理员
- **必填字段**: 经销商名称、经销商账号(用作登录用户名)、联系人、联系电话
- **可选字段**: 邮箱、初始密码
- **自动创建**:
- 创建经销商记录
- 创建对应的登录用户
- 用户名 = 经销商账号
- 默认密码 = 123456可自定义
- **必填字段**: 经销商名称、经销商账号、联系人、联系电话
- **自动创建**: 创建经销商记录的同时创建对应的登录用户
#### 4.3 编辑经销商
- **路径**: `PUT /api/dealer/{id}`
@ -240,11 +214,6 @@
- **权限**: 仅管理员
- **功能**: 删除经销商及其关联用户
#### 4.5 重置密码
- **路径**: `POST /api/auth/reset-password`
- **权限**: 仅管理员
- **功能**: 重置经销商用户的登录密码
---
### 5. 学校管理
@ -257,8 +226,8 @@
#### 5.2 学校搜索
- **路径**: `GET /api/school/search?keyword=xxx`
- **权限**: 所有用户
- **功能**: 模糊搜索学校名称(用于客户名称自动完成)
- **应用场景**: 新增客户时,输入学校名称,自动匹配学校库
- **功能**: 模糊搜索学校名称
- **应用场景**: 报备时学校名称的自动完成提示
#### 5.3 批量导入学校
- **路径**: `POST /api/school/import`
@ -266,13 +235,8 @@
- **功能**: 上传 Excel 文件批量导入学校数据
- **文件格式**: `.xls``.xlsx`
#### 5.4 从 docs 目录导入
- **路径**: `POST /api/school/import-from-docs`
- **权限**: 仅管理员
- **功能**: 直接导入项目 docs 目录下的 `school.xls` 文件
**学校数据结构**:
- 学校标识码
- 学校标识码(唯一)
- 学校名称
- 所在地(省份 + 城市)
@ -284,21 +248,18 @@
- **配置项**: `report.protect.days`
- **默认值**: `90`
- **说明**: 控制报备通过后的保护期时长
- **影响**:
- 保护期计算
- 自动失效定时任务
#### 6.2 报备冲突控制
- **配置项**: `report.allow-overlap`
- **配置项**: `report.allow.overlap`
- **默认值**: `false`
- **说明**: 是否允许同一客户的报备重叠(仅用于测试)
- **生产**: 必须为 `false`
- **说明**: 是否允许重叠报备(仅用于测试)
- **生产环境**: 必须为 `false`
---
## 业务流程
### 报备生命周期
### 报备完整生命周期
```
┌─────────────────────────────────────────────────────────────┐
@ -306,50 +267,61 @@
└─────────────────────────────────────────────────────────────┘
1. 经销商提交报备
├─ 选择客户(只能选择"可报备"状态的客户)
├─ 填写报备说明
├─ 学校名称(可从学校库选择或手动输入)
├─ 所属产品(必填)
├─ 项目类型(必填)
├─ 报备说明(可选)
└─ 提交审核
状态:待审核(0)
客户状态:可报备(0)
2. 管理员审核
├─ 通过 → 设置保护期(90天
├─ 通过 → 设置保护期(默认90天
│ ├─ 报备状态:已通过(1)
│ └─ 客户状态:保护中(1)
│ └─ 保护期倒计时开始
└─ 驳回 → 填写驳回原因
├─ 报备状态:已驳回(2)
└─ 客户状态:可报备(0)
└─ 报备状态:已驳回(2)
3. 保护期内
├─ 客户被锁定,其他经销商无法报备
├─ 原报备经销商可跟进客户
├─ 该"学校+产品+项目类型"组合被锁定
├─ 其他经销商无法报备相同组合
├─ 经销商可添加进展记录
└─ 剩余保护天数倒计时
4. 保护期到期(定时任务)
4. 管理员操作(可选)
├─ 编辑报备:修改产品、项目类型、说明
└─ 作废报备:状态改为已作废(4),填写原因
└─ 作废后该组合可重新报备
5. 保护期到期(定时任务)
└─ 每天凌晨1点自动执行
├─ 报备状态:已通过 → 已失效(3)
└─ 客户状态:保护中 → 可报备(0)
5. 客户释放
└─ 所有经销商可再次报备该客户
└─ 该"学校+产品+项目类型"组合可再次报备
```
### 客户状态流转
### 报备状态转换规则
```
可报备(0) ──[提交报备]──> 锁定(其他经销商不可报备)
[管理员审核通过]
保护中(1) ──[90天后]──> 可报备(0)
[报备被驳回]
[管理员驳回]
可报备(0)
待审核(0)
↓ [管理员审核通过]
已通过(1) ←───────┐
↓ │
│ [保护期到期] │ [管理员编辑恢复]
↓ │
已失效(3) ─────────┘
待审核(0)
↓ [管理员驳回]
已驳回(2) ←─── 终态
待审核(0)
↓ [经销商撤回]
删除记录 ←─── 物理删除
已通过(1)
↓ [管理员作废]
已作废(4) ←─── 终态
```
---
@ -358,39 +330,31 @@
### 核心表结构
#### 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)
#### 1. 报备表 (crm_report)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 报备ID主键 |
| dealer_id | BIGINT | 经销商ID |
| customer_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-已失效 |
| 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 | 更新时间 |
**唯一索引**: `uk_customer_valid (customer_id, status)` - 同一客户只能有一个有效报备
**索引**:
- `idx_dealer_id` - 经销商ID索引
- `idx_status` - 状态索引
- `idx_protect_end_date` - 保护期结束日期索引(定时任务使用)
#### 3. 经销商表 (crm_dealer)
#### 2. 经销商表 (crm_dealer)
| 字段 | 类型 | 说明 |
|------|------|------|
@ -404,28 +368,58 @@
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 更新时间 |
#### 4. 用户表 (crm_user)
#### 3. 用户表 (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)
#### 4. 学校表 (crm_school)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 学校ID主键 |
| school_code | VARCHAR(50) | 学校标识码(唯一) |
| school_name | VARCHAR(200) | 学校名称 |
| location | VARCHAR(255) | 所在地(省份+城市) |
| 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 | 更新时间 |
@ -441,8 +435,87 @@
**功能**:
1. 查询所有保护期已到的报备(`protect_end_date <= 今天` 且 `status = 1`
2. 批量更新报备状态为"已失效"`status = 3`
3. 批量更新关联客户状态为"可报备"`status = 0`
4. 保证渠道资源公平分配
3. 释放资源,允许相同"学校+产品+项目类型"组合重新报备
**核心代码**: `ReportExpireTask.java`
```java
@Scheduled(cron = "0 0 1 * * ?")
public void handleExpiredReports() {
log.info("开始处理过期报备...");
try {
reportService.handleExpiredReports();
log.info("过期报备处理完成");
} catch (Exception e) {
log.error("处理过期报备失败", e);
}
}
```
---
## 防撞单机制详解
### 核心规则
**唯一性维度**: 学校 + 产品 + 项目类型
**有效报备定义**: 状态为"待审核(0)"或"已通过(1)"的报备
### 校验逻辑
```java
// 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` 进行匹配
### 设计优势
- **降低门槛**: 学校不在库中也能报备,不影响业务
- **数据规范化**: 优先使用学校库数据,保证数据质量
- **灵活扩展**: 支持批量导入学校数据
---
@ -459,36 +532,15 @@
#### 权限拦截
- 前端路由守卫:未登录重定向到登录页
- 后端拦截器:验证 Token 有效性
- 接口权限注解:`@AuthRequired`
- 数据权限:经销商只能查看自己的报备
### 2. 密码安全
#### 双重加密
```
用户输入 → [前端 SHA-256 哈希] → HTTPS 传输 → [后端 BCrypt 加密] → 存储
```
#### 密码规则
- 长度6-20 位
- 加密BCrypt自动加盐
- 传输SHA-256 哈希后传输
- 修改密码:需验证原密码
### 3. 数据安全
#### SQL 注入防护
- 使用 MyBatis 参数化查询
- 所有用户输入都经过参数绑定
#### XSS 防护
- 前端输出自动转义
- 后端返回 JSON 格式
#### CSRF 防护
- 使用 JWT 无状态认证
- 不依赖 Session Cookie
---
## 项目结构
@ -502,10 +554,10 @@ by-crm/
│ │ ├── config/ # 配置类CORS、MyBatis、Swagger等
│ │ ├── controller/ # 控制器层
│ │ │ ├── AuthController.java
│ │ │ ├── CustomerController.java
│ │ │ ├── ReportController.java
│ │ │ ├── DealerController.java
│ │ │ └── SchoolController.java
│ │ │ ├── SchoolController.java
│ │ │ └── ReportProgressController.java
│ │ ├── dto/ # 数据传输对象
│ │ ├── entity/ # 实体类
│ │ ├── exception/ # 异常处理
@ -533,7 +585,6 @@ by-crm/
│ │ │ ├── Login.vue # 登录页
│ │ │ ├── Layout.vue # 主布局
│ │ │ ├── Dashboard.vue # 首页
│ │ │ ├── Customer.vue # 客户管理
│ │ │ ├── Report.vue # 报备管理
│ │ │ └── Dealer.vue # 经销商管理
│ │ ├── App.vue # 根组件
@ -542,14 +593,12 @@ by-crm/
│ ├── package.json # 依赖配置
│ └── tsconfig.json # TS 配置
├── sql/ # 数据库脚本
│ ├── init.sql # 初始化脚本
│ ├── school_table.sql # 学校表
│ └── *.sql # 其他 SQL 脚本
├── docs/ # 文档目录
│ └── README.md # 项目文档
└── docs/ # 文档目录
├── api.md # API 文档Swagger 导出)
└── password-security.md # 密码安全说明
└── sql/ # 数据库脚本
├── init.sql # 初始化脚本
└── *.sql # 其他 SQL 脚本
```
---
@ -581,7 +630,7 @@ jwt:
# 保护期配置
crm:
report:
ttl-days: 90 # 保护期天数默认90天
protect-days: 90 # 保护期天数默认90天
allow-overlap: false # 是否允许报备重叠测试用生产必须false
```
@ -593,9 +642,6 @@ const BASE_URL = 'http://localhost:8080/api'
// Token 存储
const TOKEN_KEY = 'token'
// 路由模式history 或 hash
const routerMode = 'history'
```
---
@ -660,21 +706,19 @@ mysql -u root -p by_crm < sql/init.sql
- `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` - 创建报备
- `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` - 创建经销商
@ -686,7 +730,6 @@ mysql -u root -p by_crm < sql/init.sql
- `GET /api/school/search?keyword=xxx` - 搜索学校
- `POST /api/school` - 创建学校
- `POST /api/school/import` - 文件上传导入
- `POST /api/school/import-from-docs` - 从 docs 目录导入
---
@ -694,20 +737,19 @@ mysql -u root -p by_crm < sql/init.sql
### v1.0.0 (当前版本)
**新增功能**:
- ✅ 客户管理(增删改查)
- ✅ 报备管理(提交、审核、撤回)
**核心功能**:
- ✅ 报备管理(提交、审核、撤回、编辑、作废)
- ✅ 经销商管理(增删改查、重置密码)
- ✅ 学校管理(查询、搜索、导入)
- ✅ 用户认证(登录、密码管理)
- ✅ 进展记录(添加、编辑、查询)
- ✅ 自动保护期到期处理
- ✅ 学校数据自动完成功能
- ✅ 密码传输加密SHA-256 + BCrypt 双重加密
- ✅ 防撞单机制(学校+产品+项目类型)
- ✅ 灵活的学校输入(库选择+手动输入
**安全特性**:
- ✅ JWT Token 认证
- ✅ BCrypt 密码加密存储
- ✅ SHA-256 前端哈希传输
- ✅ 权限注解拦截
- ✅ SQL 注入防护
- ✅ XSS 防护
@ -716,40 +758,33 @@ mysql -u root -p by_crm < sql/init.sql
## 常见问题
### 1. 默认密码是什么?
- 新增经销商时,如果不设置初始密码,默认为 `123456`
### 1. 默认账号是什么?
- 管理员账号:`admin` / `admin123`
- 经销商账号:`user001~003` / `admin123`
- 经销商账号:根据经销商数据创建
### 2. 保护期可以修改吗?
- 可以,通过修改系统配置 `report.protect.days`
- 默认为 90 天,可根据业务需求调整
### 3. 同一客户可以同时被多个经销商报备吗?
### 3. 同一学校项目可以同时被多个经销商报备吗?
- **不可以**,这是系统的核心规则
- 同一时间只能有一个"已通过"的报备
- 其他经销商报备该客户会被拒绝
- 同一"学校+产品+项目类型"组合只能有一个"有效"报备
- 有效报备定义:状态为"待审核(0)"或"已通过(1)"
### 4. 保护期到期后会发生什么?
- 定时任务每天凌晨1点自动执行
- 将过期报备状态改为"已失效"
- 客户状态自动变为"可报备"
- 其他经销商可以再次报备
- 该"学校+产品+项目类型"组合可重新报备
### 5. 如何确保传输安全?
- 前端:使用 Web Crypto API 进行 SHA-256 哈希
- 传输HTTPS + 哈希后的密码
- 后端BCrypt 二次加密存储
- 三重安全保障
### 5. 学校名称可以手动输入吗?
- 可以,支持从学校库选择或手动输入
- 从学校库选择时会保存 `school_id` 和标准学校名称
- 手动输入时 `school_id` 为 NULL保存用户输入的文本
### 6. 管理员可以重置其他管理员密码吗?
- **不可以**,只能重置经销商用户的密码
- 管理员修改自己的密码通过"修改密码"功能
### 7. 学校数据从哪里来?
- 存放在 `docs/school.xls` 文件中
- 包含:学校标识码、学校名称、所在地
- 可通过 API 导入到数据库
### 6. 报备可以作废吗?
- 可以,只有管理员可以作废已通过的报备
- 作废时必须填写作废原因
- 作废后该"学校+产品+项目类型"组合可重新报备
---

View File

@ -41,7 +41,6 @@ CREATE TABLE IF NOT EXISTS crm_customer (
CREATE TABLE IF NOT EXISTS crm_report (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '报备ID',
dealer_id BIGINT NOT NULL COMMENT '经销商ID',
customer_id BIGINT NOT NULL COMMENT '客户ID',
description VARCHAR(500) COMMENT '报备说明',
status TINYINT NOT NULL DEFAULT 0 COMMENT '状态0-待审核 1-已通过 2-已驳回 3-已失效',
reject_reason VARCHAR(255) COMMENT '驳回原因',
@ -49,11 +48,14 @@ CREATE TABLE IF NOT EXISTS crm_report (
protect_end_date DATE DEFAULT NULL COMMENT '保护期结束日期',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
school_id BIGINT DEFAULT NULL COMMENT '学校ID',
school_name VARCHAR(255) COMMENT '学校名称(冗余存储)',
product VARCHAR(255) COMMENT '所属产品',
project_type VARCHAR(255) COMMENT '项目类型',
cancel_reason VARCHAR(500) COMMENT '作废原因',
INDEX idx_dealer_id (dealer_id),
INDEX idx_customer_id (customer_id),
INDEX idx_status (status),
INDEX idx_protect_end_date (protect_end_date),
UNIQUE KEY uk_customer_valid (customer_id, status) COMMENT '同一客户只能有一个有效报备'
INDEX idx_protect_end_date (protect_end_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='报备表';
-- 用户表(管理员和经销商用户)