billing-service/docs/reference/model.md
2026-04-23 15:59:41 +08:00

182 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

# model 包参考
本文档覆盖 `internal/model/types.go`。该包只定义结构体,不包含行为函数,是 `exporter`、`repository`、`service`、`httpapi` 共享的数据契约层。
## 1. 上游快照模型
### `Sample`
签名:`type Sample struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `UUID` | `string` | `uuid` | 账户 UUID服务层会校验其为合法 UUID |
| `Email` | `string` | `email` | 上游样本附带的邮箱,当前服务层不使用此字段做计费决策 |
| `InboundTag` | `string` | `inbound_tag` | 线路标签,写入分钟桶的 `line_code` |
| `UplinkBytesTotal` | `int64` | `uplink_bytes_total` | 截止采样时累计上行字节 |
| `DownlinkBytesTotal` | `int64` | `downlink_bytes_total` | 截止采样时累计下行字节 |
### `Snapshot`
签名:`type Snapshot struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `CollectedAt` | `time.Time` | `collected_at` | 快照采集时间,服务层会按分钟截断 |
| `NodeID` | `string` | `node_id` | 上游节点标识 |
| `Env` | `string` | `env` | 环境标识,例如 `prod` |
| `Samples` | `[]Sample` | `samples` | 本次快照中的样本列表 |
### `SnapshotWindowPage`
签名:`type SnapshotWindowPage struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `NodeID` | `string` | `node_id` | 分页级别的节点标识 |
| `Env` | `string` | `env` | 分页级别的环境标识 |
| `Snapshots` | `[]Snapshot` | `snapshots` | 窗口拉取结果中的快照列表 |
| `HasMore` | `bool` | `has_more` | 是否还有下一页 |
| `NextCursor` | `string` | `next_cursor` | 下一页游标,当前实现要求其为 RFC3339 时间字符串 |
## 2. 持久化实体模型
### `Checkpoint`
签名:`type Checkpoint struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `NodeID` | `string` | 存储层节点 ID实际由 `env:node_id` 组合而成 |
| `AccountUUID` | `string` | 账户 UUID |
| `LastUplinkTotal` | `int64` | 上次记录的累计上行字节 |
| `LastDownlinkTotal` | `int64` | 上次记录的累计下行字节 |
| `LastSeenAt` | `time.Time` | 上次成功处理该样本的采样时间 |
| `XrayRevision` | `string` | 当前写路径版本标识 |
| `ResetEpoch` | `int64` | 检测到累计值回退后的重置计数 |
### `MinuteBucket`
签名:`type MinuteBucket struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `BucketStart` | `time.Time` | 分钟桶起始时间,按 UTC 分钟对齐 |
| `NodeID` | `string` | 存储层节点 ID |
| `AccountUUID` | `string` | 账户 UUID |
| `Region` | `string` | 地域代码,当前来自 `cfg.DefaultRegion` |
| `LineCode` | `string` | 线路代码,当前来自 `sample.InboundTag` |
| `UplinkBytes` | `int64` | 当前分钟增量上行字节 |
| `DownlinkBytes` | `int64` | 当前分钟增量下行字节 |
| `TotalBytes` | `int64` | 当前分钟总字节数 |
| `Multiplier` | `float64` | 最终乘数,当前为地域乘数 * 线路乘数 |
| `RatingStatus` | `string` | 当前实现固定写 `rated` |
| `SourceRevision` | `string` | 写路径版本或计费规则版本 |
### `LedgerEntry`
签名:`type LedgerEntry struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `ID` | `string` | 账本主键,基于分钟桶确定性生成 |
| `AccountUUID` | `string` | 账户 UUID |
| `BucketStart` | `time.Time` | 对应分钟桶起始时间 |
| `BucketEnd` | `time.Time` | 对应分钟桶结束时间,当前实现为 `BucketStart + 1 分钟` |
| `EntryType` | `string` | 当前实现固定为 `traffic_charge` |
| `RatedBytes` | `int64` | 真正参与计费的字节数,已扣除包含流量 |
| `AmountDelta` | `float64` | 本次金额变化,当前为负值表示扣费 |
| `BalanceAfter` | `float64` | 扣费后的余额 |
| `PricingRuleVersion` | `string` | 实际应用的定价规则版本 |
### `QuotaState`
签名:`type QuotaState struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `AccountUUID` | `string` | 账户 UUID |
| `RemainingIncludedQuota` | `int64` | 剩余包含流量字节数 |
| `CurrentBalance` | `float64` | 当前余额 |
| `Arrears` | `bool` | 是否欠费 |
| `ThrottleState` | `string` | 节流状态,当前实现用 `normal` / `throttled` |
| `SuspendState` | `string` | 挂起状态,当前新建状态默认 `active` |
| `LastRatedBucketAt` | `*time.Time` | 最近一次成功计费的分钟桶时间 |
| `EffectiveAt` | `time.Time` | 当前状态生效时间 |
### `BillingProfile`
签名:`type BillingProfile struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `AccountUUID` | `string` | 账户 UUID |
| `PackageName` | `string` | 套餐名 |
| `IncludedQuotaBytes` | `int64` | 套餐包含流量 |
| `BasePricePerByte` | `float64` | 基础单价 |
| `RegionMultiplier` | `float64` | 地域乘数 |
| `LineMultiplier` | `float64` | 线路乘数 |
| `PeakMultiplier` | `float64` | 峰时乘数,当前代码未参与计算 |
| `OffPeakMultiplier` | `float64` | 非峰时乘数,当前代码未参与计算 |
| `PricingRuleVersion` | `string` | 计费规则版本号 |
### `SourceSyncState`
签名:`type SourceSyncState struct`
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `SourceID` | `string` | 来源 ID |
| `LastCompletedUntil` | `*time.Time` | 最近一次成功推进到的窗口终点 |
| `LastAttemptedAt` | `*time.Time` | 最近一次开始尝试采集的时间 |
| `LastSucceededAt` | `*time.Time` | 最近一次成功完成采集的时间 |
| `LastError` | `string` | 最近一次失败信息 |
## 3. HTTP 与运行状态模型
### `SourceStatus`
签名:`type SourceStatus struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `SourceID` | `string` | `source_id` | 来源 ID |
| `LastCompletedUntil` | `*time.Time` | `last_completed_until` | 对外暴露的窗口完成位置 |
| `LastAttemptedAt` | `*time.Time` | `last_attempted_at` | 对外暴露的最近尝试时间 |
| `LastSucceededAt` | `*time.Time` | `last_succeeded_at` | 对外暴露的最近成功时间 |
| `LastError` | `string` | `last_error` | 最近错误 |
### `JobResult`
签名:`type JobResult struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `Job` | `string` | `job` | 作业名,当前为 `collect-and-rate``reconcile` |
| `StartedAt` | `time.Time` | `started_at` | 作业开始时间 |
| `FinishedAt` | `time.Time` | `finished_at` | 作业结束时间 |
| `ProcessedSamples` | `int` | `processed_samples` | 成功进入处理主路径的样本数 |
| `WrittenMinutes` | `int` | `written_minutes` | 新写入的分钟桶数 |
| `ReplayedMinutes` | `int` | `replayed_minutes` | 由于回放或重复写命中的分钟数 |
| `Status` | `string` | `status` | `ok` / `partial` / `error` |
| `Error` | `string` | `error` | 汇总错误信息 |
| `SourceStatuses` | `[]SourceStatus` | `source_statuses` | 各来源状态摘要 |
### `PingInfo`
签名:`type PingInfo struct`
| 字段 | 类型 | JSON 字段 | 含义 |
| --- | --- | --- | --- |
| `Image` | `string` | `image` | 原始镜像引用 |
| `Tag` | `string` | `tag` | 解析出的镜像 tag |
| `Commit` | `string` | `commit` | 解析出的完整 commit |
| `Version` | `string` | `version` | 当前实现等于 commit |
## 4. 使用约束
- 所有 `time.Time` 在服务层和仓储层都按 UTC 处理
- `Snapshot` / `Sample` 是上游输入契约,不应被仓储层直接修改
- `MinuteBucket`、`LedgerEntry`、`QuotaState` 共同构成计费事实链
- `JobResult``SourceStatus` 只代表最近一次执行的内存快照,不是持久化审计日志