fix(test-round): 测试清单一批 bug 修复 + 团队月限额持久化
后端: - accounts: 团队级月限额持久化(Team.monthly_credit_limit 三态 + PATCH/GET /api/auth/team/settings/,刷新不丢,PMC#12);头像上传 500→502 可读错误; 设备下线真失效(删并重建 token,旧 token 401) - assets: 审核类目加 model_portrait,消除三视图"无需审核"误报 - ai/projects/products: 模特已有三视图复用、产品三视图同步回商品库 (metadata.view=three_view)、真人模特三视图回写 前端: - 商品图删除判断改用真实图片数;团队成员弹窗禁点外部关闭 - 图片预览骨架铺满占位;平台套图左侧栏折叠改导航同款;收件箱长文换行对齐 - 团队月限额改真落库(乐观更新+失败回滚) 测试:新增 TeamSettingsTests(5 条),accounts 全套 36 tests 通过;前端 build 0 error Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -596,9 +596,58 @@ class ProjectViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
from apps.assets.review import submit_asset_for_review
|
||||
|
||||
transaction.on_commit(lambda a=asset: submit_asset_for_review(a))
|
||||
# ZWQ#4 · 沿用模特库已有三视图:选了「已带三视图」的模特(官方/自有)作角色时,
|
||||
# 把该模特的三视图也挂进项目(triview_of=本组采用立绘 id),前端即判定「已有三视图」、
|
||||
# 不再要求点「AI 生成三视图」。仅对人物组生效;asset_id 传的是模特的 portrait_asset(原始 id),
|
||||
# 故按原始 portrait_asset 反查 Model;官方模特三视图同立绘一样克隆进本团队再挂(team 隔离)。
|
||||
if asset.category in (Asset.Category.PERSON, Asset.Category.MODEL_PORTRAIT):
|
||||
self._attach_existing_model_triview(project, group, asset, source_portrait_id=asset_id, user=request.user)
|
||||
promote_base_asset_stage_if_ready(project)
|
||||
return Response(BaseAssetGroupSerializer(group).data)
|
||||
|
||||
def _attach_existing_model_triview(self, project, portrait_group, portrait_asset, *, source_portrait_id, user):
|
||||
"""若被采用的立绘来自「已带三视图」的模特,把该三视图挂进项目并采用(triview_of=立绘 id)。
|
||||
幂等:已存在该立绘的三视图组就跳过,不重复挂。best-effort,出错不阻断主采用流程。"""
|
||||
from apps.assets.models import Model as ModelEntity
|
||||
|
||||
# source_portrait_id = 前端传的资产 id(模特的原始 portrait_asset);官方模特会被克隆,
|
||||
# 故 portrait_asset 可能是克隆体。两边都查一遍找到对应 Model。
|
||||
candidate_ids = {str(portrait_asset.id)}
|
||||
if source_portrait_id:
|
||||
candidate_ids.add(str(source_portrait_id))
|
||||
model = (
|
||||
ModelEntity.objects.filter(portrait_asset_id__in=candidate_ids, is_deleted=False)
|
||||
.exclude(triview_asset__isnull=True)
|
||||
.select_related("triview_asset")
|
||||
.first()
|
||||
)
|
||||
if model is None or model.triview_asset is None:
|
||||
return
|
||||
portrait_key = str(portrait_asset.id)
|
||||
# 已有该立绘的三视图组?幂等跳过。
|
||||
existing = next(
|
||||
(g for g in project.base_asset_groups.filter(kind=BaseAssetGroup.Kind.PERSON)
|
||||
if (g.metadata or {}).get("triview_of") == portrait_key),
|
||||
None,
|
||||
)
|
||||
if existing is not None:
|
||||
return
|
||||
tri_asset = model.triview_asset
|
||||
# 官方模特三视图跨团队 → 克隆进本团队再挂(与立绘同款);本团队三视图直接用。
|
||||
if tri_asset.team_id != project.team_id:
|
||||
tri_asset = _clone_asset_into_team(tri_asset, team=project.team, user=user)
|
||||
tri_group = BaseAssetGroup.objects.create(
|
||||
project=project, kind=BaseAssetGroup.Kind.PERSON, prompt="",
|
||||
metadata={"label": "·三视图", "triview_of": portrait_key, "adopt": "adopted"},
|
||||
)
|
||||
tri_group.candidate_assets.add(tri_asset)
|
||||
tri_group.adopted_asset = tri_asset
|
||||
tri_group.save(update_fields=["adopted_asset", "updated_at"])
|
||||
if tri_asset.category in Asset.REVIEW_CATEGORIES and tri_asset.review_status != "active":
|
||||
from apps.assets.review import submit_asset_for_review
|
||||
|
||||
transaction.on_commit(lambda a=tri_asset: submit_asset_for_review(a))
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="generate-triview")
|
||||
def generate_triview(self, request, pk=None):
|
||||
"""流程步骤4 · 据某一版立绘资产生成它配套的三视图(image_edit 锁角色一致性,三视图绑定该立绘)。"""
|
||||
|
||||
Reference in New Issue
Block a user