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

137 lines
5.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 实施计划:后台管理 - 共享订阅显示
## 问题描述
设备组成员加入后,其原始订阅被删除,使用所有者的共享订阅。
在后台管理的用户订阅面板中,查看设备组成员的订阅时显示为空,因为数据已在合并时被删除。
需要在后台自动检测并显示共享订阅信息。
## 技术方案
**纯前端方案**,不需要后端 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