# as-daycard-openapi 项目交接文档 ## 一、项目概述 天卡(24小时制视频权益卡)开放API平台,为酒店电视设备提供天卡购买、支付、激活、权益管理的完整能力。设备端通过本平台完成微信支付购买天卡,支付成功后异步调用橘视厅接口激活权益,设备获取 `thirdCardNo` 凭证后直连橘视厅SDK播放内容。 **技术栈**: Java 8 / Spring Boot 2.6.6 / MyBatis-Plus / MySQL / Redis / Shiro + JWT / Quartz / wechatpay-java SDK 0.2.14 **部署**: Docker (eclipse-temurin:17-jre-alpine),端口 8084,Spring Profile = prod --- ## 二、组织架构与数据隔离 ### 四层组织体系 ``` 平台(Platform) > 渠道(Channel) > 代理商(Agent/SERVICE_PROVIDER) > 门店(Store) ``` - 每个组织节点有 `mpath` 物化路径(如 `1.2.3.`),用于快速判断层级关系 - 用户通过 `sys_user.org_id` 关联到某个组织节点 - `org_id = NULL` 为超级管理员,不限制数据范围 ### 数据隔离机制 - **隔离工具**: `SysOrgService.applyBusinessOrgScope(wrapper, orgColumn, operatorId)` —— 向业务表 QueryWrapper 注入子查询,按 `sys_org.mpath` 前缀匹配过滤 - **权限校验**: `SysOrgService.isOrgInScope(orgId, operatorId)` —— 判断目标组织是否在操作人范围内 - **已接入隔离的模块**: 设备、订单、退款、统计、定价、支付配置、激活码、用户管理 - **平台级资源不隔离**: 天卡池(全局库存)、系统菜单 --- ## 三、核心业务流程 ### 3.1 设备初始化与鉴权 ``` 设备 App 启动 -> POST /api/jst/init (body: activationCode + mac/deviceSn/deviceUid) -> 后端创建/更新设备记录,生成 sign -> 返回 { sign, deviceId } -> 后续所有请求在 header 携带 sign ``` - **激活码模式**: 设备通过管理后台生成的门店激活码初始化,绑定到门店组织 - **代销模式**: 第三方合作伙伴的设备免激活码,通过设备标识匹配已注册设备 - **sign 机制**: 后端 `ApiDeviceSignFilter` 验证 sign,解析出 deviceId 放入 request attribute。sign 有时效,可通过 `heartBeat` 续期或 `refreshSign` 刷新 ### 3.2 天卡购买流程 ``` 1. 设备查询权益 -> POST /api/jst/checkBenefits -> UNBOUND(无权益) 2. 设备查询价格 -> POST /api/jst/price -> 返回价格 3. 设备创建订单 -> POST /api/jst/daycard/create -> Redis 防重锁(按 deviceId) -> 解析设备组织链路(设备 -> 门店 -> 渠道 -> 代理商) -> 3层定价匹配(门店 > 渠道 > 平台默认) -> 插入订单(状态 PENDING) -> 调微信 Native 下单,获取 code_url -> SKIP LOCKED 抢卡(UNUSED -> RESERVED) -> 返回 { orderNo, codeUrl, amount, expireAt } 4. App 用 codeUrl 生成二维码,用户扫码支付 5. 用户取消支付 -> POST /api/jst/daycard/cancel/{orderNo} -> 校验订单归属 -> 微信关单 -> 释放卡(RESERVED -> UNUSED)-> 订单 CANCELLED -> 清理 Redis 锁 6. 用户支付成功 -> 微信回调 -> 异步激活 ``` ### 3.3 支付回调与异步激活 ``` 微信支付回调 -> POST /openapi/payment/wechat/notify -> 验签(从 header 提取 Wechatpay-Serial/Nonce/Signature/Timestamp,HttpServletRequest.getHeader() 大小写不敏感) -> 解密获取交易信息 -> 幂等检查(订单已 PAID 则直接返回) -> 乐观锁更新订单 PAID + 卡状态 RESERVED -> USED -> 异步激活(@Async): 1. jushitingApiService.getToken(device, thirdCardNo) -> 获取 token + memberCode 2. jushitingApiService.activateCard(deviceId, cardCode, memberCode, token) -> 激活卡密 3. jushitingApiService.queryVipExpireTime(deviceId, thirdCardNo) -> 查询VIP到期时间 4. 更新 day_card_pool: expire_at + activate_status=SUCCESS -> 清理 Redis 防重锁 ``` ### 3.4 橘视厅权益映射 ``` 关键标识 thirdCardNo: - 卡密导入时自动生成(D + 24位随机字符) - 存储在 day_card_pool.third_card_no - 作为橘视厅的 uid 和 mac 参数(mac = thirdCardNo + "_bul_" + businessLine) - 设备通过 checkBenefits 获取 thirdCardNo - 设备用 thirdCardNo 直连橘视厅获取 token,激活卡密,获得 VIP 权益 设备与卡的关联: day_card_pool.device_id -> device.id -> device.mac/device_sn/device_uid 解绑时清除 device_id,卡状态保持 USED(终态不可复用) ``` ### 3.5 卡状态流转 ``` UNUSED -> RESERVED -> USED(终态) 导入 下单抢卡 支付成功 取消/超时: RESERVED -> UNUSED(释放回池) 退款: USED -> UNUSED(清除 device_id/order_no/expire_at/activate_status) 解绑: USED 保持不变,仅清除 device_id ``` ### 3.6 代销模式(合作伙伴) ``` 采购: 平台天卡池 UNUSED -> RESERVED,写入 partner_card_pool(status=AVAILABLE) 消耗: POST /openapi/partner/card/consume -> partner 池取1张 AVAILABLE 卡 -> day_card_pool RESERVED -> USED + device_id -> partner_card_pool AVAILABLE -> CONSUMED -> 异步激活(同上) -> App 后续用同一设备标识调 init + checkBenefits 回退: partner_card_pool AVAILABLE -> DISABLED,平台卡 RESERVED -> UNUSED ``` ### 3.7 分账(需微信商户号开通分账权限) ``` 支付成功后触发 executeSharing(orderNo): -> 匹配分润规则(全局/渠道/代理商/门店四级) -> 计算各方分账金额(比例或固定,非平台方总比例 ≤ 30%) -> 创建 profit_sharing_order + profit_sharing_detail -> 调微信 createProfitSharing API(设置 unfreezeUnsplit=true 自动解冻剩余) -> 调微信 finishProfitSharing 完结分账 注意: 当前分账标记(SettleInfo.profitSharing=true)已注释,需商户号开通分账权限后取消注释 ``` --- ## 四、接口体系 ### 4.1 设备端接口(/api/jst/,sign 鉴权) | 接口 | 说明 | |------|------| | POST /api/jst/init | 设备初始化,返回 sign | | POST /api/jst/checkBenefits | 查询天卡权益,返回 thirdCardNo | | POST /api/jst/price | 查询天卡价格 | | POST /api/jst/daycard/create | 创建天卡订单 | | GET /api/jst/daycard/status/{orderNo} | 查询支付状态 | | POST /api/jst/daycard/cancel/{orderNo} | 取消订单 | | POST /api/jst/heartBeat | 心跳续期 | | POST /api/jst/refreshSign | 刷新 sign | ### 4.2 管理后台接口(/manage/,JWT 鉴权) 覆盖:系统管理(用户/角色/菜单/组织/门店)、设备管理、天卡池/订单/退款/激活码、定价策略、支付配置、分账规则/记录、统计概览、合作伙伴管理、媒资管理等,共约 100+ 个接口。 ### 4.3 合作伙伴接口(/openapi/partner/,appKey/appSecret 签名鉴权) | 接口 | 说明 | |------|------| | POST /openapi/partner/card/consume | 消耗天卡(代销) | | GET /openapi/partner/card/stock | 查询库存 | | GET /openapi/partner/media/* | 媒资接口(内容包/栏目树/搜索等) | ### 4.4 微信回调接口(/openapi/payment/,无鉴权) | 接口 | 说明 | |------|------| | POST /openapi/payment/wechat/notify | 支付回调 | | POST /openapi/payment/refund/notify | 退款回调 | | GET /openapi/payment/query/{orderNo} | 查询支付状态 | ### 4.5 统一响应结构 ```json { "code": 0, "msg": "", "data": {} } ``` - code=0 成功,code!=0 失败(500 业务错误,403 权限错误) - data 始终存在,列表分页用 `data.page`,详情用 `data.{业务字段}` --- ## 五、定价体系 三层维度优先级匹配:门店配置价 > 渠道配置价 > 平台默认价 | 配置层级 | channel_org_id | store_org_id | |---------|---------------|-------------| | 平台默认 | NULL | NULL | | 渠道级 | 具体值 | NULL | | 门店级 | NULL | 具体值 | --- ## 六、定时任务 | 任务 | 说明 | |------|------| | DayCardOrderTimeoutTask | 取消超时未支付订单(15分钟),释放卡 | | DayCardActivateRetryTask | 重试激活失败的天卡 | | ProfitSharingQueryTask | 查询分账状态,更新分账单 | --- ## 七、橘视厅平台对接 ### 平台级凭证(用于内容/UI接口,非设备级) ``` SP_TOKEN = "9006874392099a89506142c6df9e3314" KEY = "alltvjs" PORTAL_CODE = "PCODE-JC" sign = MD5(sptoken + timeshift + portalcode + key).toUpperCase() ``` App 用此凭证直连橘视厅 `/IPTVPortalInterface/` 下的接口(topButton、gettagslist、getcontentlist、search 等),获取内容和 UI。**不经过我们后端**。 ### 设备级权益凭证(thirdCardNo) - 由后端在卡密导入时自动生成 - 设备通过 `checkBenefits` 获取 - 用于橘视厅免密登录(getToken)和卡密激活(activateCard) - `getToken` 的 uid 和 mac 参数都基于 thirdCardNo 构造,设备实体不传给橘视厅 ### 橘视厅 API 基地址 - 正式环境: `https://api.jetsenx.huashi.tv/` - 测试环境: `https://external.jetsenx.huashi.tv/` - 媒资接口: `https://servhub-ep03.future.jetsentv.com/` --- ## 八、微信支付配置 - 支持多商户号配置(DB `wechat_pay_config` 表,四级优先级:门店 > 代理商 > 渠道 > 全局) - DB 配置优先,回退到 yml 环境变量 - SDK 配置实例按 商户号+证书序列号+密钥+私钥 缓存,配置变更自动重建 - 回调验签支持多商户号(遍历所有 ACTIVE 配置逐个尝试解析) --- ## 九、待办与注意事项 1. **分账功能**: 代码已实现但分账标记(`SettleInfo.profitSharing=true`)已注释,需微信商户号开通分账权限后取消注释(WechatPayServiceImpl 和 DayCardOrderServiceImpl.payCallback 中) 2. **退款SDK**: `WechatPayServiceImpl.refund()` 目前是骨架实现(TODO 接入 wechatpay-java RefundService) 3. **设备标识改造**: 当前卡池用 `device_id`(Long, 自增) 关联设备,计划改为 `device_uid`(String) 关联,已搁置 4. **天卡池不隔离**: 天卡池为平台级库存资源,不按组织隔离,所有管理员可见 5. **媒资接口分页不统一**: 3个媒资相关端点用 `data.list` + `data.totalCount` 平铺式分页,与其他接口的 `data.page` 不同 6. **支付接口路径**: 当前支付接口在 `/openapi/payment/` 下,计划迁移到 `/payment/` 与第三方接口区分(未实施) 7. **密码哈希**: Shiro `Sha256Hash(password, salt)` 哈希顺序为 `salt + password`(不是 password + salt) --- ## 十、数据库与部署 ### 数据库 - MySQL 8.0,数据库名 `jstopenapi` - 完整建表脚本: `db/full-schema.sql` - 初始化数据: `db/init-sys-data.sql`(菜单 + 角色 + 超管用户 admin/admin) - 逻辑删除: `delete_flag` 字段(UNDEL/DEL) ### Redis - 用于:设备 sign 缓存、防重锁、橘视厅 token 缓存、激活锁 - Key 前缀: `daycard:device:`(订单防重)、`api:thirdparty:activate:`(激活相关) ### 部署 ```bash # 构建 mvn clean package -DskipTests # Docker 部署 docker-compose up -d # 关键环境变量(docker-compose.yml 中配置) SPRING_PROFILES_ACTIVE=prod DB_URL / DB_USERNAME / DB_PASSWORD REDIS_HOST / REDIS_PORT / REDIS_PASSWORD JWT_SECRET / API_SIGN_SECRET WECHAT_PAY_* # 微信支付配置(或通过 DB wechat_pay_config 表配置) ``` ### 超管账号 - 用户名: admin / 密码: admin - 新增超管: wangrui / 密码: Admin@2026.