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

## 问题描述

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

## 技术方案

**纯前端方案**，不需要后端 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
