# 实施计划:后台管理 - 共享订阅显示 ## 问题描述 设备组成员加入后,其原始订阅被删除,使用所有者的共享订阅。 在后台管理的用户订阅面板中,查看设备组成员的订阅时显示为空,因为数据已在合并时被删除。 需要在后台自动检测并显示共享订阅信息。 ## 技术方案 **纯前端方案**,不需要后端 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 (
{isSharedView && } [只读操作] } : { render: () => [完整操作] }} ... />
) } ``` **关键变更点**: - 将 ProTable 的 `request` 回调改为 React Query 管理数据获取 - 或者保持 ProTable request 模式,在外层用 state 管理共享视图切换 - 推荐方案:保持 ProTable 的 request 模式,但在 request 回调内部做链式检查 ### Step 2: 添加共享订阅信息横幅 在 ProTable 上方显示提示信息: ``` ┌─────────────────────────────────────────────────────┐ │ ℹ️ 该用户为设备组成员,当前显示所有者 (ID: 258) │ │ 的共享订阅。[查看设备组] [查看所有者] │ └─────────────────────────────────────────────────────┘ ``` - 使用 Alert 组件展示 - 提供跳转到设备组详情和所有者用户页面的链接 - 标题列后追加 `共享` 标识 ### 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