重构(会员契约): 会员结构体改用 ts-rs 生成类型

- shared-contracts 为 10 个会员结构体补 ts_rs::TS / ts(export),64 位整数字段补 ts(type = "number") 对齐前端 number
- 新增 10 个生成绑定;packages/shared 与 admin-web 改为 re-export 生成类型,仅 kind / status 等前端收窄联合用 Omit 组合保留
- 同步设计文档 §2.5 与决策日志
This commit is contained in:
2026-10-05 15:51:07 +08:00
parent 0930e402be
commit bc3e36ff33
15 changed files with 332 additions and 120 deletions
@@ -191,9 +191,23 @@ pub struct ProfileDashboardSummaryResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileDailyFreePointsResponse {
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub day_key: i64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub granted_points: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub remaining_points: u64,
pub resets_at: String,
pub updated_at: String,
@@ -467,12 +481,26 @@ where
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileRechargeProductResponse {
pub product_id: String,
pub title: String,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub price_cents: u64,
pub kind: String,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub points_amount: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub bonus_points: u64,
pub duration_days: u32,
pub badge_label: String,
@@ -481,14 +509,28 @@ pub struct ProfileRechargeProductResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileMembershipPlanResponse {
/// 档位 token:`normal` / `starter` / `plus` / `pro` / `max`;未知取值按 `max` 兜底。
#[serde(deserialize_with = "deserialize_forward_compat_membership_plan")]
pub plan: ProfileMembershipPlanToken,
pub title: String,
pub rank: u8,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub month_price_cents: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub year_price_cents: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub period_points: u64,
/// 模型权限 token:`basic` / `full`;未知取值按 `full` 兜底。
#[serde(deserialize_with = "deserialize_forward_compat_model_access")]
@@ -504,6 +546,17 @@ pub struct ProfileMembershipPlanResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileMembershipResponse {
/// 会员状态 token:`normal` / `active`;未知取值按 `active` 兜底。
#[serde(deserialize_with = "deserialize_forward_compat_membership_status")]
@@ -516,7 +569,9 @@ pub struct ProfileMembershipResponse {
pub updated_at: Option<String>,
pub cycle_started_at: Option<String>,
pub cycle_resets_at: Option<String>,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub cycle_granted_points: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub cycle_remaining_points: u64,
/// 当前期序号(1-based)。
pub cycle_index: u32,
@@ -529,17 +584,30 @@ pub struct ProfileMembershipResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileRechargeOrderResponse {
pub order_id: String,
pub product_id: String,
pub product_title: String,
pub kind: String,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub amount_cents: u64,
pub status: String,
pub payment_channel: String,
pub paid_at: Option<String>,
pub provider_transaction_id: Option<String>,
pub created_at: String,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub points_delta: i64,
pub membership_expires_at: Option<String>,
pub expired_at: Option<String>,
@@ -591,6 +659,17 @@ pub struct WechatNativePaymentResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileRechargeCenterResponse {
pub mud_point_balance: ProfileMudPointBalanceResponse,
pub membership: ProfileMembershipResponse,
@@ -908,6 +987,17 @@ pub struct AdminUpsertProfileRechargeProductRequest {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileMembershipUpgradeQuoteRequest {
/// 目标档位 token:`starter` / `plus` / `pro` / `max`。
pub target_plan: ProfileMembershipPlanToken,
@@ -916,6 +1006,17 @@ pub struct ProfileMembershipUpgradeQuoteRequest {
/// 补差升级报价:付款前展示应付金额、补点与变更后的月度余额;下单时后端会用同一函数重算。
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileMembershipUpgradeQuoteResponse {
/// 当前档位 token;未知取值按 `max` 兜底。
#[serde(deserialize_with = "deserialize_forward_compat_membership_plan")]
@@ -930,10 +1031,13 @@ pub struct ProfileMembershipUpgradeQuoteResponse {
pub cycle_index: u32,
pub cycle_count: u32,
/// 应付金额(分);月付 = 月价差,年付 = 年价差 ÷ 12 × (后续完整月数 + 本期剩余比例),向上取整到分。
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub amount_cents: u64,
/// 本期补点;年付按本期剩余比例向下取整。
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub granted_points_delta: u64,
/// 变更后月度余额 = 原剩余月度余额 + 补点(已用点不返还,永久泥点不动)。
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub monthly_balance_after: u64,
pub expires_at: Option<String>,
pub cycle_resets_at: Option<String>,
@@ -942,6 +1046,17 @@ pub struct ProfileMembershipUpgradeQuoteResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct ProfileMembershipPlanAdminListResponse {
pub entries: Vec<ProfileMembershipPlanResponse>,
/// 并发哨兵阈值:`concurrent_job_limit >= 该值` 即不设上限;由后端下发,前端不硬编码。
@@ -951,12 +1066,26 @@ pub struct ProfileMembershipPlanAdminListResponse {
/// 后台按档位改会员价格与权益;`rank` 不在请求里,升级比较基准由代码内目录决定。
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
#[cfg_attr(
feature = "ts-bindings",
ts(
export,
export_to = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../packages/shared/src/contracts/generated/"
)
)
)]
pub struct AdminUpsertProfileMembershipPlanRequest {
/// 档位 token,作为主键即档位身份。
pub plan: ProfileMembershipPlanToken,
pub title: String,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub month_price_cents: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub year_price_cents: u64,
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
pub period_points: u64,
/// 模型权限 token:`basic` / `full`;本期只落字段、不做服务端拦截。
pub model_access: ProfileMembershipModelAccessToken,