# 卡密数据模型

<cite>
**本文档引用的文件**  
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)
- [v_1_card_apple_history_info.go](file://internal/dao/internal/v_1_card_apple_history_info.go)
- [v_1_card_apple_account_info_history.go](file://internal/dao/internal/v_1_card_apple_account_info_history.go)
- [entity/v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go)
- [do/v_1_card_apple_recharge_info.go](file://internal/model/do/v_1_card_apple_recharge_info.go)
- [card_apple.go](file://internal/consts/card_apple.go)
- [card_apple_order.go](file://internal/model/card_apple_order.go)
</cite>

## 目录
1. [引言](#引言)
2. [卡密数据模型概述](#卡密数据模型概述)
3. [核心实体字段定义](#核心实体字段定义)
4. [主键与索引设计](#主键与索引设计)
5. [外键关系与实体关联](#外键关系与实体关联)
6. [验证规则与业务逻辑](#验证规则与业务逻辑)
7. [数据表模式与示例](#数据表模式与示例)
8. [访问模式与缓存策略](#访问模式与缓存策略)
9. [数据生命周期管理](#数据生命周期管理)
10. [GoFrame ORM映射与DAO实现](#goframe-orm映射与dao实现)

## 引言
本文档全面阐述kami_backend系统中卡密数据模型的设计与实现，重点围绕苹果礼品卡（Apple Card）相关实体展开。文档详细说明了卡密数据的核心实体、字段定义、业务规则、数据关系以及在GoFrame框架下的实现方式，为系统维护、开发和优化提供权威参考。

## 卡密数据模型概述
卡密数据模型是kami_backend系统的核心组成部分，主要用于管理苹果礼品卡的充值信息、账户信息及操作历史。该模型通过多个相互关联的数据表实现，主要包括卡密充值信息表（card_apple_recharge_info）、卡密账户信息表（card_apple_account_info）和操作历史记录表（card_apple_history_info）等。这些实体共同构成了一个完整的卡密生命周期管理体系，支持从卡密生成、充值处理到状态核销的全流程业务。

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)

## 核心实体字段定义
本节详细说明卡密相关核心实体的所有字段定义、数据类型和业务含义。

### v_1_card_apple_recharge_info 实体
该实体存储卡密充值的详细信息，是卡密业务的核心数据表。

| 字段名 | 数据类型 | 业务含义 |
|--------|---------|---------|
| id | uint | 主键，唯一标识一条充值记录 |
| order_no | string | 订单号，关联外部订单系统 |
| account_id | string | 账户ID，关联卡密账户信息 |
| account_name | string | 账户名称，便于识别 |
| card_no | string | 卡号，苹果礼品卡的号码 |
| card_pass | string | 卡密，苹果礼品卡的密码 |
| merchant_id | string | 商户ID，标识交易商户 |
| balance | float64 | 余额，账户当前余额 |
| card_amount | float64 | 卡面充值金额，卡密的标称价值 |
| notify_status | int | 通知状态，标识回调通知结果 |
| status | int | 状态，当前充值订单的状态 |
| actual_amount | float64 | 实际充值金额，最终确认的充值金额 |
| callback_url | string | 回调URL，用于异步通知结果 |
| callback_count | int | itunes回调次数，记录回调尝试次数 |
| distribution_count | int | 分发次数，记录调度分发次数 |
| created_user_id | string | 创建者ID，记录创建人 |
| attach | string | 附加信息，用于携带额外数据 |
| remark | string | 备注，业务备注信息 |
| created_at | *gtime.Time | 创建日期，记录创建时间 |
| updated_at | *gtime.Time | 更新日期，记录最后更新时间 |
| deleted_at | *gtime.Time | 删除日期，软删除标记 |

**文档来源**
- [entity/v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)

### v_1_card_apple_account_info 实体
该实体存储卡密账户的详细信息，用于管理充值账户的状态和限额。

| 字段名 | 数据类型 | 业务含义 |
|--------|---------|---------|
| id | string | 主键，唯一标识一个账户 |
| account | string | 账户，苹果ID账号 |
| password | string | 密码，苹果ID密码 |
| balance | float64 | 余额，账户当前余额 |
| balance_itunes | float64 | balance_itunes，itunes商店余额 |
| status | int | 状态，账户当前状态 |
| today_recharge_amount | float64 | today_recharge_amount，今日已充值金额 |
| today_recharge_count | int | today_recharge_count，今日已充值次数 |
| today_recharge_datetime | string | today_recharge_datetime，今日最后充值时间 |
| created_user_id | string | created_user_id，创建者ID |
| created_user_role | string | created_user_role，创建者角色 |
| max_amount_limit | float64 | max_amount_limit，最大单日充值金额限制 |
| max_count_limit | int | max_count_limit，最大单日充值次数限制 |
| remark | string | remark，账户备注 |
| created_at | string | created_at，创建日期 |
| updated_at | string | updated_at，更新日期 |
| deleted_at | string | deleted_at，删除日期 |

**文档来源**
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)

## 主键与索引设计
卡密数据模型采用合理的主键和索引设计以确保数据完整性和查询性能。

### 主键设计
- **v_1_card_apple_recharge_info** 表使用 `id` 字段作为自增主键，确保每条充值记录的唯一性。
- **v_1_card_apple_account_info** 表使用 `id` 字段作为主键，通常为UUID格式，保证账户标识的全局唯一性。

### 唯一索引
- **card_no** 字段在 `v_1_card_apple_recharge_info` 表上建立了唯一索引，防止同一卡号被重复提交充值，确保卡密的唯一性使用原则。

### 普通索引
- **order_no** 字段建立了索引，用于快速通过订单号查询充值记录。
- **account_id** 字段建立了索引，用于高效关联账户信息和查询特定账户的所有充值记录。
- **status** 字段建立了索引，支持按状态（如"交易中"、"交易成功"）进行高效筛选和批量处理。

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)

## 外键关系与实体关联
卡密数据模型通过外键和逻辑关联建立实体间的关系。

### 实体关系图
```mermaid
erDiagram
v_1_card_apple_recharge_info {
uint id PK
string order_no UK
string account_id FK
string card_no UK
string card_pass
float64 card_amount
int status
float64 actual_amount
}
v_1_card_apple_account_info {
string id PK
string account UK
string password
float64 balance
int status
float64 today_recharge_amount
int today_recharge_count
}
v_1_card_apple_history_info {
uint id PK
string account_id FK
string order_no FK
int recharge_id FK
string operation
string remark
}
v_1_card_apple_recharge_info ||--o{ v_1_card_apple_history_info : "充值记录-历史"
v_1_card_apple_account_info ||--o{ v_1_card_apple_recharge_info : "账户-充值"
v_1_card_apple_account_info ||--o{ v_1_card_apple_history_info : "账户-操作历史"
```

**图示来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)
- [v_1_card_apple_history_info.go](file://internal/dao/internal/v_1_card_apple_history_info.go)

### 关系说明
- **v_1_card_apple_recharge_info** 与 **v_1_card_apple_account_info** 通过 `account_id` 字段关联，表示每条充值记录都归属于一个具体的充值账户。
- **v_1_card_apple_recharge_info** 与 **v_1_card_apple_history_info** 通过 `order_no` 和 `recharge_id` 字段关联，记录针对特定充值订单的所有操作历史。
- **v_1_card_apple_account_info** 与 **v_1_card_apple_history_info** 通过 `account_id` 字段关联，记录账户级别的所有操作历史。

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)
- [v_1_card_apple_history_info.go](file://internal/dao/internal/v_1_card_apple_history_info.go)

## 验证规则与业务逻辑
卡密数据模型包含严格的验证规则和复杂的业务逻辑，确保数据的准确性和业务流程的正确性。

### 卡密格式校验
- 卡号（card_no）和卡密（card_pass）必须符合苹果礼品卡的格式规范，通常为16位数字组合。
- 在提交充值请求时，系统会对卡号和卡密进行基础格式验证，防止无效数据进入处理流程。

### 状态流转规则
卡密充值订单的状态遵循严格的流转规则：

```mermaid
stateDiagram-v2
[*] --> Created
Created --> Processing : 开始处理
Created --> Failed : 格式校验失败
Processing --> Success : 充值成功
Processing --> Failed : 充值失败
Processing --> Refunded : 退款
Success --> Refunded : 申请退款
Failed --> Processing : 重试
Refunded --> [*]
```

**图示来源**
- [card_apple.go](file://internal/consts/card_apple.go)

状态码定义：
- **0**: 创建（等待处理）
- **1**: 交易成功
- **2**: 交易中（处理中）
- **3**: 交易失败
- **4**: 充值受限
- **5**: 充值过期

### 业务规则
- **最小充值金额**：根据 `AppleCardMinRechargeAmount` 常量（50元），低于此金额的充值可能导致账户被冻结。
- **最大充值金额**：根据 `AppleCardMaxRechargeAmount` 常量（4500元），单次充值不能超过此上限。
- **账户状态控制**：账户状态（status）直接影响充值操作，如"账号禁用"或"账号密码错误"状态的账户无法进行充值。
- **防重复提交**：通过 `card_no` 唯一索引和业务层逻辑双重校验，防止同一卡密被重复提交。

**文档来源**
- [card_apple.go](file://internal/consts/card_apple.go)
- [card_apple_order.go](file://internal/model/card_apple_order.go)

## 数据表模式与示例
本节提供卡密数据表的模式定义和示例数据。

### 数据表模式
```sql
-- 卡密充值信息表
CREATE TABLE card_apple_recharge_info (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no VARCHAR(64) UNIQUE NOT NULL,
    account_id VARCHAR(36) NOT NULL,
    account_name VARCHAR(64),
    card_no VARCHAR(32) UNIQUE NOT NULL,
    card_pass VARCHAR(32) NOT NULL,
    merchant_id VARCHAR(36),
    balance DECIMAL(10,2),
    card_amount DECIMAL(10,2) NOT NULL,
    notify_status INT,
    status INT NOT NULL DEFAULT 0,
    actual_amount DECIMAL(10,2),
    callback_url VARCHAR(255),
    callback_count INT DEFAULT 0,
    distribution_count INT DEFAULT 0,
    created_user_id VARCHAR(36),
    attach TEXT,
    remark VARCHAR(255),
    created_at DATETIME,
    updated_at DATETIME,
    deleted_at DATETIME
);

-- 卡密账户信息表
CREATE TABLE card_apple_account_info (
    id VARCHAR(36) PRIMARY KEY,
    account VARCHAR(64) UNIQUE NOT NULL,
    password VARCHAR(64) NOT NULL,
    balance DECIMAL(10,2),
    balance_itunes DECIMAL(10,2),
    status INT NOT NULL DEFAULT 1,
    today_recharge_amount DECIMAL(10,2) DEFAULT 0,
    today_recharge_count INT DEFAULT 0,
    today_recharge_datetime DATETIME,
    created_user_id VARCHAR(36),
    created_user_role VARCHAR(36),
    max_amount_limit DECIMAL(10,2) DEFAULT 4500,
    max_count_limit INT DEFAULT 100,
    remark VARCHAR(255),
    created_at DATETIME,
    updated_at DATETIME,
    deleted_at DATETIME
);
```

### 示例数据
```json
// 卡密充值记录示例
{
  "id": 1001,
  "order_no": "ORD202311070001",
  "account_id": "acc-001",
  "account_name": "Apple账户A",
  "card_no": "1234567890123456",
  "card_pass": "ABCDEF123456",
  "card_amount": 100.00,
  "status": 1,
  "actual_amount": 100.00,
  "created_at": "2023-11-07T10:00:00Z"
}

// 卡密账户信息示例
{
  "id": "acc-001",
  "account": "user@apple.com",
  "password": "encrypted_password",
  "balance": 5000.00,
  "balance_itunes": 4800.00,
  "status": 1,
  "today_recharge_amount": 300.00,
  "today_recharge_count": 3
}
```

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)

## 访问模式与缓存策略
卡密数据具有特定的访问模式，系统采用了相应的缓存策略来优化性能。

### 访问模式
- **高频查询**：通过 `order_no` 和 `card_no` 查询单条记录是最常见的访问模式，属于高频低延迟操作。
- **列表查询**：按 `account_id` 和 `status` 查询充值记录列表，用于管理后台的订单列表展示。
- **聚合查询**：统计特定时间段内的充值总额、成功率等，用于业务报表生成。

### 缓存策略
- **Redis缓存**：系统使用Redis缓存热点数据，如活跃账户信息和最近成功的充值记录，减少数据库直接访问。
- **缓存键设计**：采用 `card_recharge:{card_no}` 和 `card_account:{account_id}` 的键名模式，确保缓存的唯一性和可预测性。
- **缓存失效**：当充值记录状态更新或账户信息变更时，立即失效相关缓存，保证数据一致性。
- **批量操作优化**：对于批量查询场景，采用管道（pipeline）技术减少网络往返次数，提高吞吐量。

**文档来源**
- [utility/cache/cache.go](file://utility/cache/cache.go)
- [card_apple_order.go](file://internal/model/card_apple_order.go)

## 数据生命周期管理
卡密数据遵循完整的生命周期管理策略，从生成到归档。

### 生命周期流程
```mermaid
flowchart TD
A[卡密生成] --> B[提交充值]
B --> C{状态检查}
C --> |成功| D[核销完成]
C --> |失败| E[重试处理]
E --> C
D --> F[归档标记]
F --> G[定期归档]
G --> H[冷存储]
```

**图示来源**
- [card_apple_order.go](file://internal/model/card_apple_order.go)

### 保留策略
- **在线数据**：所有未完成和最近完成的充值记录保留在主数据库中，支持实时查询和操作。
- **归档策略**：已完成超过6个月的充值记录会被标记为归档状态，从主查询路径中移除，但仍可通过特定接口访问。
- **软删除**：采用 `deleted_at` 字段实现软删除，所有删除操作都只是标记删除时间，保留数据用于审计和对账。

### 归档规则
- **触发条件**：订单状态为"交易成功"或"交易失败"且最后更新时间超过180天。
- **归档方式**：将数据从主表移动到历史归档表，并从Redis缓存中清除。
- **恢复机制**：提供专门的恢复接口，可在必要时将归档数据恢复到在线状态。

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [v_1_card_apple_account_info.go](file://internal/dao/internal/v_1_card_apple_account_info.go)

## GoFrame ORM映射与DAO实现
系统使用GoFrame ORM框架实现卡密数据模型到Go结构体的映射和数据访问。

### ORM结构体映射
GoFrame通过结构体标签（struct tags）实现数据库字段到Go结构体的映射：

```go
// V1CardAppleRechargeInfo is the golang structure for table v1card_apple_recharge_info.
type V1CardAppleRechargeInfo struct {
	Id                uint        `json:"id"                orm:"id"                 description:""`
	OrderNo           string      `json:"orderNo"           orm:"order_no"           description:"订单号"`
	CardNo            string      `json:"cardNo"            orm:"card_no"            description:"卡号"`
	Status            int         `json:"status"            orm:"status"             description:"状态 0.创建 1.交易成功 2.交易中 3.交易失败"`
	// ... 其他字段
	CreatedAt         *gtime.Time `json:"createdAt"         orm:"created_at"         description:"创建日期"`
}
```

**文档来源**
- [entity/v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go)

### DAO层实现
DAO层提供了对卡密数据的批量操作和状态更新功能：

#### 批量操作
- **批量添加**：支持从Excel模板批量导入卡密信息，通过事务确保数据一致性。
- **批量查询**：提供分页查询接口，支持按多种条件组合筛选。
- **批量更新**：支持批量修改卡密状态，用于大规模运维操作。

#### 状态更新
- **原子更新**：使用数据库事务和行级锁确保状态更新的原子性。
- **状态变更历史**：每次状态更新都会在 `v_1_card_apple_history_info` 表中记录操作日志。
- **并发控制**：通过乐观锁或悲观锁机制防止并发更新导致的数据不一致。

```go
// 示例：状态更新方法
func (s *service) UpdateStatus(ctx context.Context, id int, status int) error {
    return dao.V1CardAppleRechargeInfo.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
        // 1. 查询当前记录
        record, err := dao.V1CardAppleRechargeInfo.Ctx(ctx).Where("id", id).One()
        if err != nil {
            return err
        }
        // 2. 记录历史
        history := &entity.V1CardAppleHistoryInfo{
            RechargeId: id,
            Operation:  "状态更新",
            Remark:     fmt.Sprintf("从 %d 更新到 %d", record["status"], status),
        }
        _, err = dao.V1CardAppleHistoryInfo.Ctx(ctx).Data(history).Insert()
        if err != nil {
            return err
        }
        // 3. 更新状态
        _, err = dao.V1CardAppleRechargeInfo.Ctx(ctx).Where("id", id).Data("status", status).Update()
        return err
    })
}
```

**文档来源**
- [v_1_card_apple_recharge_info.go](file://internal/dao/internal/v_1_card_apple_recharge_info.go)
- [card_apple_order.go](file://internal/model/card_apple_order.go)
- [entity/v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go)