模块: 10-客户回访管理
版本: v1.0
日期: 2025-10-03
关联文档:
- 需求文档:
docs/10-客户回访管理模块需求.md- 原型图:
yuanxing/10-客户回访管理原型图.md- 数据库:
sql/crm/10-crm_visit.sql
接口地址: GET /crm/visit/list
接口描述: 分页查询回访记录列表,支持多条件筛选
权限标识: crm:visit:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | visitNo | String | 否 | 回访编号(模糊查询) | | customerId | Long | 否 | 客户ID | | customerName | String | 否 | 客户名称(模糊查询) | | contractId | Long | 否 | 合同ID | | contactId | Long | 否 | 联系人ID | | visitType | String | 否 | 回访类型 | | visitMethod | String | 否 | 回访方式 | | status | String | 否 | 回访状态 | | satisfaction | String | 否 | 满意度 | | hasProblem | Integer | 否 | 是否有问题(0否 1是) | | hasOpportunity | Integer | 否 | 是否有商机(0否 1是) | | ownerUserId | Long | 否 | 负责人ID | | visitDateBegin | String | 否 | 回访时间开始 | | visitDateEnd | String | 否 | 回访时间结束 | | beginTime | String | 否 | 创建开始时间 | | endTime | String | 否 | 创建结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"visitNo": "VIS20250125001",
"customerId": 1,
"customerName": "华为技术有限公司",
"contractId": 1,
"contractNo": "CON20250115001",
"contactId": 1,
"contactName": "张伟",
"contactMobile": "13800138000",
"visitType": "delivery",
"visitDate": "2025-01-25 14:00:00",
"visitMethod": "phone",
"visitContent": "回访CRM系统部署情况,了解使用中的问题",
"visitSummary": "客户对系统整体满意,提出了2个功能优化建议",
"satisfaction": "satisfied",
"satisfactionScore": 4,
"hasProblem": 1,
"problemCount": 2,
"problemSummary": "权限设置复杂、报表导出慢",
"hasOpportunity": 1,
"opportunityDesc": "客户有意向采购移动端APP",
"opportunityAmount": 80000.00,
"nextVisitDate": "2025-02-25",
"nextVisitContent": "跟进功能优化进度",
"status": "visited",
"ownerUserId": 1,
"ownerUserName": "张三",
"duration": 45,
"attachments": "/upload/visit/file1.pdf",
"createTime": "2025-01-25 14:00:00",
"remark": ""
}
],
"total": 89
}
接口地址: GET /crm/visit/{id}
接口描述: 根据ID查询回访记录详细信息
权限标识: crm:visit:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 回访记录ID |
返回示例: 同1.1列表返回的单条数据,包含完整字段
接口地址: POST /crm/visit
接口描述: 新增回访记录信息
权限标识: crm:visit:add
请求体:
{
"visitNo": "VIS20250125001",
"customerId": 1,
"contractId": 1,
"contactId": 1,
"visitType": "delivery",
"visitDate": "2025-01-25 14:00:00",
"visitMethod": "phone",
"visitContent": "回访CRM系统部署情况,了解使用中的问题",
"visitSummary": "客户对系统整体满意,提出了2个功能优化建议",
"satisfaction": "satisfied",
"satisfactionScore": 4,
"hasProblem": 1,
"problemSummary": "权限设置复杂、报表导出慢",
"hasOpportunity": 1,
"opportunityDesc": "客户有意向采购移动端APP",
"opportunityAmount": 80000.00,
"nextVisitDate": "2025-02-25",
"nextVisitContent": "跟进功能优化进度",
"ownerUserId": 1,
"duration": 45,
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | visitNo | String | 是 | 回访编号,唯一,格式:VIS+yyyyMMdd+4位流水号 | | customerId | Long | 是 | 客户ID | | contractId | Long | 否 | 合同ID | | contactId | Long | 否 | 联系人ID | | visitType | String | 是 | 回访类型:contract/delivery/regular/renew/complaint/other | | visitDate | String | 是 | 回访时间 yyyy-MM-dd HH:mm:ss | | visitMethod | String | 是 | 回访方式:phone/wechat/email/visit/other | | visitContent | String | 是 | 回访内容,文本类型 | | visitSummary | String | 否 | 回访小结,最大1000字符 | | satisfaction | String | 否 | 满意度:very_satisfied/satisfied/general/dissatisfied/very_dissatisfied | | satisfactionScore | Integer | 否 | 满意度评分:1-5分 | | hasProblem | Integer | 否 | 是否有问题:0-否,1-是 | | problemSummary | String | 否 | 问题概述,最大500字符 | | hasOpportunity | Integer | 否 | 是否有商机:0-否,1-是 | | opportunityDesc | String | 否 | 商机描述,文本类型 | | opportunityAmount | BigDecimal | 否 | 预估商机金额,保留2位小数 | | nextVisitDate | String | 否 | 下次回访日期 yyyy-MM-dd | | nextVisitContent | String | 否 | 下次回访计划,最大500字符 | | ownerUserId | Long | 否 | 负责人ID,默认当前用户 | | duration | Integer | 否 | 回访时长(分钟) | | attachments | String | 否 | 附件路径,多个用逗号分隔 | | remark | String | 否 | 备注,最大500字符 |
校验规则:
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"visitNo": "VIS20250125001"
}
}
接口地址: PUT /crm/visit
接口描述: 修改回访记录信息
权限标识: crm:visit:edit
请求体: 同新增接口,但需要传入id
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: DELETE /crm/visit/{ids}
接口描述: 删除回访记录(支持批量)
权限标识: crm:visit:remove
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 回访记录ID数组 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: POST /crm/visit/plan
接口描述: 创建回访计划
权限标识: crm:visit:add
请求体:
{
"visitNo": "VIS20250210001",
"customerId": 1,
"contractId": 1,
"contactId": 1,
"visitType": "regular",
"visitDate": "2025-02-10 14:00:00",
"visitMethod": "phone",
"ownerUserId": 1,
"remark": "定期回访"
}
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/complete
接口描述: 完成计划的回访
权限标识: crm:visit:edit
请求体:
{
"id": 1,
"visitContent": "回访内容...",
"visitSummary": "回访小结...",
"satisfaction": "satisfied",
"satisfactionScore": 4,
"hasProblem": 1,
"problemSummary": "有2个问题",
"duration": 30,
"remark": ""
}
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/cancel
接口描述: 取消计划的回访
权限标识: crm:visit:edit
请求体:
{
"id": 1,
"cancelReason": "客户临时有事,延期回访",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 回访记录ID | | cancelReason | String | 是 | 取消原因,最大500字符 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: GET /crm/visit/customer/{customerId}
接口描述: 查询指定客户的所有回访记录
权限标识: crm:visit:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"visitNo": "VIS20250125001",
"visitType": "delivery",
"visitDate": "2025-01-25 14:00:00",
"satisfaction": "satisfied",
"status": "visited"
}
]
}
接口地址: GET /crm/visit/contract/{contractId}
接口描述: 查询指定合同的所有回访记录
权限标识: crm:visit:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractId | Long | 是 | 合同ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"visitNo": "VIS20250125001",
"visitType": "delivery",
"visitDate": "2025-01-25 14:00:00",
"satisfaction": "satisfied",
"status": "visited"
}
]
}
接口地址: POST /crm/visit/export
接口描述: 导出回访记录数据到Excel
权限标识: crm:visit:export
请求体: 查询参数同1.1列表查询接口
返回: Excel文件流
接口地址: GET /crm/visit/problem/list
接口描述: 查询指定回访的问题列表
权限标识: crm:visit:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | visitId | Long | 是 | 回访ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"visitId": 1,
"visitNo": "VIS20250125001",
"customerId": 1,
"customerName": "华为技术有限公司",
"problemType": "product",
"problemDesc": "权限设置界面操作复杂,建议简化",
"problemImages": "/upload/problem/img1.jpg",
"severity": "medium",
"solution": "已安排产品部门优化权限设置界面",
"status": "processing",
"solveBy": 2,
"solveByName": "李四",
"solveTime": "2025-01-26 10:00:00",
"createTime": "2025-01-25 15:00:00",
"remark": ""
}
]
}
接口地址: POST /crm/visit/problem
接口描述: 添加回访问题
权限标识: crm:visit:edit
请求体:
{
"visitId": 1,
"customerId": 1,
"problemType": "product",
"problemDesc": "权限设置界面操作复杂,建议简化",
"problemImages": "/upload/problem/img1.jpg",
"severity": "medium",
"solution": "已安排产品部门优化权限设置界面",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | visitId | Long | 是 | 回访ID | | customerId | Long | 是 | 客户ID | | problemType | String | 是 | 问题类型:product/service/delivery/quality/price/other | | problemDesc | String | 是 | 问题描述,文本类型 | | problemImages | String | 否 | 问题图片路径,多个用逗号分隔 | | severity | String | 是 | 严重程度:low/medium/high/urgent | | solution | String | 否 | 解决方案,文本类型 | | remark | String | 否 | 备注,最大500字符 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/problem
接口描述: 修改回访问题信息
权限标识: crm:visit:edit
请求体: 同添加接口,但需要传入id
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: DELETE /crm/visit/problem/{ids}
接口描述: 删除回访问题(支持批量)
权限标识: crm:visit:edit
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 问题ID数组 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/problem/solve
接口描述: 处理回访问题
权限标识: crm:visit:problem:solve
请求体:
{
"id": 1,
"solution": "已安排产品部门优化权限设置界面,预计下周完成",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 问题ID | | solution | String | 是 | 解决方案,文本类型 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/problem/resolve
接口描述: 标记问题已解决
权限标识: crm:visit:problem:solve
请求体:
{
"id": 1,
"customerFeedback": "客户确认问题已解决",
"isSatisfied": 1,
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 问题ID | | customerFeedback | String | 否 | 客户反馈,文本类型 | | isSatisfied | Integer | 否 | 客户是否满意:0-否,1-是 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/visit/problem/close
接口描述: 关闭问题
权限标识: crm:visit:problem:solve
请求体:
{
"id": 1,
"closeReason": "客户不再关注此问题",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 问题ID | | closeReason | String | 是 | 关闭原因,最大500字符 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: GET /crm/visit/statistics/overview
接口描述: 获取回访统计概览数据
权限标识: crm:visit:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ownerUserId | Long | 否 | 负责人ID,不传则查询当前用户 | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": {
"totalCount": 89,
"plannedCount": 12,
"visitedCount": 72,
"canceledCount": 5,
"problemCount": 156,
"solvedCount": 128,
"opportunityCount": 23,
"opportunityAmount": 1850000.00,
"avgSatisfactionScore": 4.2,
"verySatisfiedRate": 45.83,
"satisfiedRate": 38.89,
"generalRate": 12.50,
"dissatisfiedRate": 2.78
}
}
接口地址: GET /crm/visit/statistics/satisfaction
接口描述: 按满意度统计回访分布情况
权限标识: crm:visit:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"satisfaction": "very_satisfied",
"satisfactionName": "非常满意",
"count": 33,
"percent": 45.83
},
{
"satisfaction": "satisfied",
"satisfactionName": "满意",
"count": 28,
"percent": 38.89
}
]
}
接口地址: GET /crm/visit/statistics/type
接口描述: 按回访类型统计分布情况
权限标识: crm:visit:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"visitType": "delivery",
"typeName": "交付回访",
"count": 35,
"percent": 48.61
},
{
"visitType": "regular",
"typeName": "定期回访",
"count": 25,
"percent": 34.72
}
]
}
接口地址: GET /crm/visit/statistics/problem
接口描述: 按问题类型统计问题分布情况
权限标识: crm:visit:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"problemType": "product",
"typeName": "产品问题",
"count": 58,
"solvedCount": 45,
"solvedRate": 77.59,
"percent": 37.18
},
{
"problemType": "service",
"typeName": "服务问题",
"count": 42,
"solvedCount": 38,
"solvedRate": 90.48,
"percent": 26.92
}
]
}
接口地址: GET /crm/visit/statistics/trend
接口描述: 分析回访数量和满意度趋势
权限标识: crm:visit:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 统计类型:month/quarter/year,默认month | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"period": "2025-01",
"totalCount": 28,
"problemCount": 45,
"solvedCount": 38,
"opportunityCount": 8,
"avgSatisfactionScore": 4.3,
"satisfiedRate": 85.71
}
]
}
接口地址: GET /crm/visit/statistics/owner
接口描述: 按负责人统计回访情况
权限标识: crm:visit:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"ownerUserId": 1,
"ownerUserName": "张三",
"totalCount": 25,
"problemCount": 38,
"solvedCount": 32,
"opportunityCount": 6,
"avgSatisfactionScore": 4.4
}
]
}
接口地址: GET /crm/visit/statistics/reminder
接口描述: 查询即将到期的回访计划
权限标识: crm:visit:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | days | Integer | 否 | 提前天数,默认3天 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 5,
"visitNo": "VIS20250210001",
"customerName": "华为技术有限公司",
"visitType": "regular",
"visitDate": "2025-02-10 14:00:00",
"remainDays": 2,
"ownerUserName": "张三"
}
]
}
public class CrmVisitBo extends BaseEntity {
/** 主键ID */
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/** 回访编号 */
@NotBlank(message = "回访编号不能为空", groups = { AddGroup.class, EditGroup.class })
private String visitNo;
/** 客户ID */
@NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long customerId;
/** 合同ID */
private Long contractId;
/** 联系人ID */
private Long contactId;
/** 回访类型 */
@NotBlank(message = "回访类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String visitType;
/** 回访时间 */
@NotBlank(message = "回访时间不能为空", groups = { AddGroup.class, EditGroup.class })
private String visitDate;
/** 回访方式 */
@NotBlank(message = "回访方式不能为空", groups = { AddGroup.class, EditGroup.class })
private String visitMethod;
/** 回访内容 */
@NotBlank(message = "回访内容不能为空", groups = { AddGroup.class, EditGroup.class })
private String visitContent;
/** 回访小结 */
private String visitSummary;
/** 满意度 */
private String satisfaction;
/** 满意度评分 */
@Min(value = 1, message = "满意度评分最小为1")
@Max(value = 5, message = "满意度评分最大为5")
private Integer satisfactionScore;
/** 是否有问题 */
private Integer hasProblem;
/** 问题概述 */
private String problemSummary;
/** 是否有商机 */
private Integer hasOpportunity;
/** 商机描述 */
private String opportunityDesc;
/** 预估商机金额 */
private BigDecimal opportunityAmount;
/** 下次回访日期 */
private String nextVisitDate;
/** 下次回访计划 */
private String nextVisitContent;
/** 负责人ID */
private Long ownerUserId;
/** 回访时长(分钟) */
private Integer duration;
/** 附件 */
private String attachments;
/** 备注 */
private String remark;
}
public class CrmVisitProblemBo extends BaseEntity {
/** 主键ID */
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/** 回访ID */
@NotNull(message = "回访ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long visitId;
/** 客户ID */
@NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long customerId;
/** 问题类型 */
@NotBlank(message = "问题类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String problemType;
/** 问题描述 */
@NotBlank(message = "问题描述不能为空", groups = { AddGroup.class, EditGroup.class })
private String problemDesc;
/** 问题图片 */
private String problemImages;
/** 严重程度 */
@NotBlank(message = "严重程度不能为空", groups = { AddGroup.class, EditGroup.class })
private String severity;
/** 解决方案 */
private String solution;
/** 客户反馈 */
private String customerFeedback;
/** 客户是否满意 */
private Integer isSatisfied;
/** 备注 */
private String remark;
}
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| contract | 合同回访 | 合同签订后回访 |
| delivery | 交付回访 | 产品交付后回访 |
| regular | 定期回访 | 定期客户维护 |
| renew | 续约回访 | 合同到期续约 |
| complaint | 投诉回访 | 客户投诉处理 |
| other | 其他 | 其他类型回访 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| phone | 电话 | 电话回访 |
| 微信 | 微信回访 | |
| 邮件 | 邮件回访 | |
| visit | 上门拜访 | 现场回访 |
| other | 其他 | 其他方式 |
| 字典值 | 字典标签 | 评分 |
|---|---|---|
| very_satisfied | 非常满意 | 5分 |
| satisfied | 满意 | 4分 |
| general | 一般 | 3分 |
| dissatisfied | 不满意 | 2分 |
| very_dissatisfied | 非常不满意 | 1分 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| planned | 已计划 | 计划中 |
| visited | 已回访 | 已完成 |
| canceled | 已取消 | 已取消 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| product | 产品问题 | 产品功能问题 |
| service | 服务问题 | 服务质量问题 |
| delivery | 交付问题 | 交付延期等 |
| quality | 质量问题 | 产品质量问题 |
| price | 价格问题 | 价格相关问题 |
| other | 其他 | 其他问题 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| low | 低 | 影响较小 |
| medium | 中 | 一般影响 |
| high | 高 | 影响较大 |
| urgent | 紧急 | 需立即处理 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| pending | 待处理 | 尚未处理 |
| processing | 处理中 | 正在处理 |
| solved | 已解决 | 已经解决 |
| closed | 已关闭 | 已关闭 |
| 权限标识 | 说明 |
|---|---|
| crm:visit:list | 回访记录列表查询 |
| crm:visit:query | 回访记录详情查询 |
| crm:visit:add | 回访记录新增 |
| crm:visit:edit | 回访记录修改 |
| crm:visit:remove | 回访记录删除 |
| crm:visit:export | 回访记录导出 |
| crm:visit:problem:solve | 回访问题处理 |
文档结束