# 商机管理模块 - API接口文档 > **模块**: 04-商机管理 > **版本**: v1.0 > **日期**: 2025-10-03 > **关联文档**: > - 需求文档: `docs/04-商机管理模块需求.md` > - 原型图: `yuanxing/04-商机管理原型图.md` > - 数据库: `sql/crm/04-crm_opportunity.sql` --- ## 📋 目录 - [1. 商机管理接口](#1-商机管理接口) - [2. 商机产品接口](#2-商机产品接口) - [3. 商机跟进记录接口](#3-商机跟进记录接口) - [4. 商机阶段日志接口](#4-商机阶段日志接口) - [5. 统计分析接口](#5-统计分析接口) - [6. 数据模型](#6-数据模型) --- ## 1. 商机管理接口 ### 1.1 查询商机列表(分页) **接口地址**: `GET /crm/opportunity/list` **接口描述**: 分页查询商机列表,支持多条件筛选 **权限标识**: `crm:opportunity:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | opportunityName | String | 否 | 商机名称(模糊查询) | | opportunityNo | String | 否 | 商机编号(模糊查询) | | customerId | Long | 否 | 客户ID | | customerName | String | 否 | 客户名称(模糊查询) | | stage | String | 否 | 商机阶段 | | status | String | 否 | 商机状态 | | ownerUserId | Long | 否 | 负责人ID | | minAmount | BigDecimal | 否 | 最小金额 | | maxAmount | BigDecimal | 否 | 最大金额 | | source | String | 否 | 商机来源 | | expectDealDateBegin | String | 否 | 预计成交日期开始 | | expectDealDateEnd | String | 否 | 预计成交日期结束 | | beginTime | String | 否 | 创建开始时间 | | endTime | String | 否 | 创建结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 1, "opportunityNo": "OPP20250101001", "opportunityName": "华为CRM系统采购项目", "customerId": 1, "customerName": "华为技术有限公司", "contactId": 1, "contactName": "张伟", "amount": 150000.00, "stage": "quote", "stagePercent": 60, "status": "following", "winRate": 75.00, "expectDealDate": "2025-03-31", "actualDealDate": null, "ownerUserId": 1, "ownerUserName": "张三", "productCount": 2, "lastTime": "2025-01-02 10:30:00", "nextTime": "2025-01-05 14:00:00", "followCount": 8, "contractId": null, "source": "website", "createTime": "2024-12-01 09:00:00", "remark": "" } ], "total": 89 } ``` --- ### 1.2 查询商机详情 **接口地址**: `GET /crm/opportunity/{id}` **接口描述**: 根据ID查询商机详细信息 **权限标识**: `crm:opportunity:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 商机ID | **返回示例**: 同1.1列表返回的单条数据,包含完整字段 --- ### 1.3 新增商机 **接口地址**: `POST /crm/opportunity` **接口描述**: 新增商机信息 **权限标识**: `crm:opportunity:add` **请求体**: ```json { "opportunityNo": "OPP20250101001", "opportunityName": "华为CRM系统采购项目", "customerId": 1, "contactId": 1, "amount": 150000.00, "stage": "initial", "expectDealDate": "2025-03-31", "winRate": 50.00, "ownerUserId": 1, "source": "website", "nextTime": "2025-01-05 14:00:00", "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityNo | String | 是 | 商机编号,唯一,格式:OPP+yyyyMMdd+4位流水号 | | opportunityName | String | 是 | 商机名称,最大200字符 | | customerId | Long | 是 | 客户ID | | contactId | Long | 否 | 联系人ID | | amount | BigDecimal | 是 | 商机金额,保留2位小数 | | stage | String | 否 | 商机阶段,默认initial | | expectDealDate | String | 否 | 预计成交日期,格式yyyy-MM-dd | | winRate | BigDecimal | 否 | 赢单率,百分比,默认50.00 | | ownerUserId | Long | 否 | 负责人ID,默认当前用户 | | source | String | 否 | 商机来源 | | nextTime | String | 否 | 下次联系时间 yyyy-MM-dd HH:mm:ss | | remark | String | 否 | 备注,最大500字符 | **校验规则**: - 商机编号必须唯一 - 商机金额必须大于0 - 客户ID必须存在 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.4 修改商机 **接口地址**: `PUT /crm/opportunity` **接口描述**: 修改商机信息 **权限标识**: `crm:opportunity:edit` **请求体**: 同新增接口,但需要传入id **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.5 删除商机 **接口地址**: `DELETE /crm/opportunity/{ids}` **接口描述**: 删除商机(支持批量) **权限标识**: `crm:opportunity:remove` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 商机ID数组 | **业务规则**: - 已赢单的商机不允许删除 - 关联了合同的商机不允许删除 - 删除商机时同时删除关联的产品、跟进记录、阶段日志 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.6 修改商机阶段 **接口地址**: `PUT /crm/opportunity/stage` **接口描述**: 修改商机阶段,记录阶段日志 **权限标识**: `crm:opportunity:edit` **请求体**: ```json { "id": 1, "stage": "quote", "stagePercent": 60, "winRate": 75.00, "changeReason": "客户已确认需求,准备报价", "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 商机ID | | stage | String | 是 | 新阶段:initial/requirement/solution/quote/negotiate/contract/won | | stagePercent | Integer | 是 | 阶段进度:10/20/40/60/80/90/100 | | winRate | BigDecimal | 否 | 赢单率 | | changeReason | String | 否 | 变更原因 | | remark | String | 否 | 备注 | **业务规则**: - 自动记录阶段变更日志 - 自动判断是前进还是后退 - 赢单后状态自动变为won **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.7 赢单 **接口地址**: `PUT /crm/opportunity/win` **接口描述**: 将商机标记为赢单 **权限标识**: `crm:opportunity:edit` **请求体**: ```json { "id": 1, "actualDealDate": "2025-01-10", "actualAmount": 148000.00, "remark": "成功签约" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 商机ID | | actualDealDate | String | 是 | 实际成交日期 yyyy-MM-dd | | actualAmount | BigDecimal | 否 | 实际成交金额(如果与预估不同) | | remark | String | 否 | 备注说明 | **业务规则**: - 商机状态变更为won - 阶段自动设置为won - 进度设置为100% - 可选择是否自动创建合同 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.8 输单 **接口地址**: `PUT /crm/opportunity/lost` **接口描述**: 将商机标记为输单 **权限标识**: `crm:opportunity:edit` **请求体**: ```json { "id": 1, "lostReason": "价格因素", "remark": "客户选择了竞争对手的产品" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 商机ID | | lostReason | String | 是 | 输单原因,最大500字符 | | remark | String | 否 | 备注说明 | **业务规则**: - 商机状态变更为lost - 记录输单原因 - 赢单率设置为0 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.9 批量分配商机 **接口地址**: `PUT /crm/opportunity/assign` **接口描述**: 批量分配商机给指定负责人 **权限标识**: `crm:opportunity:assign` **请求体**: ```json { "ids": [1, 2, 3], "ownerUserId": 5, "remark": "分配原因" } ``` **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.10 导出商机 **接口地址**: `POST /crm/opportunity/export` **接口描述**: 导出商机数据到Excel **权限标识**: `crm:opportunity:export` **请求体**: 查询参数同1.1列表查询接口 **返回**: Excel文件流 --- ## 2. 商机产品接口 ### 2.1 查询商机产品列表 **接口地址**: `GET /crm/opportunity/product/list` **接口描述**: 查询指定商机的产品列表 **权限标识**: `crm:opportunity:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityId | Long | 是 | 商机ID | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "id": 1, "opportunityId": 1, "productId": 1, "productName": "CRM系统标准版", "productNo": "PRD20250101001", "categoryId": 11, "categoryName": "CRM系统", "model": "V5.4.0", "unit": "套", "quantity": 1, "price": 12800.00, "discount": 10.00, "subtotal": 11520.00, "sort": 1, "remark": "" } ] } ``` --- ### 2.2 添加商机产品 **接口地址**: `POST /crm/opportunity/product` **接口描述**: 向商机添加产品 **权限标识**: `crm:opportunity:edit` **请求体**: ```json { "opportunityId": 1, "productId": 1, "quantity": 1, "price": 12800.00, "discount": 10.00, "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityId | Long | 是 | 商机ID | | productId | Long | 是 | 产品ID | | quantity | Integer | 是 | 数量,必须大于0 | | price | BigDecimal | 是 | 单价,保留2位小数 | | discount | BigDecimal | 否 | 折扣百分比,默认0 | | remark | String | 否 | 备注 | **业务规则**: - 自动计算小计金额:quantity * price * (1 - discount/100) - 添加产品后自动更新商机的productCount - 自动更新商机总金额 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.3 修改商机产品 **接口地址**: `PUT /crm/opportunity/product` **接口描述**: 修改商机产品信息 **权限标识**: `crm:opportunity:edit` **请求体**: 同添加接口,但需要传入id **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.4 删除商机产品 **接口地址**: `DELETE /crm/opportunity/product/{ids}` **接口描述**: 删除商机产品(支持批量) **权限标识**: `crm:opportunity:edit` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 商机产品ID数组 | **业务规则**: - 删除后自动更新商机的productCount - 自动更新商机总金额 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ## 3. 商机跟进记录接口 ### 3.1 查询跟进记录列表 **接口地址**: `GET /crm/opportunity/follow/list` **接口描述**: 查询指定商机的跟进记录列表 **权限标识**: `crm:opportunity:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityId | Long | 是 | 商机ID | | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 1, "opportunityId": 1, "followType": "phone", "followContent": "电话沟通了客户需求,客户对CRM系统很感兴趣", "stage": "requirement", "stagePercent": 20, "nextTime": "2025-01-08 14:00:00", "nextContent": "下次需要进行产品演示", "attachments": "/upload/2025/01/file1.pdf", "contactId": 1, "contactName": "张伟", "location": null, "duration": 30, "createBy": 1, "createByName": "张三", "createTime": "2025-01-05 10:30:00", "remark": "" } ], "total": 8 } ``` --- ### 3.2 新增跟进记录 **接口地址**: `POST /crm/opportunity/follow` **接口描述**: 新增商机跟进记录 **权限标识**: `crm:opportunity:follow` **请求体**: ```json { "opportunityId": 1, "followType": "phone", "followContent": "电话沟通了客户需求", "stage": "requirement", "stagePercent": 20, "nextTime": "2025-01-08 14:00:00", "nextContent": "下次需要进行产品演示", "attachments": "/upload/2025/01/file1.pdf", "contactId": 1, "duration": 30, "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityId | Long | 是 | 商机ID | | followType | String | 是 | 跟进方式:phone/wechat/email/visit/meeting/other | | followContent | String | 是 | 跟进内容,文本类型 | | stage | String | 否 | 当前商机阶段 | | stagePercent | Integer | 否 | 阶段进度百分比 | | nextTime | String | 否 | 下次联系时间 yyyy-MM-dd HH:mm:ss | | nextContent | String | 否 | 下次联系计划,最大500字符 | | attachments | String | 否 | 附件路径,多个用逗号分隔 | | contactId | Long | 否 | 联系人ID | | location | String | 否 | 跟进地点(拜访时填写) | | duration | Integer | 否 | 跟进时长(分钟) | | remark | String | 否 | 备注 | **业务规则**: - 新增跟进记录后,自动更新商机的lastTime和followCount - 如果设置了nextTime,同步更新商机的nextTime **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 3.3 修改跟进记录 **接口地址**: `PUT /crm/opportunity/follow` **接口描述**: 修改跟进记录信息 **权限标识**: `crm:opportunity:follow` **请求体**: 同新增接口,但需要传入id **业务规则**: - 只能修改自己创建的跟进记录 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 3.4 删除跟进记录 **接口地址**: `DELETE /crm/opportunity/follow/{ids}` **接口描述**: 删除跟进记录(支持批量) **权限标识**: `crm:opportunity:follow` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 跟进记录ID数组 | **业务规则**: - 只能删除自己创建的跟进记录 - 删除后需要重新计算商机的followCount **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ## 4. 商机阶段日志接口 ### 4.1 查询阶段变更日志 **接口地址**: `GET /crm/opportunity/stageLog/list` **接口描述**: 查询商机的阶段变更历史 **权限标识**: `crm:opportunity:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | opportunityId | Long | 是 | 商机ID | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "id": 1, "opportunityId": 1, "fromStage": "initial", "fromStageName": "初步接触", "fromPercent": 10, "toStage": "requirement", "toStageName": "需求分析", "toPercent": 20, "changeType": "forward", "changeReason": "客户明确了需求", "changeAmount": 150000.00, "createBy": 1, "createByName": "张三", "createTime": "2024-12-10 14:30:00", "remark": "" } ] } ``` --- ## 5. 统计分析接口 ### 5.1 商机统计概览 **接口地址**: `GET /crm/opportunity/statistics/overview` **接口描述**: 获取商机统计概览数据 **权限标识**: `crm:opportunity:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ownerUserId | Long | 否 | 负责人ID,不传则查询当前用户 | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": { "totalCount": 89, "followingCount": 67, "wonCount": 15, "lostCount": 7, "totalAmount": 5680000.00, "wonAmount": 1250000.00, "avgAmount": 63820.22, "winRate": 68.18, "avgCycle": 45.5 } } ``` --- ### 5.2 商机阶段分布 **接口地址**: `GET /crm/opportunity/statistics/stage` **接口描述**: 按商机阶段统计分布情况 **权限标识**: `crm:opportunity:list` **请求参数**: 无 **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "stage": "initial", "stageName": "初步接触", "count": 12, "amount": 560000.00, "percent": 13.48 }, { "stage": "requirement", "stageName": "需求分析", "count": 18, "amount": 980000.00, "percent": 20.22 } ] } ``` --- ### 5.3 商机赢单率分析 **接口地址**: `GET /crm/opportunity/statistics/winRate` **接口描述**: 分析商机赢单率趋势 **权限标识**: `crm:opportunity:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 统计类型:month/quarter/year,默认month | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "period": "2025-01", "totalCount": 23, "wonCount": 15, "lostCount": 8, "winRate": 65.22, "wonAmount": 1250000.00 } ] } ``` --- ### 5.4 负责人商机统计 **接口地址**: `GET /crm/opportunity/statistics/owner` **接口描述**: 按负责人统计商机数量和金额 **权限标识**: `crm:opportunity:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "ownerUserId": 1, "ownerUserName": "张三", "totalCount": 28, "followingCount": 20, "wonCount": 6, "lostCount": 2, "totalAmount": 1850000.00, "wonAmount": 650000.00, "winRate": 75.00 } ] } ``` --- ## 6. 数据模型 ### 6.1 OpportunityBo(商机业务对象) ```java public class CrmOpportunityBo extends BaseEntity { /** 主键ID */ @NotNull(message = "主键不能为空", groups = { EditGroup.class }) private Long id; /** 商机编号 */ @NotBlank(message = "商机编号不能为空", groups = { AddGroup.class, EditGroup.class }) private String opportunityNo; /** 商机名称 */ @NotBlank(message = "商机名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String opportunityName; /** 客户ID */ @NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long customerId; /** 联系人ID */ private Long contactId; /** 商机金额 */ @NotNull(message = "商机金额不能为空", groups = { AddGroup.class, EditGroup.class }) @Min(value = 0, message = "商机金额必须大于0") private BigDecimal amount; /** 商机阶段 */ private String stage; /** 阶段进度百分比 */ private Integer stagePercent; /** 赢单率 */ private BigDecimal winRate; /** 预计成交日期 */ private String expectDealDate; /** 负责人ID */ private Long ownerUserId; /** 商机来源 */ private String source; /** 下次联系时间 */ private LocalDateTime nextTime; /** 备注 */ private String remark; } ``` --- ### 6.2 OpportunityVo(商机视图对象) ```java public class CrmOpportunityVo implements Serializable { /** 主键ID */ @ExcelProperty(value = "ID") private Long id; /** 商机编号 */ @ExcelProperty(value = "商机编号") private String opportunityNo; /** 商机名称 */ @ExcelProperty(value = "商机名称") private String opportunityName; /** 客户ID */ private Long customerId; /** 客户名称 */ @ExcelProperty(value = "客户名称") private String customerName; /** 联系人ID */ private Long contactId; /** 联系人姓名 */ @ExcelProperty(value = "联系人") private String contactName; /** 商机金额 */ @ExcelProperty(value = "商机金额") private BigDecimal amount; /** 商机阶段 */ @ExcelProperty(value = "商机阶段", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_opportunity_stage") private String stage; /** 阶段进度百分比 */ @ExcelProperty(value = "阶段进度") private Integer stagePercent; /** 商机状态 */ @ExcelProperty(value = "商机状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_opportunity_status") private String status; /** 赢单率 */ @ExcelProperty(value = "赢单率") private BigDecimal winRate; /** 预计成交日期 */ @ExcelProperty(value = "预计成交日期") private String expectDealDate; /** 实际成交日期 */ @ExcelProperty(value = "实际成交日期") private String actualDealDate; /** 输单原因 */ private String lostReason; /** 负责人ID */ private Long ownerUserId; /** 负责人姓名 */ @ExcelProperty(value = "负责人") private String ownerUserName; /** 产品数量 */ @ExcelProperty(value = "产品数量") private Integer productCount; /** 最后跟进时间 */ @ExcelProperty(value = "最后跟进时间") private LocalDateTime lastTime; /** 下次联系时间 */ @ExcelProperty(value = "下次联系时间") private LocalDateTime nextTime; /** 跟进次数 */ @ExcelProperty(value = "跟进次数") private Integer followCount; /** 关联合同ID */ private Long contractId; /** 商机来源 */ @ExcelProperty(value = "商机来源", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_clue_source") private String source; /** 创建时间 */ @ExcelProperty(value = "创建时间") private LocalDateTime createTime; /** 备注 */ @ExcelProperty(value = "备注") private String remark; } ``` --- ## 7. 数据字典 ### 7.1 商机阶段 (crm_opportunity_stage) | 字典值 | 字典标签 | 进度 | 说明 | |--------|---------|------|------| | initial | 初步接触 | 10% | 初次接触客户 | | requirement | 需求分析 | 20% | 分析客户需求 | | solution | 方案制定 | 40% | 制定解决方案 | | quote | 报价 | 60% | 提供报价 | | negotiate | 谈判审核 | 80% | 商务谈判 | | contract | 合同签订 | 90% | 签订合同 | | won | 赢单 | 100% | 成功赢单 | --- ### 7.2 商机状态 (crm_opportunity_status) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | following | 跟进中 | 正在跟进 | | won | 赢单 | 成功签约 | | lost | 输单 | 失去商机 | | invalid | 无效 | 无效商机 | --- ## 8. 权限标识汇总 | 权限标识 | 说明 | |---------|------| | crm:opportunity:list | 商机列表查询 | | crm:opportunity:query | 商机详情查询 | | crm:opportunity:add | 商机新增 | | crm:opportunity:edit | 商机修改 | | crm:opportunity:remove | 商机删除 | | crm:opportunity:export | 商机导出 | | crm:opportunity:assign | 商机分配 | | crm:opportunity:follow | 添加跟进记录 | --- ## 9. 注意事项 1. **商机编号唯一性**: 同一租户下商机编号必须唯一 2. **数据权限**: 需要根据负责人字段实现数据权限控制 3. **阶段变更记录**: 修改商机阶段时自动记录变更日志 4. **金额自动计算**: 添加/修改产品时自动计算商机总金额 5. **跟进记录关联**: 新增跟进记录时自动更新商机的跟进统计信息 6. **赢单/输单限制**: 已赢单或已输单的商机不允许修改关键信息 7. **合同关联**: 赢单后可自动创建合同 8. **产品计数**: 添加/删除产品时自动维护商机的productCount 9. **批量操作**: 批量操作需要注意事务一致性 10. **阶段推进**: 阶段推进有逻辑顺序,不能跳跃太大 --- **文档结束**