重构(会员契约): 会员结构体改用 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:
@@ -2,6 +2,7 @@ import type {
|
||||
ProfileMembershipCycleKind,
|
||||
ProfileMembershipModelAccess,
|
||||
ProfileMembershipPlan,
|
||||
ProfileMembershipPlanResponse,
|
||||
ProfileMembershipStatus,
|
||||
} from '@genarrative/shared';
|
||||
|
||||
@@ -784,39 +785,13 @@ export interface ProfileRechargeProductConfigAdminListResponse {
|
||||
entries: ProfileRechargeProductConfigAdminResponse[];
|
||||
}
|
||||
|
||||
/** 会员档位目录行;后端 `profile_membership_plan` 表是价格与权益的唯一真相源。 */
|
||||
export interface ProfileMembershipPlanAdminResponse {
|
||||
plan: ProfileMembershipPlan;
|
||||
title: string;
|
||||
rank: number;
|
||||
monthPriceCents: number;
|
||||
yearPriceCents: number;
|
||||
periodPoints: number;
|
||||
modelAccess: ProfileMembershipModelAccess;
|
||||
/** 并发上限;`128` 表示不设上限。 */
|
||||
concurrentJobLimit: number;
|
||||
/** 是否不设并发上限;由后端按哨兵值派生,前端不再自己比较 `128`。 */
|
||||
unlimitedConcurrency: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
/** 会员档位目录行;直接复用 `@genarrative/shared` 的后端生成类型,避免手工漂移。 */
|
||||
export type ProfileMembershipPlanAdminResponse = ProfileMembershipPlanResponse;
|
||||
|
||||
export interface ProfileMembershipPlanAdminListResponse {
|
||||
entries: ProfileMembershipPlanAdminResponse[];
|
||||
/** 并发哨兵阈值:`concurrentJobLimit >= 该值` 即不设上限;由后端下发,前端不硬编码。 */
|
||||
unlimitedConcurrencyThreshold: number;
|
||||
}
|
||||
|
||||
/** 后台按档位改价 / 改权益;`rank` 不在请求里,升级比较基准由代码内目录决定。 */
|
||||
export interface AdminUpsertProfileMembershipPlanRequest {
|
||||
plan: ProfileMembershipPlan;
|
||||
title: string;
|
||||
monthPriceCents: number;
|
||||
yearPriceCents: number;
|
||||
periodPoints: number;
|
||||
modelAccess: ProfileMembershipModelAccess;
|
||||
concurrentJobLimit: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
export type {
|
||||
AdminUpsertProfileMembershipPlanRequest,
|
||||
ProfileMembershipPlanAdminListResponse,
|
||||
} from '@genarrative/shared';
|
||||
|
||||
export interface ProfileWalletConfigAdminResponse {
|
||||
configId: string;
|
||||
|
||||
@@ -9757,3 +9757,10 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 决策:该 PR 尚未上线,不留兼容,直接改名为 `ProductIdUnparseable`,wire 串同步改为 `"product_id_unparseable"`;重新生成 SpacetimeDB 绑定。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active/mapper/runtime_profile.rs,module_bindings/runtime_profile_membership_error_code_type.rs}`、`server-rs/crates/api-server/src/runtime_profile.rs`、设计文档 §5.8、本文件(含上文旧条目中的同名标识符同步更正)。
|
||||
- 验证:`cargo test -p module-runtime --lib` 114 passed;`cargo test -p spacetime-client` 32 passed;`cargo test -p api-server runtime_profile::tests` 43 passed;`cargo test -p spacetime-module` 271 passed。
|
||||
|
||||
## 2026-10-03 会员结构体也走 ts-rs 生成类型(去前端手写漂移)
|
||||
|
||||
- 背景:会员 token 枚举早已 `ts(export)`,但承载它们的结构体(`ProfileMembershipPlanResponse`、`ProfileMembershipResponse`、`ProfileRechargeCenterResponse`、`ProfileRechargeProductResponse`、`ProfileRechargeOrderResponse`、`ProfileMembershipUpgradeQuoteResponse`、`ProfileMembershipPlanAdminListResponse`、`AdminUpsertProfileMembershipPlanRequest`、`ProfileDailyFreePointsResponse`、`ProfileMembershipUpgradeQuoteRequest`)仍是前端手写,字段与后端可能漂移。
|
||||
- 决策:这 10 个结构体补齐 `ts_rs::TS` + `ts(export)`;64 位整数字段统一补 `ts(type = "number")`(ts-rs 默认 `bigint`,JSON 实际是 number)。`packages/shared/src/contracts/runtime.ts` 与 `apps/admin-web/src/api/adminApiTypes.ts` 改为 re-export 生成类型,仅前端需要收窄的 `kind` / `status` 联合用 `Omit` 组合保留;`ProfileRechargeCenterResponse` 的 `pointProducts` / `latestOrder` 同样收窄。
|
||||
- 影响范围:`server-rs/crates/shared-contracts/src/runtime.rs`、`packages/shared/src/contracts/generated/*`(新增 10 个文件)、`packages/shared/src/contracts/runtime.ts`、`apps/admin-web/src/api/adminApiTypes.ts`、设计文档 §2.5。
|
||||
- 验证:`npm run check:generated-bindings`(shared-contracts 23 个文件);根 `npm run typecheck`、`apps/admin-web` 与 AGC shell 主 `tsc` 通过;`cargo test -p shared-contracts --lib` 106 passed;`PlatformProfileRechargeModal` / `usePlatformProfileCenterController` vitest 14 passed。
|
||||
|
||||
@@ -160,6 +160,14 @@ pub struct ProfileMembershipPlan {
|
||||
避免 AGC Tauri shell 等 Rust 客户端因后端新增一个 token 就整包解析失败(权益与下单金额仍以后端重算为准)。
|
||||
这 5 个 token 枚举统一 `#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]`
|
||||
导出到 `packages/shared/src/contracts/generated/`,前端只 re-export,不再手写字符串联合类型。
|
||||
- **会员结构体也走生成类型(2026-10-03 补)**:`ProfileMembershipPlanResponse` / `ProfileMembershipResponse` /
|
||||
`ProfileRechargeProductResponse` / `ProfileRechargeOrderResponse` / `ProfileRechargeCenterResponse` /
|
||||
`ProfileMembershipUpgradeQuoteResponse` / `ProfileMembershipPlanAdminListResponse` /
|
||||
`AdminUpsertProfileMembershipPlanRequest` / `ProfileDailyFreePointsResponse` /
|
||||
`ProfileMembershipUpgradeQuoteRequest` 同样 `ts(export)`;64 位整数字段补 `ts(type = "number")`
|
||||
对齐前端 `number`(ts-rs 默认 `bigint`)。`packages/shared/src/contracts/runtime.ts` 与
|
||||
`apps/admin-web/src/api/adminApiTypes.ts` 改为 re-export 生成类型,只手写保留 `kind` / `status`
|
||||
这类前端收窄联合(用 `Omit` 组合),`ProfileRechargeCenterResponse` 同理收窄内层商品 / 订单数组。
|
||||
- **删除** `RuntimeProfileMembershipTier { Normal, Month, Season, Year, Starter, Basic, Pro, Ultimate }`
|
||||
及其 `as_str()`(会员侧零存量,用户已授权破坏性变更)。
|
||||
- `RuntimeProfileWalletLedgerSourceType` **末尾追加** `MembershipUpgradeGrant`
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipModelAccessToken } from "./ProfileMembershipModelAccessToken";
|
||||
import type { ProfileMembershipPlanToken } from "./ProfileMembershipPlanToken";
|
||||
|
||||
/**
|
||||
* 后台按档位改会员价格与权益;`rank` 不在请求里,升级比较基准由代码内目录决定。
|
||||
*/
|
||||
export type AdminUpsertProfileMembershipPlanRequest = {
|
||||
/**
|
||||
* 档位 token,作为主键即档位身份。
|
||||
*/
|
||||
plan: ProfileMembershipPlanToken, title: string, monthPriceCents: number, yearPriceCents: number, periodPoints: number,
|
||||
/**
|
||||
* 模型权限 token:`basic` / `full`;本期只落字段、不做服务端拦截。
|
||||
*/
|
||||
modelAccess: ProfileMembershipModelAccessToken,
|
||||
/**
|
||||
* 并发上限;`128` 表示不设上限。
|
||||
*/
|
||||
concurrentJobLimit: number, enabled: boolean, };
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ProfileDailyFreePointsResponse = { dayKey: number, grantedPoints: number, remainingPoints: number, resetsAt: string, updatedAt: string, };
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipPlanResponse } from "./ProfileMembershipPlanResponse";
|
||||
|
||||
export type ProfileMembershipPlanAdminListResponse = { entries: Array<ProfileMembershipPlanResponse>,
|
||||
/**
|
||||
* 并发哨兵阈值:`concurrent_job_limit >= 该值` 即不设上限;由后端下发,前端不硬编码。
|
||||
*/
|
||||
unlimitedConcurrencyThreshold: number, };
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipModelAccessToken } from "./ProfileMembershipModelAccessToken";
|
||||
import type { ProfileMembershipPlanToken } from "./ProfileMembershipPlanToken";
|
||||
|
||||
export type ProfileMembershipPlanResponse = {
|
||||
/**
|
||||
* 档位 token:`normal` / `starter` / `plus` / `pro` / `max`;未知取值按 `max` 兜底。
|
||||
*/
|
||||
plan: ProfileMembershipPlanToken, title: string, rank: number, monthPriceCents: number, yearPriceCents: number, periodPoints: number,
|
||||
/**
|
||||
* 模型权限 token:`basic` / `full`;未知取值按 `full` 兜底。
|
||||
*/
|
||||
modelAccess: ProfileMembershipModelAccessToken,
|
||||
/**
|
||||
* 并发上限;`128` 表示不设上限。
|
||||
*/
|
||||
concurrentJobLimit: number,
|
||||
/**
|
||||
* 是否为运营推荐档位;由后端目录决定,前端不再硬编码档位 token。
|
||||
*/
|
||||
recommended: boolean,
|
||||
/**
|
||||
* 是否不设并发上限;由后端按哨兵值派生,前端不再硬编码 `128`。
|
||||
*/
|
||||
unlimitedConcurrency: boolean, enabled: boolean, };
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipCycleKindToken } from "./ProfileMembershipCycleKindToken";
|
||||
import type { ProfileMembershipPlanToken } from "./ProfileMembershipPlanToken";
|
||||
import type { ProfileMembershipStatusToken } from "./ProfileMembershipStatusToken";
|
||||
|
||||
export type ProfileMembershipResponse = {
|
||||
/**
|
||||
* 会员状态 token:`normal` / `active`;未知取值按 `active` 兜底。
|
||||
*/
|
||||
status: ProfileMembershipStatusToken,
|
||||
/**
|
||||
* 当前档位 token;非会员与已到期都是 `normal`;未知取值按 `max` 兜底。
|
||||
*/
|
||||
plan: ProfileMembershipPlanToken, startedAt: string | null, expiresAt: string | null, updatedAt: string | null, cycleStartedAt: string | null, cycleResetsAt: string | null, cycleGrantedPoints: number, cycleRemainingPoints: number,
|
||||
/**
|
||||
* 当前期序号(1-based)。
|
||||
*/
|
||||
cycleIndex: number,
|
||||
/**
|
||||
* 本次购买总期数(月付 1 / 年付 12)。
|
||||
*/
|
||||
cycleCount: number,
|
||||
/**
|
||||
* 周期 token:`monthly` / `yearly`;未知取值按 `yearly` 兜底。
|
||||
*/
|
||||
cycleKind: ProfileMembershipCycleKindToken, };
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipPlanToken } from "./ProfileMembershipPlanToken";
|
||||
|
||||
export type ProfileMembershipUpgradeQuoteRequest = {
|
||||
/**
|
||||
* 目标档位 token:`starter` / `plus` / `pro` / `max`。
|
||||
*/
|
||||
targetPlan: ProfileMembershipPlanToken, };
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileMembershipCycleKindToken } from "./ProfileMembershipCycleKindToken";
|
||||
import type { ProfileMembershipPlanToken } from "./ProfileMembershipPlanToken";
|
||||
|
||||
/**
|
||||
* 补差升级报价:付款前展示应付金额、补点与变更后的月度余额;下单时后端会用同一函数重算。
|
||||
*/
|
||||
export type ProfileMembershipUpgradeQuoteResponse = {
|
||||
/**
|
||||
* 当前档位 token;未知取值按 `max` 兜底。
|
||||
*/
|
||||
currentPlan: ProfileMembershipPlanToken,
|
||||
/**
|
||||
* 目标档位 token;未知取值按 `max` 兜底。
|
||||
*/
|
||||
targetPlan: ProfileMembershipPlanToken,
|
||||
/**
|
||||
* 周期 token:`monthly` / `yearly`;有效期内不支持月年互换,所以变更前后一致;
|
||||
* 未知取值按 `yearly` 兜底。
|
||||
*/
|
||||
cycleKind: ProfileMembershipCycleKindToken, cycleIndex: number, cycleCount: number,
|
||||
/**
|
||||
* 应付金额(分);月付 = 月价差,年付 = 年价差 ÷ 12 × (后续完整月数 + 本期剩余比例),向上取整到分。
|
||||
*/
|
||||
amountCents: number,
|
||||
/**
|
||||
* 本期补点;年付按本期剩余比例向下取整。
|
||||
*/
|
||||
grantedPointsDelta: number,
|
||||
/**
|
||||
* 变更后月度余额 = 原剩余月度余额 + 补点(已用点不返还,永久泥点不动)。
|
||||
*/
|
||||
monthlyBalanceAfter: number, expiresAt: string | null, cycleResetsAt: string | null, remainingFullMonths: number, };
|
||||
@@ -0,0 +1,13 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ProfileDailyFreePointsResponse } from "./ProfileDailyFreePointsResponse";
|
||||
import type { ProfileMembershipPlanResponse } from "./ProfileMembershipPlanResponse";
|
||||
import type { ProfileMembershipResponse } from "./ProfileMembershipResponse";
|
||||
import type { ProfileMudPointBalanceResponse } from "./ProfileMudPointBalanceResponse";
|
||||
import type { ProfileRechargeOrderResponse } from "./ProfileRechargeOrderResponse";
|
||||
import type { ProfileRechargeProductResponse } from "./ProfileRechargeProductResponse";
|
||||
|
||||
export type ProfileRechargeCenterResponse = { mudPointBalance: ProfileMudPointBalanceResponse, membership: ProfileMembershipResponse, pointProducts: Array<ProfileRechargeProductResponse>,
|
||||
/**
|
||||
* 会员档位目录(后台可改价),会员购买 / 升级的价格与权益都以它为准。
|
||||
*/
|
||||
membershipPlans: Array<ProfileMembershipPlanResponse>, latestOrder: ProfileRechargeOrderResponse | null, hasPointsRecharged: boolean, dailyFreePoints: ProfileDailyFreePointsResponse, };
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ProfileRechargeOrderResponse = { orderId: string, productId: string, productTitle: string, kind: string, amountCents: number, status: string, paymentChannel: string, paidAt: string | null, providerTransactionId: string | null, createdAt: string, pointsDelta: number, membershipExpiresAt: string | null, expiredAt: string | null, expirationCheckedAt: string | null, expirationProviderState: string | null, expirationLastError: string | null, };
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ProfileRechargeProductResponse = { productId: string, title: string, priceCents: number, kind: string, pointsAmount: number, bonusPoints: number, durationDays: number, badgeLabel: string, description: string, };
|
||||
@@ -1,18 +1,37 @@
|
||||
import type { JsonObject } from './common';
|
||||
import type { AdminUpsertProfileMembershipPlanRequest } from './generated/AdminUpsertProfileMembershipPlanRequest';
|
||||
import type { ProfileDailyFreePointsResponse } from './generated/ProfileDailyFreePointsResponse';
|
||||
import type { ProfileMembershipChangeKindToken } from './generated/ProfileMembershipChangeKindToken';
|
||||
import type { ProfileMembershipCycleKindToken } from './generated/ProfileMembershipCycleKindToken';
|
||||
import type { ProfileMembershipModelAccessToken } from './generated/ProfileMembershipModelAccessToken';
|
||||
import type { ProfileMembershipPlanAdminListResponse } from './generated/ProfileMembershipPlanAdminListResponse';
|
||||
import type { ProfileMembershipPlanResponse } from './generated/ProfileMembershipPlanResponse';
|
||||
import type { ProfileMembershipPlanToken } from './generated/ProfileMembershipPlanToken';
|
||||
import type { ProfileMembershipResponse } from './generated/ProfileMembershipResponse';
|
||||
import type { ProfileMembershipStatusToken } from './generated/ProfileMembershipStatusToken';
|
||||
import type { ProfileMembershipUpgradeQuoteRequest } from './generated/ProfileMembershipUpgradeQuoteRequest';
|
||||
import type { ProfileMembershipUpgradeQuoteResponse } from './generated/ProfileMembershipUpgradeQuoteResponse';
|
||||
import type { ProfileMudPointBalanceResponse as ProfileMudPointBalance } from './generated/ProfileMudPointBalanceResponse';
|
||||
import type { ProfileRechargeCenterResponse as GeneratedProfileRechargeCenterResponse } from './generated/ProfileRechargeCenterResponse';
|
||||
import type { ProfileRechargeOrderResponse } from './generated/ProfileRechargeOrderResponse';
|
||||
import type { ProfileRechargeProductResponse } from './generated/ProfileRechargeProductResponse';
|
||||
|
||||
export type {
|
||||
AdminUpsertProfileMembershipPlanRequest,
|
||||
ProfileDailyFreePointsResponse,
|
||||
ProfileMembershipChangeKindToken,
|
||||
ProfileMembershipCycleKindToken,
|
||||
ProfileMembershipModelAccessToken,
|
||||
ProfileMembershipPlanAdminListResponse,
|
||||
ProfileMembershipPlanResponse,
|
||||
ProfileMembershipPlanToken,
|
||||
ProfileMembershipResponse,
|
||||
ProfileMembershipStatusToken,
|
||||
ProfileMembershipUpgradeQuoteRequest,
|
||||
ProfileMembershipUpgradeQuoteResponse,
|
||||
ProfileMudPointBalance,
|
||||
ProfileRechargeOrderResponse,
|
||||
ProfileRechargeProductResponse,
|
||||
};
|
||||
|
||||
export const SAVE_SNAPSHOT_VERSION = 2;
|
||||
@@ -69,13 +88,7 @@ export type ProfileDashboardSummary = {
|
||||
dailyFreePoints?: ProfileDailyFreePoints;
|
||||
};
|
||||
|
||||
export type ProfileDailyFreePoints = {
|
||||
dayKey: number;
|
||||
grantedPoints: number;
|
||||
remainingPoints: number;
|
||||
resetsAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
export type ProfileDailyFreePoints = ProfileDailyFreePointsResponse;
|
||||
|
||||
export type ProfileWalletLedgerEntry = {
|
||||
id: string;
|
||||
@@ -148,101 +161,39 @@ export type ProfileRechargeOrderStatus =
|
||||
| 'refunded'
|
||||
| 'expired';
|
||||
|
||||
export type ProfileRechargeProduct = {
|
||||
productId: string;
|
||||
title: string;
|
||||
priceCents: number;
|
||||
/** 充点 / 会员商品;`kind` 是前端收窄的联合类型,其余字段以后端生成为准。 */
|
||||
export type ProfileRechargeProduct = Omit<
|
||||
ProfileRechargeProductResponse,
|
||||
'kind'
|
||||
> & {
|
||||
kind: ProfileRechargeProductKind;
|
||||
pointsAmount: number;
|
||||
bonusPoints: number;
|
||||
durationDays: number;
|
||||
badgeLabel: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
/** 会员档位目录行;价格与权益的唯一真相源是后端 `profile_membership_plan` 表。 */
|
||||
export type ProfileMembershipPlanRecord = {
|
||||
plan: ProfileMembershipPlan;
|
||||
title: string;
|
||||
rank: number;
|
||||
monthPriceCents: number;
|
||||
yearPriceCents: number;
|
||||
periodPoints: number;
|
||||
modelAccess: ProfileMembershipModelAccess;
|
||||
/** 并发上限;`128` 表示不设上限。 */
|
||||
concurrentJobLimit: number;
|
||||
/** 是否为运营推荐档位;由后端目录给出,前端不再硬编码档位 token。 */
|
||||
recommended: boolean;
|
||||
/** 是否不设并发上限;由后端按哨兵值派生,前端不再硬编码 `128`。 */
|
||||
unlimitedConcurrency: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
export type ProfileMembershipPlanRecord = ProfileMembershipPlanResponse;
|
||||
|
||||
export type ProfileMembership = {
|
||||
status: ProfileMembershipStatus;
|
||||
/** 当前档位;非会员与已到期都是 `normal`。 */
|
||||
plan: ProfileMembershipPlan;
|
||||
startedAt: string | null;
|
||||
expiresAt: string | null;
|
||||
updatedAt: string | null;
|
||||
cycleStartedAt: string | null;
|
||||
cycleResetsAt: string | null;
|
||||
cycleGrantedPoints: number;
|
||||
cycleRemainingPoints: number;
|
||||
/** 当前期序号(1-based)。 */
|
||||
cycleIndex: number;
|
||||
/** 本次购买总期数(月付 1 / 年付 12)。 */
|
||||
cycleCount: number;
|
||||
cycleKind: ProfileMembershipCycleKind;
|
||||
};
|
||||
export type ProfileMembership = ProfileMembershipResponse;
|
||||
|
||||
/** 补差升级报价;下单时后端会用同一套规则重算,前端展示的数字不是真相源。 */
|
||||
export type ProfileMembershipUpgradeQuote = {
|
||||
currentPlan: ProfileMembershipPlan;
|
||||
targetPlan: ProfileMembershipPlan;
|
||||
cycleKind: ProfileMembershipCycleKind;
|
||||
cycleIndex: number;
|
||||
cycleCount: number;
|
||||
/** 应付金额(分)。 */
|
||||
amountCents: number;
|
||||
/** 本期补点。 */
|
||||
grantedPointsDelta: number;
|
||||
/** 变更后月度余额 = 原剩余月度余额 + 补点。 */
|
||||
monthlyBalanceAfter: number;
|
||||
expiresAt: string | null;
|
||||
cycleResetsAt: string | null;
|
||||
remainingFullMonths: number;
|
||||
};
|
||||
export type ProfileMembershipUpgradeQuote =
|
||||
ProfileMembershipUpgradeQuoteResponse;
|
||||
|
||||
export type ProfileRechargeOrder = {
|
||||
orderId: string;
|
||||
productId: string;
|
||||
productTitle: string;
|
||||
/** 充值订单;`kind` / `status` 是前端收窄的联合类型,其余字段以后端生成为准。 */
|
||||
export type ProfileRechargeOrder = Omit<
|
||||
ProfileRechargeOrderResponse,
|
||||
'kind' | 'status'
|
||||
> & {
|
||||
kind: ProfileRechargeProductKind;
|
||||
amountCents: number;
|
||||
status: ProfileRechargeOrderStatus;
|
||||
paymentChannel: string;
|
||||
paidAt: string | null;
|
||||
providerTransactionId: string | null;
|
||||
createdAt: string;
|
||||
pointsDelta: number;
|
||||
membershipExpiresAt: string | null;
|
||||
expiredAt?: string | null;
|
||||
expirationCheckedAt?: string | null;
|
||||
expirationProviderState?: string | null;
|
||||
expirationLastError?: string | null;
|
||||
};
|
||||
|
||||
export type ProfileRechargeCenterResponse = {
|
||||
// TODO mirror backend: should not nullable
|
||||
mudPointBalance?: ProfileMudPointBalance;
|
||||
membership: ProfileMembership;
|
||||
/** 充值中心;结构以后端生成为准,仅把商品 / 订单收窄成带联合类型的本地别名。 */
|
||||
export type ProfileRechargeCenterResponse = Omit<
|
||||
GeneratedProfileRechargeCenterResponse,
|
||||
'pointProducts' | 'latestOrder'
|
||||
> & {
|
||||
pointProducts: ProfileRechargeProduct[];
|
||||
/** 会员档位目录(后台可改价);会员购买 / 升级的价格与权益以它为准。 */
|
||||
membershipPlans: ProfileMembershipPlanRecord[];
|
||||
latestOrder: ProfileRechargeOrder | null;
|
||||
hasPointsRecharged: boolean;
|
||||
dailyFreePoints?: ProfileDailyFreePoints;
|
||||
};
|
||||
|
||||
export type WechatMiniProgramPayParams = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user