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>
This commit is contained in:
272
docs/crm-bug-fix-report.md
Normal file
272
docs/crm-bug-fix-report.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# CRM线索管理功能问题修复报告
|
||||
|
||||
## 发现的问题
|
||||
|
||||
### 问题1:新增线索时缺少手机号格式验证
|
||||
|
||||
**问题描述**:
|
||||
- 后端:CrmLeadBo缺少手机号格式校验注解
|
||||
- 前端:新建线索表单缺少手机号格式验证
|
||||
|
||||
**影响**:
|
||||
- 用户可以输入任意格式的手机号,导致数据质量下降
|
||||
|
||||
---
|
||||
|
||||
### 问题2:负责人显示为手机号而非姓名
|
||||
|
||||
**问题描述**:
|
||||
- UserNameTranslationImpl翻译类调用userService.selectUserNameById返回userName(登录账号)
|
||||
- 应该调用userService.selectNicknameById返回nickName(用户昵称/真实姓名)
|
||||
|
||||
**影响**:
|
||||
- 线索列表负责人列显示登录账号(如admin),而不是用户真实姓名(如管理员)
|
||||
- 用户期望看到负责人姓名,而非登录账号
|
||||
|
||||
---
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 问题1修复:手机号格式验证
|
||||
|
||||
#### 后端修复
|
||||
|
||||
**修改文件**:`hzhub-system/src/main/java/org/hzhub/crm/domain/bo/CrmLeadBo.java`
|
||||
|
||||
**修改内容**:
|
||||
```java
|
||||
// 导入Pattern注解
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
// mobile字段添加正则验证
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
@Size(min = 0, max = 50, message = "手机号长度不能超过{max}个字符")
|
||||
private String mobile;
|
||||
```
|
||||
|
||||
**正则表达式说明**:
|
||||
- `^1` - 以1开头
|
||||
- `[3-9]` - 第二位为3-9
|
||||
- `\d{9}` - 后面9位为数字
|
||||
- `$` - 结尾
|
||||
|
||||
验证中国手机号格式:13x, 14x, 15x, 16x, 17x, 18x, 19x开头的11位数字
|
||||
|
||||
---
|
||||
|
||||
#### 前端修复
|
||||
|
||||
**修改文件**:`hzhub-portal-employee/src/pages/crm/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
|
||||
1. **添加手机号验证函数**:
|
||||
```typescript
|
||||
// 手机号验证规则
|
||||
const mobileValidator = (value: string) => {
|
||||
if (!value) {
|
||||
return '请输入手机号';
|
||||
}
|
||||
const mobileRegex = /^1[3-9]\d{9}$/;
|
||||
if (!mobileRegex.test(value)) {
|
||||
return '手机号格式不正确';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
```
|
||||
|
||||
2. **修改submitLead方法添加验证**:
|
||||
```typescript
|
||||
async function submitLead() {
|
||||
if (!leadForm.value.companyName || !leadForm.value.contactName || !leadForm.value.mobile) {
|
||||
ElMessage.warning('请填写必填信息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
const mobileError = mobileValidator(leadForm.value.mobile);
|
||||
if (mobileError) {
|
||||
ElMessage.warning(mobileError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createLead(leadForm.value);
|
||||
ElMessage.success('线索创建成功');
|
||||
showAddLeadDialog.value = false;
|
||||
await loadLeads();
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error?.message || '创建线索失败');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 问题2修复:负责人显示姓名
|
||||
|
||||
#### 后端修复
|
||||
|
||||
**修改文件**:`hzhub-ai/hzhub-common/hzhub-common-translation/src/main/java/org/hzhub/common/translation/core/impl/UserNameTranslationImpl.java`
|
||||
|
||||
**修改内容**:
|
||||
```java
|
||||
@Override
|
||||
public String translation(Object key, String other) {
|
||||
if (key instanceof Long id) {
|
||||
// 返回用户昵称而不是登录账号
|
||||
return userService.selectNicknameById(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**修复说明**:
|
||||
- 从调用`selectUserNameById`改为调用`selectNicknameById`
|
||||
- `selectNicknameById`返回用户昵称(真实姓名),而不是登录账号
|
||||
- 使用缓存`@Cacheable(cacheNames = CacheNames.SYS_NICKNAME)`,性能更好
|
||||
|
||||
**影响范围**:
|
||||
- 所有使用`USER_ID_TO_NAME`翻译的地方都会显示用户昵称
|
||||
- 包括:createBy, updateBy, ownerUserId, followUserId等字段
|
||||
|
||||
---
|
||||
|
||||
## 编译和部署
|
||||
|
||||
### 编译步骤
|
||||
|
||||
```bash
|
||||
# 1. 编译hzhub-ai(包含translation模块修改)
|
||||
cd /data/hzhub/hzhub-ai
|
||||
mvn clean install -DskipTests
|
||||
|
||||
# 2. 编译hzhub-system(包含CrmLeadBo修改)
|
||||
cd /data/hzhub/hzhub-system
|
||||
mvn clean compile -DskipTests
|
||||
|
||||
# 3. 重启所有服务
|
||||
cd /data/hzhub
|
||||
./restart-all.sh
|
||||
```
|
||||
|
||||
**编译结果**:
|
||||
- ✅ hzhub-ai: BUILD SUCCESS (55.753s)
|
||||
- ✅ hzhub-system: BUILD SUCCESS (19.007s)
|
||||
|
||||
---
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 测试1:手机号格式验证
|
||||
|
||||
#### 后端验证测试
|
||||
|
||||
使用Postman或curl测试:
|
||||
|
||||
```bash
|
||||
POST http://localhost:8080/crm/lead
|
||||
Headers:
|
||||
Authorization: Bearer {token}
|
||||
ClientID: employee-portal
|
||||
Body:
|
||||
{
|
||||
"companyName": "测试公司",
|
||||
"contactName": "张三",
|
||||
"mobile": "12345678901", // 错误格式
|
||||
"sourceType": "activity"
|
||||
}
|
||||
```
|
||||
|
||||
**预期响应**:
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "手机号格式不正确"
|
||||
}
|
||||
```
|
||||
|
||||
#### 前端验证测试
|
||||
|
||||
**测试步骤**:
|
||||
1. 登录员工门户:http://localhost:5137
|
||||
2. 导航到"销售CRM" → "线索管理"
|
||||
3. 点击"新建线索"
|
||||
4. 输入错误手机号:12345678901
|
||||
5. 点击"创建线索"
|
||||
|
||||
**预期结果**:
|
||||
- 显示提示:"手机号格式不正确"
|
||||
- 表单不提交
|
||||
|
||||
---
|
||||
|
||||
### 测试2:负责人显示姓名
|
||||
|
||||
**测试步骤**:
|
||||
1. 创建线索并分配负责人
|
||||
2. 查看线索列表负责人列
|
||||
|
||||
**预期结果**:
|
||||
- 负责人列显示用户真实姓名(如"管理员")
|
||||
- 不显示登录账号(如"admin")
|
||||
|
||||
**后端验证**:
|
||||
|
||||
```sql
|
||||
-- 查询用户数据
|
||||
SELECT user_id, user_name, nick_name, phonenumber FROM sys_user WHERE user_id = 1;
|
||||
|
||||
-- 预期结果:
|
||||
-- user_name: admin (登录账号)
|
||||
-- nick_name: 管理员 (真实姓名)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 修复影响范围
|
||||
|
||||
### 手机号验证
|
||||
|
||||
**影响模块**:
|
||||
- CRM线索管理(新建、编辑线索)
|
||||
- 未来可能影响:经销商管理、客户管理
|
||||
|
||||
**数据质量提升**:
|
||||
- 防止错误手机号数据进入系统
|
||||
- 提升线索数据质量
|
||||
|
||||
---
|
||||
|
||||
### 负责人显示
|
||||
|
||||
**影响模块**:
|
||||
- CRM线索管理(负责人显示)
|
||||
- CRM经销商管理(负责人显示)
|
||||
- 系统通知管理(创建人、更新人显示)
|
||||
- OSS对象存储(创建人显示)
|
||||
- 跟进记录(跟进人显示)
|
||||
|
||||
**用户体验提升**:
|
||||
- 显示真实姓名,更符合用户认知
|
||||
- 避免混淆登录账号和真实姓名
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [CRM线索管理测试指引](docs/crm-testing-guide.md)
|
||||
- [CRM线索转化测试指引](docs/crm-convert-testing-guide.md)
|
||||
- [CRM线索分配删除测试指引](docs/crm-assign-delete-testing-guide.md)
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
两个问题均已修复:
|
||||
1. ✅ 手机号格式验证(后端Pattern注解 + 前端验证函数)
|
||||
2. ✅ 负责人显示姓名(修改UserNameTranslationImpl返回昵称)
|
||||
|
||||
请重新测试验证功能。
|
||||
Reference in New Issue
Block a user