# 数据模型与ORM映射

<cite>
**本文档中引用的文件**  
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go)
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go)
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go)
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go)
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go)
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go)
- [v_1_account_info.go](file://internal/dao/v_1_account_info.go)
- [v_1_order_info.go](file://internal/dao/v_1_order_info.go)
- [v_1_card_apple_recharge_info.go](file://internal/dao/v_1_card_apple_recharge_info.go)
- [v_1_jd_cookie_account.go](file://internal/dao/v_1_jd_cookie_account.go)
- [v_1_jd_cookie_jd_order.go](file://internal/dao/v_1_jd_cookie_jd_order.go)
- [v_1_jd_cookie_order.go](file://internal/dao/v_1_jd_cookie_order.go)
- [merchant_v1_merchant_config_add.go](file://internal/controller/merchant/merchant_v1_merchant_config_add.go)
- [account.go](file://internal/logic/account/account.go)
- [order.go](file://internal/logic/merchant_order/order.go)
- [order.go](file://internal/logic/card_apple_order/order.go)
- [status.go](file://internal/logic/card_apple_order/status.go)
- [recharge_history.go](file://internal/logic/card_apple_order/recharge_history.go)
- [steal_relation.go](file://internal/logic/card_apple_order/steal_relation.go)
- [account.go](file://internal/logic/jd_cookie/account.go)
- [order.go](file://internal/logic/jd_cookie/order.go)
- [rotation.go](file://internal/logic/jd_cookie/rotation.go)
</cite>

## 更新摘要
**已更新内容**  

- 在核心数据模型部分更新了京东Cookie管理模块的三个核心数据表，添加了新字段
- 在实体关系与约束部分更新了外键关系说明
- 在数据验证与业务规则部分新增了京东订单复用和风控失败状态的业务规则
- 在GoFrame ORM映射机制部分更新了DO和Entity层说明
- 在DAO层实现部分更新了DAO结构说明
- 更新了文档引用文件列表，包含新模块的相关文件

## 目录
1. [引言](#引言)
2. [核心数据模型](#核心数据模型)
3. [实体关系与约束](#实体关系与约束)
4. [数据验证与业务规则](#数据验证与业务规则)
5. [数据库模式图](#数据库模式图)
6. [数据访问模式与缓存策略](#数据访问模式与缓存策略)
7. [数据生命周期与归档](#数据生命周期与归档)
8. [数据迁移与版本管理](#数据迁移与版本管理)
9. [GoFrame ORM映射机制](#goframe-orm映射机制)
10. [DAO层实现](#dao层实现)

## 引言
本文档全面阐述kami_backend系统的数据模型设计，涵盖核心数据表的结构定义、实体关系、约束规则、业务逻辑以及GoFrame ORM框架下的映射实现。重点分析v_1_account_info、v_1_order_info和v_1_card_apple_recharge_info三个核心数据表，揭示其在系统中的作用和相互关系。同时，新增了京东Cookie管理模块的三个核心数据表，展示了系统在订单复用和智能轮询方面的最新设计。

## 核心数据模型

### v_1_account_info（账户信息表）
该表存储系统内所有账户（商户或代理）的财务信息。

**字段定义：**
- **id**: 主键，自增，类型为`uint`
- **status**: 账户状态，类型为`string`
- **accountUid**: 账户唯一标识，对应merchant_uid或agent_uid，类型为`string`
- **accountName**: 账户名称，类型为`string`
- **balance**: 账户余额，使用`decimal.Decimal`精确存储
- **settleAmount**: 已结算金额，使用`decimal.Decimal`精确存储
- **loanAmount**: 押款金额，使用`decimal.Decimal`精确存储
- **waitAmount**: 待结算资金，使用`decimal.Decimal`精确存储
- **freezeAmount**: 账户冻结金额，使用`decimal.Decimal`精确存储
- **payforAmount**: 账户代付中金额，使用`decimal.Decimal`精确存储
- **updateTime**: 更新时间，类型为`*gtime.Time`
- **createTime**: 创建时间，类型为`*gtime.Time`

**Section sources**
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go#L12-L25)

### v_1_order_info（订单信息表）
该表存储所有支付订单的详细信息。

**字段定义：**
- **id**: 主键ID，类型为`uint`
- **merchantOrderId**: 商户订单ID，类型为`string`
- **shopName**: 商品名称，类型为`string`
- **orderPeriod**: 订单有效时间，类型为`string`
- **bankOrderId**: 系统订单ID，类型为`string`
- **bankTransId**: 上游流水ID，类型为`string`
- **orderAmount**: 订单提交金额，使用`decimal.Decimal`精确存储
- **showAmount**: 待支付金额，使用`decimal.Decimal`精确存储
- **factAmount**: 实际支付金额，使用`decimal.Decimal`精确存储
- **rollPoolCode**: 轮询池编码，类型为`string`
- **rollPoolName**: 轮询池名称，类型为`string`
- **roadUid**: 支付通道ID，类型为`string`
- **roadName**: 支付通道名称，类型为`string`
- **payProductCode**: 上游支付公司编码，类型为`string`
- **payProductName**: 上游支付公司名称，类型为`string`
- **payTypeCode**: 支付产品编码，类型为`string`
- **payTypeName**: 支付产品名称，类型为`string`
- **osType**: 操作系统类型，类型为`string`
- **status**: 订单支付状态，类型为`string`
- **refund**: 退款状态，类型为`string`
- **freeze**: 冻结状态，类型为`string`
- **notifyUrl**: 回调通知地址，类型为`string`
- **merchantUid**: 商户ID，类型为`string`
- **agentUid**: 代理ID，类型为`string`
- **response**: 上游返回的结果，类型为`string`
- **updateTime**: 更新时间，类型为`*gtime.Time`
- **createTime**: 创建时间，类型为`*gtime.Time`
- **cardReturnTime**: 上游返回数据时间，类型为`*gtime.Time`
- **payTime**: 支付时间，类型为`*gtime.Time`
- **ip**: IP地址，类型为`string`
- **isIpRestricted**: IP是否被限制，类型为`int`
- **isBlock**: 是否在黑名单中，类型为`int`
- **payUrl**: 支付链接，类型为`string`
- **isReplace**: 是否是偷卡的链接，类型为`int`
- **isSucceedByReplace**: 是否通过偷卡成功，类型为`int`
- **sendCount**: 核销次数，类型为`int`
- **isAmountDifferent**: 金额是否不同，类型为`int`
- **sendRecord**: 历史记录，类型为`string`
- **poolOrderId**: 池订单ID，类型为`string`

**Section sources**
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go#L12-L63)

### v_1_card_apple_recharge_info（苹果卡密充值信息表）
该表存储苹果卡密充值订单的详细信息。

**字段定义：**
- **id**: 主键，类型为`uint`
- **orderNo**: 订单号，类型为`string`
- **accountId**: 账户ID，类型为`string`
- **accountName**: 账户名称，类型为`string`
- **cardNo**: 卡号，类型为`string`
- **cardPass**: 卡密，类型为`string`
- **merchantId**: 商户ID，类型为`string`
- **balance**: 余额，类型为`float64`
- **cardAmount**: 卡面充值金额，类型为`float64`
- **notifyStatus**: 通知状态，类型为`int`
- **status**: 状态（0.创建 1.交易成功 2.交易中 3.交易失败），类型为`int`
- **actualAmount**: 实际充值金额，类型为`float64`
- **callbackUrl**: 回调URL，类型为`string`
- **callbackCount**: itunes回调次数，类型为`int`
- **distributionCount**: 分发次数，类型为`int`
- **createdUserId**: 创建者ID，类型为`string`
- **attach**: 附加信息，类型为`string`
- **remark**: 备注，类型为`string`
- **createdAt**: 创建日期，类型为`*gtime.Time`
- **updatedAt**: 更新日期，类型为`*gtime.Time`
- **deletedAt**: 删除日期，类型为`*gtime.Time`

**Section sources**
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go#L11-L33)

### v_1_jd_cookie_account（京东Cookie账户表）
该表存储京东Cookie账户的详细信息，支持智能轮询和状态管理。

**字段定义：**
- **id**: 主键，自增，类型为`uint`
- **cookieId**: Cookie唯一标识，类型为`string`
- **cookieValue**: Cookie内容，类型为`string`
- **accountName**: 账户名称，类型为`string`
- **status**: 账户状态（1.正常 2.暂停 3.失效），类型为`int`
- **failureCount**: 连续失败次数，类型为`int`
- **lastUsedAt**: 最后使用时间，类型为`*gtime.Time`
- **suspendUntil**: 暂停解除时间，用于自动恢复，类型为`*gtime.Time`
- **remark**: 备注信息，类型为`string`
- **createdAt**: 创建时间，类型为`*gtime.Time`
- **updatedAt**: 更新时间，类型为`*gtime.Time`
- **deletedAt**: 删除时间，类型为`*gtime.Time`

**Section sources**
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)

### v_1_jd_cookie_jd_order（京东订单表）
该表存储京东平台订单的详细信息，支持订单复用功能。

**字段定义：**
- **id**: 主键，自增，类型为`uint`
- **jdOrderId**: 京东订单号，类型为`string`
- **realJdOrderId**: 京东客户端返回的真实订单ID，类型为`string`
- **payId**: 支付ID，类型为`string`
- **amount**: 订单金额，使用`decimal.Decimal`精确存储
- **category**: 商品品类，类型为`string`
- **cookieId**: 使用的Cookie ID，类型为`string`
- **status**: 订单状态（1.待支付 2.已支付 3.已过期 4.已取消），类型为`int`
- **wxPayUrl**: 微信支付链接，类型为`string`
- **wxPayExpireAt**: 微信支付链接过期时间，类型为`*gtime.Time`
- **orderExpireAt**: 订单过期时间(默认24小时)，类型为`*gtime.Time`
- **orderId**: 关联的用户订单号，类型为`string`
- **paidAt**: 支付完成时间，类型为`*gtime.Time`
- **cardNo**: 卡号，类型为`string`
- **cardPassword**: 卡密，类型为`string`
- **cardExtractedAt**: 卡密提取时间，类型为`*gtime.Time`
- **createdAt**: 创建时间，类型为`*gtime.Time`
- **updatedAt**: 更新时间，类型为`*gtime.Time`
- **deletedAt**: 删除时间，类型为`*gtime.Time`

**Section sources**

- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)

### v_1_jd_cookie_order（京东Cookie订单表）
该表存储系统订单与京东Cookie的关联信息。

**字段定义：**
- **id**: 主键，自增，类型为`uint`
- **orderId**: 订单号，类型为`string`
- **userOrderId**: 用户订单号，类型为`string`
- **amount**: 订单金额，使用`decimal.Decimal`精确存储
- **category**: 商品品类，类型为`string`
- **jdOrderId**: 关联的京东订单号，类型为`string`
- **status**: 状态（1.待支付 2.已支付 3.已过期 4.已取消），类型为`int`
- **lastRequestAt**: 最后请求时间，类型为`*gtime.Time`
- **createdAt**: 创建时间，类型为`*gtime.Time`
- **updatedAt**: 更新时间，类型为`*gtime.Time`
- **deletedAt**: 删除时间，类型为`*gtime.Time`

**Section sources**

- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)

## 实体关系与约束

### 主键与索引
- 所有核心表均以`id`作为主键，且为自增字段。
- `v_1_account_info`表的`accountUid`字段是业务主键，确保账户的唯一性。
- `v_1_order_info`表的`bankOrderId`和`merchantOrderId`字段有唯一性约束，用于快速查询订单。
- `v_1_card_apple_recharge_info`表的`orderNo`字段是业务主键，确保订单号的唯一性。
- `v_1_jd_cookie_account`表的`cookieId`字段有唯一性约束，防止重复添加相同的Cookie。
- `v_1_jd_cookie_jd_order`表的`jdOrderId`字段是业务主键，确保京东订单的唯一性。
- `v_1_jd_cookie_order`表的`orderId`字段是业务主键，确保系统订单与京东订单关联的唯一性。
- `v_1_jd_cookie_order`表的`userOrderId`字段有索引，用于快速查询用户订单。

### 外键关系
本系统采用弱外键约束，主要通过业务逻辑来维护数据一致性：
- `v_1_order_info.merchantUid` 关联 `v_1_account_info.accountUid`，表示订单所属的商户账户。
- `v_1_card_apple_recharge_info.merchantId` 关联 `v_1_account_info.accountUid`，表示充值订单所属的商户。
- `v_1_card_apple_recharge_info.accountId` 关联 `v_1_card_apple_account_info.id`（未在文档中详述），表示充值所使用的苹果账户。
- `v_1_jd_cookie_jd_order.cookieId` 关联 `v_1_jd_cookie_account.cookieId`，表示京东订单所属的Cookie账户。
- `v_1_jd_cookie_jd_order.orderId` 关联 `v_1_jd_cookie_order.userOrderId`，表示京东订单对应的用户订单。
- `v_1_jd_cookie_order.jdOrderId` 关联 `v_1_jd_cookie_jd_order.jdOrderId`，表示订单关联的京东平台订单。

### 约束规则

- **状态约束**：各表的`status`字段有明确的枚举值范围，如`v_1_jd_cookie_account.status`只能为1,2,3。
- **金额约束**：涉及金额的字段（如`balance`, `actualAmount`）使用`decimal.Decimal`或`float64`，确保精度。
- **时间约束**：`createdAt`必须小于等于`updatedAt`，`deletedAt`在软删除时才不为空。
- **唯一性约束**：关键业务字段如`cookieId`、`jdOrderId`等有唯一性约束，防止数据重复。

**Section sources**
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go#L12-L25)
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go#L12-L63)
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go#L11-L33)
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)

## 数据验证与业务规则

### 数据验证规则
- **非空验证**：所有主键、业务主键（如`orderNo`, `accountUid`）、创建时间等字段均不能为空。
- **格式验证**：`cardPass`（卡密）需符合特定的长度和字符规则；`ip`字段需为有效的IP地址格式；`cookieValue`字段需为有效的京东Cookie格式。
- **范围验证**：`status`字段必须在预定义的枚举值范围内；金额字段不能为负数。

### 核心业务规则
1.  **账户创建规则**：当创建新商户时，系统会自动为其创建一个初始账户（`v_1_account_info`），初始余额为0，状态为激活。
    ```go
    // 示例：在创建商户时同时创建账户
    err = service.Account().Add(ctx, do.V1AccountInfo{
        Status:      consts.StatusActive,
        AccountUid:  merchantUid,
        AccountName: req.MerchantName,
    }, tx)
    ```
    **Section sources**
    - [merchant_v1_merchant_config_add.go](file://internal/controller/merchant/merchant_v1_merchant_config_add.go#L17-L47)

2.  **订单黑名单规则**：当订单被标记为黑名单（`isBlock=1`）时，后续的支付请求将被拒绝。
    ```go
    // 示例：设置订单黑名单状态
    _, err = db.Where(dao.V1OrderInfo.Columns().BankOrderId, orderNo).Update(do.V1OrderInfo{
        IsBlock: status,
    })
    ```
    **Section sources**
    - [order.go](file://internal/logic/merchant_order/order.go#L13-L26)

3.  **苹果卡密充值规则**：
    - **重复卡密检测**：系统会检查`cardPass`是否已存在，防止重复充值。
    - **金额校验**：提交的充值金额必须与卡面金额（`cardAmount`）一致。
    - **状态流转**：订单状态从“创建”(0) -> “交易中”(2) -> “交易成功”(1) 或 “交易失败”(3)，不可逆。

    ```go
    // 示例：根据卡密查询订单，用于重复检测
    err = m.Where(dao.V1CardAppleRechargeInfo.Columns().CardPass, cardPass).Scan(&data)
    ```
    **Section sources**
    - [recharge_history.go](file://internal/logic/card_apple_order/recharge_history.go#L131-L141)

4.  **偷卡规则**：系统支持“偷卡”功能，即当满足特定条件（如金额、用户）时，自动将订单分配给特定账户。
    ```go
    // 示例：检查是否符合偷卡规则
    isTargetUserNormal, _ := service.SysUser().CheckUserNormal(ctx, rule.TargetUserId)
    isStorageUserNormal, _ := service.SysUser().CheckUserNormal(ctx, rule.StorageUserId)
    if totalAmount > float64(rule.Amount) && amount == float64(rule.TargetAmount) &&
        isTargetUserNormal && isStorageUserNormal {
        return true, &rule, nil
    }
    ```
    **Section sources**
    - [steal_relation.go](file://internal/logic/card_apple_order/steal_relation.go#L19-L64)

5.  **京东Cookie轮询规则**：系统采用最少使用原则进行Cookie轮询分配，优先选择失败次数少且最近未使用的Cookie账户。
    ```go
    // 示例：获取可用的Cookie账户
    err = dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
        Where(dao.V1JdCookieAccount.Columns().Status, consts.JdCookieStatusNormal).
        Where(dao.V1JdCookieAccount.Columns().SuspendUntil+" IS NULL OR "+dao.V1JdCookieAccount.Columns().SuspendUntil+" < ?", gtime.Now()).
        OrderAsc(dao.V1JdCookieAccount.Columns().LastUsedAt).
        OrderAsc(dao.V1JdCookieAccount.Columns().FailureCount).
        Scan(&accounts)
    ```
    **Section sources**
    - [rotation.go](file://internal/logic/jd_cookie/rotation.go#L15-L30)

6.  **京东订单复用规则**：当新的系统订单金额与未过期的京东订单金额匹配时，系统会复用现有的京东订单，避免重复下单。
    ```go
    // 示例：查询可复用的京东订单
    err = dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
        Where(dao.V1JdCookieJdOrder.Columns().Status, consts.JdOrderStatusPending).
        Where(dao.V1JdCookieJdOrder.Columns().Amount, amount).
        Where(dao.V1JdCookieJdOrder.Columns().OrderExpireAt+" > ?", gtime.Now()).
        Scan(&orders)
    ```
    **Section sources**
    - [order.go](file://internal/logic/jd_cookie/order.go#L45-L60)

7. **风控失败状态规则**：当京东订单因风控失败时，系统会记录失败状态和备注信息，并更新相关订单状态。
   ```go
   // 示例：处理风控失败的京东订单
   err = s.UpdateJdOrderStatus(ctx, jdOrderId, consts.JdOrderStatusCkFailed, "", "风控校验失败")
   ```
   **Section sources**
    - [rotation.go](file://internal/logic/jd_cookie/rotation.go#L250-L265)

## 数据库模式图

```mermaid
erDiagram
v_1_account_info {
uint id PK
string status
string accountUid UK
string accountName
decimal balance
decimal settleAmount
decimal loanAmount
decimal waitAmount
decimal freezeAmount
decimal payforAmount
timestamp updateTime
timestamp createTime
}
v_1_order_info {
uint id PK
string merchantOrderId UK
string bankOrderId UK
string shopName
decimal orderAmount
decimal showAmount
decimal factAmount
string status
string merchantUid FK
string agentUid FK
timestamp createTime
timestamp updateTime
}
v_1_card_apple_recharge_info {
uint id PK
string orderNo UK
string accountId FK
string merchantId FK
string cardNo
string cardPass
float64 balance
float64 cardAmount
float64 actualAmount
int status
int callbackCount
int distributionCount
string createdUserId
timestamp createdAt
timestamp updatedAt
timestamp deletedAt
}
v_1_jd_cookie_account {
uint id PK
string cookieId UK
string cookieValue
string accountName
int status
int failureCount
timestamp lastUsedAt
timestamp suspendUntil
string remark
timestamp createdAt
timestamp updatedAt
timestamp deletedAt
}
v_1_jd_cookie_jd_order {
uint id PK
string jdOrderId UK
string realJdOrderId
string payId
decimal amount
string category
string cookieId FK
int status
string wxPayUrl
timestamp wxPayExpireAt
timestamp orderExpireAt
string orderId FK
timestamp paidAt
string cardNo
string cardPassword
timestamp cardExtractedAt
timestamp createdAt
timestamp updatedAt
timestamp deletedAt
}
v_1_jd_cookie_order {
uint id PK
string orderId UK
string userOrderId
decimal amount
string category
string jdOrderId FK
int status
timestamp lastRequestAt
timestamp createdAt
timestamp updatedAt
timestamp deletedAt
}
v_1_account_info ||--o{ v_1_order_info : "merchantUid"
v_1_account_info ||--o{ v_1_card_apple_recharge_info : "merchantId"
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_jd_order : "cookieId"
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_order : "关联"
v_1_jd_cookie_order ||--o{ v_1_jd_cookie_jd_order : "orderId"
v_1_jd_cookie_jd_order ||--o{ v_1_jd_cookie_order : "jdOrderId"
```

**Diagram sources**
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go#L12-L25)
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go#L12-L63)
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go#L11-L33)
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)

## 数据访问模式与缓存策略

### 数据访问模式
- **读多写少**：`v_1_account_info`和`v_1_order_info`表的查询频率远高于写入，特别是订单状态查询。
- **分页查询**：所有列表接口（如订单列表、充值记录列表）均采用分页查询，避免全表扫描。
    ```go
    // 示例：苹果充值订单分页查询
    err2 := m.Page(input.Current, input.PageSize).
        OrderDesc(dao.V1CardAppleRechargeInfo.Columns().CreatedAt).
        ScanAndCount(&data, &total, false)
    ```
    **Section sources**
    - [order.go](file://internal/logic/card_apple_order/order.go#L23-L65)

- **事务性操作**：涉及多个表的更新操作（如创建商户及其账户）使用数据库事务保证原子性。

### 缓存策略
- **Redis缓存**：高频访问的数据（如商户配置、系统参数）使用Redis进行缓存，减少数据库压力。
- **本地缓存**：对于极热点的数据，可考虑使用内存缓存（如sync.Map）进行二次加速。
- **缓存失效**：当数据更新时，通过发布/订阅模式或直接删除缓存键来保证缓存一致性。

## 数据生命周期与归档

### 数据生命周期
- **v_1_account_info**：账户数据为永久存储，除非商户被彻底删除（软删除）。
- **v_1_order_info**：订单数据保留至少2年，满足财务审计要求。
- **v_1_card_apple_recharge_info**：充值记录保留3年，之后可进行归档。
- **v_1_jd_cookie_account**：Cookie账户数据永久存储，失效账户标记为失效状态。
- **v_1_jd_cookie_jd_order**：京东订单数据保留1年，过期订单可进行归档。
- **v_1_jd_cookie_order**：订单关联数据保留2年，与系统订单生命周期一致。

### 保留策略
- **软删除**：所有支持删除的操作均采用软删除，通过`deletedAt`字段标记删除时间，而非物理删除。
- **日志记录**：所有关键数据的变更（创建、更新、删除）均记录操作日志，便于追溯。

### 归档规则
- **自动归档**：系统提供定时任务，将超过保留期限的旧数据从主库迁移至归档库。
- **归档查询**：提供独立的归档数据查询接口，不影响主库性能。

## 数据迁移与版本管理

### 数据迁移路径
- **工具**：使用GoFrame内置的`goframe migrate`工具进行数据库迁移。
- **版本控制**：每个迁移脚本都有唯一的版本号，按时间顺序执行。
- **回滚机制**：每个`up`脚本都必须有对应的`down`脚本，支持版本回滚。

### 版本管理策略
- **语义化版本**：数据库模式版本与应用版本保持一致，遵循`主版本.次版本.修订号`的规则。
- **变更管理**：任何数据库结构的变更都必须通过代码审查，并在测试环境充分验证后才能上线。

## GoFrame ORM映射机制

### DO (Data Object) 层
DO层是ORM框架与数据库表之间的直接映射，位于`internal/model/do`目录下。
- **结构体定义**：使用`g.Meta`标签指定表名，字段类型为`any`，由ORM框架在运行时动态解析。
- **作用**：主要用于ORM的查询、插入、更新等操作的参数传递。

```go
type V1JdCookieOrder struct {
	g.Meta        `orm:"table:jd_cookie_order, do:true"`
	Id            any         // 主键ID
	OrderId       any         // 订单号
	UserOrderId   any         // 用户订单号
	Amount        any         // 订单金额
	Category      any         // 商品品类
	JdOrderId     any         // 关联的京东订单号
	Status        any         // 状态：1待支付 2已支付 3已过期 4已取消
	LastRequestAt *gtime.Time // 最后请求时间
	CreatedAt     *gtime.Time // 创建时间
	UpdatedAt     *gtime.Time // 更新时间
	DeletedAt     *gtime.Time // 删除时间
}
```

**Section sources**

- [v_1_jd_cookie_order.go](file://internal/model/do/v_1_jd_cookie_order.go#L12-L26)

### Entity 层
Entity层是业务逻辑中使用的数据结构，位于`internal/model/entity`目录下。
- **结构体定义**：字段类型为具体的Go类型（如`uint`, `string`, `decimal.Decimal`），并包含JSON和ORM标签。
- **作用**：作为业务逻辑层和DAO层之间的数据传输对象（DTO）。

```go
type V1JdCookieOrder struct {
	Id            int64           `json:"id"            orm:"id"              description:"主键ID"`
	OrderId       string          `json:"orderId"       orm:"order_id"        description:"订单号"`
	UserOrderId   string          `json:"userOrderId"   orm:"user_order_id"   description:"用户订单号"`
	Amount        decimal.Decimal `json:"amount"        orm:"amount"          description:"订单金额"`
	Category      string          `json:"category"      orm:"category"        description:"商品品类"`
	JdOrderId     string          `json:"jdOrderId"     orm:"jd_order_id"     description:"关联的京东订单号"`
	Status        int             `json:"status"        orm:"status"          description:"状态：1待支付 2已支付 3已过期 4已取消"`
	LastRequestAt *gtime.Time     `json:"lastRequestAt" orm:"last_request_at" description:"最后请求时间"`
	CreatedAt     *gtime.Time     `json:"createdAt"     orm:"created_at"      description:"创建时间"`
	UpdatedAt     *gtime.Time     `json:"updatedAt"     orm:"updated_at"      description:"更新时间"`
	DeletedAt     *gtime.Time     `json:"deletedAt"     orm:"deleted_at"      description:"删除时间"`
}
```

**Section sources**

- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)

### 映射关系
- **Entity -> DO**：当DAO层需要执行数据库操作时，会将`Entity`对象转换为`DO`对象。
- **DO -> Entity**：当从数据库查询数据时，ORM框架会将结果映射为`DO`对象，再转换为`Entity`对象返回给业务层。

## DAO层实现

### DAO结构
DAO层位于`internal/dao`目录下，是数据访问的入口。
- **内部DAO**：`internal/dao/internal`目录下的DAO是GoFrame CLI自动生成的，包含基础的CRUD方法。
- **外部DAO**：`internal/dao`目录下的DAO是对内部DAO的包装，可以添加自定义方法。

```go
// v1JdCookieOrderDao 是对内部DAO的包装
type v1JdCookieOrderDao struct {
	internalV1JdCookieOrderDao
}

// V1JdCookieOrder 是全局可访问的DAO对象
var V1JdCookieOrder = v1JdCookieOrderDao{
	internal.NewV1JdCookieOrderDao(),
}
```

**Section sources**

- [v_1_jd_cookie_order.go](file://internal/dao/v_1_jd_cookie_order.go#L1-L27)

### 自定义方法实现
DAO层可以定义自定义方法来实现复杂的查询逻辑。

- **示例：添加京东订单**
    ```go
    func (s *sJdCookie) CreateJdOrder(ctx context.Context, jdOrderId, payId, cookieId, category string, amount float64) (err error) {
        m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
        _, err = m.Insert(&do.V1JdCookieJdOrder{
            JdOrderId:     jdOrderId,
            PayId:         payId,
            Amount:        amount,
            Category:      category,
            CookieId:      cookieId,
            Status:        int(consts.JdOrderStatusPending),
            OrderExpireAt: gtime.Now().Add(time.Hour * consts.JdOrderExpireDuration),
        })
        return
    }
    ```
    **Section sources**
    - [rotation.go](file://internal/logic/jd_cookie/rotation.go#L150-L165)

- **示例：修改订单状态**
    ```go
    func (h *sAppleOrder) ModifyOrderStatus(ctx context.Context, orderNo string, status consts.AppleRechargeOrderStatus, remark string, tx gdb.TX) (err error) {
        m := dao.V1CardAppleRechargeInfo.Ctx(ctx).DB(config.GetDatabaseV1())
        if tx != nil {
            m = m.TX(tx)
        }
        _, err = m.Where(dao.V1CardAppleRechargeInfo.Columns().OrderNo, orderNo).Update(d)
        return
    }
    ```
    **Section sources**
    - [status.go](file://internal/logic/card_apple_order/status.go#L14-L30)

- **示例：获取可用的Cookie账户**
    ```go
    func (s *sJdCookie) GetAvailableCookie(ctx context.Context) (cookieId string, err error) {
        m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
        var availableCookies []*entity.V1JdCookieAccount
        err = m.Where(dao.V1JdCookieAccount.Columns().Status, int(consts.JdCookieStatusNormal)).
            OrderAsc(dao.V1JdCookieAccount.Columns().LastUsedAt).
            Scan(&availableCookies)
        if err != nil {
            return "", gerror.Wrap(err, "查询可用Cookie失败")
        }
        if len(availableCookies) == 0 {
            return "", gerror.New(consts.ErrCodeCookieNotAvailable)
        }
        selectedCookie := availableCookies[0]
        return selectedCookie.CookieId, nil
    }
    ```
    **Section sources**
    - [rotation.go](file://internal/logic/jd_cookie/rotation.go#L15-L30)