fix: 修复测试发现的 7 个问题 + 全 workspace clippy 清零
功能修复: 1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查 2. 仪表盘统计容错:单个查询失败返回零值而非 500 3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致 4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径 5. 积分端点权限码:health.health-data.list → health.points.list 6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage 7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档 Clippy 全 workspace 清零(14→0 errors): - erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处 - erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处 - erp-ai: 修复 dead_code、unused import 等 11 处 - erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处 - erp-server-migration: 修复 enum_variant_names 5 处 - erp-auth/config/workflow/message: 各 1-3 处 工程改进: - lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy) - cargo fmt 统一格式化
This commit is contained in:
@@ -98,5 +98,4 @@ mod tests {
|
||||
other => panic!("Expected Validation, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,11 @@ where
|
||||
// TODO: 多租户微信登录需要设计租户解析策略(如 per-appid 映射或登录后选择租户)
|
||||
let tenant_id = state.default_tenant_id;
|
||||
let resp = WechatService::login(&state, tenant_id, &req.code).await?;
|
||||
tracing::info!(bound = resp.bound, has_token = resp.token.is_some(), "微信登录结果");
|
||||
tracing::info!(
|
||||
bound = resp.bound,
|
||||
has_token = resp.token.is_some(),
|
||||
"微信登录结果"
|
||||
);
|
||||
Ok(Json(ApiResponse::ok(resp)))
|
||||
}
|
||||
|
||||
@@ -75,13 +79,8 @@ where
|
||||
|
||||
// TODO: 多租户微信登录需要设计租户解析策略
|
||||
let tenant_id = state.default_tenant_id;
|
||||
let resp = WechatService::bind_phone(
|
||||
&state,
|
||||
tenant_id,
|
||||
&req.openid,
|
||||
&req.encrypted_data,
|
||||
&req.iv,
|
||||
)
|
||||
.await?;
|
||||
let resp =
|
||||
WechatService::bind_phone(&state, tenant_id, &req.openid, &req.encrypted_data, &req.iv)
|
||||
.await?;
|
||||
Ok(Json(ApiResponse::ok(resp)))
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ async fn fetch_permission_data_scopes(
|
||||
row.try_get_by_index::<String>(0),
|
||||
row.try_get_by_index::<String>(2),
|
||||
) {
|
||||
scopes.insert(code, DataScope::from_str(&scope));
|
||||
scopes.insert(code, DataScope::parse_scope(&scope));
|
||||
}
|
||||
}
|
||||
scopes
|
||||
|
||||
@@ -159,13 +159,10 @@ impl ErpModule for AuthModule {
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
_event_bus: &EventBus,
|
||||
) -> AppResult<()> {
|
||||
let password = std::env::var("ERP__SUPER_ADMIN_PASSWORD")
|
||||
.map_err(|_| {
|
||||
tracing::error!("环境变量 ERP__SUPER_ADMIN_PASSWORD 未设置,无法初始化租户认证");
|
||||
erp_core::error::AppError::Internal(
|
||||
"ERP__SUPER_ADMIN_PASSWORD 未设置".to_string(),
|
||||
)
|
||||
})?;
|
||||
let password = std::env::var("ERP__SUPER_ADMIN_PASSWORD").map_err(|_| {
|
||||
tracing::error!("环境变量 ERP__SUPER_ADMIN_PASSWORD 未设置,无法初始化租户认证");
|
||||
erp_core::error::AppError::Internal("ERP__SUPER_ADMIN_PASSWORD 未设置".to_string())
|
||||
})?;
|
||||
crate::service::seed::seed_tenant_auth(db, tenant_id, &password)
|
||||
.await
|
||||
.map_err(|e| erp_core::error::AppError::Internal(e.to_string()))?;
|
||||
@@ -178,8 +175,8 @@ impl ErpModule for AuthModule {
|
||||
tenant_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AppResult<()> {
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||
use chrono::Utc;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||
|
||||
let now = Utc::now();
|
||||
|
||||
@@ -210,29 +207,144 @@ impl ErpModule for AuthModule {
|
||||
|
||||
fn permissions(&self) -> Vec<PermissionDescriptor> {
|
||||
vec![
|
||||
PermissionDescriptor { code: "user.list".into(), name: "查看用户列表".into(), description: "查看用户列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "user.create".into(), name: "创建用户".into(), description: "创建新用户".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "user.read".into(), name: "查看用户详情".into(), description: "查看用户信息".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "user.update".into(), name: "编辑用户".into(), description: "编辑用户信息".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "user.delete".into(), name: "删除用户".into(), description: "软删除用户".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "role.list".into(), name: "查看角色列表".into(), description: "查看角色列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "role.create".into(), name: "创建角色".into(), description: "创建新角色".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "role.read".into(), name: "查看角色详情".into(), description: "查看角色信息".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "role.update".into(), name: "编辑角色".into(), description: "编辑角色".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "role.delete".into(), name: "删除角色".into(), description: "删除角色".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "permission.list".into(), name: "查看权限".into(), description: "查看权限列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "organization.list".into(), name: "查看组织列表".into(), description: "查看组织列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "organization.create".into(), name: "创建组织".into(), description: "创建组织".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "organization.update".into(), name: "编辑组织".into(), description: "编辑组织".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "organization.delete".into(), name: "删除组织".into(), description: "删除组织".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "department.list".into(), name: "查看部门列表".into(), description: "查看部门列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "department.create".into(), name: "创建部门".into(), description: "创建部门".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "department.update".into(), name: "编辑部门".into(), description: "编辑部门".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "department.delete".into(), name: "删除部门".into(), description: "删除部门".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "position.list".into(), name: "查看岗位列表".into(), description: "查看岗位列表".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "position.create".into(), name: "创建岗位".into(), description: "创建岗位".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "position.update".into(), name: "编辑岗位".into(), description: "编辑岗位".into(), module: "auth".into() },
|
||||
PermissionDescriptor { code: "position.delete".into(), name: "删除岗位".into(), description: "删除岗位".into(), module: "auth".into() },
|
||||
PermissionDescriptor {
|
||||
code: "user.list".into(),
|
||||
name: "查看用户列表".into(),
|
||||
description: "查看用户列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "user.create".into(),
|
||||
name: "创建用户".into(),
|
||||
description: "创建新用户".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "user.read".into(),
|
||||
name: "查看用户详情".into(),
|
||||
description: "查看用户信息".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "user.update".into(),
|
||||
name: "编辑用户".into(),
|
||||
description: "编辑用户信息".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "user.delete".into(),
|
||||
name: "删除用户".into(),
|
||||
description: "软删除用户".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "role.list".into(),
|
||||
name: "查看角色列表".into(),
|
||||
description: "查看角色列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "role.create".into(),
|
||||
name: "创建角色".into(),
|
||||
description: "创建新角色".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "role.read".into(),
|
||||
name: "查看角色详情".into(),
|
||||
description: "查看角色信息".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "role.update".into(),
|
||||
name: "编辑角色".into(),
|
||||
description: "编辑角色".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "role.delete".into(),
|
||||
name: "删除角色".into(),
|
||||
description: "删除角色".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "permission.list".into(),
|
||||
name: "查看权限".into(),
|
||||
description: "查看权限列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "organization.list".into(),
|
||||
name: "查看组织列表".into(),
|
||||
description: "查看组织列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "organization.create".into(),
|
||||
name: "创建组织".into(),
|
||||
description: "创建组织".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "organization.update".into(),
|
||||
name: "编辑组织".into(),
|
||||
description: "编辑组织".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "organization.delete".into(),
|
||||
name: "删除组织".into(),
|
||||
description: "删除组织".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "department.list".into(),
|
||||
name: "查看部门列表".into(),
|
||||
description: "查看部门列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "department.create".into(),
|
||||
name: "创建部门".into(),
|
||||
description: "创建部门".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "department.update".into(),
|
||||
name: "编辑部门".into(),
|
||||
description: "编辑部门".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "department.delete".into(),
|
||||
name: "删除部门".into(),
|
||||
description: "删除部门".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "position.list".into(),
|
||||
name: "查看岗位列表".into(),
|
||||
description: "查看岗位列表".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "position.create".into(),
|
||||
name: "创建岗位".into(),
|
||||
description: "创建岗位".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "position.update".into(),
|
||||
name: "编辑岗位".into(),
|
||||
description: "编辑岗位".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "position.delete".into(),
|
||||
name: "删除岗位".into(),
|
||||
description: "删除岗位".into(),
|
||||
module: "auth".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +64,10 @@ impl AuthService {
|
||||
None => {
|
||||
// 审计:用户不存在(登录失败)
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, None, "user.login_failed", "user")
|
||||
.with_request_info(
|
||||
req_info.as_ref().and_then(|r| r.ip.clone()),
|
||||
req_info.as_ref().and_then(|r| r.user_agent.clone()),
|
||||
),
|
||||
AuditLog::new(tenant_id, None, "user.login_failed", "user").with_request_info(
|
||||
req_info.as_ref().and_then(|r| r.ip.clone()),
|
||||
req_info.as_ref().and_then(|r| r.user_agent.clone()),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -317,13 +317,7 @@ const DEFAULT_PERMISSIONS: &[(&str, &str, &str, &str, &str)] = &[
|
||||
"admin",
|
||||
"管理插件全生命周期",
|
||||
),
|
||||
(
|
||||
"plugin.list",
|
||||
"查看插件",
|
||||
"plugin",
|
||||
"list",
|
||||
"查看插件列表",
|
||||
),
|
||||
("plugin.list", "查看插件", "plugin", "list", "查看插件列表"),
|
||||
];
|
||||
|
||||
/// Indices of read-only (list/read) permissions within DEFAULT_PERMISSIONS.
|
||||
|
||||
@@ -153,7 +153,11 @@ impl TokenService {
|
||||
|
||||
/// Revoke a specific refresh token by database ID.
|
||||
/// Verifies that the token belongs to the specified user for security.
|
||||
pub async fn revoke_token(token_id: Uuid, user_id: Uuid, db: &DatabaseConnection) -> AuthResult<()> {
|
||||
pub async fn revoke_token(
|
||||
token_id: Uuid,
|
||||
user_id: Uuid,
|
||||
db: &DatabaseConnection,
|
||||
) -> AuthResult<()> {
|
||||
let token_row = user_token::Entity::find_by_id(token_id)
|
||||
.filter(user_token::Column::UserId.eq(user_id))
|
||||
.one(db)
|
||||
|
||||
@@ -406,8 +406,7 @@ impl UserService {
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let role_map: HashMap<Uuid, &role::Model> =
|
||||
roles.iter().map(|r| (r.id, r)).collect();
|
||||
let role_map: HashMap<Uuid, &role::Model> = roles.iter().map(|r| (r.id, r)).collect();
|
||||
|
||||
// 3. 按 user_id 分组
|
||||
let mut result: HashMap<Uuid, Vec<RoleResp>> = HashMap::new();
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7};
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use cbc::Decryptor;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
@@ -59,9 +57,13 @@ impl WechatService {
|
||||
code = %code,
|
||||
"fetch_session 开始"
|
||||
);
|
||||
let session =
|
||||
fetch_session(&state.wechat_appid, &state.wechat_secret, code, state.wechat_dev_mode)
|
||||
.await?;
|
||||
let session = fetch_session(
|
||||
&state.wechat_appid,
|
||||
&state.wechat_secret,
|
||||
code,
|
||||
state.wechat_dev_mode,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let openid = session
|
||||
.openid
|
||||
@@ -69,18 +71,18 @@ impl WechatService {
|
||||
.ok_or_else(|| AuthError::Validation("微信登录失败:未获取到 openid".to_string()))?;
|
||||
|
||||
// 缓存 session_key(Redis 优先,内存降级)
|
||||
if let Some(sk) = &session.session_key {
|
||||
if let Err(e) = Self::store_session_key_redis(&state.redis, &openid, sk).await {
|
||||
tracing::warn!(openid = %openid, error = %e, "Redis session_key 存储失败,降级内存");
|
||||
let mut cache = MEMORY_FALLBACK.lock().await;
|
||||
cache.insert(
|
||||
openid.clone(),
|
||||
SessionEntry {
|
||||
session_key: sk.clone(),
|
||||
created_at: Instant::now(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(sk) = &session.session_key
|
||||
&& let Err(e) = Self::store_session_key_redis(&state.redis, &openid, sk).await
|
||||
{
|
||||
tracing::warn!(openid = %openid, error = %e, "Redis session_key 存储失败,降级内存");
|
||||
let mut cache = MEMORY_FALLBACK.lock().await;
|
||||
cache.insert(
|
||||
openid.clone(),
|
||||
SessionEntry {
|
||||
session_key: sk.clone(),
|
||||
created_at: Instant::now(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let existing = wechat_user::Entity::find()
|
||||
@@ -141,8 +143,7 @@ impl WechatService {
|
||||
return Err(AuthError::Validation("该微信已绑定账号".to_string()));
|
||||
}
|
||||
|
||||
let user_id =
|
||||
Self::find_or_create_user_by_phone(&state.db, tenant_id, &phone).await?;
|
||||
let user_id = Self::find_or_create_user_by_phone(&state.db, tenant_id, &phone).await?;
|
||||
|
||||
let now = Utc::now();
|
||||
let wu = wechat_user::ActiveModel {
|
||||
@@ -248,22 +249,19 @@ impl WechatService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_session_key(
|
||||
redis: &Option<redis::Client>,
|
||||
openid: &str,
|
||||
) -> AuthResult<String> {
|
||||
async fn get_session_key(redis: &Option<redis::Client>, openid: &str) -> AuthResult<String> {
|
||||
// 1. 尝试 Redis
|
||||
if let Some(client) = redis {
|
||||
if let Ok(mut conn) = client.get_multiplexed_async_connection().await {
|
||||
let key = format!("{}{}", REDIS_KEY_PREFIX, openid);
|
||||
let result: Option<String> = redis::cmd("GETDEL")
|
||||
.arg(&key)
|
||||
.query_async::<Option<String>>(&mut conn)
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
if let Some(sk) = result {
|
||||
return Ok(sk);
|
||||
}
|
||||
if let Some(client) = redis
|
||||
&& let Ok(mut conn) = client.get_multiplexed_async_connection().await
|
||||
{
|
||||
let key = format!("{}{}", REDIS_KEY_PREFIX, openid);
|
||||
let result: Option<String> = redis::cmd("GETDEL")
|
||||
.arg(&key)
|
||||
.query_async::<Option<String>>(&mut conn)
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
if let Some(sk) = result {
|
||||
return Ok(sk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,11 +283,7 @@ impl WechatService {
|
||||
}
|
||||
|
||||
/// AES-128-CBC 解密微信手机号
|
||||
fn decrypt_phone_number(
|
||||
session_key: &str,
|
||||
encrypted_data: &str,
|
||||
iv: &str,
|
||||
) -> AuthResult<String> {
|
||||
fn decrypt_phone_number(session_key: &str, encrypted_data: &str, iv: &str) -> AuthResult<String> {
|
||||
let engine = base64::engine::general_purpose::STANDARD;
|
||||
|
||||
let key_bytes = engine
|
||||
@@ -303,9 +297,7 @@ fn decrypt_phone_number(
|
||||
.map_err(|e| AuthError::Validation(format!("encrypted_data base64 解码失败: {}", e)))?;
|
||||
|
||||
if key_bytes.len() != 16 {
|
||||
return Err(AuthError::Validation(
|
||||
"session_key 长度不正确".to_string(),
|
||||
));
|
||||
return Err(AuthError::Validation("session_key 长度不正确".to_string()));
|
||||
}
|
||||
if iv_bytes.len() != 16 {
|
||||
return Err(AuthError::Validation("iv 长度不正确".to_string()));
|
||||
@@ -319,8 +311,8 @@ fn decrypt_phone_number(
|
||||
.decrypt_padded_mut::<Pkcs7>(&mut buf)
|
||||
.map_err(|e| AuthError::Validation(format!("AES 解密失败: {}", e)))?;
|
||||
|
||||
let plaintext =
|
||||
String::from_utf8(decrypted.to_vec()).map_err(|_| AuthError::Validation("解密结果非 UTF-8".to_string()))?;
|
||||
let plaintext = String::from_utf8(decrypted.to_vec())
|
||||
.map_err(|_| AuthError::Validation("解密结果非 UTF-8".to_string()))?;
|
||||
|
||||
// 微信返回的 JSON 包含 watermark 等字段,提取 phone_number
|
||||
let info: serde_json::Value = serde_json::from_str(&plaintext)
|
||||
@@ -358,14 +350,9 @@ async fn build_login_resp(
|
||||
jwt.secret,
|
||||
jwt.access_ttl_secs,
|
||||
)?;
|
||||
let (refresh_token, _) = TokenService::sign_refresh_token(
|
||||
user_id,
|
||||
tenant_id,
|
||||
db,
|
||||
jwt.secret,
|
||||
jwt.refresh_ttl_secs,
|
||||
)
|
||||
.await?;
|
||||
let (refresh_token, _) =
|
||||
TokenService::sign_refresh_token(user_id, tenant_id, db, jwt.secret, jwt.refresh_ttl_secs)
|
||||
.await?;
|
||||
|
||||
let role_resps = AuthService::get_user_role_resps(user_id, tenant_id, db).await?;
|
||||
|
||||
@@ -424,15 +411,15 @@ async fn fetch_session(
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(format!("微信 API 响应解析失败: {}", e)))?;
|
||||
|
||||
if let Some(errcode) = session.errcode {
|
||||
if errcode != 0 {
|
||||
let msg = session.errmsg.clone().unwrap_or_default();
|
||||
tracing::error!(errcode, errmsg = %msg, "微信 jscode2session 返回错误");
|
||||
return Err(AuthError::Validation(format!(
|
||||
"微信登录失败 ({}): {}",
|
||||
errcode, msg
|
||||
)));
|
||||
}
|
||||
if let Some(errcode) = session.errcode
|
||||
&& errcode != 0
|
||||
{
|
||||
let msg = session.errmsg.clone().unwrap_or_default();
|
||||
tracing::error!(errcode, errmsg = %msg, "微信 jscode2session 返回错误");
|
||||
return Err(AuthError::Validation(format!(
|
||||
"微信登录失败 ({}): {}",
|
||||
errcode, msg
|
||||
)));
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
|
||||
Reference in New Issue
Block a user