Some checks failed
Build and Release / Build (push) Has been cancelled
- 新增家庭共享订阅管理 - 新增用户邀请统计 - 新增签名和订阅模式设置表单 - 更新 API 服务层和国际化文件 - UI 组件优化(enhanced-input、pro-table)
5.7 KiB
5.7 KiB
实施计划:后台管理 - 共享订阅显示
问题描述
设备组成员加入后,其原始订阅被删除,使用所有者的共享订阅。 在后台管理的用户订阅面板中,查看设备组成员的订阅时显示为空,因为数据已在合并时被删除。 需要在后台自动检测并显示共享订阅信息。
技术方案
纯前端方案,不需要后端 API 变更。利用现有 API 组合实现:
getUserSubscribe({ user_id })→ 获取用户自身订阅(可能为空)getFamilyList({ user_id, page: 1, size: 1 })→ 检测用户是否属于设备组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.jsonapps/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 调用失败 | 捕获异常,静默降级为显示空列表(现有行为) |
| 所有者订阅也为空 | 正常显示空列表,不展示共享订阅横幅 |
| 用户同时有自身订阅和设备组成员身份 | 优先显示自身订阅(按描述,加入时会删除,不应同时存在) |
| 多个设备组 | 取第一个活跃的设备组即可(一个用户通常只属于一个组) |
边界情况
- 用户无订阅 + 不在设备组 → 正常空列表
- 用户无订阅 + 在设备组但为所有者 → 正常空列表(所有者自己订阅为空说明确实没有)
- 用户无订阅 + 在设备组但组已禁用 → 正常空列表
- 用户无订阅 + 在设备组且为活跃成员 → 显示所有者共享订阅 + 横幅提示
SESSION_ID
- CODEX_SESSION: N/A(纯前端方案,未调用外部模型)
- GEMINI_SESSION: N/A