hi-frontend/.claude/plan/shared-subscription-display.md
shanshanzhong 6b92979c7c
Some checks failed
Build and Release / Build (push) Has been cancelled
feat: 自定义版本功能更新
- 新增家庭共享订阅管理
- 新增用户邀请统计
- 新增签名和订阅模式设置表单
- 更新 API 服务层和国际化文件
- UI 组件优化(enhanced-input、pro-table)
2026-03-19 01:56:13 -07:00

5.7 KiB
Raw Permalink Blame History

实施计划:后台管理 - 共享订阅显示

问题描述

设备组成员加入后,其原始订阅被删除,使用所有者的共享订阅。 在后台管理的用户订阅面板中,查看设备组成员的订阅时显示为空,因为数据已在合并时被删除。 需要在后台自动检测并显示共享订阅信息。

技术方案

纯前端方案,不需要后端 API 变更。利用现有 API 组合实现:

  1. getUserSubscribe({ user_id }) → 获取用户自身订阅(可能为空)
  2. getFamilyList({ user_id, page: 1, size: 1 }) → 检测用户是否属于设备组
  3. getUserSubscribe({ user_id: owner_user_id }) → 获取所有者的共享订阅

核心逻辑:当用户自身订阅为空时,自动检查是否为设备组成员。若是非所有者成员,则展示所有者的订阅信息,并添加"共享订阅"视觉标识。

实施步骤

Step 1: 修改 UserSubscription 组件

文件: apps/admin/src/sections/user/user-subscription/index.tsx

将组件从纯 ProTable 改为带有共享订阅检测逻辑的组件:

伪代码:
function UserSubscription({ userId }) {
  // 1. 正常获取用户订阅
  const { data: ownSubscriptions } = useQuery(getUserSubscribe({ user_id: userId }))

  // 2. 当自身订阅为空时,检查设备组成员身份
  const hasOwnSubscriptions = ownSubscriptions.list.length > 0
  const { data: familyData } = useQuery(
    getFamilyList({ user_id: userId, page: 1, size: 1 }),
    { enabled: !hasOwnSubscriptions }  // 仅当订阅为空时触发
  )

  // 3. 判断是否为非所有者成员
  const family = familyData?.list?.[0]
  const isNonOwnerMember = family && family.owner_user_id !== userId && family.status === 'active'
  const ownerUserId = family?.owner_user_id

  // 4. 若为成员,获取所有者的订阅
  const { data: sharedSubscriptions } = useQuery(
    getUserSubscribe({ user_id: ownerUserId }),
    { enabled: isNonOwnerMember && !!ownerUserId }
  )

  // 5. 决定展示内容
  const isSharedView = isNonOwnerMember && sharedSubscriptions?.list?.length > 0
  const displayData = isSharedView ? sharedSubscriptions : ownSubscriptions

  return (
    <div>
      {isSharedView && <SharedSubscriptionBanner ownerUserId={ownerUserId} familyId={family.family_id} />}
      <ProTable
        data={displayData}
        actions={isSharedView ? { render: () => [只读操作] } : { render: () => [完整操作] }}
        ...
      />
    </div>
  )
}

关键变更点

  • 将 ProTable 的 request 回调改为 React Query 管理数据获取
  • 或者保持 ProTable request 模式,在外层用 state 管理共享视图切换
  • 推荐方案:保持 ProTable 的 request 模式,但在 request 回调内部做链式检查

Step 2: 添加共享订阅信息横幅

在 ProTable 上方显示提示信息:

┌─────────────────────────────────────────────────────┐
│  该用户为设备组成员,当前显示所有者 (ID: 258)      │
│    的共享订阅。[查看设备组] [查看所有者]              │
└─────────────────────────────────────────────────────┘
  • 使用 Alert 组件展示
  • 提供跳转到设备组详情和所有者用户页面的链接
  • 标题列后追加 <Badge variant="secondary">共享</Badge> 标识

Step 3: 共享视图下禁用写操作

当处于共享订阅视图时:

  • 隐藏 "添加订阅" 按钮toolbar
  • 隐藏 "编辑" 按钮
  • 隐藏 删除、停止/恢复、重置令牌等破坏性操作
  • 保留 只读操作:查看日志、流量统计、在线设备等

Step 4: 添加国际化翻译

文件:

  • apps/admin/public/assets/locales/zh-CN/user.json
  • apps/admin/public/assets/locales/en-US/user.json

新增翻译 key

Key 中文 英文
sharedSubscription 共享订阅 Shared Subscription
sharedSubscriptionInfo 该用户为设备组成员,当前显示所有者 (ID: {{ownerId}}) 的共享订阅 This user is a device group member. Showing shared subscriptions from owner (ID: {{ownerId}})
viewDeviceGroup 查看设备组 View Device Group
viewOwner 查看所有者 View Owner

关键文件

文件 操作 说明
apps/admin/src/sections/user/user-subscription/index.tsx 修改 添加共享订阅检测与展示逻辑
apps/admin/public/assets/locales/zh-CN/user.json 修改 新增共享订阅相关中文翻译
apps/admin/public/assets/locales/en-US/user.json 修改 新增共享订阅相关英文翻译

风险与缓解

风险 缓解措施
设备组 API 调用失败 捕获异常,静默降级为显示空列表(现有行为)
所有者订阅也为空 正常显示空列表,不展示共享订阅横幅
用户同时有自身订阅和设备组成员身份 优先显示自身订阅(按描述,加入时会删除,不应同时存在)
多个设备组 取第一个活跃的设备组即可(一个用户通常只属于一个组)

边界情况

  1. 用户无订阅 + 不在设备组 → 正常空列表
  2. 用户无订阅 + 在设备组但为所有者 → 正常空列表(所有者自己订阅为空说明确实没有)
  3. 用户无订阅 + 在设备组但组已禁用 → 正常空列表
  4. 用户无订阅 + 在设备组且为活跃成员 → 显示所有者共享订阅 + 横幅提示

SESSION_ID

  • CODEX_SESSION: N/A纯前端方案未调用外部模型
  • GEMINI_SESSION: N/A