# 客户管理模块 - API接口文档 > **模块**: 02-客户管理 > **版本**: v1.0 > **日期**: 2025-10-03 > **关联文档**: > - 需求文档: `docs/02-客户管理模块需求.md` > - 原型图: `yuanxing/02-客户管理原型图.md` > - 数据库: `sql/crm/02-crm_customer.sql` --- ## 📋 目录 - [1. 客户管理接口](#1-客户管理接口) - [2. 联系人管理接口](#2-联系人管理接口) - [3. 客户跟进记录接口](#3-客户跟进记录接口) - [4. 客户公海池接口](#4-客户公海池接口) - [5. 统计分析接口](#5-统计分析接口) - [6. 数据模型](#6-数据模型) --- ## 1. 客户管理接口 ### 1.1 查询客户列表(分页) **接口地址**: `GET /crm/customer/list` **接口描述**: 分页查询客户列表,支持多条件筛选 **权限标识**: `crm:customer:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | customerName | String | 否 | 客户名称(模糊查询) | | customerNo | String | 否 | 客户编号(模糊查询) | | mobile | String | 否 | 手机号(模糊查询) | | customerType | String | 否 | 客户类型:字典值crm_customer_type | | level | String | 否 | 客户级别:key/normal/potential | | source | String | 否 | 客户来源 | | industry | String | 否 | 所属行业 | | scale | String | 否 | 企业规模 | | province | String | 否 | 省份 | | city | String | 否 | 城市 | | status | String | 否 | 客户状态:normal/pool/invalid | | dealStatus | String | 否 | 成交状态:not_deal/dealed/lost | | ownerUserId | Long | 否 | 负责人ID | | beginTime | String | 否 | 开始时间 yyyy-MM-dd HH:mm:ss | | endTime | String | 否 | 结束时间 yyyy-MM-dd HH:mm:ss | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 1, "customerNo": "C202501010001", "customerName": "华为技术有限公司", "shortName": "华为", "customerType": "company", "mobile": "13900001234", "telephone": "0571-12345678", "email": "contact@huawei.com", "website": "https://www.huawei.com", "level": "key", "source": "website", "industry": "互联网/IT", "scale": "500人以上", "province": "广东省", "city": "深圳市", "district": "龙岗区", "address": "XXX科技园", "status": "normal", "dealStatus": "dealed", "ownerUserId": 1, "ownerUserName": "张三", "lastTime": "2025-01-02 10:30:00", "nextTime": "2025-01-05 14:00:00", "followCount": 15, "dealTime": "2024-12-15 16:00:00", "dealAmount": 150000.00, "contactCount": 3, "opportunityCount": 2, "contractCount": 1, "clueId": 10, "createTime": "2024-12-01 09:00:00", "remark": "" } ], "total": 230 } ``` --- ### 1.2 查询客户详情 **接口地址**: `GET /crm/customer/{id}` **接口描述**: 根据ID查询客户详细信息 **权限标识**: `crm:customer:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 客户ID | **返回示例**: ```json { "code": 200, "msg": "操作成功", "data": { "id": 1, "customerNo": "C202501010001", "customerName": "华为技术有限公司", "shortName": "华为", "customerType": "company", "mobile": "13900001234", "telephone": "0571-12345678", "email": "contact@huawei.com", "website": "https://www.huawei.com", "level": "key", "source": "website", "industry": "互联网/IT", "scale": "500人以上", "province": "广东省", "city": "深圳市", "district": "龙岗区", "address": "XXX科技园", "status": "normal", "dealStatus": "dealed", "ownerUserId": 1, "ownerUserName": "张三", "lastTime": "2025-01-02 10:30:00", "nextTime": "2025-01-05 14:00:00", "followCount": 15, "dealTime": "2024-12-15 16:00:00", "dealAmount": 150000.00, "contactCount": 3, "opportunityCount": 2, "contractCount": 1, "intoPoolTime": null, "claimTime": null, "protectTime": null, "clueId": 10, "createTime": "2024-12-01 09:00:00", "createBy": 1, "updateTime": "2025-01-02 10:30:00", "updateBy": 1, "remark": "" } } ``` --- ### 1.3 新增客户 **接口地址**: `POST /crm/customer` **接口描述**: 新增客户信息 **权限标识**: `crm:customer:add` **请求体**: ```json { "customerNo": "C202501010001", "customerName": "华为技术有限公司", "shortName": "华为", "customerType": "company", "mobile": "13900001234", "telephone": "0571-12345678", "email": "contact@huawei.com", "website": "https://www.huawei.com", "level": "key", "source": "website", "industry": "互联网/IT", "scale": "500人以上", "province": "广东省", "city": "深圳市", "district": "龙岗区", "address": "XXX科技园", "ownerUserId": 1, "nextTime": "2025-01-05 14:00:00", "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerNo | String | 是 | 客户编号,唯一,最大50字符 | | customerName | String | 是 | 客户名称,最大200字符 | | shortName | String | 否 | 客户简称,最大100字符 | | customerType | String | 否 | 客户类型:字典值crm_customer_type | | mobile | String | 否 | 手机号,最大20字符 | | telephone | String | 否 | 固定电话,最大30字符 | | email | String | 否 | 邮箱,最大100字符 | | website | String | 否 | 客户网站,最大500字符 | | level | String | 否 | 客户级别:key/normal/potential,默认normal | | source | String | 否 | 客户来源 | | industry | String | 否 | 所属行业,最大50字符 | | scale | String | 否 | 企业规模 | | province | String | 否 | 省份,最大50字符 | | city | String | 否 | 城市,最大50字符 | | district | String | 否 | 区县,最大50字符 | | address | String | 否 | 详细地址,最大255字符 | | ownerUserId | Long | 否 | 负责人ID,默认为当前用户 | | nextTime | String | 否 | 下次联系时间 yyyy-MM-dd HH:mm:ss | | remark | String | 否 | 备注,最大500字符 | **校验规则**: - 客户编号必须唯一 - 邮箱需符合邮箱格式 - 负责人ID必须存在 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.4 修改客户 **接口地址**: `PUT /crm/customer` **接口描述**: 修改客户信息 **权限标识**: `crm:customer:edit` **请求体**: 同新增接口,但需要传入id **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.5 删除客户 **接口地址**: `DELETE /crm/customer/{ids}` **接口描述**: 删除客户(支持批量) **权限标识**: `crm:customer:remove` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 客户ID数组,多个用逗号分隔 | **业务规则**: - 已成交的客户不允许删除 - 有关联商机、合同的客户不允许删除 - 删除客户时同时删除关联的联系人和跟进记录 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.6 批量分配客户 **接口地址**: `PUT /crm/customer/assign` **接口描述**: 批量分配客户给指定负责人 **权限标识**: `crm:customer:assign` **请求体**: ```json { "ids": [1, 2, 3], "ownerUserId": 5, "remark": "分配原因" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 客户ID数组 | | ownerUserId | Long | 是 | 新负责人ID | | remark | String | 否 | 分配说明 | **业务规则**: - 公海池中的客户不允许分配,需先领取 - 记录分配时间和分配人 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.7 修改客户级别 **接口地址**: `PUT /crm/customer/level` **接口描述**: 修改客户级别 **权限标识**: `crm:customer:edit` **请求体**: ```json { "id": 1, "level": "key", "remark": "客户升级为重点客户" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 客户ID | | level | String | 是 | 客户级别:key/normal/potential | | remark | String | 否 | 级别变更说明 | **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.8 修改成交状态 **接口地址**: `PUT /crm/customer/dealStatus` **接口描述**: 修改客户成交状态 **权限标识**: `crm:customer:edit` **请求体**: ```json { "id": 1, "dealStatus": "dealed", "dealTime": "2025-01-05 16:00:00", "dealAmount": 150000.00, "remark": "签订了CRM系统合同" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 客户ID | | dealStatus | String | 是 | 成交状态:not_deal/dealed/lost | | dealTime | String | 否 | 成交时间(状态为dealed时必填) | | dealAmount | BigDecimal | 否 | 成交金额(状态为dealed时必填) | | remark | String | 否 | 状态变更说明 | **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.9 放入公海池 **接口地址**: `PUT /crm/customer/intoPool` **接口描述**: 将客户放入公海池 **权限标识**: `crm:customer:pool` **请求体**: ```json { "ids": [1, 2, 3], "reason": "长期未跟进", "remark": "客户3个月未联系" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 客户ID数组 | | reason | String | 是 | 放入原因 | | remark | String | 否 | 备注说明 | **业务规则**: - 只能放入本人负责的客户 - 已成交的客户不允许放入公海 - 放入时记录进入公海时间和原因 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 1.10 导出客户 **接口地址**: `POST /crm/customer/export` **接口描述**: 导出客户数据到Excel **权限标识**: `crm:customer:export` **请求体**: 查询参数同1.1列表查询接口 **返回**: Excel文件流 --- ### 1.11 导入客户 **接口地址**: `POST /crm/customer/import` **接口描述**: 批量导入客户数据 **权限标识**: `crm:customer:import` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | file | MultipartFile | 是 | Excel文件 | | updateSupport | Boolean | 否 | 是否更新已存在的客户,默认false | **返回示例**: ```json { "code": 200, "msg": "操作成功", "data": { "total": 100, "success": 95, "fail": 5, "errors": [ { "row": 3, "error": "客户编号已存在" } ] } } ``` --- ### 1.12 下载导入模板 **接口地址**: `POST /crm/customer/importTemplate` **接口描述**: 下载客户导入Excel模板 **权限标识**: `crm:customer:import` **请求参数**: 无 **返回**: Excel文件流 --- ### 1.13 查询我的客户 **接口地址**: `GET /crm/customer/mine` **接口描述**: 查询当前用户负责的客户列表 **权限标识**: `crm:customer:list` **请求参数**: 同1.1列表查询接口 **返回示例**: 同1.1列表查询接口 --- ### 1.14 根据编号查询客户 **接口地址**: `GET /crm/customer/no/{customerNo}` **接口描述**: 根据客户编号查询客户信息 **权限标识**: `crm:customer:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerNo | String | 是 | 客户编号 | **返回示例**: 同1.2查询详情接口 --- ## 2. 联系人管理接口 ### 2.1 查询联系人列表(分页) **接口地址**: `GET /crm/contact/list` **接口描述**: 分页查询联系人列表 **权限标识**: `crm:contact:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | customerId | Long | 否 | 客户ID | | contactName | String | 否 | 联系人姓名(模糊查询) | | mobile | String | 否 | 手机号(模糊查询) | | position | String | 否 | 职位 | | role | String | 否 | 角色:decision/manager/user/other | | isKey | Integer | 否 | 是否关键决策人:0-否 1-是 | | isPrimary | Integer | 否 | 是否主要联系人:0-否 1-是 | | status | String | 否 | 状态:active/leave | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 1, "customerId": 1, "customerName": "华为技术有限公司", "contactName": "张伟", "gender": "male", "mobile": "13900001234", "telephone": "0571-12345678", "email": "zhangwei@huawei.com", "wechat": "zhangwei123", "qq": "123456789", "department": "技术部", "position": "技术总监", "role": "decision", "birthday": "1980-05-15", "address": "深圳市XXX", "isKey": 1, "isPrimary": 1, "status": "active", "createTime": "2024-12-01 09:30:00", "remark": "" } ], "total": 156 } ``` --- ### 2.2 查询联系人详情 **接口地址**: `GET /crm/contact/{id}` **接口描述**: 根据ID查询联系人详细信息 **权限标识**: `crm:contact:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 联系人ID | **返回示例**: 同2.1列表返回的单条数据 --- ### 2.3 新增联系人 **接口地址**: `POST /crm/contact` **接口描述**: 新增联系人信息 **权限标识**: `crm:contact:add` **请求体**: ```json { "customerId": 1, "contactName": "张伟", "gender": "male", "mobile": "13900001234", "telephone": "0571-12345678", "email": "zhangwei@huawei.com", "wechat": "zhangwei123", "qq": "123456789", "department": "技术部", "position": "技术总监", "role": "decision", "birthday": "1980-05-15", "address": "深圳市XXX", "isKey": 1, "isPrimary": 1, "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID | | contactName | String | 是 | 联系人姓名,最大100字符 | | gender | String | 否 | 性别:male/female | | mobile | String | 否 | 手机号,最大20字符 | | telephone | String | 否 | 固定电话,最大30字符 | | email | String | 否 | 邮箱,最大100字符 | | wechat | String | 否 | 微信号,最大100字符 | | qq | String | 否 | QQ号,最大20字符 | | department | String | 否 | 部门,最大100字符 | | position | String | 否 | 职位,最大50字符 | | role | String | 否 | 角色:decision/manager/user/other | | birthday | String | 否 | 生日,格式yyyy-MM-dd | | address | String | 否 | 住址,最大255字符 | | isKey | Integer | 否 | 是否关键决策人:0-否 1-是,默认0 | | isPrimary | Integer | 否 | 是否主要联系人:0-否 1-是,默认0 | | remark | String | 否 | 备注,最大500字符 | **业务规则**: - 新增联系人后,客户的contactCount自动+1 - 如果设置为主要联系人,需要取消其他联系人的主要标识 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.4 修改联系人 **接口地址**: `PUT /crm/contact` **接口描述**: 修改联系人信息 **权限标识**: `crm:contact:edit` **请求体**: 同新增接口,但需要传入id **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.5 删除联系人 **接口地址**: `DELETE /crm/contact/{ids}` **接口描述**: 删除联系人(支持批量) **权限标识**: `crm:contact:remove` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 联系人ID数组 | **业务规则**: - 删除联系人后,客户的contactCount自动-1 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.6 设置主要联系人 **接口地址**: `PUT /crm/contact/setPrimary/{id}` **接口描述**: 设置为主要联系人 **权限标识**: `crm:contact:edit` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 联系人ID | **业务规则**: - 同一客户只能有一个主要联系人 - 设置新的主要联系人时,自动取消其他联系人的主要标识 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 2.7 根据客户查询联系人 **接口地址**: `GET /crm/contact/customer/{customerId}` **接口描述**: 查询指定客户的所有联系人 **权限标识**: `crm:contact:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "id": 1, "contactName": "张伟", "mobile": "13900001234", "position": "技术总监", "role": "decision", "isKey": 1, "isPrimary": 1, "status": "active" } ] } ``` --- ## 3. 客户跟进记录接口 ### 3.1 查询跟进记录列表 **接口地址**: `GET /crm/customer/follow/list` **接口描述**: 查询指定客户的跟进记录列表 **权限标识**: `crm:customer:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID | | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 1, "customerId": 1, "customerName": "华为技术有限公司", "contactId": 1, "contactName": "张伟", "followType": "phone", "followContent": "电话沟通了项目进展情况", "nextTime": "2025-01-08 14:00:00", "attachments": "/upload/2025/01/file1.pdf", "createBy": 1, "createByName": "张三", "createTime": "2025-01-05 10:30:00", "remark": "" } ], "total": 15 } ``` --- ### 3.2 新增跟进记录 **接口地址**: `POST /crm/customer/follow` **接口描述**: 新增客户跟进记录 **权限标识**: `crm:customer:follow` **请求体**: ```json { "customerId": 1, "contactId": 1, "followType": "phone", "followContent": "电话沟通了项目进展情况", "nextTime": "2025-01-08 14:00:00", "attachments": "/upload/2025/01/file1.pdf", "remark": "" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID | | contactId | Long | 否 | 联系人ID | | followType | String | 是 | 跟进方式:phone/wechat/email/visit/other | | followContent | String | 是 | 跟进内容,文本类型 | | nextTime | String | 否 | 下次联系时间 yyyy-MM-dd HH:mm:ss | | attachments | String | 否 | 附件路径,多个用逗号分隔 | | remark | String | 否 | 备注,最大500字符 | **业务规则**: - 新增跟进记录后,自动更新客户的lastTime和followCount - 如果设置了nextTime,同步更新客户的nextTime **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 3.3 修改跟进记录 **接口地址**: `PUT /crm/customer/follow` **接口描述**: 修改跟进记录信息 **权限标识**: `crm:customer:follow` **请求体**: 同新增接口,但需要传入id **业务规则**: - 只能修改自己创建的跟进记录 **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 3.4 删除跟进记录 **接口地址**: `DELETE /crm/customer/follow/{ids}` **接口描述**: 删除跟进记录(支持批量) **权限标识**: `crm:customer:follow` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 跟进记录ID数组 | **业务规则**: - 只能删除自己创建的跟进记录 - 删除后需要重新计算客户的followCount **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ## 4. 客户公海池接口 ### 4.1 查询公海池列表 **接口地址**: `GET /crm/customer/pool/list` **接口描述**: 查询公海池中的客户列表 **权限标识**: `crm:customer:pool:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | customerName | String | 否 | 客户名称(模糊查询) | | level | String | 否 | 客户级别 | | industry | String | 否 | 所属行业 | | province | String | 否 | 省份 | | city | String | 否 | 城市 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "rows": [ { "id": 10, "customerNo": "C202501010010", "customerName": "XXX科技有限公司", "level": "normal", "industry": "互联网/IT", "province": "浙江省", "city": "杭州市", "intoPoolTime": "2025-01-01 10:00:00", "followCount": 3, "lastTime": "2024-12-15 14:00:00", "protectTime": null, "poolReason": "长期未跟进", "poolRemark": "客户3个月未联系" } ], "total": 45 } ``` --- ### 4.2 领取客户 **接口地址**: `POST /crm/customer/pool/claim` **接口描述**: 从公海池领取客户 **权限标识**: `crm:customer:pool:claim` **请求体**: ```json { "ids": [10, 11, 12], "remark": "领取原因" } ``` **请求参数说明**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 客户ID数组 | | remark | String | 否 | 领取说明 | **业务规则**: - 只能领取公海池中的客户 - 领取后客户状态变为normal - 负责人设置为当前用户 - 记录领取时间和领取人 - 设置保护期(如7天内不能再次放入公海) **返回示例**: ```json { "code": 200, "msg": "操作成功" } ``` --- ### 4.3 查询公海池记录 **接口地址**: `GET /crm/customer/pool/record/list` **接口描述**: 查询客户的公海池记录(进出记录) **权限标识**: `crm:customer:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | customerId | Long | 是 | 客户ID | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "id": 1, "customerId": 10, "operateType": "into", "reason": "长期未跟进", "operateBy": 1, "operateByName": "张三", "operateTime": "2025-01-01 10:00:00", "remark": "客户3个月未联系" }, { "id": 2, "customerId": 10, "operateType": "claim", "reason": "重新跟进", "operateBy": 5, "operateByName": "李四", "operateTime": "2025-01-03 14:00:00", "remark": "领取客户" } ] } ``` --- ## 5. 统计分析接口 ### 5.1 客户统计概览 **接口地址**: `GET /crm/customer/statistics/overview` **接口描述**: 获取客户统计概览数据 **权限标识**: `crm:customer:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ownerUserId | Long | 否 | 负责人ID,不传则查询当前用户 | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": { "totalCount": 230, "keyCount": 45, "normalCount": 150, "potentialCount": 35, "dealedCount": 89, "notDealCount": 128, "lostCount": 13, "weekAddCount": 12, "poolCount": 15, "dealRate": 38.70, "totalDealAmount": 5680000.00 } } ``` --- ### 5.2 客户级别分布 **接口地址**: `GET /crm/customer/statistics/level` **接口描述**: 按客户级别统计分布情况 **权限标识**: `crm:customer:list` **请求参数**: 无 **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "level": "key", "levelName": "重点客户", "count": 45, "percent": 19.57, "dealAmount": 3200000.00 }, { "level": "normal", "levelName": "普通客户", "count": 150, "percent": 65.22, "dealAmount": 2100000.00 }, { "level": "potential", "levelName": "潜在客户", "count": 35, "percent": 15.22, "dealAmount": 380000.00 } ] } ``` --- ### 5.3 客户来源统计 **接口地址**: `GET /crm/customer/statistics/source` **接口描述**: 按客户来源统计数量和成交情况 **权限标识**: `crm:customer:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "source": "website", "sourceName": "网站留言", "count": 67, "dealedCount": 28, "dealRate": 41.79, "dealAmount": 1500000.00 } ] } ``` --- ### 5.4 客户成交趋势 **接口地址**: `GET /crm/customer/statistics/trend` **接口描述**: 查询客户新增和成交趋势(按天/周/月) **权限标识**: `crm:customer:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 统计类型:day/week/month,默认day | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "date": "2025-01-01", "addCount": 8, "dealCount": 3, "dealAmount": 450000.00 } ] } ``` --- ### 5.5 负责人客户统计 **接口地址**: `GET /crm/customer/statistics/owner` **接口描述**: 按负责人统计客户数量和成交情况 **权限标识**: `crm:customer:list` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 | **返回示例**: ```json { "code": 200, "msg": "查询成功", "data": [ { "ownerUserId": 1, "ownerUserName": "张三", "totalCount": 56, "keyCount": 12, "dealedCount": 23, "dealRate": 41.07, "dealAmount": 2100000.00, "avgFollowCount": 8.5 } ] } ``` --- ## 6. 数据模型 ### 6.1 CustomerBo(客户业务对象) ```java public class CrmCustomerBo extends BaseEntity { /** 主键ID */ @NotNull(message = "主键不能为空", groups = { EditGroup.class }) private Long id; /** 客户编号 */ @NotBlank(message = "客户编号不能为空", groups = { AddGroup.class, EditGroup.class }) private String customerNo; /** 客户名称 */ @NotBlank(message = "客户名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String customerName; /** 客户简称 */ private String shortName; /** 客户类型 */ private String customerType; /** 手机号 */ private String mobile; /** 固定电话 */ private String telephone; /** 邮箱 */ @Email(message = "邮箱格式错误") private String email; /** 客户网站 */ private String website; /** 客户级别 */ private String level; /** 客户来源 */ private String source; /** 所属行业 */ private String industry; /** 企业规模 */ private String scale; /** 省 */ private String province; /** 市 */ private String city; /** 区 */ private String district; /** 详细地址 */ private String address; /** 负责人ID */ private Long ownerUserId; /** 下次联系时间 */ private LocalDateTime nextTime; /** 备注 */ private String remark; } ``` --- ### 6.2 CustomerVo(客户视图对象) ```java public class CrmCustomerVo implements Serializable { /** 主键ID */ @ExcelProperty(value = "ID") private Long id; /** 客户编号 */ @ExcelProperty(value = "客户编号") private String customerNo; /** 客户名称 */ @ExcelProperty(value = "客户名称") private String customerName; /** 客户简称 */ @ExcelProperty(value = "客户简称") private String shortName; /** 客户类型 */ @ExcelProperty(value = "客户类型", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_customer_type") private String customerType; /** 手机号 */ @ExcelProperty(value = "手机号") private String mobile; /** 固定电话 */ @ExcelProperty(value = "固定电话") private String telephone; /** 邮箱 */ @ExcelProperty(value = "邮箱") private String email; /** 客户网站 */ @ExcelProperty(value = "客户网站") private String website; /** 客户级别 */ @ExcelProperty(value = "客户级别", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_customer_level") private String level; /** 客户来源 */ @ExcelProperty(value = "客户来源", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_clue_source") private String source; /** 所属行业 */ @ExcelProperty(value = "所属行业") private String industry; /** 企业规模 */ @ExcelProperty(value = "企业规模", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_company_scale") private String scale; /** 省 */ @ExcelProperty(value = "省") private String province; /** 市 */ @ExcelProperty(value = "市") private String city; /** 区 */ @ExcelProperty(value = "区") private String district; /** 详细地址 */ @ExcelProperty(value = "详细地址") private String address; /** 客户状态 */ @ExcelProperty(value = "客户状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_customer_status") private String status; /** 成交状态 */ @ExcelProperty(value = "成交状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_deal_status") private String dealStatus; /** 负责人ID */ private Long ownerUserId; /** 负责人姓名 */ @ExcelProperty(value = "负责人") private String ownerUserName; /** 最后跟进时间 */ @ExcelProperty(value = "最后跟进时间") private LocalDateTime lastTime; /** 下次联系时间 */ @ExcelProperty(value = "下次联系时间") private LocalDateTime nextTime; /** 跟进次数 */ @ExcelProperty(value = "跟进次数") private Integer followCount; /** 成交时间 */ @ExcelProperty(value = "成交时间") private LocalDateTime dealTime; /** 累计成交金额 */ @ExcelProperty(value = "累计成交金额") private BigDecimal dealAmount; /** 联系人数量 */ private Integer contactCount; /** 商机数量 */ private Integer opportunityCount; /** 合同数量 */ private Integer contractCount; /** 进入公海时间 */ private LocalDateTime intoPoolTime; /** 领取时间 */ private LocalDateTime claimTime; /** 保护到期时间 */ private LocalDateTime protectTime; /** 关联线索ID */ private Long clueId; /** 创建时间 */ @ExcelProperty(value = "创建时间") private LocalDateTime createTime; /** 更新时间 */ @ExcelProperty(value = "更新时间") private LocalDateTime updateTime; /** 备注 */ @ExcelProperty(value = "备注") private String remark; } ``` --- ### 6.3 ContactBo(联系人业务对象) ```java public class CrmContactBo extends BaseEntity { /** 主键ID */ @NotNull(message = "主键不能为空", groups = { EditGroup.class }) private Long id; /** 客户ID */ @NotNull(message = "客户ID不能为空", groups = { AddGroup.class }) private Long customerId; /** 联系人姓名 */ @NotBlank(message = "联系人姓名不能为空", groups = { AddGroup.class, EditGroup.class }) private String contactName; /** 性别 */ private String gender; /** 手机号 */ private String mobile; /** 固定电话 */ private String telephone; /** 邮箱 */ @Email(message = "邮箱格式错误") private String email; /** 微信号 */ private String wechat; /** QQ号 */ private String qq; /** 部门 */ private String department; /** 职位 */ private String position; /** 角色 */ private String role; /** 生日 */ private String birthday; /** 住址 */ private String address; /** 是否关键决策人 */ private Integer isKey; /** 是否主要联系人 */ private Integer isPrimary; /** 备注 */ private String remark; } ``` --- ### 6.4 ContactVo(联系人视图对象) ```java public class CrmContactVo implements Serializable { /** 主键ID */ private Long id; /** 客户ID */ private Long customerId; /** 客户名称 */ @ExcelProperty(value = "客户名称") private String customerName; /** 联系人姓名 */ @ExcelProperty(value = "联系人姓名") private String contactName; /** 性别 */ @ExcelProperty(value = "性别", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_user_sex") private String gender; /** 手机号 */ @ExcelProperty(value = "手机号") private String mobile; /** 固定电话 */ @ExcelProperty(value = "固定电话") private String telephone; /** 邮箱 */ @ExcelProperty(value = "邮箱") private String email; /** 微信号 */ @ExcelProperty(value = "微信号") private String wechat; /** QQ号 */ @ExcelProperty(value = "QQ号") private String qq; /** 部门 */ @ExcelProperty(value = "部门") private String department; /** 职位 */ @ExcelProperty(value = "职位") private String position; /** 角色 */ @ExcelProperty(value = "角色", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_contact_role") private String role; /** 生日 */ @ExcelProperty(value = "生日") private String birthday; /** 住址 */ @ExcelProperty(value = "住址") private String address; /** 是否关键决策人 */ @ExcelProperty(value = "关键决策人") private Integer isKey; /** 是否主要联系人 */ @ExcelProperty(value = "主要联系人") private Integer isPrimary; /** 状态 */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "crm_contact_status") private String status; /** 创建时间 */ @ExcelProperty(value = "创建时间") private LocalDateTime createTime; /** 备注 */ @ExcelProperty(value = "备注") private String remark; } ``` --- ## 7. 数据字典 ### 7.1 客户类型 (crm_customer_type) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | company | 企业客户 | 企业法人客户 | | person | 个人客户 | 个人客户 | --- ### 7.2 客户级别 (crm_customer_level) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | key | 重点客户 | 重要客户 | | normal | 普通客户 | 普通客户 | | potential | 潜在客户 | 潜在客户 | --- ### 7.3 客户状态 (crm_customer_status) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | normal | 正常 | 正常状态 | | pool | 公海 | 公海池中 | | invalid | 无效 | 无效客户 | --- ### 7.4 成交状态 (crm_deal_status) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | not_deal | 未成交 | 尚未成交 | | dealed | 已成交 | 已成交 | | lost | 已流失 | 客户流失 | --- ### 7.5 企业规模 (crm_company_scale) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | 1_50 | 1-50人 | 小微企业 | | 50_200 | 50-200人 | 小型企业 | | 200_500 | 200-500人 | 中型企业 | | 500_plus | 500人以上 | 大型企业 | --- ### 7.6 联系人角色 (crm_contact_role) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | decision | 决策人 | 决策者 | | manager | 管理者 | 管理层 | | user | 使用者 | 使用人员 | | other | 其他 | 其他角色 | --- ### 7.7 联系人状态 (crm_contact_status) | 字典值 | 字典标签 | 说明 | |--------|---------|------| | active | 在职 | 在职状态 | | leave | 离职 | 已离职 | --- ## 8. 权限标识汇总 | 权限标识 | 说明 | |---------|------| | crm:customer:list | 客户列表查询 | | crm:customer:query | 客户详情查询 | | crm:customer:add | 客户新增 | | crm:customer:edit | 客户修改 | | crm:customer:remove | 客户删除 | | crm:customer:export | 客户导出 | | crm:customer:import | 客户导入 | | crm:customer:assign | 客户分配 | | crm:customer:follow | 添加跟进记录 | | crm:customer:pool | 放入公海池 | | crm:customer:pool:list | 公海池列表查询 | | crm:customer:pool:claim | 领取客户 | | crm:contact:list | 联系人列表查询 | | crm:contact:query | 联系人详情查询 | | crm:contact:add | 联系人新增 | | crm:contact:edit | 联系人修改 | | crm:contact:remove | 联系人删除 | --- ## 9. 注意事项 1. **客户编号唯一性**: 同一租户下客户编号必须唯一 2. **数据权限**: 需要根据负责人字段实现数据权限控制 3. **跟进记录关联**: 新增跟进记录时自动更新客户的跟进统计信息 4. **联系人计数**: 新增/删除联系人时自动维护客户的contactCount 5. **公海池规则**: 放入公海池需要满足一定条件(如长期未跟进) 6. **保护期机制**: 领取客户后设置保护期,期间不能再次放入公海 7. **主要联系人**: 同一客户只能有一个主要联系人 8. **成交限制**: 已成交的客户不允许删除和放入公海 9. **批量操作**: 批量操作需要注意事务一致性 10. **数据脱敏**: 手机号和邮箱需要根据权限进行脱敏显示 --- **文档结束**