# 自营售票 · 用户端接口(按调试顺序) 用户端前缀 `/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. 不要接统计和报表 管理端没勾渠道、没给分销商授权,用户端列表就是空的,先查管理端配置。