From 233498a9d6f95c1db37ca5cd861428e77837343b Mon Sep 17 00:00:00 2001 From: Johnny Zhang Date: Sat, 22 Aug 2026 22:54:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor(render3d):=20=E6=8A=A5=E4=BB=B7?= =?UTF-8?q?=E5=8F=AA=E7=95=99=E7=A7=AF=E5=88=86=EF=BC=8C=E5=88=A0=E6=8E=89?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E5=8D=95=E4=BB=B7=E4=B8=8E=E4=BA=BA?= =?UTF-8?q?=E6=B0=91=E5=B8=81=E6=8D=A2=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit quote() 的第二个返回值全仓无消费方;单价 0.12 是采购成本,不该留在公开仓。 三处 SpendNotAuthorized 文案改为只报积分。 Closes #546 --- .../server/orchestrator/render3d_assets.py | 4 +--- .../server/orchestrator/render3d_service.py | 3 +-- .../providers/render3d/tencent.py | 19 +++++++++---------- backend/tests/test_render3d_tencent.py | 16 ++++++++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/backend/packages/app/src/windup_app/server/orchestrator/render3d_assets.py b/backend/packages/app/src/windup_app/server/orchestrator/render3d_assets.py index da9f998d..78d48125 100644 --- a/backend/packages/app/src/windup_app/server/orchestrator/render3d_assets.py +++ b/backend/packages/app/src/windup_app/server/orchestrator/render3d_assets.py @@ -44,7 +44,6 @@ from windup_ai_engine.ports import ProgressPort from windup_framework.providers.render3d.tencent import ( - CREDIT_PRICE_CNY, CREDITS, RIG_CREDITS, ) @@ -69,7 +68,6 @@ MODEL3D_CREDITS = CREDITS["Normal"] AUTORIG_CREDITS = RIG_CREDITS BUILD_CREDITS = MODEL3D_CREDITS + AUTORIG_CREDITS -BUILD_CNY = round(BUILD_CREDITS * CREDIT_PRICE_CNY, 2) class Render3DAssetState(str, Enum): @@ -303,7 +301,7 @@ def ensure(self, outfit_key: str, master: bytes, progress: ProgressPort) -> byte if not self._may_build_assets: raise SpendNotAuthorized( f"造型 {outfit_key!r} 的 3D 资产未就绪,而本实例未获准建(建一次 " - f"{BUILD_CREDITS} 积分,约 ¥{BUILD_CNY}:图生 3D {MODEL3D_CREDITS} + " + f"{BUILD_CREDITS} 积分:图生 3D {MODEL3D_CREDITS} + " f"绑骨 {AUTORIG_CREDITS})。要现建请显式授权花钱," "或先把资产备好,或改走 video_i2v。" ) diff --git a/backend/packages/app/src/windup_app/server/orchestrator/render3d_service.py b/backend/packages/app/src/windup_app/server/orchestrator/render3d_service.py index 94ea86ee..5d405e11 100644 --- a/backend/packages/app/src/windup_app/server/orchestrator/render3d_service.py +++ b/backend/packages/app/src/windup_app/server/orchestrator/render3d_service.py @@ -31,7 +31,6 @@ from windup_app.server.orchestrator._fetch import FetchNotAllowed, fetch_own_media from windup_app.server.orchestrator.render3d_assets import ( AUTORIG_CREDITS, - BUILD_CNY, BUILD_CREDITS, MODEL3D_CREDITS, RAW_KEY_PREFIX, @@ -183,7 +182,7 @@ def build(self, outfit_key: str, master_url: str) -> dict: if not self._builder.may_build_assets: raise SpendNotAuthorized( f"本部署未开启建 3D 资产(需 WINDUP_RENDER3D_ALLOW_SPEND)。建一次 " - f"{BUILD_CREDITS} 积分,约 ¥{BUILD_CNY}。" + f"{BUILD_CREDITS} 积分。" ) state = self._builder.state(outfit_key) if state is not Render3DAssetState.ABSENT: diff --git a/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py b/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py index 32dc217c..f09c3612 100644 --- a/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py +++ b/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py @@ -43,7 +43,7 @@ __all__ = [ "TencentModel3DProvider", "TencentAutoRigProvider", "TencentCosModelUploader", - "SpendNotAuthorizedError", "CREDITS", "CREDIT_PRICE_CNY", "PRESET_MOTIONS", + "SpendNotAuthorizedError", "CREDITS", "PRESET_MOTIONS", "RIG_CREDITS", "MAX_IMAGE_BYTES", "VIEW_TYPES", ] @@ -54,7 +54,6 @@ CREDITS = {"Normal": 20, "LowPoly": 25, "Geometry": 15, "Sketch": 25} RIG_CREDITS = 10 -CREDIT_PRICE_CNY = 0.12 # 后付费单价;预付费 0.09–0.1 MAX_IMAGE_BYTES = 6 * 10**6 # ImageBase64 上限 VIEW_TYPES = ("back", "left", "right") # 正面走主参数,不在这里 @@ -266,10 +265,10 @@ def __init__( self._poll = poll_interval self._max_min = max_min - def quote(self, n_views: int = 1) -> tuple[int, float]: + def quote(self, n_views: int = 1) -> int: """返回 (积分, 预估元)。PBR、多视图各 +10 积分。纯计算,可在提交前随便调。""" credits = CREDITS[self._type] + (10 if self._pbr else 0) + (10 if n_views > 1 else 0) - return credits, round(credits * CREDIT_PRICE_CNY, 2) + return credits def build_params(self, master: bytes, extra_views: Mapping[str, bytes] | None = None) -> dict: @@ -305,10 +304,10 @@ def image_to_3d(self, master: bytes, *, want: str = "GLB", if want not in MODEL_FORMATS: raise ArtifactFormatError(f"want 只能是 {MODEL_FORMATS},收到 {want!r}") params = self.build_params(master, extra_views) - credits, cny = self.quote(1 + len(extra_views or {})) + credits = self.quote(1 + len(extra_views or {})) if not self._allow_spend: raise SpendNotAuthorizedError( - f"图生 3D 会消耗 {credits} 积分(后付费约 ¥{cny})。" + f"图生 3D 会消耗 {credits} 积分。" "确认要花这笔钱后,用 TencentModel3DProvider(..., allow_spend=True) 构造。") if self._ask_format: @@ -392,8 +391,8 @@ def __init__( def preset_motions(self) -> Mapping[str, PresetMotion]: return PRESET_MOTIONS - def quote(self) -> tuple[int, float]: - return RIG_CREDITS, round(RIG_CREDITS * CREDIT_PRICE_CNY, 2) + def quote(self) -> int: + return RIG_CREDITS def resolve_motion(self, motion: str | int | None) -> PresetMotion | None: """动作名 / 编号 → :class:`PresetMotion`。名字不认识就抛,不猜编号。""" @@ -424,10 +423,10 @@ def rig(self, model: bytes, *, want: str = "GLB", # 入口预检在**上传与提交之前**:三条硬约束违反了接口不报错,只默默出错结果。 check_model(model) - credits, cny = self.quote() + credits = self.quote() if not self._allow_spend: raise SpendNotAuthorizedError( - f"绑骨会消耗 {credits} 积分(后付费约 ¥{cny})。" + f"绑骨会消耗 {credits} 积分。" "确认要花这笔钱后,用 TencentAutoRigProvider(..., allow_spend=True) 构造。") url = self._upload(model, src_fmt) diff --git a/backend/tests/test_render3d_tencent.py b/backend/tests/test_render3d_tencent.py index 060c4629..932c1a35 100644 --- a/backend/tests/test_render3d_tencent.py +++ b/backend/tests/test_render3d_tencent.py @@ -111,7 +111,10 @@ def test_model3d_refuses_to_spend_by_default(cloud): p = TencentModel3DProvider(CREDS) with pytest.raises(SpendNotAuthorizedError) as e: p.image_to_3d(b"\x89PNG fake") - assert "20 积分" in str(e.value) and "¥2.4" in str(e.value) # 报价必须写在异常里 + # 报价写在异常里,但只给积分 —— 人民币是我们付给供应商的成本,不是用户的价钱, + # 而本仓是公开仓。同一条判据见 test_render3d_asset_endpoints 里的出参断言。 + assert "20 积分" in str(e.value) + assert "¥" not in str(e.value), "人民币成本不出现在任何对外文案里" assert fake.calls == [] # 一个请求都没发出去 @@ -142,11 +145,12 @@ def test_unknown_motion_name_dies_before_the_spend_gate(cloud): def test_quotes_are_pure_computation(): - assert TencentModel3DProvider(CREDS).quote() == (20, 2.4) - assert TencentModel3DProvider(CREDS, generate_type="Geometry").quote() == (15, 1.8) - assert TencentModel3DProvider(CREDS, enable_pbr=True).quote() == (30, 3.6) - assert TencentModel3DProvider(CREDS).quote(n_views=3) == (30, 3.6) - assert TencentAutoRigProvider(Uploader(), CREDS).quote() == (10, 1.2) + """报价只出积分。换算人民币要用供应商单价,那个数不该留在公开仓里。""" + assert TencentModel3DProvider(CREDS).quote() == 20 + assert TencentModel3DProvider(CREDS, generate_type="Geometry").quote() == 15 + assert TencentModel3DProvider(CREDS, enable_pbr=True).quote() == 30 + assert TencentModel3DProvider(CREDS).quote(n_views=3) == 30 + assert TencentAutoRigProvider(Uploader(), CREDS).quote() == 10 def test_unknown_generate_type_dies_at_construction(): From 3e796cf610d4cd3e8d0b2b897321e68440ad7a93 Mon Sep 17 00:00:00 2001 From: Johnny Zhang Date: Mon, 24 Aug 2026 01:24:23 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(render3d):=20quote=20=E7=9A=84?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E6=94=B9=E6=88=90=E5=8F=AA=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 签名与实现已只返回积分,说明仍写着 (积分, 预估元),按它解包的调用方会拿到错误指引。 --- .../src/windup_framework/providers/render3d/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py b/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py index f09c3612..fe290825 100644 --- a/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py +++ b/backend/packages/framework/src/windup_framework/providers/render3d/tencent.py @@ -266,7 +266,7 @@ def __init__( self._max_min = max_min def quote(self, n_views: int = 1) -> int: - """返回 (积分, 预估元)。PBR、多视图各 +10 积分。纯计算,可在提交前随便调。""" + """返回本次要花的积分。PBR、多视图各 +10。纯计算,花钱之前就该问得出花多少。""" credits = CREDITS[self._type] + (10 if self._pbr else 0) + (10 if n_views > 1 else 0) return credits