feat: add TenantLedger model and query generation

- Introduced TenantLedger model with fields for managing tenant transactions, including ID, TenantID, UserID, OrderID, transaction Type, Amount, and balance details.
- Implemented CRUD operations for TenantLedger with methods for Create, Update, Delete, and Reload.
- Generated query methods for TenantLedger to facilitate database interactions, including filtering, pagination, and aggregation functions.
- Established relationships with Order model for foreign key references.
This commit is contained in:
2025-12-18 13:12:26 +08:00
parent f93caefcb2
commit 1da84f2af3
42 changed files with 6468 additions and 265 deletions

View File

@@ -1,5 +1,15 @@
basePath: /t/{tenantCode}/v1
basePath: /
definitions:
consts.ContentAccessStatus:
enum:
- active
- revoked
- expired
type: string
x-enum-varnames:
- ContentAccessStatusActive
- ContentAccessStatusRevoked
- ContentAccessStatusExpired
consts.ContentAssetRole:
enum:
- main
@@ -74,6 +84,30 @@ definitions:
- MediaAssetTypeVideo
- MediaAssetTypeAudio
- MediaAssetTypeImage
consts.OrderStatus:
enum:
- created
- paid
- refunding
- refunded
- canceled
- failed
type: string
x-enum-varnames:
- OrderStatusCreated
- OrderStatusPaid
- OrderStatusRefunding
- OrderStatusRefunded
- OrderStatusCanceled
- OrderStatusFailed
consts.OrderType:
enum:
- content_purchase
- topup
type: string
x-enum-varnames:
- OrderTypeContentPurchase
- OrderTypeTopup
consts.Role:
enum:
- user
@@ -110,6 +144,31 @@ definitions:
- UserStatusPendingVerify
- UserStatusVerified
- UserStatusBanned
dto.AdminOrderDetail:
properties:
order:
allOf:
- $ref: '#/definitions/models.Order'
description: Order is the order with items preloaded.
type: object
dto.AdminOrderRefundForm:
properties:
force:
description: |-
Force indicates bypassing the default refund window check (paid_at + 24h).
强制退款true 表示绕过默认退款时间窗限制(需审计)。
type: boolean
idempotency_key:
description: |-
IdempotencyKey ensures refund request is processed at most once.
幂等键:同一笔退款重复请求时返回一致结果,避免重复退款/重复回滚。
type: string
reason:
description: |-
Reason is the human-readable refund reason used for audit.
退款原因:建议必填(由业务侧校验);用于审计与追责。
type: string
type: object
dto.ContentAssetAttachForm:
properties:
asset_id:
@@ -261,6 +320,34 @@ definitions:
- $ref: '#/definitions/models.User'
description: User is the authenticated user derived from JWT `user_id`.
type: object
dto.PurchaseContentForm:
properties:
idempotency_key:
description: |-
IdempotencyKey is used to ensure the purchase request is processed at most once.
建议由客户端生成并保持稳定:同一笔购买重复请求时返回相同结果,避免重复扣款/重复下单。
type: string
type: object
dto.PurchaseContentResponse:
properties:
access:
allOf:
- $ref: '#/definitions/models.ContentAccess'
description: Access is the content access record after purchase grant.
amount_paid:
description: AmountPaid is the final paid amount in cents (CNY 分).
type: integer
item:
allOf:
- $ref: '#/definitions/models.OrderItem'
description: Item is the single order item of this purchase (current implementation
is 1 order -> 1 content).
order:
allOf:
- $ref: '#/definitions/models.Order'
description: Order is the created or existing order record (may be nil for
owner/free-path without order).
type: object
dto.TenantExpireUpdateForm:
properties:
duration:
@@ -386,107 +473,298 @@ definitions:
models.Content:
properties:
created_at:
description: 创建时间:默认 now();用于审计与排序
type: string
deleted_at:
$ref: '#/definitions/gorm.DeletedAt'
allOf:
- $ref: '#/definitions/gorm.DeletedAt'
description: 软删除时间:非空表示已删除;对外接口需过滤
description:
description: 描述:用于详情页展示;可为空字符串
type: string
id:
description: 主键ID自增用于内容引用
type: integer
preview_downloadable:
description: 试看是否允许下载:默认 false当前策略固定为不允许下载仅 streaming
type: boolean
preview_seconds:
description: 试看秒数:默认 60只对 preview 资源生效;必须为正整数
type: integer
published_at:
description: 发布时间:首次发布时写入;用于时间窗与排序
type: string
status:
$ref: '#/definitions/consts.ContentStatus'
allOf:
- $ref: '#/definitions/consts.ContentStatus'
description: 状态draft/reviewing/published/unpublished/blockedpublished 才对外展示
tenant_id:
description: 租户ID多租户隔离关键字段所有查询/写入必须限定 tenant_id
type: integer
title:
description: 标题:用于列表展示与搜索;建议限制长度(由业务校验)
type: string
updated_at:
description: 更新时间:默认 now();编辑内容时写入
type: string
user_id:
description: 用户ID内容创建者/发布者;用于权限与审计(例如私有内容仅作者可见)
type: integer
visibility:
$ref: '#/definitions/consts.ContentVisibility'
allOf:
- $ref: '#/definitions/consts.ContentVisibility'
description: 可见性public/tenant_only/private仅控制详情可见正片资源仍需按价格/权益校验
type: object
models.ContentAccess:
properties:
content_id:
description: 内容ID权益对应内容唯一约束 (tenant_id, user_id, content_id)
type: integer
created_at:
description: 创建时间:默认 now();用于审计
type: string
id:
description: 主键ID自增
type: integer
order_id:
description: 订单ID产生该权益的订单可为空例如后台补发/迁移)
type: integer
revoked_at:
description: 撤销时间:当 status=revoked 时写入;用于审计与追责
type: string
status:
allOf:
- $ref: '#/definitions/consts.ContentAccessStatus'
description: 权益状态active/revoked/expiredrevoked 表示立即失效(例如退款/违规)
tenant_id:
description: 租户ID多租户隔离与内容、用户归属一致
type: integer
updated_at:
description: 更新时间:默认 now();更新 status 时写入
type: string
user_id:
description: 用户ID权益所属用户用于访问校验
type: integer
type: object
models.ContentAsset:
properties:
asset_id:
description: 资源ID关联 media_assets.id用于查询资源归属内容
type: integer
content_id:
description: 内容ID关联 contents.id用于查询内容下资源列表
type: integer
created_at:
description: 创建时间:默认 now();用于审计
type: string
id:
description: 主键ID自增
type: integer
role:
$ref: '#/definitions/consts.ContentAssetRole'
allOf:
- $ref: '#/definitions/consts.ContentAssetRole'
description: 资源角色main/cover/previewpreview 必须为独立资源以满足禁下载与防绕过
sort:
description: 排序:同一 role 下的展示顺序,数值越小越靠前
type: integer
tenant_id:
description: 租户ID多租户隔离必须与 content_id、asset_id 所属租户一致
type: integer
updated_at:
description: 更新时间:默认 now();更新 sort/role 时写入
type: string
user_id:
description: 用户ID操作人/绑定人;用于审计(通常为租户管理员或作者)
type: integer
type: object
models.ContentPrice:
properties:
content_id:
description: 内容ID唯一约束 (tenant_id, content_id);一个内容在一个租户内仅一份定价
type: integer
created_at:
description: 创建时间:默认 now();用于审计
type: string
currency:
$ref: '#/definitions/consts.Currency'
allOf:
- $ref: '#/definitions/consts.Currency'
description: 币种:当前固定 CNY金额单位为分
discount_end_at:
description: 折扣结束时间:可为空;为空表示长期有效(由业务逻辑解释)
type: string
discount_start_at:
description: 折扣开始时间:可为空;为空表示立即生效(由业务逻辑解释)
type: string
discount_type:
$ref: '#/definitions/consts.DiscountType'
allOf:
- $ref: '#/definitions/consts.DiscountType'
description: 折扣类型none/percent/amount仅影响下单时成交价需写入订单快照
discount_value:
description: 折扣值percent=0-100按业务校验amount=分none 时忽略
type: integer
id:
description: 主键ID自增
type: integer
price_amount:
description: 基础价格0 表示免费(可直接访问正片资源)
type: integer
tenant_id:
description: 租户ID多租户隔离与内容归属一致
type: integer
updated_at:
description: 更新时间:默认 now();更新价格/折扣时写入
type: string
user_id:
description: 用户ID设置/更新价格的操作人(通常为 tenant_admin用于审计
type: integer
type: object
models.MediaAsset:
properties:
bucket:
description: 存储桶:对象所在 bucket与 provider 组合确定存储定位
type: string
created_at:
description: 创建时间:默认 now();用于审计与排序
type: string
deleted_at:
$ref: '#/definitions/gorm.DeletedAt'
allOf:
- $ref: '#/definitions/gorm.DeletedAt'
description: 软删除时间:非空表示已删除;对外接口需过滤
id:
description: 主键ID自增仅用于内部关联
type: integer
meta:
description: 元数据JSON包含 hash、duration、width、height、bitrate、codec 等;用于展示与计费/风控
items:
type: integer
type: array
object_key:
description: 对象键:对象在 bucket 内的 key不得暴露可长期复用的直链通过签名URL/token下发
type: string
provider:
description: 存储提供方:例如 s3/minio/oss便于多存储扩展
type: string
status:
$ref: '#/definitions/consts.MediaAssetStatus'
allOf:
- $ref: '#/definitions/consts.MediaAssetStatus'
description: 处理状态uploaded/processing/ready/failed/deletedready 才可被内容引用对外提供
tenant_id:
description: 租户ID多租户隔离关键字段所有查询/写入必须限定 tenant_id
type: integer
type:
$ref: '#/definitions/consts.MediaAssetType'
allOf:
- $ref: '#/definitions/consts.MediaAssetType'
description: 资源类型video/audio/image决定后续处理流程转码/缩略图/封面等)
updated_at:
description: 更新时间:默认 now();更新状态/元数据时写入
type: string
user_id:
description: 用户ID资源上传者用于审计与权限控制
type: integer
type: object
models.Order:
properties:
amount_discount:
description: 优惠金额amount_paid = amount_original - amount_discount下单时快照
type: integer
amount_original:
description: 原价金额:分;未折扣前金额(用于展示与对账)
type: integer
amount_paid:
description: 实付金额:分;从租户内余额扣款的金额(下单时快照)
type: integer
created_at:
description: 创建时间:默认 now();用于审计与排序
type: string
currency:
allOf:
- $ref: '#/definitions/consts.Currency'
description: 币种:当前固定 CNY金额单位为分
id:
description: 主键ID自增用于关联订单明细、账本流水、权益等
type: integer
idempotency_key:
description: 幂等键:同一租户同一用户同一业务请求可用;用于防重复下单/重复扣款(建议由客户端生成)
type: string
items:
items:
$ref: '#/definitions/models.OrderItem'
type: array
paid_at:
description: 支付/扣款完成时间:余额支付在 debit_purchase 成功后写入
type: string
refund_forced:
description: 是否强制退款true 表示租户管理侧绕过时间窗执行退款(需审计)
type: boolean
refund_operator_user_id:
description: 退款操作人用户ID租户管理员/系统;用于审计与追责
type: integer
refund_reason:
description: 退款原因:后台/用户发起退款的原因说明;用于审计
type: string
refunded_at:
description: 退款完成时间:退款落账成功后写入
type: string
snapshot:
description: 订单快照JSON建议包含 content 标题/定价/折扣、请求来源等,避免改价影响历史展示
items:
type: integer
type: array
status:
allOf:
- $ref: '#/definitions/consts.OrderStatus'
description: 订单状态created/paid/refunding/refunded/canceled/failed状态变更需与账本/权益保持一致
tenant_id:
description: 租户ID多租户隔离关键字段所有查询/写入必须限定 tenant_id
type: integer
type:
allOf:
- $ref: '#/definitions/consts.OrderType'
description: 订单类型content_purchase购买内容/topup充值当前默认 content_purchase
updated_at:
description: 更新时间:默认 now();状态变更/退款写入时更新
type: string
user_id:
description: 用户ID下单用户buyer余额扣款与权益归属以该 user_id 为准
type: integer
type: object
models.OrderItem:
properties:
amount_paid:
description: 该行实付金额:分;通常等于订单 amount_paid单内容场景
type: integer
content:
$ref: '#/definitions/models.Content'
content_id:
description: 内容ID关联 contents.id用于生成/撤销 content_access
type: integer
content_user_id:
description: 内容作者用户ID用于后续分成/对账扩展;当前可为 0 或写入内容创建者
type: integer
created_at:
description: 创建时间:默认 now()
type: string
id:
description: 主键ID自增
type: integer
order:
$ref: '#/definitions/models.Order'
order_id:
description: 订单ID关联 orders.id用于聚合订单明细
type: integer
snapshot:
description: 内容快照JSON建议包含 title/price/discount 等,用于历史展示与审计
items:
type: integer
type: array
tenant_id:
description: 租户ID多租户隔离关键字段必须与 orders.tenant_id 一致
type: integer
updated_at:
description: 更新时间:默认 now()
type: string
user_id:
description: 用户ID下单用户buyer冗余字段用于查询加速与审计
type: integer
type: object
models.Tenant:
@@ -522,6 +800,9 @@ definitions:
properties:
balance:
type: integer
balance_frozen:
description: 冻结余额:分/最小货币单位;下单冻结时从可用余额转入,最终扣款或回滚时转出;默认 0
type: integer
created_at:
type: string
id:
@@ -992,6 +1273,120 @@ paths:
summary: 设置内容价格与折扣
tags:
- Tenant
/t/{tenantCode}/v1/admin/orders:
get:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: Limit is page size; only values in {10,20,50,100} are accepted
(otherwise defaults to 10).
in: query
name: limit
type: integer
- description: Page is 1-based page index; values <= 0 are normalized to 1.
in: query
name: page
type: integer
- description: Status filters orders by order status.
enum:
- created
- paid
- refunding
- refunded
- canceled
- failed
in: query
name: status
type: string
x-enum-varnames:
- OrderStatusCreated
- OrderStatusPaid
- OrderStatusRefunding
- OrderStatusRefunded
- OrderStatusCanceled
- OrderStatusFailed
- description: UserID filters orders by buyer user id.
in: query
name: user_id
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/requests.Pager'
- properties:
items:
$ref: '#/definitions/models.Order'
type: object
summary: 订单列表(租户管理)
tags:
- Tenant
/t/{tenantCode}/v1/admin/orders/{orderID}:
get:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: OrderID
format: int64
in: path
name: orderID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.AdminOrderDetail'
summary: 订单详情(租户管理)
tags:
- Tenant
/t/{tenantCode}/v1/admin/orders/{orderID}/refund:
post:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: OrderID
format: int64
in: path
name: orderID
required: true
type: integer
- description: Form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.AdminOrderRefundForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.Order'
summary: 订单退款(租户管理)
tags:
- Tenant
/t/{tenantCode}/v1/contents:
get:
consumes:
@@ -1108,6 +1503,38 @@ paths:
summary: 获取试看资源preview role
tags:
- Tenant
/t/{tenantCode}/v1/contents/{contentID}/purchase:
post:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: ContentID
format: int64
in: path
name: contentID
required: true
type: integer
- description: Form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.PurchaseContentForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PurchaseContentResponse'
summary: 购买内容(余额支付)
tags:
- Tenant
/t/{tenantCode}/v1/me:
get:
consumes:
@@ -1128,6 +1555,84 @@ paths:
summary: 当前租户上下文信息
tags:
- Tenant
/t/{tenantCode}/v1/orders:
get:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: Limit is page size; only values in {10,20,50,100} are accepted
(otherwise defaults to 10).
in: query
name: limit
type: integer
- description: Page is 1-based page index; values <= 0 are normalized to 1.
in: query
name: page
type: integer
- description: Status filters orders by order status.
enum:
- created
- paid
- refunding
- refunded
- canceled
- failed
in: query
name: status
type: string
x-enum-varnames:
- OrderStatusCreated
- OrderStatusPaid
- OrderStatusRefunding
- OrderStatusRefunded
- OrderStatusCanceled
- OrderStatusFailed
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/requests.Pager'
- properties:
items:
$ref: '#/definitions/models.Order'
type: object
summary: 我的订单列表(当前租户)
tags:
- Tenant
/t/{tenantCode}/v1/orders/{orderID}:
get:
consumes:
- application/json
parameters:
- description: Tenant Code
in: path
name: tenantCode
required: true
type: string
- description: OrderID
format: int64
in: path
name: orderID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.Order'
summary: 我的订单详情(当前租户)
tags:
- Tenant
securityDefinitions:
BasicAuth:
type: basic