From d7dec9b47515405712ebf84670b62f53a492ff62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sun, 4 Oct 2026 11:46:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E5=8C=96(=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E5=90=8E=E5=8F=B0)=EF=BC=9A=E6=A1=A3=E4=BD=8D=E4=B8=8E?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=9D=83=E9=99=90=E8=AF=B7=E6=B1=82=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E6=94=B9=E7=94=A8=20typed=20enum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AdminUpsertProfileMembershipPlanRequest.plan/model_access 由 String 改为 token 枚举 - 未登记的档位 / 权限 token 在反序列化阶段直接拒绝,后台不能再持久化任意字符串 - admin handler 去掉字符串解析分支,改为 From 转换 - 删除仅用于手写解析的 parse_runtime_profile_membership_* 辅助函数 --- .../crates/api-server/src/runtime_profile.rs | 32 ++----------------- .../crates/shared-contracts/src/runtime.rs | 4 +-- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index def489579..c4d22d258 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -1188,19 +1188,8 @@ pub async fn admin_upsert_profile_membership_plan( Extension(admin): Extension, Json(payload): Json, ) -> Result, Response> { - let plan = parse_runtime_profile_membership_plan(&payload.plan).map_err(|error| { - runtime_profile_error_response( - &request_context, - AppError::from_status(StatusCode::BAD_REQUEST).with_message(error), - ) - })?; - let model_access = parse_runtime_profile_membership_model_access(&payload.model_access) - .map_err(|error| { - runtime_profile_error_response( - &request_context, - AppError::from_status(StatusCode::BAD_REQUEST).with_message(error), - ) - })?; + let plan = payload.plan.into(); + let model_access = payload.model_access.into(); let updated_at_micros = OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000; let record = state .spacetime_client() @@ -2434,22 +2423,7 @@ fn parse_profile_recharge_product_kind( } } -/// 会员档位 token → 枚举。JSON 没有枚举,但解析必须严格:未知 token 一律 400。 -fn parse_runtime_profile_membership_plan( - raw: &str, -) -> Result { - module_runtime::RuntimeProfileMembershipPlan::parse(raw) - .ok_or_else(|| "会员档位无效".to_string()) -} - -/// 模型权限 token → 枚举;`basic` = 仅基础模型,`full` = 基础及高性能模型。 -fn parse_runtime_profile_membership_model_access( - raw: &str, -) -> Result { - module_runtime::RuntimeProfileMembershipModelAccess::parse(raw) - .ok_or_else(|| "模型权限无效".to_string()) -} - +/// 会员档位 / 模型权限 / 周期改由 typed enum 在反序列化阶段校验,未知 token 直接 4xx。 fn parse_tracking_scope_kind(raw: &str) -> Result { match raw.trim().to_ascii_lowercase().as_str() { TRACKING_SCOPE_KIND_SITE => Ok(RuntimeTrackingScopeKind::Site), diff --git a/server-rs/crates/shared-contracts/src/runtime.rs b/server-rs/crates/shared-contracts/src/runtime.rs index bab42b467..fc576d464 100644 --- a/server-rs/crates/shared-contracts/src/runtime.rs +++ b/server-rs/crates/shared-contracts/src/runtime.rs @@ -801,13 +801,13 @@ pub struct ProfileMembershipPlanAdminListResponse { #[serde(rename_all = "camelCase")] pub struct AdminUpsertProfileMembershipPlanRequest { /// 档位 token,作为主键即档位身份。 - pub plan: String, + pub plan: ProfileMembershipPlanToken, pub title: String, pub month_price_cents: u64, pub year_price_cents: u64, pub period_points: u64, /// 模型权限 token:`basic` / `full`;本期只落字段、不做服务端拦截。 - pub model_access: String, + pub model_access: ProfileMembershipModelAccessToken, /// 并发上限;`128` 表示不设上限。 pub concurrent_job_limit: u32, #[serde(default = "default_true")]