Files
hzhub/docs/CRM销售模块详细设计说明书V3.md
大壮 3f643ef31f feat: 完成CRM商机和线索管理模块开发
## 新增功能

### 商机中心 (/opportunity)
- Stats统计卡片(商机总数、金额、赢单、转化率)
- Pipeline商机管道(阶段Tab:全部/线索/谈判中/方案/赢单)
- 商机列表真实数据渲染(来自crm_opportunity表)
- 商机卡片详情(经销商、负责人、金额、概率)
- Pipeline计数实时更新

### 线索中心 (/lead)
- 线索列表完整功能(CRUD)
- 线索详情Drawer(基础信息 + 跟进记录Timeline)
- 新建线索(含ERP客户关联、手机号验证)
- 添加跟进记录(跟进方式、内容、下次时间)
- 分配负责人(用户选择器,显示真实姓名)
- 线索转经销商(自动创建商机)
- 删除线索(逻辑删除)

## 后端开发

### 数据库表
- crm_lead(线索表)
- crm_lead_follow(线索跟进记录表)
- crm_dealer(经销商表)
- crm_opportunity(商机表)
- crm_opportunity_follow(商机跟进记录表)
- 数据字典初始化

### API接口
- 线索管理:CRUD、详情、跟进、分配、转化
- 商机管理:列表查询
- 用户选择器:员工门户专用API

### 核心功能
- 线索转化自动创建经销商和商机
- 负责人翻译显示真实姓名(修复)
- 手机号前后端双重格式验证(修复)

## 前端开发

### 页面架构改进
- 商机中心:保留原CRM设计风格(Stats + Pipeline)
- 线索中心:独立页面(完整线索管理)
- 左侧菜单:独立菜单项(商机中心、线索中心)

### API模块
- src/api/crm/:线索和商机API类型定义和调用方法
- src/api/user/:用户选择器API

### 样式设计
- 商机中心:100%保持原CRM Pipeline设计风格
- 使用CSS变量系统(var(--radius-lg), var(--shadow-sm)等)
- Pipeline Tab白色圆角设计
- 商机卡片阴影和hover效果
- 头像堆叠显示

## 配置修改

- Gateway路由:添加CRM模块路由配置
- Gateway路由:添加system模块路由配置
- Aside菜单:拆分商机中心和线索中心

## Bug修复

- 修复负责人显示手机号问题(UserNameTranslationImpl返回昵称)
- 修复手机号格式验证缺失(前后端双重验证)
- 修复商机管道设计风格不一致(完整复制原CRM样式)

## 文档

- CRM销售模块详细设计说明书V3.md
- CRM线索转化API契约
- CRM线索转化开发计划
- CRM线索转化测试指引
- CRM线索管理测试指引
- CRM商机管理测试指引
- CRM架构改进报告
- CRM Bug修复报告

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 09:46:59 +00:00

1201 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CRM销售自动化渠道版执行级详细设计说明书 V3
# 1. 文档说明
## 1.1 文档目标
本文档用于指导HZHub项目中CRM销售自动化渠道版系统的
- 数据库设计(适配 MySQL 8 + MyBatis-Plus
- 后端接口开发(适配 hzhub-system 服务)
- 前端页面开发(适配 **hzhub-portal-employee 员工门户**
- AI能力集成适配 LangChain4j + Weaviate
- 企业微信集成
- 自动化流程配置(适配 warm-flow
- 权限与组织体系建设(适配 Sa-Token
本文档属于"开发执行版设计文档 V3",针对**员工门户hzhub-portal-employee**进行定制化适配。
---
## 1.2 与 V1/V2 版本的主要差异
| 方面 | V1 版本 | V2 版本 | V3 版本(员工门户适配) |
|---|---|---|---|
| 前端项目 | Vue3 + Element Plus通用 | Vben Admin管理后台 | **Element Plus员工门户** |
| 前端路径 | `/api/**` | `/crm/**`(管理后台) | **`/crm/**`(员工门户)** |
| 现有基础 | 无 | 无 | **已有CRM和经销商页面** |
| 数据来源 | 独立CRM表 | 独立CRM表 | **ERP客户数据 + CRM表** |
| 技术栈 | Python FastAPI | LangChain4j | **LangChain4j + Vue3 Composition API** |
| UI风格 | 未指定 | Vben Admin 企业级 | **Element Plus 卡片式** |
| 用户角色 | 通用销售人员 | 管理员 + 销售 | **企业员工 + 销售人员** |
---
## 1.3 员工门户现有功能
### 已实现页面
| 页面 | 路径 | 功能 | 数据来源 |
|---|---|---|---|
| 销售CRM | `/crm` | 商机管道视图 + 客户列表 | ERP Customer动态API |
| 经销商管理 | `/dealer` | 经销商卡片视图 + 筛选 | ERP Customer动态API |
### 现有API
```typescript
// ERP 客户数据动态API
GET /erp/dynamic/v1/customer/list // 客户列表
GET /erp/dynamic/v1/customer/detail // 客户详情
GET /erp/dynamic/v1/customer/sales-areas // 销区列表
GET /erp/dynamic/v1/customer/brands // 品牌列表
```
### 现有数据模型CustomerVO
```typescript
interface CustomerVO {
customerCode: string; // 客户编码
customerName: string; // 客户名称
companyCode: string; // 公司编码
companyName: string; // 公司名称
brand: string; // 品牌
brandName: string; // 品牌名称
contactName: string; // 联系人
salesAreaCode: string; // 销区编码
salesAreaName: string; // 销区名称
salesPersonCode: string; // 销售人员编码
salesPersonName: string; // 销售人员姓名
province: string; // 省
city: string; // 市
isStop: number; // 状态0合作中1停用
// ... 其他字段
}
```
---
# 2. 系统总体架构
## 2.1 HZHub 集成架构(员工门户)
### 服务归属
CRM模块归属于 **hzhub-system** 服务(端口 8083前端在 **hzhub-portal-employee** 实现。
```
┌─────────────────────────────────────────┐
│ hzhub-portal-employee (员工门户) │
│ Vue3 + Element Plus + TypeScript │
│ Port: 5137 │
│ 已有: /crm (销售CRM) + /dealer (经销商) │
└────────────┬────────────────────────────┘
┌────────┴────────┐
│ hzhub-gateway │ (API Gateway, port 8080)
│ Spring Cloud │ JWT auth, routing
└───┬──────┬──────┘
│ │
┌───▼──────────┐
│ hzhub-system │ CRM 模块归属服务
│ 8083 │ (Users, Roles, CRM)
└─────────────┘
```
### Gateway 路由配置
`hzhub-gateway/src/main/resources/application.yml` 中添加 CRM 路由:
```yaml
spring:
cloud:
gateway:
routes:
# CRM 路由(新增)
- id: hzhub-crm
uri: lb://hzhub-system
predicates:
- Path=/crm/**
filters:
- StripPrefix=1 # 去除 /crm 前缀
```
---
## 2.2 技术栈适配(员工门户)
| 层级 | HZHub 技术栈 | 说明 |
|---|---|---|
| 前端Web | Vue3 + Element Plus + TypeScript | **hzhub-portal-employee 已部署** |
| 前端路由 | Vue Router | 静态路由配置(`staticRouter.ts` |
| 状态管理 | Pinia + persistedstate | 已集成 |
| HTTP请求 | hook-fetch + SSE | 已集成 |
| 后端 | Spring Boot 3.5.8 + MyBatis-Plus | hzhub-system 服务 |
| 数据库 | MySQL 8.0 | 已部署 |
| ERP数据 | SQL Server 2008 R2 | **hzhub-erp 服务** |
| 缓存 | Redis 7 | 已部署 |
| 向量数据库 | Weaviate 1.25.0 | 已部署 |
| AI服务 | LangChain4j + LangGraph4j | hzhub-ai 服务集成 |
| 文件存储 | MinIO | 已部署 |
| 工作流 | warm-flow 1.8.2 | 已集成在 hzhub-system |
| 权限 | Sa-Token | 已集成JWT-based |
---
# 3. 数据库设计规范HZHub 适配)
## 3.1 通用字段规范
所有CRM业务表继承 `TenantEntity`,包含以下字段:
| 字段 | 类型 | 注解 | 说明 |
|---|---|---|---|
| tenant_id | varchar(20) | 无注解(继承) | 租户ID多租户 |
| create_dept | bigint | @TableField(fill = INSERT) | 创建部门 |
| create_by | bigint | @TableField(fill = INSERT) | 创建人 |
| create_time | datetime | @TableField(fill = INSERT) | 创建时间 |
| update_by | bigint | @TableField(fill = INSERT_UPDATE) | 更新人 |
| update_time | datetime | @TableField(fill = INSERT_UPDATE) | 更新时间 |
**注意:** 不需要手动添加 `id` 字段,使用 MyBatis-Plus 的 `@TableId` 注解。
---
## 3.2 Entity 实体类规范
### 基类继承
```java
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("crm_lead")
public class CrmLead extends TenantEntity {
@TableId(value = "lead_id", type = IdType.ASSIGN_ID)
private Long leadId;
// 业务字段...
/**
* 删除标志0代表存在 1代表删除
*/
@TableLogic
private Integer delFlag;
}
```
---
# 4. 线索中心模块设计(适配员工门户)
## 4.1 模块目标
用于管理潜在经销商,支持:
- 多渠道线索接入
- **关联ERP客户数据**customer_code
- AI意向识别LangChain4j
- AI风险分析
- 自动分配销售
- 商机转化
---
## 4.2 数据表设计
### 4.2.1 crm_lead线索表
| 字段 | 类型 | 注解 | 说明 |
|---|---|---|---|
| lead_id | bigint | @TableId(ASSIGN_ID) | 主键 |
| customer_code | varchar(100) | - | **ERP客户编码关联** |
| company_name | varchar(200) | - | 公司名称 |
| contact_name | varchar(100) | - | 联系人 |
| mobile | varchar(50) | - | 手机号 |
| wechat | varchar(100) | - | 微信号 |
| province | varchar(50) | - | 省 |
| city | varchar(50) | - | 市 |
| region_id | bigint | - | 区域ID关联 sys_dept |
| source_type | varchar(50) | - | 来源类型字典crm_lead_source |
| activity_name | varchar(100) | - | 活动名称 |
| referrer_name | varchar(100) | - | 推荐人 |
| industry | varchar(100) | - | 行业字典crm_industry |
| company_scale | varchar(100) | - | 公司规模字典crm_scale |
| store_count | int | - | 门店数 |
| intent_level | varchar(20) | - | AI意向等级字典crm_intent_level |
| ai_score | decimal(5,2) | - | AI评分 |
| risk_level | varchar(20) | - | 风险等级字典crm_risk_level |
| owner_user_id | bigint | - | 负责人(关联 sys_user |
| lead_status | varchar(50) | - | 状态字典crm_lead_status |
| converted_dealer_id | bigint | - | 转化经销商ID |
| del_flag | int | @TableLogic | 删除标志 |
**继承字段:** tenant_id, create_dept, create_by, create_time, update_by, update_time
**关键设计:**
- `customer_code` 关联 ERP 客户数据(可选,支持同步)
- 如果 `customer_code` 存在,自动从 ERP 拉取基础信息
- 线索转化时,创建 `crm_dealer` 并关联 `customer_code`
---
### 4.2.2 crm_lead_follow线索跟进记录
| 字段 | 类型 | 说明 |
|---|---|---|
| follow_id | bigint | 主键 |
| lead_id | bigint | 线索ID |
| follow_type | varchar(50) | 跟进方式字典crm_follow_type |
| content | text | 跟进内容 |
| ai_summary | text | AI摘要 |
| next_follow_time | datetime | 下次跟进时间 |
| follow_user_id | bigint | 跟进人(关联 sys_user |
| del_flag | int | 删除标志 |
---
## 4.3 接口设计
### 4.3.1 创建线索
**API** `POST /crm/lead`
**请求参数:**
```json
{
"customerCode": "C001", // ERP客户编码可选
"companyName": "XX贸易有限公司",
"contactName": "张三",
"mobile": "13800000000",
"wechat": "zhangsan",
"province": "广东省",
"city": "深圳市",
"industry": "食品",
"storeCount": 20
}
```
**业务逻辑:**
1. 如果提供 `customer_code`,调用 `hzhub-erp:8082/erp/dynamic/v1/customer/detail` 拉取客户信息
2. 自动填充客户基础信息companyName, contactName, mobile 等)
3. 校验手机号是否重复(同租户内)
4. 调用 LangChain4j AI服务分析意向等级`hzhub-ai:6039/ai/analyze/intent`
5. 自动生成AI评分
6. 根据区域规则sys_dept分配销售owner_user_id
7. 创建自动跟进任务warm-flow
---
### 4.3.2 线索转经销商
**API** `POST /crm/lead/convert`
**请求参数:**
```json
{
"leadId": 12345,
"dealerName": "XX贸易",
"dealerCode": "DL20260001",
"customerCode": "C001" // ERP客户编码可选
}
```
**逻辑:**
1. 创建 `crm_dealer` 数据
2. 如果 `customer_code` 存在,同步 ERP 客户数据
3. 迁移历史跟进记录到 `crm_dealer_follow`
4. 创建初始商机(`crm_opportunity`
5. 更新线索状态为"已转化"
6. 触发 warm-flow 工作流(经销商审批)
---
## 4.4 前端页面设计Element Plus - 员工门户)
### 4.4.1 扩展现有CRM页面
**路径:** `hzhub-portal-employee/src/pages/crm/index.vue`**已存在,需扩展**
**现有功能:**
- 商机管道视图Pipeline
- 客户列表表格(使用 ERP CustomerVO
- 新建客户对话框(基础功能)
**新增功能:**
#### 1. 线索列表Tab页
在现有CRM页面添加Tab切换
```vue
<el-tabs v-model="activeTab">
<el-tab-pane label="商机管道" name="pipeline">
<!-- 现有管道视图 -->
</el-tab-pane>
<el-tab-pane label="线索管理" name="leads">
<!-- 新增线索管理 -->
</el-tab-pane>
<el-tab-pane label="经销商" name="dealers">
<!-- 新增经销商管理 -->
</el-tab-pane>
</el-tabs>
```
#### 2. 线索管理模块
**筛选栏:**
```vue
<div class="filter-bar">
<el-input v-model="filters.keyword" placeholder="搜索公司、联系人..." />
<el-select v-model="filters.intentLevel" placeholder="AI意向等级">
<el-option label="高意向" value="high" />
<el-option label="中意向" value="medium" />
<el-option label="低意向" value="low" />
</el-select>
<el-select v-model="filters.ownerUserId" placeholder="负责人">
<!-- 用户选择器 -->
</el-select>
<el-date-picker v-model="filters.createTimeRange" type="daterange" />
<el-button type="primary" @click="loadLeads">搜索</el-button>
</div>
```
**线索列表Element Plus Table**
```vue
<el-table :data="leadList" v-loading="loading">
<el-table-column prop="companyName" label="公司名称">
<template #default="{ row }">
<el-tag v-if="row.intentLevel === 'high'" type="danger" effect="plain">
{{ row.companyName }}
</el-tag>
<span v-else>{{ row.companyName }}</span>
</template>
</el-table-column>
<el-table-column prop="contactName" label="联系人" />
<el-table-column prop="mobile" label="手机">
<template #default="{ row }">
{{ maskPhone(row.mobile) }}
</template>
</el-table-column>
<el-table-column prop="intentLevel" label="AI意向">
<template #default="{ row }">
<el-badge :value="row.aiScore" :type="getIntentBadgeType(row.intentLevel)">
{{ getIntentLabel(row.intentLevel) }}
</el-badge>
</template>
</el-table-column>
<el-table-column prop="ownerUserName" label="负责人">
<template #default="{ row }">
<el-avatar :size="32">{{ row.ownerUserName?.charAt(0) }}</el-avatar>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="showLeadDetail(row)">详情</el-button>
<el-button type="primary" link @click="showFollowDrawer(row)">跟进</el-button>
<el-button type="success" link @click="convertToDealer(row)">转经销商</el-button>
</template>
</el-table-column>
</el-table>
```
#### 3. 线索详情 Drawer
```vue
<el-drawer v-model="showDetailDrawer" title="线索详情" size="50%">
<el-descriptions :column="2" border>
<el-descriptions-item label="公司名称">{{ currentLead.companyName }}</el-descriptions-item>
<el-descriptions-item label="联系人">{{ currentLead.contactName }}</el-descriptions-item>
<el-descriptions-item label="手机">{{ currentLead.mobile }}</el-descriptions-item>
<el-descriptions-item label="ERP客户编码">
<el-link v-if="currentLead.customerCode" @click="viewErpCustomer">
{{ currentLead.customerCode }}
</el-link>
</el-descriptions-item>
<!-- AI分析信息 -->
<el-descriptions-item label="AI意向等级">
<el-progress :percentage="currentLead.aiScore" :color="getIntentColor(currentLead.intentLevel)" />
</el-descriptions-item>
<el-descriptions-item label="风险等级">
<el-tag :type="getRiskTagType(currentLead.riskLevel)">
{{ getRiskLabel(currentLead.riskLevel) }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
<!-- 跟进记录Timeline -->
<el-divider content-position="left">跟进记录</el-divider>
<el-timeline>
<el-timeline-item v-for="follow in followRecords" :key="follow.followId"
:timestamp="follow.createTime" placement="top">
<el-card>
<div class="follow-header">
<span class="follow-type">{{ getFollowTypeLabel(follow.followType) }}</span>
<span class="follow-user">{{ follow.followUserName }}</span>
</div>
<div class="follow-content">{{ follow.content }}</div>
<div v-if="follow.aiSummary" class="follow-ai-summary">
<el-icon><MagicStick /></el-icon> AI摘要{{ follow.aiSummary }}
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</el-drawer>
```
#### 4. 新建线索 Dialog
```vue
<el-dialog v-model="showAddLeadDialog" title="新建线索" width="600px">
<el-form :model="leadForm" label-width="100px">
<el-form-item label="ERP客户">
<el-select v-model="leadForm.customerCode" filterable placeholder="选择ERP客户可选">
<!-- 从ERP拉取客户列表 -->
</el-select>
</el-form-item>
<el-form-item label="公司名称" required>
<el-input v-model="leadForm.companyName" />
</el-form-item>
<el-form-item label="联系人" required>
<el-input v-model="leadForm.contactName" />
</el-form-item>
<el-form-item label="手机号" required>
<el-input v-model="leadForm.mobile" />
</el-form-item>
<!-- 其他字段 -->
</el-form>
<template #footer>
<el-button @click="showAddLeadDialog = false">取消</el-button>
<el-button type="primary" @click="handleAddLead">创建线索</el-button>
</template>
</el-dialog>
```
---
### 4.4.2 AI分析侧边栏Element Plus Drawer
右侧固定展示:
```vue
<el-drawer v-model="showAiDrawer" title="AI分析" size="40%" direction="rtl">
<div class="ai-analysis">
<el-card class="ai-card">
<div class="ai-score">
<el-progress type="dashboard" :percentage="aiScore" :color="getAiColor" />
<span class="ai-label">AI意向评分</span>
</div>
</el-card>
<el-alert v-if="riskLevel === 'high'" title="高风险提示" type="error" :closable="false">
{{ riskReason }}
</el-alert>
<el-collapse>
<el-collapse-item title="推荐动作" name="actions">
<el-steps :active="recommendedStep" direction="vertical">
<el-step title="首次拜访" description="建议本周完成首次拜访" />
<el-step title="发送招商政策" description="发送返点政策文档" />
<el-step title="样品测试" description="寄送样品进行测试" />
</el-steps>
</el-collapse-item>
<el-collapse-item title="推荐销售话术" name="scripts">
<div v-html="recommendedScript"></div>
</el-collapse-item>
</el-collapse>
</div>
</el-drawer>
```
**AI服务调用** 通过 Gateway 调用 `hzhub-ai:6039/ai/chat/analyze`
---
# 5. 经销商中心模块设计
## 5.1 现有页面扩展
**路径:** `hzhub-portal-employee/src/pages/dealer/index.vue`**已存在,需扩展**
**现有功能:**
- 经销商卡片视图(使用 ERP CustomerVO
- 筛选(销区、品牌、状态、搜索)
- 统计数据展示
**新增功能:**
### 5.1.1 关联CRM经销商数据
```typescript
interface DealerVO extends CustomerVO {
// CRM扩展字段
dealerId?: number; // CRM经销商ID
level?: string; // 经销商等级
lifecycle?: string; // 生命周期
totalOrderAmount?: number; // 累计订单金额
totalPaymentAmount?: number; // 累计回款金额
activityScore?: number; // 活跃评分
riskScore?: number; // 风险评分
tags?: DealerTagVO[]; // AI标签
}
```
### 5.1.2 经销商详情 Dialog
```vue
<el-dialog v-model="showDealerDetail" title="经销商详情" width="800px">
<el-tabs>
<el-tab-pane label="基础档案">
<el-descriptions :column="2" border>
<el-descriptions-item label="ERP客户编码">{{ dealer.customerCode }}</el-descriptions-item>
<el-descriptions-item label="经销商等级">
<el-tag>{{ dealer.level }}</el-tag>
</el-descriptions-item>
<!-- ... -->
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="商机管理">
<!-- 商机列表 -->
</el-tab-pane>
<el-tab-pane label="拜访记录">
<el-timeline>
<!-- 拜访Timeline -->
</el-timeline>
</el-tab-pane>
<el-tab-pane label="AI画像">
<el-row :gutter="20">
<el-col :span="12">
<el-card>
<div class="ai-metric">
<span class="metric-label">活跃度评分</span>
<el-progress :percentage="dealer.activityScore" />
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div class="ai-metric">
<span class="metric-label">风险评分</span>
<el-progress :percentage="dealer.riskScore" :color="getRiskColor" />
</div>
</el-card>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
</el-dialog>
```
---
## 5.2 数据表设计
### 5.2.1 crm_dealer经销商表
| 字段 | 类型 | 说明 |
|---|---|---|
| dealer_id | bigint | 主键 |
| customer_code | varchar(100) | **ERP客户编码关联** |
| dealer_name | varchar(200) | 经销商名称 |
| dealer_code | varchar(100) | 编码 |
| contact_name | varchar(100) | 联系人 |
| mobile | varchar(50) | 手机 |
| province | varchar(50) | 省 |
| city | varchar(50) | 市 |
| level | varchar(50) | 等级字典crm_dealer_level |
| lifecycle | varchar(50) | 生命周期字典crm_lifecycle |
| signed_at | datetime | 签约时间 |
| store_count | int | 门店数 |
| team_size | int | 团队规模 |
| total_order_amount | decimal(18,2) | 累计订单金额 |
| total_payment_amount | decimal(18,2) | 累计回款金额 |
| activity_score | decimal(5,2) | 活跃评分 |
| risk_score | decimal(5,2) | 飆险评分 |
| owner_user_id | bigint | 负责人(关联 sys_user |
| del_flag | int | 删除标志 |
---
# 6. API 接口设计(员工门户适配)
## 6.1 前端 API 定义
**路径:** `hzhub-portal-employee/src/api/crm/`**新建目录**
创建:
- `index.ts`API调用
- `types.ts`(类型定义)
### API示例
```typescript
import request from '@/utils/request';
// 线索列表
export function getLeadList(params: LeadQueryParams) {
return request.get<TableDataInfo<LeadVO>>('/crm/lead/list', params).json();
}
// 创建线索
export function createLead(data: LeadForm) {
return request.post<R<void>>('/crm/lead', data).json();
}
// 线索详情
export function getLeadDetail(leadId: number) {
return request.get<R<LeadVO>>(`/crm/lead/${leadId}`).json();
}
// 线索跟进记录
export function getLeadFollowRecords(leadId: number) {
return request.get<R<LeadFollowVO[]>>(`/crm/lead/follow/${leadId}`).json();
}
// 添加跟进记录
export function addLeadFollow(data: LeadFollowForm) {
return request.post<R<void>>('/crm/lead/follow', data).json();
}
// 线索转经销商
export function convertLeadToDealer(data: ConvertForm) {
return request.post<R<void>>('/crm/lead/convert', data).json();
}
```
---
## 6.2 后端接口设计hzhub-system
### Controller规范
```java
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/crm/lead")
public class CrmLeadController extends BaseController {
private final ICrmLeadService leadService;
/**
* 获取线索列表
*/
@GetMapping("/list")
public TableDataInfo<CrmLeadVo> list(CrmLeadBo lead, PageQuery pageQuery) {
return leadService.selectPageLeadList(lead, pageQuery);
}
/**
* 新增线索
*/
@Log(title = "线索管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody CrmLeadBo lead) {
return R.ok(leadService.insertLead(lead));
}
/**
* 线索详情
*/
@GetMapping("/{leadId}")
public R<CrmLeadVo> detail(@PathVariable Long leadId) {
return R.ok(leadService.selectLeadById(leadId));
}
/**
* 线索跟进记录
*/
@GetMapping("/follow/{leadId}")
public R<List<CrmLeadFollowVo>> followRecords(@PathVariable Long leadId) {
return R.ok(leadService.selectFollowRecordsByLeadId(leadId));
}
/**
* 添加跟进记录
*/
@Log(title = "线索跟进", businessType = BusinessType.INSERT)
@PostMapping("/follow")
public R<Void> addFollow(@Validated @RequestBody CrmLeadFollowBo follow) {
return R.ok(leadService.insertFollowRecord(follow));
}
/**
* 线索转经销商
*/
@Log(title = "线索转化", businessType = BusinessType.INSERT)
@PostMapping("/convert")
public R<Void> convert(@Validated @RequestBody CrmLeadConvertBo convert) {
return R.ok(leadService.convertToDealer(convert));
}
}
```
---
# 7. ERP数据集成设计
## 7.1 数据同步策略
### 同步方式
**方式1关联查询实时**
线索/经销商表存储 `customer_code`,实时调用 ERP 动态API 拉取客户信息。
```java
public CrmLeadVo selectLeadById(Long leadId) {
CrmLead lead = leadMapper.selectById(leadId);
CrmLeadVo vo = BeanUtil.toBean(lead, CrmLeadVo.class);
// 如果有ERP客户编码拉取ERP数据补充
if (StringUtils.isNotBlank(lead.getCustomerCode())) {
CustomerVO erpCustomer = erpService.getCustomerDetail(lead.getCustomerCode());
vo.setErpCustomerName(erpCustomer.getCustomerName());
vo.setErpSalesAreaName(erpCustomer.getSalesAreaName());
// ...
}
return vo;
}
```
**方式2定时同步批量**
使用 XXL-Job 定时任务同步 ERP 客户数据到 CRM 表。
```java
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
public void syncErpCustomers() {
// 调用 hzhub-erp:8082/erp/dynamic/v1/customer/list
// 批量更新 crm_dealer 表
}
```
---
## 7.2 ERP动态API调用
**hzhub-system 服务调用 ERP**
```java
@Service
public class ErpIntegrationService {
@Value("${erp.base-url}")
private String erpBaseUrl; // http://localhost:8082
/**
* 获取ERP客户详情
*/
public CustomerVO getCustomerDetail(String customerCode) {
String url = erpBaseUrl + "/erp/dynamic/v1/customer/detail?customerCode=" + customerCode;
ResponseEntity<R<CustomerVO>> response = restTemplate.getForEntity(url, R.class);
return response.getBody().getData();
}
/**
* 获取ERP客户列表
*/
public List<CustomerVO> getCustomerList(CustomerQueryParam param) {
String url = erpBaseUrl + "/erp/dynamic/v1/customer/list";
// ...
}
}
```
---
# 8. 企业微信集成(员工门户)
## 8.1 企业微信侧边栏
**展示内容:**
- 经销商画像(从 CRM + ERP 拉取)
- 最近订单
- 最近拜访
- 商机阶段
- AI风险
**实现:**
1. 企业微信应用配置应用ID、Secret
2. 后端接口:`GET /crm/wecom/sidebar/{externalUserId}`
3. 前端嵌入企业微信侧边栏H5页面
---
## 8.2 移动端H5页面
**员工门户支持企业微信H5**
```typescript
// 企业微信环境判断
if (window.location.href.includes('wework')) {
// 使用企业微信JS-SDK
wx.config({
beta: true,
appId: 'YOUR_APPID',
// ...
});
}
```
**移动端页面:**
- `/crm/mobile/visit` - 拜访记录(语音录入)
- `/crm/mobile/follow` - 快速跟进
---
# 9. AI能力集成设计
## 9.1 AI服务调用LangChain4j
**调用 hzhub-ai 服务:**
```java
@Service
public class CrmAiService {
@Autowired
private ChatClient chatClient; // LangChain4j
/**
* AI意向分析
*/
public LeadIntentAnalysis analyzeIntent(CrmLead lead) {
String prompt = String.format(
"分析以下线索的意向等级和风险:%s联系人%s行业%s",
lead.getCompanyName(), lead.getContactName(), lead.getIndustry()
);
AiMessage response = chatClient.generate(prompt);
// 解析AI响应提取意向等级、评分、风险等级
return parseIntentAnalysis(response.text());
}
/**
* AI跟进摘要生成
*/
public String generateFollowSummary(String followContent) {
String prompt = "请为以下跟进记录生成摘要:" + followContent;
AiMessage response = chatClient.generate(prompt);
return response.text();
}
}
```
---
## 9.2 向量检索Weaviate
**使用 Weaviate 存储历史线索案例:**
```java
@Service
public class LeadSimilarityService {
/**
* 查找相似线索案例
*/
public List<LeadCase> findSimilarLeads(CrmLead newLead) {
// 将新线索信息转为向量
// 在 Weaviate 中检索相似案例
// 返回历史成功案例供参考
}
}
```
---
# 10. 自动化工作流设计warm-flow
## 10.1 线索分配流程
```text
线索创建
-> AI意向分析
-> 自动分配规则(根据区域)
-> warm-flow审批流程
-> 销售确认接手
```
**实现:**
```java
// 触发warm-flow流程
workflowService.startProcessInstanceByKey("crm_lead_assign_flow", leadId);
```
---
## 10.2 线索转化流程
```text
线索转化申请
-> warm-flow审批流程区域经理审批
-> 创建经销商数据
-> 创建商机
-> 更新线索状态
```
---
# 11. 权限与组织设计Sa-Token
## 11.1 数据权限层级
```text
总部sys_dept: root
-> 大区sys_dept: level 1
-> 区域sys_dept: level 2
-> 城市sys_dept: level 3
-> 销售sys_user
```
**实现:** 使用 `sys_dept` 表的树形结构,通过 `DataPermissionHelper` 实现数据隔离。
---
## 11.2 权限控制点
| 模块 | 权限标识 | Sa-Token 注解 |
|---|---|---|
| 线索 | crm:lead:list, crm:lead:add, crm:lead:edit, crm:lead:remove | @SaCheckPermission |
| 经销商 | crm:dealer:list, crm:dealer:edit | - |
| 合同 | crm:contract:approve, crm:contract:amount-edit | - |
| 回款 | crm:payment:list, crm:payment:verify | - |
| BI | crm:bi:view区域数据隔离 | - |
**注意:** 员工门户不需要 Sa-Token注解权限由 Gateway 统一控制。
---
# 12. 前端开发规范Element Plus
## 12.1 页面结构规范
```vue
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { getLeadList, type LeadVO } from '@/api/crm';
import { ElMessage } from 'element-plus';
// 数据
const leadList = ref<LeadVO[]>([]);
const loading = ref(false);
// 方法
async function loadLeads() {
loading.value = true;
try {
const res = await getLeadList({ pageNum: 1, pageSize: 10 });
leadList.value = res.rows || [];
} catch (error: any) {
ElMessage.error(error?.message || '加载失败');
} finally {
loading.value = false;
}
}
onMounted(() => {
loadLeads();
});
</script>
<template>
<div class="crm-page">
<!-- Element Plus组件 -->
</div>
</template>
<style scoped lang="scss">
.crm-page {
padding: 20px;
}
</style>
```
---
## 12.2 目录结构
```
hzhub-portal-employee/src/
├── api/
│ └── crm/
│ │ ├── index.ts # CRM API调用
│ │ └── types.ts # 类型定义
├── pages/
│ └── crm/
│ │ ├── index.vue # CRM主页扩展
│ │ ├── LeadDetailDrawer.vue # 线索详情组件
│ │ ├── FollowDrawer.vue # 跟进记录组件
│ │ └── ConvertDialog.vue # 转化对话框
│ └── dealer/
│ │ ├── index.vue # 经销商主页(扩展)
│ │ ├── DealerDetailDialog.vue # 经销商详情
```
---
# 13. 实施计划(员工门户)
## 第一阶段1-2个月
**建设内容:**
- 扩展员工门户现有CRM页面线索管理Tab
- 后端CRM基础接口hzhub-system
- ERP数据关联customer_code
- 线索跟进记录Timeline展示
- warm-flow审批流程线索分配
**技术栈:**
- 后端Spring Boot + MyBatis-Plus + Sa-Token + warm-flow
- 前端Vue3 + Element Plus + TypeScript
- ERP集成hzhub-erp 动态API
---
## 第二阶段2-3个月
**建设内容:**
- AI意向分析LangChain4j
- AI跟进摘要生成
- AI风险分析
- 企业微信侧边栏集成
- 移动端H5拜访页面
**技术栈:**
- AILangChain4j + hzhub-ai 服务
- 向量Weaviate案例检索
- 移动端企业微信JS-SDK
---
## 第三阶段3-4个月
**建设内容:**
- AI预测模型成交预测、流失预测
- AI CopilotLangGraph4j
- BI仪表盘扩展现有 `/bi` 页面)
- 自动化工作流XXL-Job
**技术栈:**
- AILangGraph4j + hzhub-ai 服务
- 定时任务XXL-Job
---
# 14. 与 HZHub 项目的集成要点
## 14.1 Gateway 路由配置
**位置:** `hzhub-gateway/src/main/resources/application.yml`
```yaml
spring:
cloud:
gateway:
routes:
# CRM 路由(添加到现有路由列表)
- id: hzhub-crm
uri: lb://hzhub-system
predicates:
- Path=/crm/**
filters:
- StripPrefix=1
```
---
## 14.2 前端路由扩展
**位置:** `hzhub-portal-employee/src/routers/modules/staticRouter.ts`
**现有路由(已存在):**
```typescript
{
path: '/crm',
name: 'crm',
component: () => import('@/pages/crm/index.vue'),
meta: {
title: '销售CRM',
subtitle: '客户关系管理',
icon: 'TrendCharts',
},
},
{
path: '/dealer',
name: 'dealer',
component: () => import('@/pages/dealer/index.vue'),
meta: {
title: '经销商管理',
subtitle: '经销商信息管理',
icon: 'Shop',
},
},
```
**无需修改,直接扩展现有页面即可。**
---
## 14.3 数据库初始化
**SQL 文件:** `hzhub-system/src/main/resources/db/crm_init.sql`
包含:
- CRM 表结构crm_lead, crm_dealer, crm_opportunity 等)
- 数据字典crm_lead_source, crm_opportunity_stage 等)
- 菜单配置sys_menu - 告知管理员在后台配置)
---
# 15. 核心差异总结(员工门户 vs 管理后台)
| 方面 | 管理后台V2 | 员工门户V3 |
|---|---|---|
| 前端项目 | hzhub-admin | **hzhub-portal-employee** |
| UI框架 | Vben Admin + Ant Design Vue | **Element Plus** |
| 现有基础 | 无,需要新建 | **已有CRM和经销商页面** |
| 开发方式 | 新建独立页面 | **扩展现有页面** |
| 数据来源 | 独立CRM表 | **ERP客户数据 + CRM表** |
| 用户角色 | 管理员 + 销售 | **企业员工 + 销售人员** |
| 权限控制 | Sa-Token注解 | **Gateway统一控制** |
| 移动端 | 企业微信H5新建 | **企业微信H5员工门户支持** |
---
# 总结
本文档将 CRM 销售自动化系统完全适配到 **员工门户hzhub-portal-employee**,充分利用:
1. **现有页面**`/crm`销售CRM`/dealer`(经销商)已存在,直接扩展
2. **ERP数据集成**:关联 `customer_code`,实时拉取 ERP 客户信息
3. **Element Plus UI**使用卡片式、Timeline、Drawer等组件
4. **Vue3 Composition API**`<script setup>` + TypeScript
5. **hook-fetch请求**统一API调用方式
核心改动:
1. 前端项目:**hzhub-portal-employee**(不是 hzhub-admin
2. UI组件**Element Plus**(不是 Ant Design Vue
3. 开发方式:**扩展现有页面**(不是新建)
4. 数据关联:**ERP Customer + CRM Lead**(双数据源)
5. 路由配置:**无需修改**(直接使用现有路由)
后续实施时,严格按照本文档执行,确保与员工门户现有功能的一致性。