生图模型可选(火山/gpt-image)+ 模特上身图提示词强化 + 多会话等改动

本轮(生图模型选择 + 火山接入):
- 工作室新增「生图模型」选择器(模特上身图/平台套图头部 chip + 图片创作底部 Pill),
  默认火山 Seedream,可切 gpt-image-2;选择写入 localStorage,下次进页面读回
- 后端 resolve_image_model 解析所选模型;enqueue_standalone_images 接 image_model
- worker 按模型能力分流:有 image_edit(gpt-image)走多图编辑;无(火山)走
  image_generation(image=参考图);新增 _ratio_to_volcano_size 让火山按比例出图
  → 内衣等敏感品类用火山可绕开 gpt-image 的 sexual 内容审核

模特上身图提示词:
- 穿戴/非穿戴分流、按 index 变化动作场景镜头、负面词尾接、多图参考序号自适应
- _product_reference_urls:商品参考真实上传图优先、排除 AI 生成图、可多张

其他(并入此前各会话未提交改动):
- 图片创作多会话(ImageConversation + migration 0019)、任务中心按类型过滤
- accounts/projects/assets/team/auth 等零散调整、相关测试
- 测试脚本(tryon_*.py)、测试清单(core/bug/*.xlsx)
This commit is contained in:
zyc
2026-06-27 14:00:28 +08:00
parent 0a519ca892
commit ce7eaefe1d
21 changed files with 1279 additions and 158 deletions
+59
View File
@@ -126,6 +126,65 @@ class InvitationFlowTests(TestCase):
self.assertEqual(gen2.status_code, 403)
class MemberQuotaAndRoleTests(TestCase):
"""PMC#3:① 新建子账号不填限额时默认 100(不再 0=不限,避免子账号无上限花团队池);
② login/me 返回当前用户在团队的 role,前端据此只给主账号(owner)显示「团队」「消费」页。"""
def _register(self, client, username, **extra):
return client.post(
"/api/auth/register/",
{"username": username, "password": "strong-password", **extra},
format="json",
)
def setUp(self):
self.owner_client = APIClient()
r = self._register(self.owner_client, "q-owner", team_name="Q Team", invite_code=make_create_team_code())
self.assertEqual(r.status_code, 201)
self.token = r.data["token"]
self.team = Team.objects.get(name="Q Team")
self.owner_client.credentials(HTTP_AUTHORIZATION=f"Token {self.token}")
def test_register_owner_payload_role_is_owner(self):
# 注册即登录的返回体带 role=owner(主账号)
r = self._register(APIClient(), "solo-owner", team_name="Solo", invite_code=make_create_team_code())
self.assertEqual(r.data.get("role"), "owner")
def test_create_member_defaults_limit_100(self):
res = self.owner_client.post(
"/api/auth/team/members/",
{"username": "sub-a", "password": "strong-password", "role": "member"},
format="json",
)
self.assertEqual(res.status_code, 201, res.content)
member = TeamMember.objects.get(user__username="sub-a", team=self.team)
self.assertEqual(member.monthly_credit_limit, Decimal("100"))
def test_create_member_explicit_limit_respected(self):
res = self.owner_client.post(
"/api/auth/team/members/",
{"username": "sub-b", "password": "strong-password", "role": "member", "monthly_credit_limit": 800},
format="json",
)
self.assertEqual(res.status_code, 201, res.content)
member = TeamMember.objects.get(user__username="sub-b", team=self.team)
self.assertEqual(member.monthly_credit_limit, Decimal("800"))
def test_member_login_returns_member_role(self):
self.owner_client.post(
"/api/auth/team/members/",
{"username": "sub-c", "password": "strong-password", "role": "member"},
format="json",
)
login = APIClient().post(
"/api/auth/login/",
{"username": "sub-c", "password": "strong-password"},
format="json",
)
self.assertEqual(login.status_code, 200)
self.assertEqual(login.data.get("role"), "member")
class PlatformAdminTests(TestCase):
"""Phase 0:平台超管基础 —— 管理命令 / 权限类 / 登录 me 返回标志且团队为空。"""
+18 -2
View File
@@ -25,11 +25,25 @@ from .serializers import (
)
# 子账号默认月度限额(0=不限,只留给主账号/超管)
DEFAULT_MEMBER_MONTHLY_LIMIT = 100
def member_role(user, team):
"""当前用户在当前团队的成员角色(owner/admin/member);无团队成员关系返回 ""。
前端据此决定「团队」「消费」等仅主账号(超管)可见的页面是否展示(PMC#3)。"""
if team is None:
return ""
m = TeamMember.objects.filter(team=team, user=user, status=TeamMember.Status.ACTIVE).first()
return m.role if m else ""
def auth_payload(user, team, token):
return {
"token": token.key,
"user": UserSerializer(user).data,
"team": TeamSerializer(team).data if team is not None else None,
"role": member_role(user, team),
}
@@ -157,6 +171,7 @@ def me(request):
{
"user": UserSerializer(user).data,
"team": TeamSerializer(team).data if team is not None else None,
"role": member_role(user, team),
}
)
@@ -276,7 +291,8 @@ def team_members(request):
team=team,
user=user,
role=role,
monthly_credit_limit=request.data.get("monthly_credit_limit") or request.data.get("monthly") or 0,
# 子账号默认月度限额 100(0=不限只留给主账号/超管)—— 避免新成员不设限就无上限地花主账号团队池的钱(PMC#3)。
monthly_credit_limit=request.data.get("monthly_credit_limit") or request.data.get("monthly") or DEFAULT_MEMBER_MONTHLY_LIMIT,
)
return Response(TeamMemberSerializer(member).data, status=status.HTTP_201_CREATED)
@@ -299,7 +315,7 @@ def team_invitations(request):
kind=Invitation.Kind.JOIN_TEAM, # 团管发的码恒为「加入本团队」
role=role,
email=str(request.data.get("email") or "").strip(),
monthly_credit_limit=request.data.get("monthly_credit_limit") or request.data.get("monthly") or 0,
monthly_credit_limit=request.data.get("monthly_credit_limit") or request.data.get("monthly") or DEFAULT_MEMBER_MONTHLY_LIMIT,
created_by=request.user,
)
return Response(InvitationSerializer(invite).data, status=status.HTTP_201_CREATED)