Selaa lähdekoodia

docs: 补充自营售票管理端/用户端接口文档

管理端文档为自营管理菜单(票种/景点/分销商/销售日历)的接口依据, 设计文档 docs/superpowers/specs/2026-10-02-self-manage-menu-design.md 已引用
LaveyD 5 päivää sitten
vanhempi
commit
dfc7900fcc
2 muutettua tiedostoa jossa 1112 lisäystä ja 0 poistoa
  1. 519 0
      自营售票用户端接口.md
  2. 593 0
      自营售票管理端接口.md

+ 519 - 0
自营售票用户端接口.md

@@ -0,0 +1,519 @@
+# 自营售票 · 用户端接口(按调试顺序)
+
+用户端前缀 `/thirdParty`。三种身份共用这一套路径,靠登录时写入 token 的类型区分:
+
+| 身份 | 登录方式 | 能看到的票 |
+|---|---|---|
+| 微信公众号 `wx` | 网页授权 | 票种勾了渠道 `wx` |
+| 微信小程序 `wx-mini` | `wx.login` 换 code | 票种勾了渠道 `wx-mini` |
+| 自营分销商 `third-party` | 账号密码 | 票种勾了渠道 `third-party` **且**管理端已授权给该分销商 |
+
+请求头 token 名:`satoken-thirdParty`(和租户管理端的 `satoken` 不是同一个)。登录接口本身不带这个头。
+
+统一 `POST` + `application/json`(登录里几个微信接口是 `GET`)。业务参数放在 `data` 里:
+
+```text
+{ "data": { ... } }
+```
+
+成功:`code = 200`,业务数据在 `data`。写接口成功时 `data` 可能为空。
+
+分页列表的 `data` 是 MyBatis-Plus 分页对象:
+
+| 字段 | 说明 |
+|---|---|
+| records | 当前页列表 |
+| total | 总条数 |
+| size | 每页条数 |
+| current | 当前页 |
+
+公共分页字段(挂在 `data` 上):
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| pageNum | 否 | 页码,建议传 1 |
+| pageSize | 否 | 每页条数。要一次拉全量可传 `-1` |
+
+删除类统一 `data.idList`。
+
+**哪些接口谁能调**
+
+| 模块 | 微信 / 小程序 | 自营分销商 |
+|---|---|---|
+| 登录、景点、票种、下单、订单、退单、开票 | 可以 | 可以 |
+| 微信支付 `/thirdParty/pay/wxPay` | 可以(待支付订单) | 一般不用 |
+| 当前登录 `/thirdParty/auth/me` | 只回 token | 回余额、资料、已授权票种 |
+| 售票/核销统计、报表 | 调了会「无权访问」 | 只看自己的单 |
+
+管理端配置走 [自营售票管理端接口.md](./自营售票管理端接口.md),不要从用户端调 `/tenant/**`。
+
+**接口目录(按联调顺序)**
+
+| 顺序 | 模块 | 方法 | 路径 | 备注 |
+|---|---|---|---|---|
+| 1 | 登录 | GET | `/thirdParty/auth/wxAuth` | 仅公众号,302 跳微信 |
+| 1 | 登录 | GET | `/thirdParty/auth/wxLogin` | 仅公众号回调 |
+| 1 | 登录 | GET | `/thirdParty/auth/wxMiniLogin` | 仅小程序 |
+| 1 | 登录 | POST | `/thirdParty/auth/thirdPartyLogin` | 仅分销商 |
+| 1 | 登录 | POST | `/thirdParty/auth/me` | 已登录后刷新资料 |
+| 2 | 景点 | POST | `/thirdParty/selfScenic` 列表 | 非分页 |
+| 3 | 票种 | POST | `/thirdParty/selfTicketType` 列表 | 非分页,价格是底价 |
+| 3 | 票种 | POST | `/thirdParty/selfTicketType/get` 某天详情 | 日历价、场次库存 |
+| 4 | 订单 | POST | `/thirdParty/order/create` 下单 | |
+| 4 | 支付 | POST | `/thirdParty/pay/wxPay` | 仅微信/小程序待支付单 |
+| 4 | 订单 | POST | `/thirdParty/order/list` 我的订单 | |
+| 4 | 订单 | POST | `/thirdParty/order/{id}` 详情 | |
+| 4 | 订单 | POST | `/thirdParty/order/cancel` 退单 | |
+| 5 | 发票 | POST | `/thirdParty/invoice/query` | 只能查自己的票 |
+| 5 | 发票 | POST | `/thirdParty/invoice/doInvoice` | |
+| 6 | 统计 | POST | `/thirdParty/statistics/orderSaleStatistics` | **仅分销商** |
+| 6 | 统计 | POST | `/thirdParty/statistics/orderCheckStatistics` | **仅分销商** |
+| 6 | 报表 | POST | `/thirdParty/reportMission` 列表 | **仅分销商** |
+| 6 | 报表 | POST | `/thirdParty/reportMission/delete` | **仅分销商** |
+
+建议联调:先登录拿到 token → 景点列表有数据 → 票种列表有数据 → 用详情接口看某一天价格 → 再下单。分销商下单用余额;微信/小程序下单后走微信支付。
+
+---
+
+## 1. 登录 `/thirdParty/auth`
+
+### 1.1 自营分销商登录
+
+`POST /thirdParty/auth/thirdPartyLogin`
+
+账号由管理端「自营分销商」创建。密码传输方式和现有管理端登录一样:前端 DES 加密后再放进 `password`,不要明文。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| projectName | 是 | 租户项目名 |
+| userName | 是 | 登录名(管理端的 `loginName`) |
+| password | 是 | DES 加密后的密码 |
+
+失败常见文案:用户名或密码不正确、账号已禁用。禁用后已登录的 token 也会被踢掉。
+
+返回:
+
+| 字段 | 说明 |
+|---|---|
+| token | 后续请求头 `satoken-thirdParty` |
+| userThirdParty | 分销商资料,**不含密码** |
+
+`userThirdParty` 主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| id | 分销商 id |
+| name | 名称,下单后订单渠道就是这个 name |
+| loginName | 登录名 |
+| balance | 当前余额,不足不能用余额下单 |
+| contactName / phone / remark | 资料 |
+| selfTicketTypeList | 已授权票种(结构接近管理端票种详情)。售卖当天价仍以 3.2 详情为准 |
+
+### 1.2 当前登录信息
+
+`POST /thirdParty/auth/me`
+
+body 可空:`{ "data": {} }`。必须带 token。
+
+- 分销商:刷新余额、资料、授权票种(账号被禁用会失败)
+- 微信/小程序:只回 `token`,没有 `userThirdParty`
+
+### 1.3 微信公众号授权(引导跳转)
+
+`GET /thirdParty/auth/wxAuth?projectName={项目名}`
+
+浏览器打开这个地址,后端 302 到微信授权页。前端要做的是拼好 `projectName` 让用户点进去,不是 POST。
+
+### 1.4 微信公众号登录回调
+
+`GET /thirdParty/auth/wxLogin?projectName={项目名}&code={code}&state={state}`
+
+微信授权回来带 `code`、`state`。返回 `{ token }`。
+
+网关如果把用户端映射成 `/app/**`,授权回调地址可能配的是 `/app/auth/wxLogin`,和这里是同一套登录,以实际部署的对外路径为准。
+
+### 1.5 微信小程序登录
+
+`GET /thirdParty/auth/wxMiniLogin?projectName={项目名}&code={code}`
+
+小程序 `wx.login` 拿到 `code` 再调。返回 `{ token }`。请求头同样是 `satoken-thirdParty`(不是 `satoken-app`)。
+
+---
+
+## 2. 景点列表 `/thirdParty/selfScenic`
+
+景点本身没有单独的用户端筛选。后端按当前身份自动过滤:至少有一个**当前渠道能买到的票种**绑了这个景点。分销商还要这个票种已被授权。
+
+`data` 可空对象。返回**数组**(不是分页)。
+
+`POST /thirdParty/selfScenic`
+
+每项:
+
+| 字段 | 说明 |
+|---|---|
+| id | 景点 id |
+| name | 名称 |
+| address | 地址 |
+| openingTime | 开放时间文案 |
+| picture | 轮播图,逗号分隔 |
+| isOpen | `1` 开放 / `0` 关闭 |
+| publicAnnouncement | 公告 |
+| scenicDescription | 图文描述 |
+
+列表接口前端不用传渠道、分销商 id,后端从 token 里取。
+
+---
+
+## 3. 票种 `/thirdParty/selfTicketType`
+
+### 3.1 列表
+
+`POST /thirdParty/selfTicketType`
+
+`data` 可空。返回**数组**。价格是票种**底价**,某一天能不能买、卖多少钱,要调 3.2。
+
+微信/小程序:只返回勾了对应渠道的票。分销商:渠道 `third-party` + 管理端授权。
+
+列表每项主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| id | 自营票种 id,下单、详情都用这个 |
+| ticketTypeId | 线下票种数字 id |
+| category | `ticket` 景区门票 / `batch` 场次 / `member` 年卡次卡 |
+| name | 对客名称 |
+| isSale | 票种自身在售;某一天还可能被日历改成禁售 |
+| price / highPrice | 底价、划线价 |
+| realNameType | `0` 不限;`1` 一证一人;`2` 一证多人 |
+| saleDateStart / saleDateEnd | 可售日期 |
+| saleTimeStart / saleTimeEnd | 可售时段 |
+| saleMinNums / saleMaxNums | 单订单张数限制,`>0` 才生效 |
+| saleMaxPreDays / saleMinPreDays | 最多可订未来 X 天 / 最少提前 X 天 |
+| isAllowCancel | `1` 可退 |
+| isAgreeProtocol / buyProtocol | 是否必须勾协议、协议正文 |
+| dayStock / totalStock | 日库存 / 总库存,`0` 不限 |
+| useDays / useDateType / useDateStart / useDateEnd | 门票有效期相关 |
+| memberActiveType | 年卡:`1` 购买即激活 / `2` 首次使用激活 |
+
+列表**不带**绑定景点、场次。要展示景点、要算某天价,走详情。
+
+### 3.2 某天详情(下单前必调)
+
+`POST /thirdParty/selfTicketType/get`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| id | 是 | 自营票种 id |
+| playDateBegin | 是 | 游玩日期 `yyyy-MM-dd` |
+
+未授权会「票种未授权」或「票种不存在」。
+
+在列表字段基础上:
+
+| 字段 | 说明 |
+|---|---|
+| price / isSale | **已被销售日历覆盖**。下单时传入的票价必须等于这里的 `price`,否则「票价已更新」 |
+| scenicList | 绑定景点 |
+| scenicList[].selfScenicId / selfScenicName | 景点 |
+| batchConfigList | 场次票的场次配置(时段、停售/停退规则) |
+| batchList | 该游玩日的场次实例:已售、剩余、是否可售 |
+
+`batchList` 项:
+
+| 字段 | 说明 |
+|---|---|
+| batchConfigId | 场次 id |
+| batchDate | 日期 |
+| maxSaleNums / soledNums / leftNums | 最大、已售、剩余 |
+| status | `1` 可售 |
+
+前端下单页:先选日期调这个接口,用返回的 `price` 填到每张票上。
+
+---
+
+## 4. 订单 `/thirdParty/order`
+
+只能操作**当前登录用户自己的单**。订单渠道 `otaSourceName`:微信是「微信」,小程序是「微信小程序」,分销商是分销商 **name**。
+
+### 4.1 下单
+
+`POST /thirdParty/order/create`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| payWay | 分销商余额支付时必填 | 分销商传 `USER_BALANCE`:校验余额、扣款、直接出票。不传或其它值:订单保持待支付,留给后续支付(如主扫)。微信/小程序可忽略,一律待支付 |
+| buyerName / buyerPhone | 否 | 购买人 |
+| buyerIdentify / buyerIdentifyType | 否 | 证件;类型见下表 |
+| ticketList | 是 | 至少 1 张 |
+
+`ticketList` 每一张:
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| selfTicketTypeId | 是 | 自营票种 id |
+| price | 是 | 必须等于详情接口当天价 |
+| playDateBegin | 是 | 游玩日,不能早于今天 |
+| guestName / guestPhone | 否 | 游客 |
+| guestIdentify / guestIdentifyType | 视实名 | `realNameType=1` 每张都要证件;`=2` 整单至少一张有证件 |
+
+证件类型:`0` 身份证(默认),`1` 护照,`2` 军官证,`3` 台胞证,`4` 港澳通行证,`5` 大陆居民往来台湾通行证,`6` 港澳居民来往内地通行证,`7` 外国人永久居留证,`8` 港澳台居民居住证。
+
+同一票种多张就往 `ticketList` 里重复加,张数受 `saleMinNums` / `saleMaxNums`、日库存、总库存限制。
+
+金额为 0 会直接出票。分销商 `USER_BALANCE` 且余额不足会失败。账号禁用会「下单未授权」。
+
+返回完整订单(含票),字段见 4.3。分销商余额支付成功后 `payStatus=PAYED`,票进入出票流程;微信/小程序此时一般是 `WAIT_PAY`,再调支付。
+
+待支付单超时会被关掉(`CLOSED`)。
+
+### 4.2 微信支付(微信 / 小程序)
+
+`POST /thirdParty/pay/wxPay`
+
+只处理当前用户、且 `payStatus=WAIT_PAY` 的订单。走微信 JSAPI。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| orderId | 是 | 订单 id(4.1 返回的 `id`) |
+| openId | 是 | 当前微信用户 openId |
+
+返回微信 `wx.requestPayment` 所需字段(`appId`、`timeStamp`、`nonceStr`、`package`、`signType`、`paySign` 等)。支付成功由微信回调,前端轮询订单详情看是否已支付。
+
+分销商余额下单不要调这个接口。
+
+### 4.3 我的订单
+
+`POST /thirdParty/order/list`
+
+`data` 里目前没有业务筛选项,带分页即可。只返回当前用户的单,含票列表。
+
+订单主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| id | 订单 id,详情、支付、退单用这个 |
+| orderNo / partnerOrderNo | 订单号 |
+| otaSourceName | 销售渠道 |
+| payStatus | `WAIT_PAY` / `PAYED` / `CLOSED` |
+| payTime / closeTime | 支付、关闭时间 |
+| status | 见下表 |
+| price | 订单总额 |
+| buyerName / buyerPhone / buyerIdentify | 购买人 |
+| isSelfOrder | 自营单为 `1` |
+| ticketList | 票 |
+
+订单 `status`:
+
+| 值 | 含义 |
+|---|---|
+| WAIT_PAY | 待支付 |
+| WAIT_CONFIRM | 待确认(出票中) |
+| WAIT_USE | 待使用 |
+| PART_USE | 部分核销 |
+| REFUNDING | 退款中 |
+| CANCELED | 已取消 |
+| PART_CANCEL | 部分退 |
+| COMPLETE | 已完成 |
+| CLOSED | 已关闭 |
+
+票主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| id | 票 id,退单时用 |
+| ticketTypeName | 票种名 |
+| ticketNo / otaTicketNo | 线下票号 / 线上票号 |
+| qrcodeUrl | 二维码 |
+| playDateBegin / playDateEnd | 可用日期 |
+| status | 见下表 |
+| price | 售价 |
+| guestName / guestPhone / guestIdentify | 游客 |
+| firstCheckTime / checkTime | 首次/最近核销 |
+| cancelPrice / cancelCompleteTime | 退款金额、完成时间 |
+
+票 `status`:
+
+| 值 | 含义 |
+|---|---|
+| WAIT_USE | 待使用 |
+| USED | 已核销 |
+| REFUND_REQUEST | 退款请求中 |
+| REFUNDING | 退款中 |
+| CANCELED | 已退 |
+| CLOSED | 已关闭 |
+
+### 4.4 订单详情
+
+`POST /thirdParty/order/{id}`
+
+路径上的 `{id}` 是订单 id。body 仍走统一 `Request`,`data` 可空。不是自己的单会「订单不存在」。结构同列表单项。
+
+### 4.5 退单
+
+`POST /thirdParty/order/cancel`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| orderId | 是 | 订单 id |
+| ticketCancelRequestList | 是 | 要退的票,至少 1 张 |
+| ticketCancelRequestList[].ticketId | 是 | 票的 `id` |
+
+两种情况:
+
+1. **待确认**(`WAIT_CONFIRM`):必须一次退掉订单里全部票,不能部分退。
+2. **已出票成功**:可以部分退。会转到线下确认,确认后票进入退款中;分销商余额支付的会把钱退回余额。
+
+用户端不能退:未支付、已关闭、已核销、票种 `isAllowCancel=0`、已经在退款中/已退的票。管理端强制退不受这些限制,用户端会。
+
+---
+
+## 5. 发票 `/thirdParty/invoice`
+
+只能开**自己订单**上的票。票号可以是 `ticketNo` 或 `otaTicketNo`。
+
+### 5.1 开票查询
+
+`POST /thirdParty/invoice/query`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| ticketNo | 是 | 票号 |
+
+不是自己的票会「门票不存在」。
+
+若已经开过,返回里带 `invoiceRecordList`,前端展示已开记录即可。
+
+尚未开过时,主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| success | 是否可开 |
+| orderNo / orderCreateTime | 订单号、下单时间 |
+| price | 本单可开总金额 |
+| ticketNos | 本单可开的全部票号,开票时原样带回 |
+| ticketTypeNames | 票种名 |
+| sellerGroups | 按销售方拆开的金额。一张票可能拆成多家开 |
+| sellerGroups[].sellerName / price | 销售方名称、金额 |
+
+已开记录里前端常用:`status`、`downloadUrl`、`qrcodeUrl`、`fpdm` / `fphm`、买卖方信息。
+
+开票 `status`:`SUCCESS` 已开、`INVOICING` 开票中、`FAILED` 失败、`RED_INVOICING` 红冲中、`RED_SUCCESS` 已红冲。
+
+### 5.2 开票
+
+`POST /thirdParty/invoice/doInvoice`
+
+建议把 5.1 返回里需要的字段拷到请求里,再补买方信息。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| ticketNos | 是 | 可开票号集合,来自查询 |
+| sellerGroups | 是 | 来自查询 |
+| orderNo / orderCreateTime / price | 否 | 来自查询,建议带回 |
+| invoiceBuyerName | 是 | 买方名称 |
+| invoiceBuyerTaxNum | 企业要税号 | 买方税号 |
+| invoiceBuyerAddress / invoiceBuyerPhone | 否 | 地址、电话 |
+| invoiceBuyerBank / invoiceBuyerAccount | 否 | 开户行 |
+| invoiceBuyerReceiveEmail / invoiceBuyerReceivePhone | 否 | 收票邮箱、手机 |
+| remark | 否 | 备注 |
+
+已开或开票中会失败。提交后异步出票,再查 5.1 看状态和下载地址。
+
+---
+
+## 6. 售票统计 / 核销统计 / 报表(仅自营分销商)
+
+微信、小程序调这些接口会「无权访问」。数据被后端写成**当前分销商自己的订单**,不用再传销售来源。
+
+`ticketTypeIdList` 是**线下票种数字 id**(票种上的 `ticketTypeId`),不是自营票种 id。
+
+导出:`export=true` 异步出 Excel,到 `/thirdParty/reportMission` 拿 `downloadUrl`。不要去租户的 `/tenant/reportMission`。`exportDetail=true` 时售票统计多售票/退票明细 sheet,核销统计多核销明细,需同时 `export=true`。
+
+### 6.1 售票统计
+
+`POST /thirdParty/statistics/orderSaleStatistics`
+
+按票种 + 单价分组,最后一行是总计。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| timeBegin / timeEnd | 是 | `yyyy-MM-dd HH:mm:ss`。售票按购票时间,退票按退票完成时间 |
+| ticketTypeIdList | 否 | 线下票种数字 id |
+| groupByList | 否 | 目前支持 `orderDateDay`(按天) |
+| export / exportDetail | 否 | 见上 |
+
+`otaSourceNameList` 不用传,后端已按当前账号过滤。
+
+返回数组,每项:
+
+| 字段 | 说明 |
+|---|---|
+| orderDateDay | 按天时的日期;总计行为「总计」 |
+| otaSourceName | 渠道(自己的分销商名) |
+| ticketTypeName | 票种 |
+| unitPrice | 单价 |
+| saleNum / salePrice / saleSettlementPrice | 售 |
+| cancelNum / cancelPrice / cancelSettlementPrice | 退 |
+| actualNum / actualPrice / actualSettlementPrice | 实售(售 − 退) |
+
+### 6.2 核销统计
+
+`POST /thirdParty/statistics/orderCheckStatistics`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| checkTimeBegin / checkTimeEnd | 是 | 首次核销时间 |
+| ticketTypeIdList | 否 | 线下票种数字 id |
+| groupByList | 否 | `orderDateDay` |
+| export / exportDetail | 否 | 同售票统计 |
+
+返回:`checkCount` 张数、`checkPrice` 金额、`checkSettlementPrice` 结算价,以及票种、单价、日期。
+
+### 6.3 报表列表
+
+`POST /thirdParty/reportMission`
+
+只看到自己导出的任务。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| createTimeBegin / createTimeEnd | 否 | 任务创建时间 |
+| fileName | 否 | 文件名 |
+| type | 否 | `0` 导出 / `1` 导入 |
+| status | 否 | `0` 进行中 / `1` 成功 / `2` 失败 |
+
+返回 `records` 每项:`id`、`fileName`、`type`、`downloadUrl`、`status`、`createTime`。
+
+点导出后轮询列表,`status=1` 再用 `downloadUrl`。
+
+### 6.4 删除报表
+
+`POST /thirdParty/reportMission/delete`
+
+`data.idList`。只能删自己的。
+
+---
+
+## 联调顺序(建议)
+
+**分销商端**
+
+1. `thirdPartyLogin` 拿到 token 和余额  
+2. `/me` 能刷到最新余额  
+3. 景点列表、票种列表有管理端授权过的数据  
+4. 票种详情换日期,确认日历价  
+5. `payWay=USER_BALANCE` 下单,订单已支付、余额减少  
+6. 订单列表 / 详情 / 退单(退成功后余额加回)  
+7. 用票号查开票、开票  
+8. 售票统计、核销统计;`export=true` 后到分销商报表列表下载  
+
+**微信 / 小程序**
+
+1. 对应登录拿到 token  
+2. 景点、票种、详情  
+3. 下单(待支付)→ `wxPay` → 轮询详情已支付  
+4. 订单列表、退单、开票  
+5. 不要接统计和报表  
+
+管理端没勾渠道、没给分销商授权,用户端列表就是空的,先查管理端配置。

+ 593 - 0
自营售票管理端接口.md

@@ -0,0 +1,593 @@
+# 自营售票 · 管理端接口(按调试顺序)
+
+租户管理端,前缀 `/tenant`。请求头带现有租户 token:`satoken`。
+
+统一 `POST` + `application/json`。业务参数放在 `data` 里,和现有管理端其它模块一致。
+
+```text
+{ "data": { ... } }
+```
+
+成功:`code = 200`,业务数据在 `data`。写接口成功时 `data` 可能为空。
+
+分页列表的 `data` 是 MyBatis-Plus 分页对象,前端主要用:
+
+| 字段 | 说明 |
+|---|---|
+| records | 当前页列表 |
+| total | 总条数 |
+| size | 每页条数 |
+| current | 当前页 |
+
+列表筛选请求都挂在 `data` 上,公共分页字段:
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| pageNum | 否 | 页码,默认按后端,建议传 1 |
+| pageSize | 否 | 每页条数。要下拉全量时可传 `-1` |
+
+删除类统一:
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| idList | 是 | 要删的 id 数组,至少 1 个 |
+
+修改类统一带 `id`。
+
+**接口目录(按联调顺序)**
+
+| 顺序 | 模块 | 方法 | 路径 |
+|---|---|---|---|
+| 1 | 景点 | POST | `/tenant/selfScenic` 列表 |
+| 1 | 景点 | POST | `/tenant/selfScenic/add` 新增 |
+| 1 | 景点 | POST | `/tenant/selfScenic/update` 编辑 |
+| 1 | 景点 | POST | `/tenant/selfScenic/delete` 删除 |
+| 2 | 线下票种 | POST | `/tenant/otaTicketType` 列表(只读,创建自营票种时选) |
+| 3 | 自营票种 | POST | `/tenant/selfTicketType` 列表 |
+| 3 | 自营票种 | POST | `/tenant/selfTicketType/{id}` 详情 |
+| 3 | 自营票种 | POST | `/tenant/selfTicketType/add` 新增 |
+| 3 | 自营票种 | POST | `/tenant/selfTicketType/update` 编辑 |
+| 3 | 自营票种 | POST | `/tenant/selfTicketType/delete` 删除 |
+| 4 | 销售日历 | POST | `/tenant/selfTicketSaleCalendar/list` 查询 |
+| 4 | 销售日历 | POST | `/tenant/selfTicketSaleCalendar/add` 全量保存 |
+| 4 | 销售日历 | POST | `/tenant/selfTicketSaleCalendar/delete` 删除规则 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty` 列表 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/add` 新增 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/update` 修改资料/密码/启用禁用 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/bind` 票种授权(可批量) |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/recharge` 充值 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/decrease` 扣款 |
+| 5 | 分销商 | POST | `/tenant/userThirdParty/delete` 删除 |
+| 6 | 余额流水 | POST | `/tenant/thirdPartyBalanceLog` 列表 |
+| 7 | 售票统计 | POST | `/tenant/statistics/orderSaleStatistics` 查询,`export=true` 导出 |
+| 7 | 核销统计 | POST | `/tenant/statistics/orderCheckStatistics` 查询,`export=true` 导出 |
+| 7 | 报表 | POST | `/tenant/reportMission` 导出任务列表 |
+| 7 | 报表 | POST | `/tenant/reportMission/delete` 删除报表 |
+
+建议前端按上面顺序联调:先有景点,才能绑票种;先有票种,才能做日历和分销商授权。统计、报表走租户已有接口,不需要新接 `/thirdParty/**`。
+
+---
+
+## 1. 自营景点 `/tenant/selfScenic`
+
+景点是抽象游玩项目。票种要绑定景点,C 端景点列表也依赖「有票种绑了这个景点」。**先做这个模块。**
+
+轮播图、富文本图片:沿用现有管理端文件上传,把返回的地址填进对应字段。`picture` 多张用英文逗号拼接,最多 6 张。
+
+### 1.1 列表
+
+`POST /tenant/selfScenic`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| name | 否 | 景点名称,模糊 |
+| isOpen | 否 | 是否开放:`1` 开放 / `0` 关闭 |
+
+返回 `records` 每项:
+
+| 字段 | 说明 |
+|---|---|
+| id | 景点 id,后续票种绑定用这个 |
+| name | 名称 |
+| address | 地址 |
+| openingTime | 开放时间文案,例如「08:00~18:00」。真正能不能买由票种控制 |
+| picture | 轮播图,逗号分隔 |
+| isOpen | 是否开放 |
+| publicAnnouncement | 公告 |
+| scenicDescription | 图文描述(富文本) |
+| createTime | 创建时间 |
+
+### 1.2 新增
+
+`POST /tenant/selfScenic/add`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| name | 是 | 景点名称 |
+| isOpen | 是 | `1` / `0` |
+| address | 否 | 地址 |
+| openingTime | 否 | 开放时间文案 |
+| picture | 否 | 轮播图 URL,逗号分隔,最多 6 张 |
+| publicAnnouncement | 否 | 公告 |
+| scenicDescription | 否 | 富文本 |
+
+### 1.3 编辑
+
+`POST /tenant/selfScenic/update`
+
+在新增字段基础上多一个 `id`(必填)。没有单独详情接口,编辑回填用列表数据即可。
+
+### 1.4 删除
+
+`POST /tenant/selfScenic/delete`
+
+`data.idList`。已绑到票种上的景点删掉后,C 端列表可能不再展示该景点(票种侧绑定会失效),删除前建议前端提示。
+
+---
+
+## 2. 线下票种(创建自营票种时的下拉)
+
+自营票种必须绑一个**线下已同步**的票种,一个线下票种只能创建一次自营票种。
+
+### 2.1 线下票种列表
+
+`POST /tenant/otaTicketType`
+
+这个接口管理端其它地方可能已经接过,这里只作为自营票种的选择源。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| ticketTypeId | 否 | 线下票种数字 id |
+| name | 否 | 名称 |
+
+返回每项主要用:
+
+| 字段 | 说明 |
+|---|---|
+| ticketTypeId | 线下票种 id,创建自营票种时传这个 |
+| name | 线下票种名称 |
+| category | 大类:`ticket` 景区门票 / `batch` 场次售票 / `member` 年卡次卡。创建自营票种时大类应与这个一致 |
+| batchConfigList | 仅场次票有。线下场次配置,自营侧只读展示,不用单独维护 |
+
+场次配置 `batchConfigList` 项(只读):
+
+| 字段 | 说明 |
+|---|---|
+| batchConfigId | 线下场次 id |
+| name | 场次名称 |
+| startTime / endTime | 场次时段 |
+| maxSaleNums | 无座位时最大数量 |
+
+---
+
+## 3. 自营票种 `/tenant/selfTicketType`
+
+依赖:景点列表、线下票种列表。
+
+渠道决定 C 端谁能看到:
+
+| 值 | 含义 |
+|---|---|
+| wx | 微信公众号 |
+| wx-mini | 微信小程序 |
+| third-party | 自营分销商 |
+
+要给分销商卖,必须勾选 `third-party`,然后再走第 5 步授权。只授权不勾渠道,分销商端看不到。
+
+### 3.1 列表
+
+`POST /tenant/selfTicketType`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| category | 否 | `ticket` / `batch` / `member` |
+| name | 否 | 名称,模糊 |
+| isSale | 否 | `1` 在售 / `0` 禁售 |
+
+返回每项字段见 3.2 详情(列表也会带绑定景点、渠道、场次)。
+
+### 3.2 详情(编辑页回填用这个)
+
+`POST /tenant/selfTicketType/{id}`
+
+路径上的 `{id}` 是自营票种 id。body 仍走统一 `Request`,`data` 可空对象。
+
+返回除票种字段外还有:
+
+| 字段 | 说明 |
+|---|---|
+| scenicList | 已绑定景点 |
+| scenicList[].selfScenicId | 景点 id |
+| scenicList[].selfScenicName | 景点名称 |
+| channels | 渠道字符串数组 |
+| batchConfigList | 场次票只读,来自线下票种 |
+| ticketTypeId / category | 创建后不可改,编辑页只展示 |
+
+### 3.3 新增
+
+`POST /tenant/selfTicketType/add`
+
+**公共必填**
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| ticketTypeId | 是 | 线下票种 id(数字)。已创建过会报「票种已存在」 |
+| category | 是 | 与线下票种大类保持一致:`ticket` / `batch` / `member` |
+| name | 是 | 对客名称 |
+| price | 是 | 底价。具体某天售价可被销售日历覆盖 |
+| realNameType | 是 | `0` 不限;`1` 一证一人;`2` 一证多人 |
+| saleDateStart / saleDateEnd | 是 | 可售日期,`yyyy-MM-dd` |
+| saleTimeStart / saleTimeEnd | 是 | 可售时段,`HH:mm:ss`,例如 `00:00:00` ~ `23:59:59` |
+
+**公共选填**
+
+| 字段 | 说明 |
+|---|---|
+| isSale | 在售,默认按 `1` 理解 |
+| highPrice | 划线价 |
+| buyProtocol | 购买协议富文本 |
+| isAgreeProtocol | `1` 必须勾选协议才能买,默认 `0` |
+| saleMaxPreDays | 最多可订未来 X 天 |
+| saleMinPreDays | 最少提前 X 天,`0` 可订当天 |
+| saleMinNums / saleMaxNums | 单订单最小/最大张数,`>0` 才生效 |
+| isAllowCancel | `1` 可退,默认 `1` |
+| dayStock | 每日库存上限,`0` 不限 |
+| totalStock | 总库存上限,`0` 不限 |
+| scenicIds | 绑定的自营景点 id 数组 |
+| channels | 渠道数组,取值见上表 |
+
+**按大类展示的字段**
+
+景区门票 `ticket`:
+
+| 字段 | 说明 |
+|---|---|
+| useDays | 使用天数,当天有效一般是 `1` |
+| useDateType | `1` 日历票(只能游玩所选日期);`2` 期票(期限内都能用) |
+| useDateStart / useDateEnd | 期票时的有效期 |
+
+年卡次卡 `member`:
+
+| 字段 | 说明 |
+|---|---|
+| memberUseDateType | `1` 按天(看 `useDays`);`2` 固定日期(看 `useDateStart` / `useDateEnd`) |
+| useDays | 按天时的有效天数,例如 `365` |
+| useDateStart / useDateEnd | 固定日期模式 |
+| memberActiveType | `1` 购买即激活;`2` 首次使用激活 |
+
+场次票 `batch`:场次来自线下票种,这里不用填场次。售卖规则用上面公共字段即可。
+
+### 3.4 修改
+
+`POST /tenant/selfTicketType/update`
+
+必填 `id`。**不能改** `ticketTypeId`、`category`。其余与新增相同(含 `scenicIds`、`channels`)。
+
+景点、渠道是整表覆盖:传当前完整数组。要清空就传空数组。
+
+### 3.5 删除
+
+`POST /tenant/selfTicketType/delete`
+
+`data.idList`。
+
+---
+
+## 4. 销售日历 `/tenant/selfTicketSaleCalendar`
+
+依赖:已有自营票种。用来覆盖「某一天」的价格和是否可售。C 端**列表**仍是票种底价,**详情/下单**才走日历。
+
+规则优先级:**特定日期 > 周期性规则**。同类型里 `priority` 越小越优先。前端拖拽排序后把序号写成 `priority`(建议从 1 开始)。
+
+没有单独的修改接口。`/add` 会先删掉该票种全部旧规则再写入,等于**全量保存**。编辑页应:先 list 回填,用户改完把**完整规则列表**再提交 add。
+
+### 4.1 查询某票种的规则
+
+`POST /tenant/selfTicketSaleCalendar/list`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| selfTicketTypeId | 是 | 自营票种 id |
+
+返回数组(不是分页),按 `priority` 升序。每项含 `id`(删除单条用)、价格、是否可售、特定日期或周期字段。
+
+### 4.2 保存规则(全量覆盖)
+
+`POST /tenant/selfTicketSaleCalendar/add`
+
+注意:这个接口的 `data` 是**数组**,不是对象。
+
+```text
+{ "data": [ { 规则1 }, { 规则2 } ] }
+```
+
+数组里每条规则:
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| selfTicketTypeId | 是 | 同一批必须是同一个票种 |
+| price | 是 | 该规则价格 |
+| isSale | 是 | `1` 可售 / `0` 禁售 |
+| priority | 是 | 越小越优先 |
+
+两种规则二选一:
+
+**特定日期**
+
+| 字段 | 说明 |
+|---|---|
+| specificDate | 日期字符串。多天用逗号,例如 `2026-10-01,2026-10-02` |
+
+**周期性**(没传 `specificDate` 时三个都要有)
+
+| 字段 | 说明 |
+|---|---|
+| startDate | 周期开始 `yyyy-MM-dd` |
+| endDate | 周期结束 |
+| weekdays | 周几,逗号分隔。`1` 周日,`2` 周一 … `7` 周六。例如周末:`1,7` |
+
+空数组不要调这个接口(会取第 0 条票种 id)。要清空规则用下面的删除,把 list 返回的 id 全部删掉。
+
+### 4.3 删除规则
+
+`POST /tenant/selfTicketSaleCalendar/delete`
+
+`data.idList` 为日历规则 id,不是票种 id。
+
+---
+
+## 5. 自营分销商 `/tenant/userThirdParty`
+
+旅行社、酒店等,登录分销商端售票。可与票种并行开发账号 CRUD,但**授权必须先有票种**。
+
+登录名、名称全局唯一。名称不能和总库 OTA 渠道名冲突。密码:至少 8 位,且同时包含大小写字母和数字。
+
+分销商的资料、余额、授权、**启用/禁用**都在本模块完成,只做一个「自营分销商」菜单即可。不要用 `/tenant/user` 管分销商:那个接口只服务微信/小程序 C 端用户拉黑,列表里也不会再出现分销商。
+
+### 5.1 列表
+
+`POST /tenant/userThirdParty`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| name | 否 | 分销商名称,模糊 |
+| contactName | 否 | 联系人,精确 |
+| phone | 否 | 电话,精确 |
+| remark | 否 | 备注 |
+| status | 否 | `1` 正常 / `0` 禁用 |
+
+返回每项:
+
+| 字段 | 说明 |
+|---|---|
+| id | 分销商 id,授权/充扣/禁用都用这个 |
+| name | 名称 |
+| contactName / phone | 联系人、电话 |
+| loginName | 登录名 |
+| status | `1` 正常 / `0` 禁用。禁用后不能登录、不能下单 |
+| lastLoginTime | 最近一次登录时间 |
+| balance | 余额,不足不能下单 |
+| selfTicketTypeList | 已授权的自营票种,结构同票种详情(含景点、渠道、场次) |
+| remark | 备注 |
+| createTime | 创建时间 |
+
+列表**不返回密码**。没有单独详情接口,编辑回填用列表。
+
+### 5.2 新增
+
+`POST /tenant/userThirdParty/add`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| name | 是 | 分销商名称 |
+| loginName | 是 | 登录名 |
+| password | 是 | 登录密码,复杂度见上 |
+| contactName | 否 | 联系人 |
+| phone | 否 | 联系方式 |
+| remark | 否 | 备注 |
+
+新建余额为 0。创建后不会自动授权票种。
+
+### 5.3 修改
+
+`POST /tenant/userThirdParty/update`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| id | 是 | 分销商 id |
+| contactName / phone / remark | 否 | 资料。不传的字段保持原值,可以只传 `id` + `status` 做启用/禁用 |
+| password | 否 | 不传或空字符串表示不改密码;传了就要满足复杂度 |
+| status | 否 | `1` 启用 / `0` 禁用。不传不改。禁用会立即踢下线 |
+
+**不能改** `name`、`loginName`。要改名称只能删了重建。
+
+### 5.4 票种授权
+
+`POST /tenant/userThirdParty/bind`
+
+可一次给多个分销商绑定**同一批**票种。每个被选中的分销商,原有授权都会被这批票种覆盖。只改某一个时,列表里传 1 个分销商 id 即可。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| userThirdPartyIdList | 是 | 分销商 id 数组,至少一个 |
+| selfTicketTypeIdList | 是 | 票种 id 数组。传 `[]` 表示清空所选分销商的授权,不能不传该字段 |
+
+前端交互建议:列表多选分销商,弹窗多选票种,提交完整选中结果。不同分销商要配不同票种时,分开调两次。
+
+被授权的票种自身还需要勾了渠道 `third-party`,分销商端才能看到。
+
+### 5.5 充值
+
+`POST /tenant/userThirdParty/recharge`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| userThirdPartyId | 是 | 分销商 id |
+| amount | 是 | 金额,必须 `> 0`,最小 `0.01` |
+
+### 5.6 扣款
+
+`POST /tenant/userThirdParty/decrease`
+
+字段同充值。扣款不能大于当前余额。
+
+### 5.7 删除
+
+`POST /tenant/userThirdParty/delete`
+
+`data.idList`。以下情况会失败,前端先拦一下更好:
+
+- 余额大于 0
+- 还有未完结订单(不是已取消 / 已关闭 / 已完成)
+
+删除会踢掉该分销商登录,并清掉票种授权。
+
+---
+
+## 6. 余额流水 `/tenant/thirdPartyBalanceLog`
+
+依赖:已有分销商。充值、扣款、分销商下单消费、退单退款都会记流水。建议和分销商列表放一起,从某行点进流水。
+
+### 6.1 流水列表
+
+`POST /tenant/thirdPartyBalanceLog`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| userThirdPartyId | 否 | 按分销商筛 |
+| type | 否 | 见下表 |
+| tradeTimeStart / tradeTimeEnd | 否 | 交易时间,`yyyy-MM-dd HH:mm:ss` |
+
+`type`:
+
+| 值 | 含义 |
+|---|---|
+| 1 | 充值(管理端) |
+| 2 | 人工扣款(管理端) |
+| 3 | 消费(分销商下单扣余额) |
+| 4 | 退款(分销商退单退余额) |
+
+返回每项:
+
+| 字段 | 说明 |
+|---|---|
+| id | 流水 id |
+| userThirdPartyId | 分销商 id |
+| userThirdPartyName | 分销商名称 |
+| type | 类型 |
+| amount | 发生额 |
+| beforeBalance / afterBalance | 变动前 / 后余额 |
+| orderId | 消费、退款时关联订单 |
+| operatorAdminId | 管理端充扣时的操作人 |
+| tradeTime | 交易时间 |
+| createTime | 创建时间 |
+
+流水只读,没有新增/修改/删除。充扣走 5.5 / 5.6。
+
+---
+
+## 7. 售票统计 / 核销统计 / 报表(沿用租户现有接口)
+
+管理端看自营售票数据,用租户已经接过的统计和报表,不要调 `/thirdParty/statistics`、`/thirdParty/reportMission`。那两个是**分销商自己登录后**用的,小程序/公众号调会返回「无权访问」。
+
+自营分销商下单时,订单上的销售来源 `otaSourceName` = 分销商 **name**(不是 id)。微信是「微信」,小程序是「微信小程序」。
+
+导出:统计请求里 `export` 设为 `true` 会异步出 Excel,到报表列表拿 `downloadUrl`。`exportDetail` 为 `true` 时,售票统计会多两个 sheet(售票明细、退票明细),核销统计会多核销明细。
+
+### 7.1 售票统计
+
+`POST /tenant/statistics/orderSaleStatistics`
+
+按分销渠道 + 票种 + 单价分组,最后一行是总计。
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| timeBegin / timeEnd | 是 | 统计时间,`yyyy-MM-dd HH:mm:ss`。售票按购票时间,退票按退票完成时间 |
+| ticketTypeIdList | 否 | **线下票种数字 id**,不是自营票种 id |
+| otaSourceNameList | 否 | 销售来源名称。只看某个自营分销商时,传该分销商的 `name` |
+| groupByList | 否 | 额外维度,目前支持 `orderDateDay`(按天) |
+| export | 否 | `true` 异步导出 |
+| exportDetail | 否 | `true` 导出时带明细,需同时 `export=true` |
+
+返回数组,每项:
+
+| 字段 | 说明 |
+|---|---|
+| orderDateDay | 按天分组时的日期;未分组可为空;总计行为「总计」 |
+| otaSourceName | 分销渠道 |
+| ticketTypeName | 票种名称 |
+| unitPrice | 单价 |
+| saleNum / salePrice / saleSettlementPrice | 售票张数、金额、结算价 |
+| cancelNum / cancelPrice / cancelSettlementPrice | 退票张数、金额、结算价 |
+| actualNum / actualPrice / actualSettlementPrice | 实售(售 − 退) |
+
+### 7.2 核销统计
+
+`POST /tenant/statistics/orderCheckStatistics`
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| checkTimeBegin / checkTimeEnd | 是 | 首次核销时间 |
+| ticketTypeIdList | 否 | 线下票种数字 id |
+| otaSourceNameList | 否 | 销售来源名称,自营分销商传 `name` |
+| groupByList | 否 | `orderDateDay` |
+| export / exportDetail | 否 | 同售票统计 |
+
+返回数组,每项:
+
+| 字段 | 说明 |
+|---|---|
+| orderDateDay | 按天时的日期 |
+| otaSourceName | 订单渠道 |
+| ticketTypeName | 票种 |
+| unitPrice | 单价 |
+| checkCount | 核销张数 |
+| checkPrice | 核销金额 |
+| checkSettlementPrice | 结算价 |
+
+### 7.3 报表列表
+
+`POST /tenant/reportMission`
+
+导出任务进这张表。筛选字段:
+
+| 字段 | 必填 | 说明 |
+|---|---|---|
+| createTimeBegin / createTimeEnd | 否 | 任务创建时间 |
+| name | 否 | 文件名称(请求字段名是 `name`) |
+| type | 否 | `0` 导出 / `1` 导入 |
+| status | 否 | `0` 进行中 / `1` 成功 / `2` 失败 |
+
+返回 `records` 每项:
+
+| 字段 | 说明 |
+|---|---|
+| id | 任务 id |
+| fileName | 文件名 |
+| type | 导入/导出 |
+| downloadUrl | 成功后的下载地址 |
+| status | 任务状态 |
+| adminId / adminName | 操作人 |
+| createTime | 创建时间 |
+
+点导出后先轮询列表,`status=1` 再用 `downloadUrl`。
+
+### 7.4 删除报表
+
+`POST /tenant/reportMission/delete`
+
+`data.idList`。
+
+---
+
+## 联调顺序(建议)
+
+1. 景点:列表 → 新增 → 列表能看到 → 编辑 → 删除  
+2. 线下票种列表能拉到数据(数据来自线下同步,管理端只读)  
+3. 自营票种:列表 → 新增(绑景点 + 勾渠道)→ 详情回填 → 编辑 → 删除  
+4. 销售日历:按票种 list → 用数组全量 add → 再 list 核对 → 按规则 id 删除  
+5. 分销商:列表 → 新增 → 修改资料/密码 → bind 授权(可一次多个分销商同一批票种)→ 充值 → 扣款 → 流水列表能看到 1/2 两类  
+6. 删除分销商:余额清零且无未完结订单后再删  
+7. 统计:售票统计、核销统计能出数;`export=true` 后到报表列表能下到文件  
+
+C 端微信/小程序售票、下单、支付,以及分销商自己的统计/报表,不在本文。管理端订单、退单沿用现有订单模块即可。