# 授权记录管理 - 技术设计

## Context

### 背景

企业卡授权功能已实现授权/回收操作，数据存储在 `tb_enterprise_card_authorization` 表中。但目前缺少对授权记录本身的管理视角，无法审计授权历史。

### 现有架构

```
tb_enterprise_card_authorization
├── id, created_at, updated_at, deleted_at
├── enterprise_id (被授权企业ID)
├── card_id (被授权卡ID)
├── authorized_by (授权人账号ID)
├── authorized_at (授权时间)
├── authorizer_type (授权人类型：2=平台，3=代理)
├── revoked_by (回收人账号ID)
├── revoked_at (回收时间)
└── remark (授权备注)
```

### 约束

1. 表中没有 `shop_id` 字段，需要通过 `enterprise_id` 关联 `tb_enterprise.owner_shop_id` 来判断归属
2. 代理用户只能看到自己店铺的数据，不包含下级店铺
3. 现有 GORM Callback 对无 `shop_id` 字段的表直接跳过过滤，存在权限漏洞

## Goals / Non-Goals

**Goals:**
- 提供授权记录的列表、详情、备注修改接口
- 修复数据权限过滤，确保代理只能看到自己店铺下企业的授权记录
- 列表接口支持多条件筛选和分页

**Non-Goals:**
- 不提供删除授权记录的能力（只能通过回收操作标记）
- 不提供统计接口（后续按需添加）
- 不提供导出功能

## Decisions

### 决策 1：数据权限过滤方式

**选择**：在 GORM Callback 中为授权记录表添加特殊处理，使用子查询过滤

**方案对比**：

| 方案 | 优点 | 缺点 |
|------|------|------|
| A. Callback 子查询 ✓ | 自动应用，无需手动过滤 | 子查询可能影响性能 |
| B. 添加冗余 shop_id 字段 | 查询简单高效 | 需要迁移，数据一致性风险 |
| C. Service 层手动过滤 | 灵活可控 | 容易遗漏，不符合现有模式 |

**理由**：方案 A 与现有架构一致，子查询性能可接受（授权记录量不大），且自动应用避免遗漏。

**实现**：
```go
// pkg/gorm/callback.go
if tableName == "tb_enterprise_card_authorization" {
    // 代理用户：只能看自己店铺下企业的授权记录
    tx.Where("enterprise_id IN (SELECT id FROM tb_enterprise WHERE owner_shop_id = ?)", shopID)
    return
}
```

### 决策 2：列表接口关联查询

**选择**：在 Store 层使用 JOIN 一次性获取关联数据

**方案对比**：

| 方案 | 优点 | 缺点 |
|------|------|------|
| A. Store 层 JOIN ✓ | 单次查询，性能好 | SQL 稍复杂 |
| B. Service 层多次查询 | 逻辑清晰 | N+1 问题 |
| C. 使用 GORM Preload | 代码简洁 | 需要定义关联关系（项目禁止） |

**理由**：项目禁止使用 GORM 关联关系，JOIN 是最佳选择。

**实现**：
```sql
SELECT 
    a.*,
    e.enterprise_name,
    c.iccid, c.msisdn,
    acc1.username as authorizer_name,
    acc2.username as revoker_name
FROM tb_enterprise_card_authorization a
LEFT JOIN tb_enterprise e ON a.enterprise_id = e.id
LEFT JOIN tb_iot_card c ON a.card_id = c.id
LEFT JOIN tb_account acc1 ON a.authorized_by = acc1.id
LEFT JOIN tb_account acc2 ON a.revoked_by = acc2.id
WHERE ...
```

### 决策 3：备注修改权限

**选择**：平台用户可改任意记录，代理用户只能修改可见范围内的记录

**理由**：
- 平台需要管理能力，可以修正任何备注
- 代理只能操作自己店铺的数据，符合数据隔离原则
- 不限制"只能修改自己创建的"，便于同店铺协作

## Risks / Trade-offs

| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 子查询性能 | 大数据量时可能变慢 | 授权记录量可控；必要时添加索引 |
| JOIN 查询复杂 | 维护成本增加 | 封装在 Store 层，对外透明 |
| 权限逻辑特殊 | 与其他表不一致 | 在 Callback 中添加清晰注释 |

## Migration Plan

1. 先发布 Callback 修复（修复权限漏洞）
2. 再发布新接口（列表、详情、备注修改）
3. 无需数据迁移，无破坏性变更

## Open Questions

无
