模块: 07-合同管理
版本: v1.0
日期: 2025-10-03
关联文档:
- 需求文档:
docs/07-合同管理模块需求.md- 原型图:
yuanxing/07-合同管理原型图.md- 数据库:
sql/crm/07-crm_contract.sql
接口地址: GET /crm/contract/list
接口描述: 分页查询合同列表,支持多条件筛选
权限标识: crm:contract:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | contractNo | String | 否 | 合同编号(模糊查询) | | contractName | String | 否 | 合同名称(模糊查询) | | customerId | Long | 否 | 客户ID | | customerName | String | 否 | 客户名称(模糊查询) | | opportunityId | Long | 否 | 商机ID | | quoteId | Long | 否 | 报价单ID | | contractType | String | 否 | 合同类型 | | status | String | 否 | 合同状态 | | approvalStatus | String | 否 | 审批状态 | | ownerUserId | Long | 否 | 负责人ID | | minAmount | BigDecimal | 否 | 最小金额 | | maxAmount | BigDecimal | 否 | 最大金额 | | signDateBegin | String | 否 | 签订日期开始 | | signDateEnd | String | 否 | 签订日期结束 | | beginTime | String | 否 | 创建开始时间 | | endTime | String | 否 | 创建结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"contractNo": "CON20250115001",
"contractName": "华为CRM系统采购合同",
"customerId": 1,
"customerName": "华为技术有限公司",
"opportunityId": 1,
"quoteId": 1,
"contactId": 1,
"contactName": "张伟",
"contactMobile": "13800138000",
"contractType": "sale",
"amount": 135000.00,
"signDate": "2025-01-15",
"startDate": "2025-01-15",
"endDate": "2026-01-14",
"contractPeriod": 12,
"status": "executing",
"approvalStatus": "approved",
"approvalFlowId": 1,
"contractFile": "/upload/contract/CON20250115001.pdf",
"ownerUserId": 1,
"ownerUserName": "张三",
"receivedAmount": 40500.00,
"unreceivedAmount": 94500.00,
"invoiceAmount": 40500.00,
"uninvoiceAmount": 94500.00,
"partyA": "华为技术有限公司",
"partyB": "XX软件科技有限公司",
"createTime": "2025-01-15 09:00:00",
"remark": ""
}
],
"total": 78
}
接口地址: GET /crm/contract/{id}
接口描述: 根据ID查询合同详细信息
权限标识: crm:contract:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 合同ID |
返回示例: 同1.1列表返回的单条数据,包含完整字段
接口地址: POST /crm/contract
接口描述: 新增合同信息
权限标识: crm:contract:add
请求体:
{
"contractNo": "CON20250115001",
"contractName": "华为CRM系统采购合同",
"customerId": 1,
"opportunityId": 1,
"quoteId": 1,
"contactId": 1,
"contractType": "sale",
"amount": 135000.00,
"signDate": "2025-01-15",
"startDate": "2025-01-15",
"endDate": "2026-01-14",
"contractPeriod": 12,
"ownerUserId": 1,
"partyA": "华为技术有限公司",
"partyAAddress": "深圳市龙岗区xxx",
"partyAContact": "张伟",
"partyAMobile": "13800138000",
"partyB": "XX软件科技有限公司",
"partyBAddress": "杭州市西湖区xxx",
"partyBContact": "李四",
"partyBMobile": "13900139000",
"paymentTerms": "签订后7日内支付30%,交付后7日内支付60%,验收后7日内支付10%",
"deliveryTerms": "签订后15个工作日内完成部署",
"contractTerms": "详细合同条款...",
"products": [
{
"productId": 1,
"quantity": 1,
"price": 11520.00,
"discount": 0,
"deliveryDate": "2025-02-01"
}
],
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractNo | String | 是 | 合同编号,唯一,格式:CON+yyyyMMdd+4位流水号 | | contractName | String | 是 | 合同名称,最大200字符 | | customerId | Long | 是 | 客户ID | | opportunityId | Long | 否 | 商机ID | | quoteId | Long | 否 | 报价单ID | | contactId | Long | 否 | 联系人ID | | contractType | String | 是 | 合同类型:sale/service/lease/other | | amount | BigDecimal | 是 | 合同金额,保留2位小数 | | signDate | String | 否 | 签订日期 yyyy-MM-dd | | startDate | String | 否 | 开始日期 yyyy-MM-dd | | endDate | String | 否 | 结束日期 yyyy-MM-dd | | contractPeriod | Integer | 否 | 合同期限(月) | | ownerUserId | Long | 否 | 负责人ID,默认当前用户 | | partyA | String | 否 | 甲方(客户方),最大200字符 | | partyB | String | 否 | 乙方(我方),最大200字符 | | paymentTerms | String | 否 | 付款条款,文本类型 | | deliveryTerms | String | 否 | 交付条款,文本类型 | | contractTerms | String | 否 | 合同条款,文本类型 | | products | Array | 是 | 产品列表,至少1个 | | remark | String | 否 | 备注,最大500字符 |
校验规则:
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"contractNo": "CON20250115001"
}
}
接口地址: PUT /crm/contract
接口描述: 修改合同信息
权限标识: crm:contract:edit
请求体: 同新增接口,但需要传入id
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: DELETE /crm/contract/{ids}
接口描述: 删除合同(支持批量)
权限标识: crm:contract:remove
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 合同ID数组 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/submitApproval
接口描述: 提交合同进行审批
权限标识: crm:contract:submit
请求体:
{
"id": 1,
"approverIds": [5, 6, 7],
"remark": "提交审批"
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 合同ID | | approverIds | Long[] | 是 | 审批人ID列表(按顺序审批) | | remark | String | 否 | 提交说明 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/withdrawApproval
接口描述: 撤回已提交的审批
权限标识: crm:contract:edit
请求体:
{
"id": 1,
"remark": "需要修改合同内容"
}
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/startExecute
接口描述: 开始执行合同
权限标识: crm:contract:execute
请求体:
{
"id": 1,
"remark": "合同开始执行"
}
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/finish
接口描述: 完成合同
权限标识: crm:contract:execute
请求体:
{
"id": 1,
"remark": "合同已完成"
}
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/invalid
接口描述: 作废合同
权限标识: crm:contract:invalid
请求体:
{
"id": 1,
"invalidReason": "客户要求终止合同",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 合同ID | | invalidReason | String | 是 | 作废原因,最大500字符 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: POST /crm/contract/uploadFile
接口描述: 上传合同扫描件或电子版
权限标识: crm:contract:edit
请求参数: multipart/form-data | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 合同ID | | file | File | 是 | 合同文件(PDF/图片) |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"fileUrl": "/upload/contract/CON20250115001.pdf"
}
}
接口地址: POST /crm/contract/export
接口描述: 导出合同数据到Excel
权限标识: crm:contract:export
请求体: 查询参数同1.1列表查询接口
返回: Excel文件流
接口地址: GET /crm/contract/product/list
接口描述: 查询指定合同的产品列表
权限标识: crm:contract:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractId | Long | 是 | 合同ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"contractId": 1,
"productId": 1,
"productName": "CRM系统标准版",
"productNo": "PRD20250101001",
"categoryId": 11,
"categoryName": "CRM系统",
"model": "V5.4.0",
"unit": "套",
"quantity": 1,
"price": 11520.00,
"discount": 0.00,
"subtotal": 11520.00,
"deliveryDate": "2025-02-01",
"deliveryStatus": "delivered",
"deliveredQuantity": 1,
"description": "包含客户管理、销售管理等基础功能",
"sort": 1,
"remark": ""
}
]
}
接口地址: POST /crm/contract/product
接口描述: 向合同添加产品
权限标识: crm:contract:edit
请求体:
{
"contractId": 1,
"productId": 1,
"quantity": 1,
"price": 11520.00,
"discount": 0.00,
"deliveryDate": "2025-02-01",
"description": "包含客户管理、销售管理等基础功能",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractId | Long | 是 | 合同ID | | productId | Long | 是 | 产品ID | | quantity | Integer | 是 | 数量,必须大于0 | | price | BigDecimal | 是 | 单价,保留2位小数 | | discount | BigDecimal | 否 | 折扣百分比,默认0 | | deliveryDate | String | 否 | 交付日期 yyyy-MM-dd | | description | String | 否 | 产品描述,最大1000字符 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/product
接口描述: 修改合同产品信息
权限标识: crm:contract:edit
请求体: 同添加接口,但需要传入id
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: DELETE /crm/contract/product/{ids}
接口描述: 删除合同产品(支持批量)
权限标识: crm:contract:edit
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 合同产品ID数组 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/contract/product/delivery
接口描述: 更新产品交付状态
权限标识: crm:contract:execute
请求体:
{
"id": 1,
"deliveredQuantity": 1,
"deliveryStatus": "delivered",
"remark": "已完成交付"
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 合同产品ID | | deliveredQuantity | Integer | 是 | 已交付数量 | | deliveryStatus | String | 是 | 交付状态:pending/partial/delivered | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: GET /crm/contract/approval/list
接口描述: 查询指定合同的审批记录列表
权限标识: crm:contract:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractId | Long | 是 | 合同ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"contractId": 1,
"flowId": 1,
"step": 1,
"stepName": "部门经理审批",
"approverId": 5,
"approverName": "王经理",
"approverDept": "销售部",
"approvalResult": "approved",
"approvalOpinion": "同意",
"approvalTime": "2025-01-16 10:00:00",
"approvalDuration": 2,
"isCurrent": 0,
"createTime": "2025-01-16 08:00:00",
"remark": ""
}
]
}
接口地址: POST /crm/contract/approval
接口描述: 审批人审批合同
权限标识: crm:contract:approval
请求体:
{
"id": 1,
"approvalResult": "approved",
"approvalOpinion": "同意通过",
"attachments": "/upload/approval/file1.pdf",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 审批记录ID | | approvalResult | String | 是 | 审批结果:approved/rejected | | approvalOpinion | String | 否 | 审批意见,文本类型 | | attachments | String | 否 | 附件路径,多个用逗号分隔 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: GET /crm/contract/statistics/overview
接口描述: 获取合同统计概览数据
权限标识: crm:contract:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ownerUserId | Long | 否 | 负责人ID,不传则查询当前用户 | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": {
"totalCount": 78,
"draftCount": 5,
"approvingCount": 8,
"executingCount": 52,
"finishedCount": 10,
"invalidCount": 3,
"totalAmount": 8750000.00,
"receivedAmount": 3850000.00,
"unreceivedAmount": 4900000.00,
"receivedRate": 44.00,
"invoiceAmount": 3850000.00,
"uninvoiceAmount": 4900000.00,
"invoiceRate": 44.00
}
}
接口地址: GET /crm/contract/statistics/status
接口描述: 按状态统计合同分布情况
权限标识: crm:contract:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"status": "executing",
"statusName": "执行中",
"count": 52,
"amount": 6850000.00,
"percent": 66.67
}
]
}
接口地址: GET /crm/contract/statistics/type
接口描述: 按合同类型统计分布情况
权限标识: crm:contract:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"contractType": "sale",
"typeName": "销售合同",
"count": 58,
"amount": 7200000.00,
"percent": 74.36
}
]
}
接口地址: GET /crm/contract/statistics/trend
接口描述: 分析合同数量和金额趋势
权限标识: crm:contract:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 统计类型:month/quarter/year,默认month | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"period": "2025-01",
"totalCount": 12,
"totalAmount": 1580000.00,
"receivedAmount": 450000.00,
"receivedRate": 28.48
}
]
}
接口地址: GET /crm/contract/statistics/expiring
接口描述: 查询即将到期的合同
权限标识: crm:contract:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | days | Integer | 否 | 提前天数,默认30天 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"contractNo": "CON20250115001",
"contractName": "华为CRM系统采购合同",
"customerName": "华为技术有限公司",
"amount": 135000.00,
"endDate": "2026-01-14",
"remainDays": 28,
"ownerUserName": "张三"
}
]
}
public class CrmContractBo extends BaseEntity {
/** 主键ID */
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/** 合同编号 */
@NotBlank(message = "合同编号不能为空", groups = { AddGroup.class, EditGroup.class })
private String contractNo;
/** 合同名称 */
@NotBlank(message = "合同名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String contractName;
/** 客户ID */
@NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long customerId;
/** 商机ID */
private Long opportunityId;
/** 报价单ID */
private Long quoteId;
/** 联系人ID */
private Long contactId;
/** 合同类型 */
@NotBlank(message = "合同类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String contractType;
/** 合同金额 */
@NotNull(message = "合同金额不能为空", groups = { AddGroup.class, EditGroup.class })
@Min(value = 0, message = "合同金额必须大于0")
private BigDecimal amount;
/** 签订日期 */
private String signDate;
/** 开始日期 */
private String startDate;
/** 结束日期 */
private String endDate;
/** 合同期限(月) */
private Integer contractPeriod;
/** 负责人ID */
private Long ownerUserId;
/** 甲方 */
private String partyA;
/** 甲方地址 */
private String partyAAddress;
/** 甲方联系人 */
private String partyAContact;
/** 甲方联系电话 */
private String partyAMobile;
/** 乙方 */
private String partyB;
/** 乙方地址 */
private String partyBAddress;
/** 乙方联系人 */
private String partyBContact;
/** 乙方联系电话 */
private String partyBMobile;
/** 付款条款 */
private String paymentTerms;
/** 交付条款 */
private String deliveryTerms;
/** 合同条款 */
private String contractTerms;
/** 产品列表 */
@NotEmpty(message = "产品列表不能为空", groups = { AddGroup.class })
private List<CrmContractProductBo> products;
/** 备注 */
private String remark;
}
public class CrmContractVo implements Serializable {
/** 主键ID */
@ExcelProperty(value = "ID")
private Long id;
/** 合同编号 */
@ExcelProperty(value = "合同编号")
private String contractNo;
/** 合同名称 */
@ExcelProperty(value = "合同名称")
private String contractName;
/** 客户ID */
private Long customerId;
/** 客户名称 */
@ExcelProperty(value = "客户名称")
private String customerName;
/** 商机ID */
private Long opportunityId;
/** 报价单ID */
private Long quoteId;
/** 联系人ID */
private Long contactId;
/** 联系人姓名 */
@ExcelProperty(value = "联系人")
private String contactName;
/** 联系人手机 */
private String contactMobile;
/** 合同类型 */
@ExcelProperty(value = "合同类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_contract_type")
private String contractType;
/** 合同金额 */
@ExcelProperty(value = "合同金额")
private BigDecimal amount;
/** 签订日期 */
@ExcelProperty(value = "签订日期")
private String signDate;
/** 开始日期 */
@ExcelProperty(value = "开始日期")
private String startDate;
/** 结束日期 */
@ExcelProperty(value = "结束日期")
private String endDate;
/** 合同期限(月) */
@ExcelProperty(value = "合同期限(月)")
private Integer contractPeriod;
/** 合同状态 */
@ExcelProperty(value = "合同状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_contract_status")
private String status;
/** 审批状态 */
@ExcelProperty(value = "审批状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_approval_status")
private String approvalStatus;
/** 审批流程ID */
private Long approvalFlowId;
/** 合同文件路径 */
private String contractFile;
/** 附件文件 */
private String attachmentFiles;
/** 负责人ID */
private Long ownerUserId;
/** 负责人姓名 */
@ExcelProperty(value = "负责人")
private String ownerUserName;
/** 已回款金额 */
@ExcelProperty(value = "已回款金额")
private BigDecimal receivedAmount;
/** 未回款金额 */
@ExcelProperty(value = "未回款金额")
private BigDecimal unreceivedAmount;
/** 已开票金额 */
@ExcelProperty(value = "已开票金额")
private BigDecimal invoiceAmount;
/** 未开票金额 */
@ExcelProperty(value = "未开票金额")
private BigDecimal uninvoiceAmount;
/** 甲方 */
@ExcelProperty(value = "甲方")
private String partyA;
/** 甲方地址 */
private String partyAAddress;
/** 甲方联系人 */
private String partyAContact;
/** 甲方联系电话 */
private String partyAMobile;
/** 乙方 */
@ExcelProperty(value = "乙方")
private String partyB;
/** 乙方地址 */
private String partyBAddress;
/** 乙方联系人 */
private String partyBContact;
/** 乙方联系电话 */
private String partyBMobile;
/** 付款条款 */
private String paymentTerms;
/** 交付条款 */
private String deliveryTerms;
/** 合同条款 */
private String contractTerms;
/** 作废原因 */
private String invalidReason;
/** 作废时间 */
private LocalDateTime invalidTime;
/** 创建时间 */
@ExcelProperty(value = "创建时间")
private LocalDateTime createTime;
/** 备注 */
@ExcelProperty(value = "备注")
private String remark;
}
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| sale | 销售合同 | 销售产品或服务 |
| service | 服务合同 | 提供服务 |
| lease | 租赁合同 | 租赁设备或软件 |
| other | 其他 | 其他类型合同 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| draft | 草稿 | 编辑中 |
| approving | 审批中 | 审批流程中 |
| approved | 已审批 | 审批通过 |
| executing | 执行中 | 正在执行 |
| finished | 已完成 | 执行完毕 |
| invalid | 已作废 | 合同作废 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| pending | 待审批 | 等待审批 |
| approved | 已通过 | 审批通过 |
| rejected | 已驳回 | 审批拒绝 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| pending | 待交付 | 未开始交付 |
| partial | 部分交付 | 部分完成 |
| delivered | 已交付 | 全部完成 |
| 权限标识 | 说明 |
|---|---|
| crm:contract:list | 合同列表查询 |
| crm:contract:query | 合同详情查询 |
| crm:contract:add | 合同新增 |
| crm:contract:edit | 合同修改 |
| crm:contract:remove | 合同删除 |
| crm:contract:export | 合同导出 |
| crm:contract:submit | 合同提交审批 |
| crm:contract:approval | 合同审批 |
| crm:contract:execute | 合同执行管理 |
| crm:contract:invalid | 合同作废 |
文档结束