Merge branch 'codex/wechat-nickname'

This commit is contained in:
2026-06-06 23:59:30 +08:00
15 changed files with 431 additions and 58 deletions

View File

@@ -2584,7 +2584,7 @@ mod tests {
}
#[tokio::test]
async fn wechat_miniprogram_login_returns_system_token_and_marks_session_source() {
async fn wechat_miniprogram_login_returns_system_token_and_marks_session_label() {
let config = AppConfig {
wechat_auth_enabled: true,
..AppConfig::default()
@@ -2606,7 +2606,8 @@ mod tests {
.header("x-mini-program-env", "develop")
.body(Body::from(
serde_json::json!({
"code": "wx-mini-code-001"
"code": "wx-mini-code-001",
"displayName": "微信旅人"
})
.to_string(),
))
@@ -2643,6 +2644,14 @@ mod tests {
login_payload["user"]["loginMethod"],
Value::String("wechat".to_string())
);
assert_eq!(
login_payload["user"]["wechatDisplayName"],
Value::String("微信旅人".to_string())
);
assert_eq!(
login_payload["user"]["wechatAccount"],
Value::String("wx-mini-code-001".to_string())
);
assert!(refresh_cookie.contains("genarrative_refresh_session="));
let sessions_response = app
@@ -2667,16 +2676,23 @@ mod tests {
let sessions_payload: Value =
serde_json::from_slice(&sessions_body).expect("sessions payload should be json");
assert_eq!(
sessions_payload["sessions"][0]["clientType"],
Value::String("mini_program".to_string())
sessions_payload["sessions"][0]["clientLabel"],
Value::String("微信小程序 / iPhone".to_string())
);
assert_eq!(
sessions_payload["sessions"][0]["clientRuntime"],
Value::String("wechat_mini_program".to_string())
sessions_payload["sessions"][0]["sessionCount"],
Value::Number(1.into())
);
assert_eq!(
sessions_payload["sessions"][0]["miniProgramAppId"],
Value::String("wx-mini-test".to_string())
sessions_payload["sessions"][0]["isCurrent"],
Value::Bool(true)
);
assert_eq!(
sessions_payload["sessions"][0]["sessionIds"]
.as_array()
.expect("session ids should exist")
.len(),
1
);
}
@@ -2703,7 +2719,8 @@ mod tests {
.header("x-mini-program-env", "develop")
.body(Body::from(
serde_json::json!({
"code": "wx-mini-code-bind-001"
"code": "wx-mini-code-bind-001",
"displayName": "微信旅人"
})
.to_string(),
))
@@ -2745,7 +2762,8 @@ mod tests {
.header("x-mini-program-env", "develop")
.body(Body::from(
serde_json::json!({
"wechatPhoneCode": "13800138000"
"wechatPhoneCode": "13800138000",
"displayName": "微信旅人"
})
.to_string(),
))
@@ -2996,7 +3014,7 @@ mod tests {
}
#[tokio::test]
async fn auth_sessions_returns_multi_device_session_fields() {
async fn auth_sessions_returns_multi_device_session_summaries() {
let state = AppState::new(AppConfig::default()).expect("state should build");
seed_phone_user_with_password(&state, "13800138013", TEST_PASSWORD).await;
let app = build_router(state);
@@ -3096,23 +3114,19 @@ mod tests {
assert_eq!(sessions.len(), 2);
assert!(sessions.iter().any(|session| {
session["clientType"] == Value::String("web_browser".to_string())
&& session["clientRuntime"] == Value::String("chrome".to_string())
&& session["clientPlatform"] == Value::String("windows".to_string())
session["clientLabel"] == Value::String("Windows / Chrome".to_string())
&& session["sessionCount"] == Value::Number(1.into())
&& session["sessionIds"]
.as_array()
.is_some_and(|ids| ids.len() == 1)
&& session["deviceDisplayName"] == Value::String("Windows / Chrome".to_string())
&& session["isCurrent"] == Value::Bool(true)
}));
assert!(sessions.iter().any(|session| {
session["clientType"] == Value::String("mini_program".to_string())
&& session["clientRuntime"] == Value::String("wechat_mini_program".to_string())
session["clientLabel"] == Value::String("微信小程序 / Android".to_string())
&& session["sessionCount"] == Value::Number(1.into())
&& session["miniProgramAppId"] == Value::String("wx-session-test".to_string())
&& session["miniProgramEnv"] == Value::String("release".to_string())
&& session["deviceDisplayName"] == Value::String("微信小程序 / Android".to_string())
&& session["sessionIds"]
.as_array()
.is_some_and(|ids| ids.len() == 1)
&& session["isCurrent"] == Value::Bool(false)
}));
}

View File

@@ -14,6 +14,7 @@ use shared_contracts::auth::{
WechatMiniProgramLoginRequest, WechatMiniProgramLoginResponse, WechatStartQuery,
WechatStartResponse,
};
use shared_kernel::normalize_optional_string;
use time::OffsetDateTime;
use url::Url;
@@ -208,6 +209,7 @@ pub async fn bind_wechat_phone(
.bind_wechat_verified_phone(BindWechatVerifiedPhoneInput {
user_id: authenticated.claims().user_id().to_string(),
phone_number: phone_profile.phone_number,
wechat_display_name: payload.display_name.clone(),
})
.await
.map_err(map_wechat_bind_phone_error)?
@@ -235,6 +237,7 @@ pub async fn bind_wechat_phone(
user_id: authenticated.claims().user_id().to_string(),
phone_number: phone.to_string(),
verify_code: code.to_string(),
wechat_display_name: payload.display_name.clone(),
},
OffsetDateTime::now_utc(),
)
@@ -313,7 +316,7 @@ pub async fn login_wechat_mini_program(
let result = state
.wechat_auth_service()
.resolve_login(module_auth::ResolveWechatLoginInput {
profile: map_wechat_profile_to_domain(profile),
profile: map_wechat_profile_to_domain_with_display_name(profile, payload.display_name),
})
.await
.map_err(map_wechat_auth_error)?;
@@ -389,6 +392,17 @@ fn map_wechat_profile_to_domain(
}
}
fn map_wechat_profile_to_domain_with_display_name(
profile: platform_auth::WechatIdentityProfile,
display_name: Option<String>,
) -> module_auth::WechatIdentityProfile {
let mut profile = map_wechat_profile_to_domain(profile);
if let Some(display_name) = normalize_optional_string(display_name) {
profile.display_name = Some(display_name);
}
profile
}
fn normalize_redirect_path(raw_value: Option<&str>, fallback: &str) -> String {
let Some(raw_value) = raw_value.map(str::trim).filter(|value| !value.is_empty()) else {
return fallback.to_string();