From 7acae0d30836a6d37b4d27980406f87dbb0575d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 3 Oct 2026 11:48:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=A5=91=E7=BA=A6=E4=B8=8E?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=80=8F=E5=87=BA=E4=BC=9A=E5=91=98=E5=8F=98?= =?UTF-8?q?=E6=9B=B4=E5=BF=AB=E7=85=A7=E4=B8=8E=E7=94=A8=E6=88=B7=E4=BC=9A?= =?UTF-8?q?=E5=91=98=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shared-contracts 新增 AdminMembershipOrderChangePayload 与 AdminProfileMembershipPayload - AdminProfileWalletPayload.membership_limited_points 改名 monthly_points - AdminRechargeOrderEntryPayload 增加 membership_change 快照字段 - AdminUserDetailResponse 增加 membership 字段 - admin_recharge 映射会员订单快照与用户会员中心投影 Co-authored-by: Junie --- .../crates/api-server/src/admin_recharge.rs | 67 ++++++++++++++++--- .../crates/shared-contracts/src/admin.rs | 48 ++++++++++++- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/server-rs/crates/api-server/src/admin_recharge.rs b/server-rs/crates/api-server/src/admin_recharge.rs index 950e53d50..88b30e8f2 100644 --- a/server-rs/crates/api-server/src/admin_recharge.rs +++ b/server-rs/crates/api-server/src/admin_recharge.rs @@ -11,6 +11,7 @@ use module_runtime::{ PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_H5, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_JSAPI, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_NATIVE, RuntimeProfileAdminWalletSnapshot, + RuntimeProfileMembershipOrderChangeSnapshot, RuntimeProfileMembershipRecord, RuntimeProfileRechargeOrderAdminEntrySnapshot, RuntimeProfileRechargeOrderStatus, RuntimeProfileRechargeProductKind, RuntimeProfileRechargeRefundHoldSnapshot, RuntimeProfileRechargeRefundHoldStatus, RuntimeProfileRechargeRefundObservationSource, @@ -31,15 +32,16 @@ use platform_wechat::pay::{ use serde_json::{Value, json}; use sha2::{Digest, Sha256}; use shared_contracts::admin::{ - ADMIN_ACTION_PROFILE_WALLET_CONSUMPTION_RECONCILE, AdminProfileWalletPayload, - AdminRechargeOrderEntryPayload, AdminRechargeOrderListQuery, AdminRechargeOrderListResponse, - AdminRechargeRefundActionResponse, AdminRechargeRefundExecuteRequest, - AdminRechargeRefundHoldPayload, AdminRechargeRefundManualReviewResolveRequest, - AdminRechargeRefundPayload, AdminRechargeRefundPreviewRequest, - AdminRechargeRefundPreviewResponse, AdminRechargeRefundRegisterRequest, - AdminUserConsumptionProjectionInitializeResponse, AdminUserConsumptionReconcileRequest, - AdminUserConsumptionReconcileResponse, AdminUserDetailQuery, AdminUserDetailResponse, - AdminUserSummaryPayload, AdminWalletManualRestrictionPayload, AdminWalletRestrictionRequest, + ADMIN_ACTION_PROFILE_WALLET_CONSUMPTION_RECONCILE, AdminMembershipOrderChangePayload, + AdminProfileMembershipPayload, AdminProfileWalletPayload, AdminRechargeOrderEntryPayload, + AdminRechargeOrderListQuery, AdminRechargeOrderListResponse, AdminRechargeRefundActionResponse, + AdminRechargeRefundExecuteRequest, AdminRechargeRefundHoldPayload, + AdminRechargeRefundManualReviewResolveRequest, AdminRechargeRefundPayload, + AdminRechargeRefundPreviewRequest, AdminRechargeRefundPreviewResponse, + AdminRechargeRefundRegisterRequest, AdminUserConsumptionProjectionInitializeResponse, + AdminUserConsumptionReconcileRequest, AdminUserConsumptionReconcileResponse, + AdminUserDetailQuery, AdminUserDetailResponse, AdminUserSummaryPayload, + AdminWalletManualRestrictionPayload, AdminWalletRestrictionRequest, AdminWalletRestrictionResponse, AdminWechatPaymentCheckPayload, }; use spacetime_client::SpacetimeClientError; @@ -158,6 +160,12 @@ pub async fn admin_get_user_detail( .map_err(|error| spacetime_error_response(&request_context, error))?; let cumulative_recharged_cents = fetch_admin_user_cumulative_recharged_cents(&state, &user.id).await; + let membership_record = state + .spacetime_client() + .get_profile_recharge_center(user.id.clone()) + .await + .map_err(|error| spacetime_error_response(&request_context, error))? + .membership; Ok(json_success_body( Some(&request_context), @@ -175,6 +183,7 @@ pub async fn admin_get_user_detail( cumulative_recharged_cents, can_reconcile_consumption: admin.can(ADMIN_ACTION_PROFILE_WALLET_CONSUMPTION_RECONCILE), wallet: map_wallet(wallet_detail.wallet, Some(&admin_display_names)), + membership: map_membership(membership_record), recharge_orders: orders .into_iter() .map(|entry| map_order_entry(entry, Some(summary.clone()))) @@ -979,6 +988,44 @@ fn map_order_entry( remaining_refundable_cents, refund_eligible: block_reason.is_none(), refund_block_reason_code: block_reason, + membership_change: entry.membership_change.map(map_membership_order_change), + } +} + +fn map_membership_order_change( + change: RuntimeProfileMembershipOrderChangeSnapshot, +) -> AdminMembershipOrderChangePayload { + AdminMembershipOrderChangePayload { + change_kind: change.change_kind, + plan_before: change.plan_before, + plan_after: change.plan_after, + cycle_kind_before: change.cycle_kind_before, + cycle_kind_after: change.cycle_kind_after, + period_points_before: change.period_points_before, + period_points_after: change.period_points_after, + granted_points_delta: change.granted_points_delta, + cycle_index: change.cycle_index, + expires_at_before_micros: change.expires_at_before_micros, + expires_at_after_micros: change.expires_at_after_micros, + cycle_resets_at_before_micros: change.cycle_resets_at_before_micros, + cycle_resets_at_after_micros: change.cycle_resets_at_after_micros, + price_breakdown_json: change.price_breakdown_json, + } +} + +fn map_membership(record: RuntimeProfileMembershipRecord) -> AdminProfileMembershipPayload { + AdminProfileMembershipPayload { + plan: record.plan.as_str().to_string(), + status: record.status.as_str().to_string(), + cycle_kind: record.cycle_kind.as_str().to_string(), + cycle_index: record.cycle_index, + cycle_count: record.cycle_count, + cycle_granted_points: record.cycle_granted_points, + cycle_remaining_points: record.cycle_remaining_points, + started_at_micros: record.started_at_micros, + expires_at_micros: record.expires_at_micros, + cycle_resets_at_micros: record.cycle_resets_at_micros, + updated_at_micros: record.updated_at_micros, } } @@ -991,7 +1038,7 @@ fn map_wallet( total_balance: wallet.total_balance, spendable_balance: wallet.spendable_balance, daily_free_points: wallet.daily_free_points, - membership_limited_points: wallet.membership_limited_points, + monthly_points: wallet.membership_limited_points, permanent_points: wallet.permanent_points, held_points: wallet.held_points, refund_debt_points: wallet.refund_debt_points, diff --git a/server-rs/crates/shared-contracts/src/admin.rs b/server-rs/crates/shared-contracts/src/admin.rs index 0309a831d..9ddc50bff 100644 --- a/server-rs/crates/shared-contracts/src/admin.rs +++ b/server-rs/crates/shared-contracts/src/admin.rs @@ -1185,7 +1185,8 @@ pub struct AdminProfileWalletPayload { pub total_balance: u64, pub spendable_balance: u64, pub daily_free_points: u64, - pub membership_limited_points: u64, + /// 月度泥点:当前会员账期内发放、随账期重置而清零的泥点;无会员时为 0。 + pub monthly_points: u64, pub permanent_points: u64, pub held_points: u64, pub refund_debt_points: u64, @@ -1233,6 +1234,31 @@ pub struct AdminRechargeRefundHoldPayload { pub updated_at_micros: i64, } +/// 会员购买 / 升级的权益变更快照(只读)。 +/// +/// 与 `profile_recharge_order` 的末位快照列一一对应,供订单列表与退款人工复核还原变更 +/// 前后的档位、账期、补点与有效期。非会员订单为 `None`。 +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct AdminMembershipOrderChangePayload { + /// `purchase` 或 `upgrade`。 + pub change_kind: String, + pub plan_before: Option, + pub plan_after: Option, + pub cycle_kind_before: Option, + pub cycle_kind_after: Option, + pub period_points_before: Option, + pub period_points_after: Option, + pub granted_points_delta: i64, + pub cycle_index: Option, + pub expires_at_before_micros: Option, + pub expires_at_after_micros: Option, + pub cycle_resets_at_before_micros: Option, + pub cycle_resets_at_after_micros: Option, + /// 订单价格拆分 JSON(目标档位价、当前档位价、实付金额);字符串原样透出供人工核对。 + pub price_breakdown_json: Option, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AdminRechargeOrderEntryPayload { @@ -1262,6 +1288,8 @@ pub struct AdminRechargeOrderEntryPayload { pub remaining_refundable_cents: u64, pub refund_eligible: bool, pub refund_block_reason_code: Option, + /// 会员购买 / 升级快照;非会员订单为 `None`。 + pub membership_change: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1294,9 +1322,27 @@ pub struct AdminUserDetailResponse { pub cumulative_recharged_cents: Option, pub can_reconcile_consumption: bool, pub wallet: AdminProfileWalletPayload, + pub membership: AdminProfileMembershipPayload, pub recharge_orders: Vec, } +/// 单用户当前会员状态与账期(只读),供后台用户详情判断档位、周期与到期可见性。 +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct AdminProfileMembershipPayload { + pub plan: String, + pub status: String, + pub cycle_kind: String, + pub cycle_index: u32, + pub cycle_count: u32, + pub cycle_granted_points: u64, + pub cycle_remaining_points: u64, + pub started_at_micros: Option, + pub expires_at_micros: Option, + pub cycle_resets_at_micros: Option, + pub updated_at_micros: Option, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AdminUserConsumptionReconcileRequest {