mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat: 强化用量计费状态机,新增钱包每日消费汇总分类账
- 将 usage.billing_status 默认值从 settled 改为 pending,完善 pending -> settled/void 的状态转换逻辑,确保终态不可逆 - 新增 WalletDailyUsageLedger 模型和聚合服务,按账单日汇总 每个钱包的消费金额、请求数和 token 用量 - 前端钱包中心页面集成每日消费流水展示,支持与充值记录混合 排序和分页 - 新增两个数据库迁移:修复历史数据状态一致性、创建每日汇总表 - 补充计费状态机单元测试 Closes #218 Co-authored-by: LewisPen <LewisPen@nyadoo.com>
This commit is contained in:
@@ -7,7 +7,7 @@ from __future__ import annotations
|
||||
import hashlib
|
||||
import secrets
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from datetime import date, datetime, timezone
|
||||
from enum import Enum as PyEnum
|
||||
from typing import Any, ClassVar
|
||||
|
||||
@@ -18,6 +18,7 @@ from sqlalchemy import (
|
||||
Boolean,
|
||||
CheckConstraint,
|
||||
Column,
|
||||
Date,
|
||||
DateTime,
|
||||
Enum,
|
||||
Float,
|
||||
@@ -413,10 +414,10 @@ class Usage(Base):
|
||||
status = Column(String(20), default="completed", nullable=False, index=True)
|
||||
|
||||
# 结算状态(与 status 解耦)
|
||||
# - pending: 等待结算(任务未完成 / 流式未结束)
|
||||
# - settled: 已结算(cost 已写入,可能 > 0 或 = 0)
|
||||
# - void: 作废(不收费,如任务未开始就取消)
|
||||
billing_status = Column(String(20), default="settled", nullable=False, index=True)
|
||||
# - pending: 等待结算(请求已创建,但账务尚未进入最终状态)
|
||||
# - settled: 已结算(cost 已写入,且钱包侧结算动作已完成)
|
||||
# - void: 作废(明确不收费)
|
||||
billing_status = Column(String(20), default="pending", nullable=False, index=True)
|
||||
finalized_at = Column(DateTime(timezone=True), nullable=True) # 结算完成时间(可选)
|
||||
wallet_balance_before = Column(Numeric(20, 8), nullable=True) # 结算前可用总余额快照
|
||||
wallet_balance_after = Column(Numeric(20, 8), nullable=True) # 结算后可用总余额快照
|
||||
@@ -556,6 +557,9 @@ class Wallet(Base):
|
||||
transactions = relationship(
|
||||
"WalletTransaction", back_populates="wallet", cascade="all, delete-orphan"
|
||||
)
|
||||
daily_usage_ledgers = relationship(
|
||||
"WalletDailyUsageLedger", back_populates="wallet", cascade="all, delete-orphan"
|
||||
)
|
||||
payment_orders = relationship("PaymentOrder", back_populates="wallet")
|
||||
refund_requests = relationship("RefundRequest", back_populates="wallet")
|
||||
|
||||
@@ -609,6 +613,50 @@ class WalletTransaction(Base):
|
||||
operator = relationship("User")
|
||||
|
||||
|
||||
class WalletDailyUsageLedger(Base):
|
||||
"""钱包按天汇总的消费流水投影。"""
|
||||
|
||||
__tablename__ = "wallet_daily_usage_ledgers"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"wallet_id",
|
||||
"billing_date",
|
||||
"billing_timezone",
|
||||
name="uq_wallet_daily_usage_ledgers_wallet_date_tz",
|
||||
),
|
||||
Index("idx_wallet_daily_usage_wallet_date", "wallet_id", "billing_date"),
|
||||
Index("idx_wallet_daily_usage_date", "billing_date"),
|
||||
)
|
||||
|
||||
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
wallet_id = Column(String(36), ForeignKey("wallets.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
billing_date = Column(Date, nullable=False)
|
||||
billing_timezone = Column(String(64), nullable=False, default="Asia/Shanghai")
|
||||
|
||||
total_cost_usd = Column(Numeric(20, 8), nullable=False, default=0)
|
||||
total_requests = Column(Integer, nullable=False, default=0)
|
||||
input_tokens = Column(BigInteger, nullable=False, default=0)
|
||||
output_tokens = Column(BigInteger, nullable=False, default=0)
|
||||
cache_creation_tokens = Column(BigInteger, nullable=False, default=0)
|
||||
cache_read_tokens = Column(BigInteger, nullable=False, default=0)
|
||||
|
||||
first_finalized_at = Column(DateTime(timezone=True), nullable=True)
|
||||
last_finalized_at = Column(DateTime(timezone=True), nullable=True)
|
||||
aggregated_at = Column(DateTime(timezone=True), nullable=False)
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
||||
)
|
||||
updated_at = Column(
|
||||
DateTime(timezone=True),
|
||||
default=lambda: datetime.now(timezone.utc),
|
||||
onupdate=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
wallet = relationship("Wallet", back_populates="daily_usage_ledgers")
|
||||
|
||||
|
||||
class PaymentOrder(Base):
|
||||
"""充值订单"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user