文件
hl-api-changelog/changelogs-v2/2026-08/04_5368_v3合同管理完整接口与订单号搜索-修改接口-管理后台.md
T

50 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, change_type, author, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer change_type author backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 5368 v3 合同管理完整接口与订单号搜索 admin 修改接口 yaosutu(GIT) deployed verified implemented pi-main-session hl-admin@5925a3ff810b5ef0dbce027a0057e343a1910446 2026-08-04T22:35:00+08:00 PR #5501 已合并 dev-v3;deploy-panel 任务 0eae2ffb 成功,hl-order-service-v3 8086/8186 双实例 UP。已通过有效管理后台鉴权验证 Gateway 合同列表 12 个只读用例与目标合同详情;列表/详情 teamNo 一致。QA 报告因未保存含 PII 的详情截图仍为 PARTIAL,不影响已完成的运行时功能门禁。 2026-08-04 dev-v3

🔧【修改接口·管理后台】v3 合同管理完整接口与订单号搜索 (#5368)

PR: #5501 | 服务: hl-order-service-v3 | 更新日期: 2026-08-04 范围: 仅 8 个 admin Controller 的 56 个 /v3/admin/* 端点;不含 internal、mp、callback、job。

1. 接口背景与历史文档关系

管理后台合同页需要统一使用 v3 读写链路,并在合同列表按订单号定位数据。本文同时给出该页面依赖的全部 admin 契约,便于前端一次性移除 v1/v3 混用。

与已发布的 2026-07/31_5368_非订单页面统一补齐真实团号与团号搜索 的关系:

  • 2026-07 文档对应 PR #5386 / commit c9d1b12618,已约定 teamNo: string|null、列表 teamNo 搜索、详情返回 teamNo 和 v3 完整写侧。
  • 本文对应 PR #5501;不重复声称上述能力是本次新增,而是补齐全量 admin 契约并说明本次仅有的两项可见变化。

2. 本次真实变更

# 接口 原来 现在
1 GET /v3/admin/contract/list 无独立 orderNo 查询参数 新增可选 orderNo,对人类可读订单号做包含匹配
2 同上 teamNo / contactName 文本边界未完整固化 orderNo / teamNo / contactName 统一 trim;全空白忽略;%、_、\ 都按字面字符匹配;多条件按 AND 组合

N+1 消除是内部性能修复,不属于前端契约变更。

3. 通用协议

  • 认证:所有端点都需要管理后台 JWT;旅行社敏感写操作按接口标注需 ADMIN 或 SUPER_ADMIN。
  • 成功包装:{ "code":200,"data":...,"message":"操作成功","success":true },字段名是 message,不是 msg。
  • 分页包装:data.records / data.total / data.page / data.pageSize。
  • 失败包装:{ "code":业务错误码,"data":null,"message":"可读提示","success":false }。
  • 幂等:GET 幂等;PUT 为目标状态更新时可重试;POST 创建/上传不得盲目重试;DELETE 不保证重复调用仍成功。
  • 金额按 JSON string 消费;雪花 ID 按 string 消费,不得转 JavaScript Number。

变更接口(完整接口清单,56)

Controller 数量 路径域
AdminContractController 15 /v3/admin/contract
AdminContractSchemeController 7 /v3/admin/contract/scheme
AdminContractSchemeAttachmentController 4 /v3/admin/contract/scheme/<schemeId>/attachments
AdminContractSchemeTourGuideController 4 /v3/admin/contract/scheme/<schemeId>/tour-guides
AdminClauseTemplateController 6 /v3/admin/contract/clause-template
AdminTravelAgencyController 11 /v3/admin/travel-agency
AdminTravelAgencyQualificationController 4 /v3/admin/travel-agency/<agencyId>/qualification
AdminTravelAgencyPaymentController 5 /v3/admin/travel-agency/<agencyId>/payment

5. 接口详情

5.1 AdminContractController(15)

5.1.1 创建合同(标准模式)

  • 场景/协议:手工填写全量签约数据;POST /v3/admin/contract/create;管理员 JWT;非幂等。
  • 请求体(全部字段):orderId:long?、contractType:string?(TOUR/INSURANCE)、platform:string?(12301/LOCAL/TENCENT_ESIGN)、vendorCode:string?、templateCode:string!、agencyCode:string?、mchId:string?、transactorName:string?、transactorPhone:string?、destination:string!、routeName:string!、days:int?、nights:int?、departureDate:date!、returnDate:date!、departureCity:string?、groupId:string?、signatoryMode:int?(1..3)、signatoryName:string!、signatoryPhone:string!、signatoryIdType:int?、signatoryIdNumber:string!、signingPlace:string?、adultCost:decimal!、childCost:decimal?、totalAmount:decimal!、paymentMethod:int?(1..3)、disputeResolution:int?(1..2)、tribunalName:string?、litigationCourt:string?、contactName:string!、contactPhone:string!、supplementaryClause:string?、travelers:array!、accordingContract:boolean?、accordingContractPhase:object?、contractNum:string?、holdNum:string?、paymentDescription:string?、paymentOther:string?、agreeToBuyInsurance:boolean?、insuranceCompany:string?、insuranceCoverage:string?、insurancePremium:string?、insuranceProductName:string?、insurancePurchaseMethod:int?(1..3)、guideServiceCost:decimal?、paymentTime:string?、startHour:int?(0..23)、endHour:int?(0..23)、touristCondition:string?、schemeId:string?。travelers[]:name:string!、gender:string?(0/1/2)、age:int?、idCardType:int?(1/2)、idCardNo:string!、phone:string?、isSigner:boolean?、isChild:boolean?、nationality:string?、race:string?、roomGroupNo:int?。
  • 响应(完整):ContractDetailVO:contractId:string, orderId:string, schemeId:string|null, orderNo:string|null, teamNo:string|null, templateCode, templateName, contractNumber, platform, contractType, contractTypeLabel, mode, modeLabel, status, statusLabel, signUrl, qrCodeUrl, fileUrl, agencyCode, travelAgencyName, destination, departureDate, returnDate, totalAmount:string, touristCount:int, contactName, contactPhone, createTime, supplementaryClause, travelers[], statusLogs[];travelers[]={ travelerId:string,name,idCardType,idCardTypeLabel,idCardNo,phone,isSigner };statusLogs[]={ logId:string,contractId:string,oldStatus,oldStatusLabel,newStatus,newStatusLabel,source,sourceLabel,rawPayload,createTime }。
  • 边界/错误:行程未确认 510220;已有有效合同 510214;模板不存在 510215;签署人手机缺失 510213;必填/长度/手机格式错误返回参数校验失败。
  • 典型示例:请求 { "orderId":"2079000000000000001","templateCode":"TOURAGE_STANDARD","destination":"呼伦贝尔","routeName":"草原5日","departureDate":"2026-08-10","returnDate":"2026-08-14","signatoryName":"张某","signatoryPhone":"138****8000","signatoryIdNumber":"110101********1234","adultCost":"3000.00","totalAmount":"6000.00","contactName":"张某","contactPhone":"138****8000","travelers":[{ "name":"张某","idCardNo":"110101********1234","isSigner":true }] };响应 { "code":200,"data":{ "contractId":"2080000000000000001","orderId":"2079000000000000001","status":"GENERATED","totalAmount":"6000.00","travelers":[],"statusLogs":[] },"message":"操作成功","success":true }。

5.1.2 按方案创建合同

  • 场景/协议:后台从订单与方案自动组装合同;POST /v3/admin/contract/create-by-scheme; JWT;非幂等。
  • 入参:请求体 orderId:string!、schemeId:string!。
  • 响应(完整):与 5.1.1 的 ContractDetailVO 字段完全一致:contractId,orderId,schemeId,orderNo,teamNo,templateCode,templateName,contractNumber,platform,contractType,contractTypeLabel,mode,modeLabel,status,statusLabel,signUrl,qrCodeUrl,fileUrl,agencyCode,travelAgencyName,destination,departureDate,returnDate,totalAmount,touristCount,contactName,contactPhone,createTime,supplementaryClause,travelers[],statusLogs[]。
  • 边界/错误:方案不存在/已停用 510205;方案未配模板 510206;订单信息失败 510207;行程未确认 510220;已有合同 510214。
  • 示例:请求 { "orderId":"2079000000000000001","schemeId":"2020001" };响应 { "code":200,"data":{ "contractId":"2080000000000000001","orderId":"2079000000000000001","schemeId":"2020001","status":"GENERATED","travelers":[],"statusLogs":[] },"message":"操作成功","success":true }。

5.1.3 作废合同

  • 场景/协议:不可恢复地作废有效合同;POST /v3/admin/contract/<id>/invalidate; JWT;重复调用不保证成功。
  • 入参:路径 id:string! 合同 ID;无请求体。
  • 响应(完整):ContractVO={ contractId,orderId,schemeId,orderNo,teamNo,templateCode,templateName,contractNumber,platform,contractType,contractTypeLabel,mode,modeLabel,status,statusLabel,signUrl,qrCodeUrl,fileUrl,agencyCode,travelAgencyName,destination,departureDate,returnDate,totalAmount:string,touristCount,contactName,contactPhone,createTime }。
  • 边界/错误:合同不存在 510001;缺合同编号 510003;上游作废失败 510104。
  • 示例:请求 POST /v3/admin/contract/2080000000000000001/invalidate 无 body;响应 { "code":200,"data":{ "contractId":"2080000000000000001","status":"VOIDED","statusLabel":"已作废" },"message":"操作成功","success":true }。

5.1.4 合同列表(本 PR 修改)

  • 场景/协议:合同管理分页列表;GET /v3/admin/contract/list; JWT;幂等。
  • Query(全部):page:int?=1、pageSize:int?=10、orderId:string?、status:string?、platform:string?、orderNo:string?、teamNo:string?、contactName:string?。三个文本条件都 trim;全空白忽略;% / _ / \ 字面匹配;条件间 AND。
  • 响应(完整):PageResult.records[] 每项为 ContractVO={ contractId,orderId,schemeId,orderNo,teamNo,templateCode,templateName,contractNumber,platform,contractType,contractTypeLabel,mode,modeLabel,status,statusLabel,signUrl,qrCodeUrl,fileUrl,agencyCode,travelAgencyName,destination,departureDate,returnDate,totalAmount:string,touristCount,contactName,contactPhone,createTime };顶层 total:long,page:int,pageSize:int。
  • 边界:orderNo=% 只匹配订单号中真实的 %,不会全表命中;orderNo= 等价于未传;无结果返回空 records。
  • 示例:请求 GET /v3/admin/contract/list?page=1&pageSize=10&orderNo=HL2026&teamNo=26-08;响应 { "code":200,"data":{ "records":[{ "contractId":"2080000000000000001","orderId":"2079000000000000001","orderNo":"HL202608040001","teamNo":"26-0804","status":"SIGNED","totalAmount":"6000.00" }],"total":1,"page":1,"pageSize":10 },"message":"操作成功","success":true }。

5.1.5 合同详情

  • 场景/协议:查看合同、出行人和状态日志;GET /v3/admin/contract/<id>; JWT;幂等。
  • 入参:路径 id:string!。
  • 响应(完整):ContractDetailVO全字段:contractId,orderId,schemeId,orderNo,teamNo,templateCode,templateName,contractNumber,platform,contractType,contractTypeLabel,mode,modeLabel,status,statusLabel,signUrl,qrCodeUrl,fileUrl,agencyCode,travelAgencyName,destination,departureDate,returnDate,totalAmount,touristCount,contactName,contactPhone,createTime,supplementaryClause,travelers[{ travelerId,name,idCardType,idCardTypeLabel,idCardNo,phone,isSigner }],statusLogs[{ logId,contractId,oldStatus,oldStatusLabel,newStatus,newStatusLabel,source,sourceLabel,rawPayload,createTime }]。
  • 错误/示例:不存在 510001。请求 GET /v3/admin/contract/2080000000000000001;响应 { "code":200,"data":{ "contractId":"2080000000000000001","orderId":"2079000000000000001","teamNo":"26-0804","totalAmount":"6000.00","travelers":[{ "travelerId":"2081000000000000001","name":"张某" }],"statusLogs":[] },"message":"操作成功","success":true }。

5.1.6 下载合同文件

  • 场景/协议:取得已签署/已上报合同下载 URL;GET /v3/admin/contract/<id>/download; JWT;幂等。
  • 入参/响应:路径 id:string!;data:string 为下载 URL,无其他 data 字段。
  • 错误/边界:510001 不存在;510101 未签署完成;510102 文件 URL 缺失。
  • 示例:请求 GET /v3/admin/contract/2080000000000000001/download;响应 { "code":200,"data":"https://example.invalid/signed/contract.pdf?token=masked","message":"操作成功","success":true }。

5.1.7 重发签署短信

  • 场景/协议:为未完成签署的合同重发短信;POST /v3/admin/contract/<id>/resend-sms; JWT;有 1 分钟频控,不可并发重试。
  • 入参/响应:路径 id:string!;data:boolean。
  • 错误/边界:510001、510005 已签署、510006 过于频繁、510007 合同编号缺失、510008 联系人手机缺失。
  • 示例:请求 POST /v3/admin/contract/2080000000000000001/resend-sms 无 body;响应 { "code":200,"data":true,"message":"操作成功","success":true }。

5.1.8 合同模板列表

  • 场景/协议:创建合同时选择平台模板;GET /v3/admin/contract/templates; JWT;幂等。
  • Query:platform:string? (12301 等)。
  • 响应(完整):data[] 每项 templateId:string,templateCode:string,templateName:string,platform:string,mode:string,status:string,description:string|null,createTime:datetime。
  • 示例:请求 GET /v3/admin/contract/templates?platform=12301;响应 { "code":200,"data":[{ "templateId":"101","templateCode":"TOURAGE_STANDARD","templateName":"标准旅游合同","platform":"12301","mode":"STANDARD","status":"ACTIVE","description":null,"createTime":"2026-08-01T10:00:00" }],"message":"操作成功","success":true }。

5.1.9 刷新合同状态

  • 场景/协议:主动从合同平台同步最新状态;GET /v3/admin/contract/<id>/status; JWT;查询可重试,但可产生状态更新。
  • 入参/响应:路径 id:string!;ContractVO完整字段同 5.1.3。
  • 错误/示例:510001;510103 缺合同编号。请求 GET /v3/admin/contract/2080000000000000001/status;响应 { "code":200,"data":{ "contractId":"2080000000000000001","status":"SIGNED","statusLabel":"已签署" },"message":"操作成功","success":true }。

5.1.10 按订单查询全部合同

  • 场景/协议:查询订单下包含已作废数据的全部合同;GET /v3/admin/contract/by-order/<orderId>; JWT;幂等。
  • 入参/响应:orderId:string!;data[] 每项为 5.1.3 的完整 ContractVO。无记录返回 []。
  • 示例:请求 GET /v3/admin/contract/by-order/2079000000000000001;响应 { "code":200,"data":[{ "contractId":"2080000000000000001","orderId":"2079000000000000001","status":"SIGNED","totalAmount":"6000.00" }],"message":"操作成功","success":true }。

5.1.11 获取订单有效合同

  • 场景/协议:取指定订单最新非作废合同;GET /v3/admin/contract/active-by-order/<orderId>; JWT;幂等。
  • 入参/响应:orderId:string!;data 为 5.1.3 的完整 ContractVO,没有有效合同时可为 null。
  • 示例:请求 GET /v3/admin/contract/active-by-order/2079000000000000001;响应 { "code":200,"data":{ "contractId":"2080000000000000001","status":"SIGNED","totalAmount":"6000.00" },"message":"操作成功","success":true }。

5.1.12 可用合同平台列表

  • 场景/协议:展示已注册平台及可用性;GET /v3/admin/contract/platforms; JWT;幂等;无入参。
  • 响应(完整):data[] 每项 platformName:string,displayName:string,available:boolean。
  • 示例:请求 GET /v3/admin/contract/platforms;响应 { "code":200,"data":[{ "platformName":"12301","displayName":"12301团队报送","available":true }],"message":"操作成功","success":true }。

5.1.13 已配置旅行社列表

  • 场景/协议:按平台筛选可用旅行社;GET /v3/admin/contract/agencies; JWT;幂等。
  • Query:platform:string?。
  • 响应(完整):data[] 每项 code,agencyName,licenseNumber,businessLicenseNumber,agencyAddress,agencyCountry,agencyState,agencyCity,agencyDistrict,transactorName,transactorPhone,regionId,businessScope,zjParentId,appId,signKey,teamReportAppId,teamReportSignKey,mchId,complaintPhone,email,supportedPlatforms[],bankCard,complaintProvince,complaintCity,complaintAreaCode,complaintAddress,tribunalName,litigationCourt。注意其中存在高敏配置,仅在授权后台使用,不得写日志/埋点。
  • 示例:请求 GET /v3/admin/contract/agencies?platform=12301;响应 { "code":200,"data":[{ "code":"hulai","agencyName":"呼籁旅行社","licenseNumber":"L-XX-100001","supportedPlatforms":["12301"],"appId":"***","signKey":"***" }],"message":"操作成功","success":true }。

5.1.14 报备合同(线下签约)

  • 场景/协议:线下签完后上报监管平台;POST /v3/admin/contract/report; JWT;非幂等。
  • 请求体:字段、必填性与校验完全同 5.1.1 CreateContractRequest。
  • 响应(完整):与 5.1.1 ContractDetailVO 完全一致。
  • 错误/边界:参数校验;未知旅行社 510201;上报失败 510203;腾讯电子签不支持 SYNC 510403。
  • 示例:请求同 5.1.1,platform=12301;响应 { "code":200,"data":{ "contractId":"2080000000000000002","mode":"SYNC","status":"REPORTED","travelers":[],"statusLogs":[] },"message":"操作成功","success":true }。

5.1.15 上传已签署合同 PDF

  • 场景/协议:仅 SYNC 合同上传签署后 PDF;POST /v3/admin/contract/<id>/upload-pdf; JWT;非幂等。
  • 入参:路径 id:string!;multipart/form-data 中 file:file!。
  • 响应(完整):5.1.3 的完整 ContractVO。
  • 错误/边界:510001、510105 缺合同编号、510106 当前状态不允许、510107 上传失败;ONLINE/STANDARD 合同不适用。
  • 示例:请求 POST /v3/admin/contract/2080000000000000002/upload-pdf + file=@signed-contract.pdf;响应 { "code":200,"data":{ "contractId":"2080000000000000002","mode":"SYNC","status":"UPLOADED","fileUrl":"https://example.invalid/signed.pdf" },"message":"操作成功","success":true }。

5.2 AdminContractSchemeController(7)

ContractSchemeVO 完整字段:schemeId:string,name,description,contractPlatform,vendorCode,channel,contractTemplateCode,templateCode,contractTemplateName,contractMode,signatoryMode,agencyCode,agencyName,supplementaryClause,transactorName,transactorPhone,sortOrder,status,createTime。ContractSchemeRequest 完整字段:name:string!(max100),description:string?(max500),contractPlatform:string?(max50),vendorCode:string?(max32),channel:string?(max32),contractTemplateCode:string?(max50;兼容 templateCode/template_code),contractMode:string?(max50),signatoryMode:int?(1..3),agencyCode:string?(max50),supplementaryClause:string?(max2000),transactorName:string?(max50),transactorPhone:string?(11位手机),sortOrder:int?。

5.2.1 启用方案列表

  • GET /v3/admin/contract/scheme/list;产品/合同方案下拉;JWT;幂等;无入参。
  • 响应(完整):data[] 每项包含上述 ContractSchemeVO 全部 19 字段,仅返回 status=ACTIVE。
  • 示例:GET .../list → { "code":200,"data":[{ "schemeId":"2020001","name":"标准方案","contractPlatform":"12301","contractMode":"STANDARD","status":"ACTIVE" }],"message":"操作成功","success":true }。

5.2.2 全部方案列表

  • GET /v3/admin/contract/scheme/list-all;方案管理页;JWT;幂等;无入参。
  • 响应(完整):data[] 每项包含 ContractSchemeVO 全部 19 字段,包含 ACTIVE/INACTIVE。
  • 示例:GET .../list-all → { "code":200,"data":[{ "schemeId":"2020001","name":"标准方案","status":"ACTIVE" },{ "schemeId":"2020002","name":"旧方案","status":"INACTIVE" }],"message":"操作成功","success":true }。

5.2.3 方案详情

  • GET /v3/admin/contract/scheme/<schemeId>;编辑回显;JWT;幂等;路径 schemeId:string!。
  • 响应(完整):ContractSchemeVO 全部 19 字段。不存在返回 510209。
  • 示例:GET .../2020001 → { "code":200,"data":{ "schemeId":"2020001","name":"标准方案","contractPlatform":"12301","contractTemplateCode":"A00001","templateCode":"A00001","status":"ACTIVE" },"message":"操作成功","success":true }。

5.2.4 创建方案

  • POST /v3/admin/contract/scheme;新增平台/模板/签约模式配置;JWT;非幂等。
  • 请求体:上述 ContractSchemeRequest 全部 13 字段。响应:上述 ContractSchemeVO 全部 19 字段。
  • 边界/错误:name 必填;手机/长度/签署模式校验;未知旅行社 510201。
  • 示例:请求 { "name":"标准方案","contractPlatform":"12301","contractTemplateCode":"A00001","contractMode":"STANDARD","signatoryMode":1,"agencyCode":"hulai","sortOrder":10 };响应 { "code":200,"data":{ "schemeId":"2020001","name":"标准方案","status":"ACTIVE" },"message":"操作成功","success":true }。

5.2.5 更新方案

  • PUT /v3/admin/contract/scheme/<schemeId>;编辑方案;JWT;目标更新可重试。
  • 入参:路径 schemeId:string! + ContractSchemeRequest 全部字段。响应:ContractSchemeVO 全部字段。
  • 边界/错误:已用方案创建的合同不受后续修改影响;不存在 510209。
  • 示例:请求 PUT .../2020001 + { "name":"标准方案V2","contractPlatform":"12301","contractMode":"STANDARD" };响应 { "code":200,"data":{ "schemeId":"2020001","name":"标准方案V2","status":"ACTIVE" },"message":"操作成功","success":true }。

5.2.6 切换启停状态

  • PUT /v3/admin/contract/scheme/<schemeId>/toggle-status;启用/停用方案;JWT;每次翻转,非幂等。
  • 入参:schemeId:string!;无 body。响应:ContractSchemeVO 全部字段。不存在 510209。
  • 示例:PUT .../2020001/toggle-status → { "code":200,"data":{ "schemeId":"2020001","status":"INACTIVE" },"message":"操作成功","success":true }。

5.2.7 删除方案

  • DELETE /v3/admin/contract/scheme/<schemeId>;软删除;JWT;不保证重复调用成功。
  • 入参/响应:schemeId:string!;成功 data:null,无其他响应字段。
  • 错误/边界:不存在 510209;被产品引用 510218;引用校验不可用 510219。
  • 示例:DELETE .../2020001 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.3 AdminContractSchemeAttachmentController(4)

AttachmentResp 完整字段:attachmentId:string,schemeId:string,fileName:string,ossUrl:string,ossKey:string,fileSize:long,fileType:string,sortOrder:int,createTime:datetime。

5.3.1 附件列表

  • GET /v3/admin/contract/scheme/<schemeId>/attachments;按 sortOrder 升序回显;JWT;幂等;schemeId:string!。
  • 响应(完整):data[] 每项含 AttachmentResp 全部 9 字段;无配置返回 []。
  • 示例:GET .../2020001/attachments → { "code":200,"data":[{ "attachmentId":"1010001","schemeId":"2020001","fileName":"service.pdf","ossUrl":"https://example.invalid/a.pdf","ossKey":"contract/a.pdf","fileSize":1024,"fileType":"PDF","sortOrder":10,"createTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.3.2 上传附件

  • POST /v3/admin/contract/scheme/<schemeId>/attachments;上传合同方案附件;JWT;非幂等。
  • 入参:schemeId:string!;multipart file:file!、fileType:string?(PDF/IMAGE/OTHER)、sortOrder:int?。响应:AttachmentResp 全部 9 字段。
  • 错误/边界:空文件、超限、OSS 不可用或上传失败都返回 510508。
  • 示例:POST .../2020001/attachments + file=@service.pdf&fileType=PDF&sortOrder=10 → { "code":200,"data":{ "attachmentId":"1010001","schemeId":"2020001","fileType":"PDF","sortOrder":10 },"message":"操作成功","success":true }。

5.3.3 删除附件

  • DELETE /v3/admin/contract/scheme/<schemeId>/attachments/<attachmentId>;软删;JWT;路径 schemeId:string!,attachmentId:string!;无 body。
  • 响应/错误:成功 data:null;附件不存在或不属于该方案 510506。已生成合同的旧 URL 不受影响。
  • 示例:DELETE .../2020001/attachments/1010001 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.3.4 更新附件排序

  • PUT /v3/admin/contract/scheme/<schemeId>/attachments/<attachmentId>/sort;JWT;目标更新可重试。
  • 入参:路径 schemeId:string!,attachmentId:string!;body sortOrder:int!(>=0)。响应:data:null。
  • 错误/示例:不存在 510506;负数为参数校验失败。请求 { "sortOrder":20 } → { "code":200,"data":null,"message":"操作成功","success":true }。

5.4 AdminContractSchemeTourGuideController(4)

GuideResp={ guideId:string,schemeId:string,name:string,phone:string|null,licenseNumber:string|null,sortOrder:int,createTime:datetime };GuideSave={ name:string!(max50),phone:string?(11位手机,max20),licenseNumber:string?(max64),sortOrder:int?(>=0) }。

5.4.1 导游列表

  • GET /v3/admin/contract/scheme/<schemeId>/tour-guides;JWT;幂等;schemeId:string!;未配导游返回 [],不兜底。
  • 响应(完整):data[] 每项含 GuideResp 全部 7 字段。
  • 示例:GET .../2020001/tour-guides → { "code":200,"data":[{ "guideId":"3030001","schemeId":"2020001","name":"张某","phone":"138****8000","licenseNumber":"L-XX-100001","sortOrder":0,"createTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.4.2 新增导游

  • POST /v3/admin/contract/scheme/<schemeId>/tour-guides;JWT;创建加锁,非幂等。
  • 入参:schemeId:string! + GuideSave 全部 4 字段。响应:GuideResp 全部 7 字段。
  • 边界/示例:姓名必填;手机格式错误拒绝。请求 { "name":"张某","phone":"13800138000","licenseNumber":"L-XX-100001","sortOrder":0 } → { "code":200,"data":{ "guideId":"3030001","schemeId":"2020001","name":"张某","phone":"13800138000","licenseNumber":"L-XX-100001","sortOrder":0,"createTime":"2026-08-04T10:00:00" },"message":"操作成功","success":true }。

5.4.3 修改导游

  • PUT /v3/admin/contract/scheme/<schemeId>/tour-guides/<guideId>;JWT;目标更新可重试。
  • 入参:schemeId:string!,guideId:string! + GuideSave 全部字段。响应:GuideResp 全部字段。
  • 错误/示例:不存在/归属不符 510507。请求 { "name":"李某","sortOrder":1 } → { "code":200,"data":{ "guideId":"3030001","schemeId":"2020001","name":"李某","sortOrder":1 },"message":"操作成功","success":true }。

5.4.4 删除导游

  • DELETE /v3/admin/contract/scheme/<schemeId>/tour-guides/<guideId>;JWT;不保证重复调用成功。
  • 入参/响应:schemeId:string!,guideId:string!;成功 data:null。不存在/归属不符 510507。
  • 示例:DELETE .../2020001/tour-guides/3030001 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.5 AdminClauseTemplateController(6)

ClauseTemplateVO={ templateId:string,name:string,content:string,sortOrder:int,status:string,createTime:datetime };ClauseTemplateRequest={ name:string!,content:string!,sortOrder:int? }。

5.5.1 启用模板列表

  • GET /v3/admin/contract/clause-template/list;创建合同时选择;JWT;幂等;无入参。
  • 响应(完整):data[] 每项含 ClauseTemplateVO 全部 6 字段,仅 ACTIVE。
  • 示例:GET .../list → { "code":200,"data":[{ "templateId":"401","name":"夏季小团","content":"脱敏示例条款","sortOrder":1,"status":"ACTIVE","createTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.5.2 全部模板列表

  • GET /v3/admin/contract/clause-template/list-all;模板管理页;JWT;幂等;无入参。
  • 响应(完整):data[] 每项含 ClauseTemplateVO 全部 6 字段,包含 ACTIVE/INACTIVE。
  • 示例:GET .../list-all → { "code":200,"data":[{ "templateId":"401","name":"夏季小团","content":"条款","sortOrder":1,"status":"INACTIVE","createTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.5.3 创建补充约定模板

  • POST /v3/admin/contract/clause-template;JWT;非幂等。
  • 请求/响应:body ClauseTemplateRequest 全部 3 字段;返回 ClauseTemplateVO 全部 6 字段。名称和内容必填。
  • 示例:请求 { "name":"夏季小团","content":"脱敏示例条款","sortOrder":1 } → { "code":200,"data":{ "templateId":"401","name":"夏季小团","content":"脱敏示例条款","sortOrder":1,"status":"ACTIVE","createTime":"2026-08-04T10:00:00" },"message":"操作成功","success":true }。

5.5.4 更新补充约定模板

  • PUT /v3/admin/contract/clause-template/<id>;JWT;可重试。
  • 入参/响应:id:string! + ClauseTemplateRequest 全部字段;返回 ClauseTemplateVO 全部字段。旧合同快照不受影响。
  • 错误/示例:不存在 510211。请求 { "name":"夏季小团V2","content":"新条款","sortOrder":2 } → { "code":200,"data":{ "templateId":"401","name":"夏季小团V2","content":"新条款","sortOrder":2,"status":"ACTIVE" },"message":"操作成功","success":true }。

5.5.5 切换模板启停

  • PUT /v3/admin/contract/clause-template/<id>/toggle-status;JWT;每次翻转,非幂等。
  • 入参/响应:id:string!;无 body;返回 ClauseTemplateVO 全部 6 字段。不存在 510211。
  • 示例:PUT .../401/toggle-status → { "code":200,"data":{ "templateId":"401","name":"夏季小团","content":"条款","sortOrder":1,"status":"INACTIVE","createTime":"2026-08-04T10:00:00" },"message":"操作成功","success":true }。

5.5.6 删除补充约定模板

  • DELETE /v3/admin/contract/clause-template/<id>;JWT;软删;重复调用不保证成功。
  • 入参/响应:id:string!;成功 data:null。已创建合同保留快照。不存在 510211。
  • 示例:DELETE .../401 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.6 AdminTravelAgencyController(11)

AgencySimpleResp={ agencyId:string,code,agencyName,isPrimary:int,sortOrder:int,createTime,updateTime }。

AgencyResp 完整字段:agencyId:string,code,agencyName,licenseNumber,businessLicenseNumber,agencyCountry,agencyState,agencyCity,agencyDistrict,agencyAddress,transactorName,transactorPhone,email,bankCard,complaintPhone,complaintProvince,complaintCity,complaintAreaCode,complaintAddress,tribunalName,litigationCourt,regionId,businessScope,zjParentId,appId,signKey,teamReportAppId,teamReportSignKey,supportedPlatforms,supportedPlatformsList[],supportedPlatformsLabels[],isPrimary,status,statusLabel,sortOrder,visible,remark,qualifications[],payments[],createTime,updateTime。 AgencySave 完整字段:code:string!(2..32,小写字母/数字/-),agencyName:string!(max128),licenseNumber:string?(max64),businessLicenseNumber:string!,agencyCountry,agencyState,agencyCity,agencyDistrict,agencyAddress,transactorName,transactorPhone,email,bankCard,complaintPhone,complaintProvince,complaintCity,complaintAreaCode,complaintAddress,tribunalName,litigationCourt,regionId,businessScope,zjParentId:int?,appId,signKey,teamReportAppId,teamReportSignKey,supportedPlatforms:string(JSON array),isPrimary:int?(0/1),status:string?,sortOrder:int?,visible:int?(0/1),remark。

5.6.1 旅行社分页

  • GET /v3/admin/travel-agency/page;管理列表;ADMIN+;幂等。
  • Query(全部):page:int?,pageSize:int?,status:string?(ENABLED/DISABLED),isPrimary:int?(0/1),agencyName:string?。
  • 响应(完整):records[] 每项含上述 AgencyResp 全部字段;顶层 total,page,pageSize。
  • 示例:GET .../page?page=1&pageSize=10&status=ENABLED → { "code":200,"data":{ "records":[{ "agencyId":"7001","code":"hulai","agencyName":"呼籁旅行社","status":"ENABLED","statusLabel":"启用","qualifications":[],"payments":[] }],"total":1,"page":1,"pageSize":10 },"message":"操作成功","success":true }。

5.6.2 启用旅行社下拉

  • GET /v3/admin/travel-agency/enabled;ADMIN+;幂等;无入参。
  • 响应(完整):data[] 每项含 AgencySimpleResp 全部 7 字段。
  • 示例:GET .../enabled → { "code":200,"data":[{ "agencyId":"7001","code":"hulai","agencyName":"呼籁旅行社","isPrimary":1,"sortOrder":1,"createTime":"2026-08-01T10:00:00","updateTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.6.3 产品页可用旅行社

  • GET /v3/admin/travel-agency/enabled-for-product;过滤经营许可证号为空的公司;ADMIN+;幂等;无入参。
  • 响应(完整):data[] 每项含 AgencySimpleResp 全部 7 字段。
  • 示例:GET .../enabled-for-product → { "code":200,"data":[{ "agencyId":"7001","code":"hulai","agencyName":"呼籁旅行社","isPrimary":1,"sortOrder":1,"createTime":"2026-08-01T10:00:00","updateTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.6.4 按合同平台筛选旅行社

  • GET /v3/admin/travel-agency/enabled-by-platform;合同方案下拉;ADMIN+;幂等。
  • Query:contractPlatform:string! (12301/TENCENT_ESIGN)。响应:AgencySimpleResp 全部 7 字段;主体公司优先、ID 升序。
  • 边界/示例:空值返回“合同平台不能为空”。GET .../enabled-by-platform?contractPlatform=12301 → { "code":200,"data":[{ "agencyId":"7001","code":"hulai","agencyName":"呼籁旅行社","isPrimary":1,"sortOrder":1,"createTime":"2026-08-01T10:00:00","updateTime":"2026-08-04T10:00:00" }],"message":"操作成功","success":true }。

5.6.5 旅行社详情

  • GET /v3/admin/travel-agency/<id>;含资质和支付子表;ADMIN+;幂等;id:string!。
  • 响应(完整):上述 AgencyResp 全部字段;qualifications[] 字段见 5.7;payments[] 字段见 5.8。不存在 594001。
  • 示例:GET .../7001 → { "code":200,"data":{ "agencyId":"7001","code":"hulai","agencyName":"呼籁旅行社","status":"ENABLED","qualifications":[],"payments":[] },"message":"操作成功","success":true }。

5.6.6 新建旅行社

  • POST /v3/admin/travel-agency;SUPER_ADMIN;非幂等。
  • 请求体:上述 AgencySave 全部 33 字段。响应:data:string 新 agencyId,无其他 data 字段。
  • 边界/错误:编码重复 594010;非 SUPER_ADMIN 594009;编码格式/必填/长度校验。高敏 key 不得打印。
  • 示例:请求 { "code":"demo-agency","agencyName":"示例旅行社","businessLicenseNumber":"9115********0001","supportedPlatforms":"[\"12301\"]","status":"ENABLED","isPrimary":0,"visible":1 } → { "code":200,"data":"7002","message":"操作成功","success":true }。

5.6.7 编辑旅行社

  • PUT /v3/admin/travel-agency/<id>;ADMIN+;目标更新可重试。
  • 入参/响应:id:string! + AgencySave 全部字段;成功 data:null。code 编辑时不可改。
  • 错误/示例:不存在 594001。请求 { "code":"hulai","agencyName":"呼籁旅行社","businessLicenseNumber":"9115********0001","status":"ENABLED" } → { "code":200,"data":null,"message":"操作成功","success":true }。

5.6.8 停用旅行社

  • PUT /v3/admin/travel-agency/<id>/disable;ADMIN+;路径 id:string!;无 body;目标状态幂等。
  • 响应/错误:成功 data:null;不存在 594001;主体公司不可停用 594005。
  • 示例:PUT .../7002/disable → { "code":200,"data":null,"message":"操作成功","success":true }。

5.6.9 启用旅行社

  • PUT /v3/admin/travel-agency/<id>/enable;ADMIN+;路径 id:string!;无 body;目标状态幂等。
  • 响应/错误:成功 data:null;不存在 594001。
  • 示例:PUT .../7002/enable → { "code":200,"data":null,"message":"操作成功","success":true }。

5.6.10 删除旅行社

  • DELETE /v3/admin/travel-agency/<id>;ADMIN+;强保留语义(状态置 DISABLED);不保证重复调用成功。
  • 入参/响应:id:string!;成功 data:null。
  • 错误/边界:594001;主体公司 594005;本接口不做物理删除。
  • 示例:DELETE .../7002 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.6.11 设为主体公司

  • PUT /v3/admin/travel-agency/<id>/set-primary;SUPER_ADMIN;路径 id:string!;无 body;目标状态幂等。
  • 响应/错误:成功 data:null;594001;目标停用或主体约束失败 594005/594006;非 SUPER_ADMIN 594009。
  • 示例:PUT .../7001/set-primary → { "code":200,"data":null,"message":"操作成功","success":true }。

5.7 AdminTravelAgencyQualificationController(4)

QualificationResp={ qualificationId:string,agencyId:string,name,qualificationType,qualificationTypeLabel,fileUrl,fileType,fileTypeLabel,issueDate,expireDate,sortOrder,remark,createTime,updateTime }。 QualificationSave={ name:string!(max64),qualificationType:string!(max32),fileUrl:string!,fileType:string?(PDF/IMAGE/OTHER),issueDate:date?,expireDate:date|null,sortOrder:int?,remark:string? }。

5.7.1 资质列表

  • GET /v3/admin/travel-agency/<agencyId>/qualification;ADMIN+;幂等;agencyId:string!。
  • 响应(完整):data[] 每项含 QualificationResp 全部 14 字段;fileUrl 为有效期 30 分钟的临时 URL。
  • 示例:GET .../7001/qualification → { "code":200,"data":[{ "qualificationId":"8001","agencyId":"7001","name":"旅行社业务经营许可证","qualificationType":"TRAVEL_AGENCY_LICENSE","qualificationTypeLabel":"旅行社业务经营许可证","fileUrl":"https://example.invalid/temp?token=masked","fileType":"PDF","fileTypeLabel":"PDF","issueDate":"2024-01-01","expireDate":null,"sortOrder":1,"remark":null,"createTime":"2026-08-01T10:00:00","updateTime":"2026-08-01T10:00:00" }],"message":"操作成功","success":true }。

5.7.2 新增资质

  • POST /v3/admin/travel-agency/<agencyId>/qualification;ADMIN+;非幂等。
  • 入参/响应:agencyId:string! + QualificationSave 全部 8 字段;成功 data:string 新 qualificationId。
  • 错误/边界:文件无效(>10MB、非 PDF/JPG/PNG、上传失败)594008;长期有效传 expireDate:null。
  • 示例:请求 { "name":"旅行社业务经营许可证","qualificationType":"TRAVEL_AGENCY_LICENSE","fileUrl":"oss://private/masked.pdf","fileType":"PDF","issueDate":"2024-01-01","expireDate":null,"sortOrder":1 } → { "code":200,"data":"8001","message":"操作成功","success":true }。

5.7.3 编辑资质

  • PUT /v3/admin/travel-agency/<agencyId>/qualification/<qualificationId>;ADMIN+;目标更新可重试。
  • 入参/响应:路径中 agencyId 用于 URL 归类,后端操作键为 qualificationId:string!;body QualificationSave 全部字段;成功 data:null。
  • 错误/示例:不存在 594001;文件无效 594008。请求 { "name":"经营许可证(新)","qualificationType":"TRAVEL_AGENCY_LICENSE","fileUrl":"oss://private/masked.pdf","fileType":"PDF" } → { "code":200,"data":null,"message":"操作成功","success":true }。

5.7.4 删除资质

  • DELETE /v3/admin/travel-agency/<agencyId>/qualification/<qualificationId>;ADMIN+;软删;不保证重复调用成功。
  • 入参/响应:qualificationId:string!;成功 data:null;不存在 594001。
  • 示例:DELETE .../7001/qualification/8001 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.8 AdminTravelAgencyPaymentController(5)

PaymentResp={ paymentId:string,agencyId:string,name,mchId,appId,apiV3Key,mchSerialNo,publicKeyId,privateKeyPath,publicKeyPath,isDefault:int,status,statusLabel,remark,createTime,updateTime }。 PaymentSave={ name:string!(max64),mchId:string!(max32),appId:string!(max64),apiV3Key:string!,mchSerialNo:string?,publicKeyId:string?,isDefault:int?(0/1),status:string?(ENABLED/DISABLED),remark:string? }。这些接口包含高敏密钥/证书信息,仅 SUPER_ADMIN 使用,响应不得写入日志、埋点或客户端持久存储。

5.8.1 支付配置列表

  • GET /v3/admin/travel-agency/<agencyId>/payment;SUPER_ADMIN;幂等;agencyId:string!。
  • 响应(完整):data[] 每项含 PaymentResp 全部 16 字段。
  • 示例(脱敏):GET .../7001/payment → { "code":200,"data":[{ "paymentId":"9001","agencyId":"7001","name":"微信支付-主商户","mchId":"1106******39","appId":"wx************ff36","apiV3Key":"***","mchSerialNo":"***","publicKeyId":"PUB_***","privateKeyPath":"cert/***/apiclient_key.pem","publicKeyPath":"cert/***/public_key.pem","isDefault":1,"status":"ENABLED","statusLabel":"启用","remark":null,"createTime":"2026-08-01T10:00:00","updateTime":"2026-08-01T10:00:00" }],"message":"操作成功","success":true }。

5.8.2 新增支付配置

  • POST /v3/admin/travel-agency/<agencyId>/payment;SUPER_ADMIN;非幂等。
  • 入参/响应:agencyId:string! + PaymentSave 全部 9 字段;成功 data:string 新 paymentId。privateKeyPath/publicKeyPath 不接受前端传入。
  • 错误/边界:商户号重复 594011;非 SUPER_ADMIN 594009;必填/长度校验。
  • 示例(虚构):请求 { "name":"微信支付-主商户","mchId":"1900000001","appId":"wxdemo000000000001","apiV3Key":"***REDACTED***","mchSerialNo":"***REDACTED***","publicKeyId":"PUB_DEMO","isDefault":1,"status":"ENABLED" } → { "code":200,"data":"9001","message":"操作成功","success":true }。

5.8.3 编辑支付配置

  • PUT /v3/admin/travel-agency/<agencyId>/payment/<paymentId>;SUPER_ADMIN;目标更新可重试。
  • 入参/响应:URL 含 agencyId,操作键为 paymentId:string!;body PaymentSave 全部字段;成功 data:null。
  • 错误/示例:不存在 594001;商户号冲突 594011。请求 { "name":"微信支付-主商户","mchId":"1900000001","appId":"wxdemo000000000001","apiV3Key":"***REDACTED***","status":"ENABLED" } → { "code":200,"data":null,"message":"操作成功","success":true }。

5.8.4 删除支付配置

  • DELETE /v3/admin/travel-agency/<agencyId>/payment/<paymentId>;SUPER_ADMIN;不保证重复调用成功。
  • 入参/响应:paymentId:string!;成功 data:null。
  • 错误/边界:不存在 594001;默认商户不可删 594007,须先将另一条设为默认。
  • 示例:DELETE .../7001/payment/9002 → { "code":200,"data":null,"message":"操作成功","success":true }。

5.8.5 设为默认支付商户

  • PUT /v3/admin/travel-agency/<agencyId>/payment/<paymentId>/set-default;SUPER_ADMIN;目标状态幂等。
  • 入参/响应:agencyId:string!,paymentId:string!;无 body;成功 data:null。
  • 错误/边界:支付配置不存在/归属不符 594001;同一公司最多一条 isDefault=1。
  • 示例:PUT .../7001/payment/9001/set-default → { "code":200,"data":null,"message":"操作成功","success":true }。

6. 枚举/数据字典(按类型分表)

6.1 contract_status

值 中文 说明
PENDING 待生成 初始等待
GENERATED 已生成 合同已生成,待签署
SIGNING 签署中 平台签署流程中
SIGNED 已签署 签署完成
REPORTED 已上报 SYNC 报备完成
UPLOADED 已上传 SYNC PDF 已上传
VOIDING 作废中 等待平台确认
VOIDED 已作废 终态

6.2 contract_platform

值 中文 说明
12301 12301 团队报送 监管平台
LOCAL 本地/线下 本地报备
TENCENT_ESIGN 腾讯电子签 电子签约

6.3 contract_mode

值 中文 说明
STANDARD 电子签约 平台生成并签署
SYNC 线下报备 线下签署后报备/上传

6.4 common_status / contract_scheme_status / contract_template_status

值 中文 说明
ACTIVE 启用 可供新业务选择
INACTIVE 停用 历史引用不受影响

6.5 contract_attachment_type

值 中文 说明
PDF PDF PDF 附件
IMAGE 图片 图片附件
OTHER 其他 其他文件

6.6 agency_status

值 中文 说明
ENABLED 启用 可被产品/合同选择
DISABLED 停用 不可用于新业务

6.7 agency_contract_platform

值 中文 说明
12301 12301 团队报送 旅行社已开通 12301
TENCENT_ESIGN 腾讯电子签 旅行社已开通腾讯电子签

6.8 agency_qualification_type

值 中文 说明
BUSINESS_LICENSE 营业执照 公司营业执照
TRAVEL_AGENCY_LICENSE 旅行社业务经营许可证 旅行社经营资质
VALUE_ADDED_TELECOM_LICENSE 增值电信业务经营许可证 电信业务资质

6.9 agency_qualification_file_type

值 中文 说明
PDF PDF PDF 文件
IMAGE 图片 JPG/PNG 等
OTHER 其他 其他类型(服务端仍会做安全校验)

6.10 其他取值

字段 值 说明
contractType TOUR / INSURANCE 旅游合同 / 保险单
signatoryMode 1 / 2 / 3 短信 / 现场 / 线下
paymentMethod 1 / 2 / 3 现金 / 转账 / 在线
disputeResolution 1 / 2 诉讼 / 仲裁
insurancePurchaseMethod 1 / 2 / 3 委托旅行社 / 自行购买 / 放弃
gender 0 / 1 / 2 未知 / 男 / 女
idCardType 1 / 2 身份证 / 护照
channel MP / ADMIN / CHANNEL / null 小程序 / 管理端 / 分销 / 兜底

7. 错误码摘要

code 含义 典型接口
510001 合同不存在 详情/作废/下载/刷新/上传
510005-510008 短信重发状态、频控或必要数据缺失 重发短信
510101-510107 下载/刷新/作废/PDF 上传业务校验 合同写操作
510201-510220 旅行社、方案、模板、订单、签署人及重复合同校验 创建/方案管理
510403 腾讯电子签不支持 SYNC 报备合同
510506-510508 附件/导游不存在或上传失败 方案附件/导游
594001 旅行社或其子资源不存在 旅行社/资质/支付
594005-594006 主体公司约束 停用/删除/设为主体
594007 默认支付商户不可删 删除支付配置
594008 资质文件无效 新增/编辑资质
594009 仅 SUPER_ADMIN 可执行 敏感管理操作
594010 旅行社编码重复 新建旅行社
594011 微信支付商户号冲突 新增/编辑支付配置

验证证据

  • PR #5501 已合并:merge commit 45117d3d5896515909e303dcbfa9d3616f881803,head f66d236f2aea2d0a63db8cb58d59f2ae74e2b6fb。
  • 基于合并代码静态核对 8 个 admin Controller,端点计数 15+7+4+4+6+11+4+5=56。
  • 文档本地自检:56 个接口小节、只有目标文件进入 commit、git diff --check 通过。
  • deploy-panel 任务 0eae2ffb 执行成功;hl-order-service-v3 在 dev-v3 的 8086/8186 双实例均为 UP。
  • 通过已登录管理后台的有效 Gateway 鉴权完成合同列表真实 HTTP 验证:code=200、total=118;共12个只读用例覆盖 orderNo 精确/模糊/trim、teamNo、%/_ 字面匹配等边界。
  • 真实非空样本为 orderNo=HL20260803220907715、teamNo=26-2489;随后在同一鉴权会话打开目标合同 MOCK-2084280429048741890 详情,详情显示 teamNo=26-2489 且含列表外字段,证明详情调用成功且列表/详情一致。
  • QA 证据报告:D:/work/project-doc/PRPs/reports/5368-contract-teamno-gateway-acceptance.md,状态仍为 PARTIAL。原因是详情页含未脱敏联系电话/签署链接,为避免 PII 落盘未保存截图;运行时列表与详情功能门禁已验证。
  • 本次未查询数据库实时行;上述结论来自部署状态与真实 Gateway HTTP/管理后台详情调用。

8. 业务边界与 v1/v3 数据隔离

  • 根据 Issue #5368 在 2026-08-04 的决策,管理后台合同整页的列表、详情、创建、作废、刷新、下载、短信和上传统一调用 /v3/admin/*。
  • v1 存量合同不迁移,自然消亡;新合同全部走 v3。
  • v1 与 v3 合同数据不互通;不得把 v3 响应的 contractId 传给 v1 端点,也不得用 v3 详情读 v1 旧 ID。
  • teamNo 未生成时为 null;不得用 orderNo 伪装团号。
  • 列表多条件为 AND;文本筛选是数据库分页前的包含匹配,total 与翻页结果一致。

9. 修改前后对比

维度 修改前 修改后
订单号搜索 合同列表不能独立按 orderNo 查询 可传 orderNo 包含匹配
文本空白 语义未完整固化 三个文本条件 trim,全空白忽略
LIKE 特殊字符 可能被当作通配符 %、_、\ 按字面字符
数据版本 前端可能仍混用 v1/v3 合同整页读写统一 v3,v1 存量不迁移

10. 影响评估/回滚

  • 向后兼容:是。orderNo 为新增可选 query;原有参数和响应字段不删除。
  • 前端是否必须同步上线:不必与后端强绑同时上线;但合同页应尽快切到本文的 v3 端点,否则无法正确消费 v3 数据。
  • 回滚后前端行为:停止传递 orderNo;保留原 teamNo/contactName 及其他查询。不得回滚为 v1/v3 混用。

11. 注意事项

  • 前端可清理“拉全量合同后在浏览器按订单号过滤”的 workaround,直接传 orderNo。
  • 请求参数中用户输入的 %、_、\ 不需前端自行转义;正常 URL 编码即可。
  • 支付配置、证书、身份证、手机号和签约密钥都不得写入前端日志/埋点;本文示例全部为虚构脱敏值。

12. 关联/联系人

关联/联系人

链接

联系人

  • 后端负责人: @wx