Feat/陶泥儿会员订阅方案 #588

Merged
k88936 merged 240 commits from feat/pricing-plan into master 2026-10-06 10:48:46 +08:00
4 changed files with 42 additions and 11 deletions
Showing only changes of commit dcaab089cc - Show all commits
+2 -1
View File
@@ -1174,7 +1174,8 @@ export interface AdminWalletRestrictionResponse {
wallet: AdminProfileWalletPayload;
}
export type AdminAgcAgentMode = 'codex' | 'cc';
export type AdminAgcModelAccess = 'basic' | 'full';
/** 复用 `@genarrative/shared` 的生成类型,与后端 `ProfileMembershipModelAccessToken` 同源。 */
export type AdminAgcModelAccess = ProfileMembershipModelAccess;
export type AdminAgcModelProtocol =
| 'openai_responses'
| 'openai_chat'
+17 -5
View File
@@ -244,10 +244,7 @@ pub async fn admin_save_agc_models(
.map_err(|message| {
AppError::from_status(StatusCode::BAD_REQUEST).with_message(message)
})?;
let access = AgcModelAccess::parse(&m.access).ok_or_else(|| {
AppError::from_status(StatusCode::BAD_REQUEST)
.with_message("权限档只允许 basic 或 full")
})?;
let access = AgcModelAccess::from(m.access);
Ok(module_runtime::AgcModel {
id: m.id,
alias: m.alias,
@@ -302,7 +299,7 @@ fn catalog_dto(catalog: AgcModelCatalog) -> AdminAgcModelCatalog {
enabled: m.enabled,
agent_mode: m.agent_mode.as_str().to_string(),
protocol: Some(protocol),
access: m.access.as_str().to_string(),
access: m.access.into(),
}
})
.collect(),
@@ -330,6 +327,21 @@ mod tests {
}))
.unwrap();
assert_eq!(legacy.agent_mode, "codex");
assert_eq!(
legacy.access,
shared_contracts::runtime::ProfileMembershipModelAccessToken::Basic
);
// 权限档 token 在反序列化阶段就收口,未知值不再推迟到 `AgcModelAccess::parse`。
assert!(
serde_json::from_value::<AdminAgcModel>(json!({
"id": "model-a",
"alias": "model-a",
"modelId": "model-a",
"enabled": true,
"access": "premium"
}))
.is_err()
);
}
#[test]
@@ -66,6 +66,24 @@ impl From<RuntimeProfileMembershipModelAccess> for AgcModelAccess {
}
}
impl From<shared_contracts::runtime::ProfileMembershipModelAccessToken> for AgcModelAccess {
fn from(value: shared_contracts::runtime::ProfileMembershipModelAccessToken) -> Self {
match value {
shared_contracts::runtime::ProfileMembershipModelAccessToken::Basic => Self::Basic,
shared_contracts::runtime::ProfileMembershipModelAccessToken::Full => Self::Full,
}
}
}
impl From<AgcModelAccess> for shared_contracts::runtime::ProfileMembershipModelAccessToken {
fn from(value: AgcModelAccess) -> Self {
match value {
AgcModelAccess::Basic => Self::Basic,
AgcModelAccess::Full => Self::Full,
}
}
}
/// 模型解析失败原因;调用方据此选择错误码与 HTTP 状态,不靠字符串匹配。
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AgcModelResolveError {
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::runtime::{
ProfileMembershipChangeKindToken, ProfileMembershipCycleKindToken, ProfileMembershipPlanToken,
ProfileMembershipStatusToken,
ProfileMembershipChangeKindToken, ProfileMembershipCycleKindToken,
ProfileMembershipModelAccessToken, ProfileMembershipPlanToken, ProfileMembershipStatusToken,
};
#[cfg(any())]
@@ -1706,8 +1706,8 @@ fn default_codex_agent_mode() -> String {
}
/// 存量后台请求缺省为 `basic`(失败开放),只有人工标注才收紧到 `full`。
fn default_basic_agc_model_access() -> String {
"basic".to_string()
fn default_basic_agc_model_access() -> ProfileMembershipModelAccessToken {
ProfileMembershipModelAccessToken::Basic
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -1726,7 +1726,7 @@ pub struct AdminAgcModel {
pub protocol: Option<String>,
/// 该模型要求的会员档位:`basic`(缺省,对全部档位开放)/ `full`(后台人工标注)。
#[serde(default = "default_basic_agc_model_access")]
pub access: String,
pub access: ProfileMembershipModelAccessToken,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]