生成项目任务/chat任务软删|生成资源管控回收|生成资源token验签API预处理

This commit is contained in:
2026-06-01 11:10:09 +08:00
parent edef601f7c
commit dfbf51b5c0
26 changed files with 1296 additions and 63 deletions
+6 -2
View File
@@ -1,4 +1,4 @@
from app.models.base import Base, TimestampMixin, engine, async_session, init_database, close_database
from app.models.base import Base, TimestampMixin, SoftDeleteMixin, engine, async_session, init_database, close_database
from app.models.user import User
from app.models.project import Project
from app.models.generation_record import GenerationRecord
@@ -18,13 +18,17 @@ from app.models.operation_log import OperationLog
from app.models.chat_generation_task import ChatGenerationTask
from app.models.chat_generation_task_event import ChatGenerationTaskEvent
from app.models.chat_provider_call_log import ChatProviderCallLog
from app.models.generated_resource import GeneratedResource
from app.models.user_resource_month_stat import UserResourceMonthStat
from app.models.user_resource_total_stat import UserResourceTotalStat
__all__ = [
"Base", "TimestampMixin", "engine", "async_session",
"Base", "TimestampMixin", "SoftDeleteMixin", "engine", "async_session",
"init_database", "close_database",
"User", "Project", "GenerationRecord", "CreditRecord",
"ModelConfig", "SystemConfig", "Notification", "PaymentOrder",
"TokenUsage", "IndustryConfig", "VideoEngine", "CreditRatio",
"MenuConfig", "RechargePackage", "OperationLog",
"ChatGenerationTask", "ChatGenerationTaskEvent", "ChatProviderCallLog",
"GeneratedResource", "UserResourceMonthStat", "UserResourceTotalStat",
]
+6
View File
@@ -51,6 +51,12 @@ class TimestampMixin:
)
class SoftDeleteMixin:
deleted_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, index=True
)
async def init_database() -> None:
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
@@ -3,10 +3,10 @@ from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
class ChatGenerationTask(Base, TimestampMixin):
class ChatGenerationTask(Base, TimestampMixin, SoftDeleteMixin):
"""Project-independent AI chat/image/video generation task.
This table is intentionally NOT linked to projects. It is used by the
@@ -0,0 +1,46 @@
from datetime import date, datetime
from sqlalchemy import BigInteger, Date, DateTime, ForeignKey, Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
class GeneratedResource(Base, TimestampMixin, SoftDeleteMixin):
"""统一生成资源账本。
只记录生成成功后的图片/视频资源,不直接绑定具体业务外键,
通过 source_model + source_id 兼容 ChatGenerationTask、GenerationRecord 以及后续新模块。
"""
__tablename__ = "generated_resources"
id: Mapped[str] = mapped_column(String(32), primary_key=True)
user_id: Mapped[str] = mapped_column(
String(32), ForeignKey("users.id", ondelete="CASCADE"), index=True, nullable=False
)
resource_type: Mapped[str] = mapped_column(String(16), index=True, nullable=False) # image / video
resource_url: Mapped[str] = mapped_column(Text, nullable=False)
remote_url: Mapped[str | None] = mapped_column(Text, nullable=True)
storage_type: Mapped[str] = mapped_column(String(32), default="local", nullable=False)
storage_path: Mapped[str | None] = mapped_column(Text, nullable=True)
file_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
source_model: Mapped[str] = mapped_column(String(64), index=True, nullable=False)
source_model_module: Mapped[str | None] = mapped_column(String(255), nullable=True)
source_id: Mapped[str] = mapped_column(String(32), index=True, nullable=False)
engine_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
engine_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
provider: Mapped[str | None] = mapped_column(String(64), nullable=True)
model_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
generated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
resource_month: Mapped[date] = mapped_column(Date, index=True, nullable=False)
extra_json: Mapped[str | None] = mapped_column(Text, nullable=True)
Index("ix_generated_resources_user_month", GeneratedResource.user_id, GeneratedResource.resource_month)
Index("ix_generated_resources_source", GeneratedResource.source_model, GeneratedResource.source_id)
Index("ix_generated_resources_active_user", GeneratedResource.user_id, GeneratedResource.deleted_at)
@@ -3,10 +3,10 @@ from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, Float
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
class GenerationRecord(Base, TimestampMixin):
class GenerationRecord(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "generation_records"
id: Mapped[str] = mapped_column(String(32), primary_key=True)
+2 -2
View File
@@ -1,10 +1,10 @@
from sqlalchemy import ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
class Project(Base, TimestampMixin):
class Project(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "projects"
id: Mapped[str] = mapped_column(String(32), primary_key=True)
@@ -0,0 +1,35 @@
from datetime import date, datetime
from sqlalchemy import BigInteger, Date, DateTime, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin
class UserResourceMonthStat(Base, TimestampMixin):
"""用户月份资源空间聚合表。"""
__tablename__ = "user_resource_month_stats"
__table_args__ = (
UniqueConstraint("user_id", "stat_month", name="uq_user_resource_month_stats_user_month"),
)
id: Mapped[str] = mapped_column(String(32), primary_key=True)
user_id: Mapped[str] = mapped_column(
String(32), ForeignKey("users.id", ondelete="CASCADE"), index=True, nullable=False
)
stat_month: Mapped[date] = mapped_column(Date, index=True, nullable=False)
active_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
deleted_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
total_generated_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
image_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
video_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
active_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
deleted_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
image_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
video_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
last_recalculated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
@@ -0,0 +1,34 @@
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base, TimestampMixin
class UserResourceTotalStat(Base, TimestampMixin):
"""用户全局资源空间聚合表。"""
__tablename__ = "user_resource_total_stats"
__table_args__ = (
UniqueConstraint("user_id", name="uq_user_resource_total_stats_user"),
)
id: Mapped[str] = mapped_column(String(32), primary_key=True)
user_id: Mapped[str] = mapped_column(
String(32), ForeignKey("users.id", ondelete="CASCADE"), index=True, nullable=False
)
active_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
deleted_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
total_generated_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
image_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
video_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False)
active_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
deleted_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
image_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
video_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
last_recalculated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)