后台契约与接口透出会员变更快照与用户会员状态

- shared-contracts 新增 AdminMembershipOrderChangePayload 与 AdminProfileMembershipPayload
- AdminProfileWalletPayload.membership_limited_points 改名 monthly_points
- AdminRechargeOrderEntryPayload 增加 membership_change 快照字段
- AdminUserDetailResponse 增加 membership 字段
- admin_recharge 映射会员订单快照与用户会员中心投影

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-03 11:48:24 +08:00
parent ff1b18c784
commit 7acae0d308
2 changed files with 104 additions and 11 deletions
@@ -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,
+47 -1
View File
@@ -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<String>,
pub plan_after: Option<String>,
pub cycle_kind_before: Option<String>,
pub cycle_kind_after: Option<String>,
pub period_points_before: Option<u64>,
pub period_points_after: Option<u64>,
pub granted_points_delta: i64,
pub cycle_index: Option<u32>,
pub expires_at_before_micros: Option<i64>,
pub expires_at_after_micros: Option<i64>,
pub cycle_resets_at_before_micros: Option<i64>,
pub cycle_resets_at_after_micros: Option<i64>,
/// 订单价格拆分 JSON(目标档位价、当前档位价、实付金额);字符串原样透出供人工核对。
pub price_breakdown_json: Option<String>,
}
#[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<String>,
/// 会员购买 / 升级快照;非会员订单为 `None`。
pub membership_change: Option<AdminMembershipOrderChangePayload>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@@ -1294,9 +1322,27 @@ pub struct AdminUserDetailResponse {
pub cumulative_recharged_cents: Option<u64>,
pub can_reconcile_consumption: bool,
pub wallet: AdminProfileWalletPayload,
pub membership: AdminProfileMembershipPayload,
pub recharge_orders: Vec<AdminRechargeOrderEntryPayload>,
}
/// 单用户当前会员状态与账期(只读),供后台用户详情判断档位、周期与到期可见性。
#[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<i64>,
pub expires_at_micros: Option<i64>,
pub cycle_resets_at_micros: Option<i64>,
pub updated_at_micros: Option<i64>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AdminUserConsumptionReconcileRequest {