feat: 空镜素材系统数据库化 + 修复积分不足弹窗叠加

后端:
- 新增 BrollCategory/BrollMaterial/BrollTag 模型及表(mjk_categories/materials/tags)
- 新增 Alembic 迁移 69274ce979a5
- 新增 broll_category/broll_material CRUD 层
- 重构 material_service:删除 JSON 配置,改用 PostgreSQL + Redis 去重
- 新增 /materials/batch-match 接口,删除 /materials/reload
- usage_count 原子递增,Redis 失败自动降级

前端:
- materials API 改为 projectId 去重,新增 batchMatch
- VideoGeneration 批量匹配改用 batchMatch,删除 usedUrls 手动维护
- 修复积分不足时进度弹窗与充值弹窗叠加的 bug
- 操作前预检积分,不足时显示提示条+立即充值按钮
This commit is contained in:
小鱼开发
2026-05-11 17:40:38 +08:00
parent 355c69a7bc
commit 447f3c2ffe
15 changed files with 1233 additions and 199 deletions
+34
View File
@@ -0,0 +1,34 @@
"""
素材分类 CRUD
==============
"""
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud.base import CRUDBase
from app.models.broll_category import BrollCategory
class BrollCategoryCRUD(CRUDBase[BrollCategory]):
"""素材分类数据访问"""
def __init__(self) -> None:
super().__init__(BrollCategory)
async def get_by_name_and_level(
self, db: AsyncSession, *, name: str, level: int
) -> BrollCategory | None:
"""根据名称和层级获取启用的分类"""
result = await db.execute(
select(BrollCategory).where(
BrollCategory.name == name,
BrollCategory.level == level,
BrollCategory.status == "active",
)
)
return result.scalar_one_or_none()
# 导出实例
broll_category = BrollCategoryCRUD()