Merge branch 'hermes/wechat'

# Conflicts:
#	.hermes/shared-memory/decision-log.md
#	docs/technical/MY_TAB_ACCOUNT_RECHARGE_IMPLEMENTATION_2026-04-25.md
#	docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md
#	server-rs/crates/module-runtime/src/errors.rs
#	src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx
#	src/components/rpg-entry/RpgEntryHomeView.tsx
This commit is contained in:
2026-05-15 11:32:51 +08:00
23 changed files with 2325 additions and 107 deletions

View File

@@ -260,7 +260,7 @@ pub fn build_runtime_profile_recharge_order_create_input(
let product_id =
normalize_required_string(product_id).ok_or(RuntimeProfileFieldError::MissingProductId)?;
let payment_channel = normalize_required_string(payment_channel)
.unwrap_or_else(|| PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK.to_string());
.ok_or(RuntimeProfileFieldError::MissingPaymentChannel)?;
Ok(RuntimeProfileRechargeOrderCreateInput {
user_id,

View File

@@ -34,6 +34,8 @@ pub const SAVE_SNAPSHOT_VERSION: u32 = 2;
pub const DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT: &str = "继续推进上一次保存的故事。";
pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK: &str = "mock";
pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM: &str = "wechat_mp";
pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_H5: &str = "wechat_h5";
pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_NATIVE: &str = "wechat_native";
pub const PROFILE_FEEDBACK_DESCRIPTION_MIN_CHARS: usize = 10;
pub const PROFILE_FEEDBACK_DESCRIPTION_MAX_CHARS: usize = 200;
pub const PROFILE_FEEDBACK_CONTACT_PHONE_MAX_CHARS: usize = 40;

View File

@@ -80,6 +80,7 @@ pub enum RuntimeProfileFieldError {
InvalidRechargeProductDuration,
InvalidRechargeProductKind,
InvalidRechargeProductTier,
MissingPaymentChannel,
MissingWorldKey,
MissingBottomTab,
MissingCheckpointSessionId,
@@ -150,6 +151,7 @@ impl std::fmt::Display for RuntimeProfileFieldError {
}
Self::InvalidRechargeProductKind => f.write_str("充值商品类型无效"),
Self::InvalidRechargeProductTier => f.write_str("会员商品 tier 无效"),
Self::MissingPaymentChannel => f.write_str("recharge.payment_channel 不能为空"),
Self::MissingWorldKey => f.write_str("profile.world_key 不能为空"),
Self::MissingBottomTab => f.write_str("runtime_snapshot.bottom_tab 不能为空"),
Self::MissingCheckpointSessionId => f.write_str("checkpoint.session_id 不能为空"),

View File

@@ -745,6 +745,19 @@ mod tests {
assert_eq!(input.payment_channel, "mock");
}
#[test]
fn build_recharge_order_input_rejects_missing_payment_channel() {
let error = build_runtime_profile_recharge_order_create_input(
"user-1".to_string(),
"points_60".to_string(),
" ".to_string(),
1,
)
.expect_err("missing payment channel should fail");
assert_eq!(error, RuntimeProfileFieldError::MissingPaymentChannel);
}
#[test]
fn runtime_profile_identity_helpers_keep_existing_key_shape() {
assert_eq!(