Files
as-daycard-openapi/HANDOVER.md
zhaogd 03e5f6fbf2 feat: 全局数据隔离 + 响应结构统一 + 交接文档
1. 新增SysOrgService业务数据隔离工具方法(applyBusinessOrgScope/getUserOrgMpath)
2. 10个Controller接入组织数据隔离(设备/订单/退款/统计/定价/支付配置/激活码/用户)
3. 统一响应结构:写操作从R.ok()改为R.dataSuccess(data)
4. 修复5处data.data双层嵌套
5. 修复SysUserController.info()不一致
6. 修复循环依赖(@Lazy)
7. 新增交接文档HANDOVER.md
2026-08-11 15:50:36 +08:00

11 KiB
Raw Permalink Blame History

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 统一响应结构

{ "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:(激活相关)

部署

# 构建
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.