- 智能合并:微信注册时用手机号盲索引匹配已有患者档案,避免重复建 档(AuthState 添加 PiiCrypto + ensure_patient_record 增加盲索引查询) - 角色冻结:小程序仅允许患者角色登录,医护角色被拦截 (auth_service.rs 添加反向拦截 + 登录页移除 credential login 表单) - 页面冻结:10 个非核心页面替换为 FrozenPage 占位组件(用药/知情同意 /透析/家属/诊断/事件),移除 profile 导航入口,移除医生端预加载 - 医生端代码保留,仅隐藏入口,后续可零成本恢复 讨论记录:docs/discussions/2026-05-23-account-registration-login-flow.md
84 lines
2.3 KiB
Rust
84 lines
2.3 KiB
Rust
use erp_core::crypto::PiiCrypto;
|
|
use erp_core::events::EventBus;
|
|
use sea_orm::DatabaseConnection;
|
|
use uuid::Uuid;
|
|
|
|
/// Auth-specific state extracted from the server's AppState via `FromRef`.
|
|
///
|
|
/// This avoids a circular dependency between erp-auth and erp-server.
|
|
/// The server crate implements `FromRef<AppState> for AuthState` so that
|
|
/// Axum handlers in erp-auth can extract `State<AuthState>` directly.
|
|
///
|
|
/// Contains everything the auth handlers need:
|
|
/// - Database connection for user/credential lookups
|
|
/// - EventBus for publishing domain events
|
|
/// - JWT configuration for token signing and validation
|
|
/// - Default tenant ID for the bootstrap phase
|
|
#[derive(Clone)]
|
|
pub struct AuthState {
|
|
pub db: DatabaseConnection,
|
|
pub event_bus: EventBus,
|
|
pub jwt_secret: String,
|
|
pub access_ttl_secs: i64,
|
|
pub refresh_ttl_secs: i64,
|
|
pub default_tenant_id: Uuid,
|
|
pub wechat_appid: String,
|
|
pub wechat_secret: String,
|
|
pub wechat_dev_mode: bool,
|
|
pub redis: Option<redis::Client>,
|
|
pub crypto: PiiCrypto,
|
|
}
|
|
|
|
/// Parse a human-readable TTL string (e.g. "15m", "7d", "1h", "900s") into seconds.
|
|
///
|
|
/// Falls back to parsing the raw string as seconds if no unit suffix is recognized.
|
|
pub fn parse_ttl(ttl: &str) -> i64 {
|
|
let ttl = ttl.trim();
|
|
if let Some(num) = ttl.strip_suffix('s') {
|
|
num.parse::<i64>().unwrap_or(900)
|
|
} else if let Some(num) = ttl.strip_suffix('m') {
|
|
num.parse::<i64>().map(|n| n * 60).unwrap_or(900)
|
|
} else if let Some(num) = ttl.strip_suffix('h') {
|
|
num.parse::<i64>().map(|n| n * 3600).unwrap_or(900)
|
|
} else if let Some(num) = ttl.strip_suffix('d') {
|
|
num.parse::<i64>().map(|n| n * 86400).unwrap_or(900)
|
|
} else {
|
|
ttl.parse::<i64>().unwrap_or(900)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn parse_ttl_seconds() {
|
|
assert_eq!(parse_ttl("900s"), 900);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_ttl_minutes() {
|
|
assert_eq!(parse_ttl("15m"), 900);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_ttl_hours() {
|
|
assert_eq!(parse_ttl("1h"), 3600);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_ttl_days() {
|
|
assert_eq!(parse_ttl("7d"), 604800);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_ttl_raw_number() {
|
|
assert_eq!(parse_ttl("300"), 300);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_ttl_fallback_on_invalid() {
|
|
assert_eq!(parse_ttl("invalid"), 900);
|
|
}
|
|
}
|