Files
medical-mall/utils/akUserMapping.uts
2026-06-12 13:03:13 +08:00

396 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import supa from '@/components/supadb/aksupainstance.uts'
// ─────────────────────────────────────────────────────────────
// akUserMapping - Supabase Auth ID 到业务用户 ID 的映射管理
//
// 核心概念:
// - authUserId: Supabase Auth 用户 IDsession.user.id
// - akUserId: 业务用户 IDak_users.id
// - 所有业务表ec_care_tasks.assigned_to、hc_dispatch_assignments.worker_id 等)
// 都使用 akUserId不是 authUserId
// ─────────────────────────────────────────────────────────────
const AUTH_USER_ID_KEY = 'auth_user_id' // Supabase Auth ID
const AK_USER_ID_KEY = 'ak_user_id' // 业务用户 ID
const AK_USER_PROFILE_KEY = 'current_ak_user' // 完整业务用户信息
export type AkUserProfile = {
id: string // ak_users.id业务用户 ID
auth_id: string // ak_users.auth_idSupabase Auth ID
username: string
email: string
role: string
user_type: string
status: string
}
/**
* 加载当前登录用户的业务账号信息
* 查询顺序:
* 1. auth_id = session.user.id
* 2. id = uni.getStorageSync('user_id')
* 3. email = 当前登录邮箱
* 4. 最后才失败
*/
export async function loadCurrentAkUser(): Promise<AkUserProfile> {
// 1. 获取当前 Supabase Auth 用户
const session = supa.getSession()
if (session == null || session.user == null) {
throw new Error('未登录或登录状态失效')
}
const authUserId = session.user.getString('id') ?? ''
if (authUserId == '') {
throw new Error('未获取到 Supabase Auth 用户 ID')
}
// 2. 查询 ak_users 表,通过 auth_id 匹配
console.log('[akUserMapping] 尝试通过 auth_id 查询:', authUserId)
let { data: profiles, error } = await supa
.from('ak_users')
.select('id, auth_id, username, email, role, user_type, status')
.eq('auth_id', authUserId)
.single()
if (error == null && profiles != null) {
console.log('[akUserMapping] 通过 auth_id 查询成功')
} else {
console.warn('[akUserMapping] auth_id 查询失败:', error?.message ?? '未知错误')
// 3. 尝试通过 user_id 缓存查询
const cachedUserId = uni.getStorageSync('user_id')
if (cachedUserId != null && String(cachedUserId).length > 0) {
console.log('[akUserMapping] 尝试通过 user_id 缓存查询:', cachedUserId)
const { data: profilesById, error: errorById } = await supa
.from('ak_users')
.select('id, auth_id, username, email, role, user_type, status')
.eq('id', cachedUserId)
.single()
if (errorById == null && profilesById != null) {
profiles = profilesById
error = null
console.log('[akUserMapping] 通过 user_id 缓存查询成功')
} else {
console.warn('[akUserMapping] user_id 缓存查询失败:', errorById?.message ?? '未知错误')
}
}
// 4. 尝试通过 session 中的 email 查询
if (profiles == null) {
const userEmail = session.user.getString('email') ?? ''
if (userEmail != '') {
console.log('[akUserMapping] 尝试通过 email 查询:', userEmail)
const { data: profilesByEmail, error: errorByEmail } = await supa
.from('ak_users')
.select('id, auth_id, username, email, role, user_type, status')
.eq('email', userEmail)
.single()
if (errorByEmail == null && profilesByEmail != null) {
profiles = profilesByEmail
error = null
console.log('[akUserMapping] 通过 email 查询成功')
} else {
console.warn('[akUserMapping] email 查询失败:', errorByEmail?.message ?? '未知错误')
}
}
}
}
if (error != null || profiles == null) {
console.error('[akUserMapping] ❌ 所有查询方式都失败')
console.error('[akUserMapping] authUserId:', authUserId)
console.error('[akUserMapping] user_id 缓存:', uni.getStorageSync('user_id'))
console.error('[akUserMapping] session email:', session.user?.getString('email'))
throw new Error('未找到当前用户的业务账号,请联系管理员绑定 ak_users.auth_id')
}
// 5. 存储到本地
const profile: AkUserProfile = {
id: profiles.getString('id') ?? '',
auth_id: profiles.getString('auth_id') ?? '',
username: profiles.getString('username') ?? '',
email: profiles.getString('email') ?? '',
role: profiles.getString('role') ?? '',
user_type: profiles.getString('user_type') ?? '',
status: profiles.getString('status') ?? ''
}
uni.setStorageSync(AUTH_USER_ID_KEY, profile.auth_id)
uni.setStorageSync(AK_USER_ID_KEY, profile.id)
uni.setStorageSync(AK_USER_PROFILE_KEY, JSON.stringify(profile))
console.log('[akUserMapping] 加载业务用户成功:', {
authUserId: profile.auth_id,
akUserId: profile.id,
username: profile.username,
role: profile.role
})
return profile
}
/**
* 获取当前业务用户 IDak_users.id
* 优先级:
* 1. uni.getStorageSync('ak_user_id')
* 2. uni.getStorageSync('current_ak_user').id
* 3. uni.getStorageSync('user_info').id
* 4. uni.getStorageSync('userInfo').id
* 5. uni.getStorageSync('user').id
* 6. uni.getStorageSync('uid')
* 7. uni.getStorageSync('user_id')
* 8. uni.getStorageSync('auth_user_id')
* 9. 最后再请求 ak_users 表或 RPC resolve_ak_user_id
*/
export async function getCurrentAkUserId(): Promise<string> {
// 1. 尝试从 ak_user_id 缓存读取
const cachedAkUserId = uni.getStorageSync(AK_USER_ID_KEY)
if (cachedAkUserId != null && String(cachedAkUserId).length > 0) {
const result = String(cachedAkUserId)
console.log('[akUserMapping] getCurrentAkUserId: 从 ak_user_id 缓存读取:', result)
return result
}
// 2. 尝试从 current_ak_user 缓存读取
const cachedProfile = uni.getStorageSync(AK_USER_PROFILE_KEY)
if (cachedProfile != null && String(cachedProfile).length > 0) {
try {
const profile = JSON.parse(cachedProfile) as AkUserProfile
if (profile.id != null && profile.id !== '') {
const result = profile.id
console.log('[akUserMapping] getCurrentAkUserId: 从 current_ak_user 缓存读取:', result)
return result
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: 解析 current_ak_user 失败:', e)
}
}
// 3. 尝试从 user_info 缓存读取
try {
const cachedUserInfo = uni.getStorageSync('user_info')
if (cachedUserInfo != null && String(cachedUserInfo).length > 0) {
try {
const userInfo = JSON.parse(cachedUserInfo) as any
if (userInfo != null && userInfo.id != null && String(userInfo.id).length > 0) {
const result = String(userInfo.id)
console.log('[akUserMapping] getCurrentAkUserId: 从 user_info 缓存读取:', result)
return result
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: 解析 user_info 失败:', e)
}
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: user_info 读取失败:', e)
}
// 4. 尝试从 userInfo 缓存读取
try {
const cachedUserInfo = uni.getStorageSync('userInfo')
if (cachedUserInfo != null && String(cachedUserInfo).length > 0) {
try {
const userInfo = JSON.parse(cachedUserInfo) as any
if (userInfo != null && userInfo.id != null && String(userInfo.id).length > 0) {
const result = String(userInfo.id)
console.log('[akUserMapping] getCurrentAkUserId: 从 userInfo 缓存读取:', result)
return result
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: 解析 userInfo 失败:', e)
}
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: userInfo 读取失败:', e)
}
// 5. 尝试从 user 缓存读取
try {
const cachedUser = uni.getStorageSync('user')
if (cachedUser != null && String(cachedUser).length > 0) {
try {
const user = JSON.parse(cachedUser) as any
if (user != null && user.id != null && String(user.id).length > 0) {
const result = String(user.id)
console.log('[akUserMapping] getCurrentAkUserId: 从 user 缓存读取:', result)
return result
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: 解析 user 失败:', e)
}
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: user 读取失败:', e)
}
// 6. 尝试从 uid 缓存读取
const cachedUid = uni.getStorageSync('uid')
if (cachedUid != null && String(cachedUid).length > 0) {
const result = String(cachedUid)
console.log('[akUserMapping] getCurrentAkUserId: 从 uid 缓存读取:', result)
return result
}
// 7. 尝试从 user_id 缓存读取
const cachedUserId = uni.getStorageSync('user_id')
if (cachedUserId != null && String(cachedUserId).length > 0) {
const result = String(cachedUserId)
console.log('[akUserMapping] getCurrentAkUserId: 从 user_id 缓存读取:', result)
return result
}
// 8. 尝试从 auth_user_id 缓存读取(可能是 Supabase Auth ID需要转换
const cachedAuthUserId = uni.getStorageSync(AUTH_USER_ID_KEY)
if (cachedAuthUserId != null && String(cachedAuthUserId).length > 0) {
const authId = String(cachedAuthUserId)
console.log('[akUserMapping] getCurrentAkUserId: 从 auth_user_id 缓存读取到 Auth ID:', authId)
// 禁止直接返回 auth_user_id必须通过 RPC 或 ak_users 表转换
console.log('[akUserMapping] 尝试通过 RPC resolve_ak_user_id 转换 Auth ID')
try {
const rpcParams = { p_auth_user_id: authId } as UTSJSONObject
const { data: resolvedData, error: resolveError } = await supa
.rpc('resolve_ak_user_id', rpcParams)
if (resolveError == null && resolvedData != null) {
const akUserId = String(resolvedData)
if (akUserId != '' && akUserId != 'null') {
console.log('[akUserMapping] RPC 转换成功akUserId:', akUserId)
// 缓存 akUserId
uni.setStorageSync(AK_USER_ID_KEY, akUserId)
return akUserId
}
}
console.warn('[akUserMapping] RPC 转换失败:', resolveError?.message ?? '未知错误')
} catch (rpcErr) {
console.warn('[akUserMapping] RPC 转换异常:', rpcErr)
}
// 如果 RPC 失败,尝试通过 ak_users 表查询
console.log('[akUserMapping] 尝试通过 ak_users 表查询 auth_id 映射')
const { data: profiles, error } = await supa
.from('ak_users')
.select('id')
.eq('auth_id', authId)
.single()
if (error == null && profiles != null) {
const akUserId = profiles.getString('id') ?? ''
if (akUserId != '') {
console.log('[akUserMapping] ak_users 表查询成功akUserId:', akUserId)
uni.setStorageSync(AK_USER_ID_KEY, akUserId)
return akUserId
}
}
console.warn('[akUserMapping] ak_users 表查询也失败')
}
// 9. 最后尝试从 Supabase session 查询 ak_users
try {
const session = supa.getSession()
if (session != null && session.user != null) {
const authUserId = session.user.getString('id') ?? ''
if (authUserId != '') {
console.log('[akUserMapping] getCurrentAkUserId: 尝试从 Supabase session 查询 ak_users, authUserId:', authUserId)
const { data: profiles, error } = await supa
.from('ak_users')
.select('id')
.eq('auth_id', authUserId)
.single()
if (error == null && profiles != null) {
const akUserId = profiles.getString('id') ?? ''
if (akUserId != '') {
console.log('[akUserMapping] getCurrentAkUserId: Supabase 查询成功:', akUserId)
// 缓存结果
uni.setStorageSync(AK_USER_ID_KEY, akUserId)
return akUserId
}
}
console.warn('[akUserMapping] getCurrentAkUserId: Supabase 查询未找到 ak_users 记录')
}
}
} catch (e) {
console.warn('[akUserMapping] getCurrentAkUserId: Supabase 查询失败:', e)
}
// 10. 全部失败,返回空字符串(不 throw
console.warn('[akUserMapping] getCurrentAkUserId: 所有降级策略失败,返回空字符串')
return ''
}
/**
* 获取当前业务用户完整信息
*/
export async function getCurrentAkUser(): Promise<AkUserProfile | null> {
// 1. 尝试从缓存读取
const cached = uni.getStorageSync(AK_USER_PROFILE_KEY)
if (cached != null && String(cached).length > 0) {
try {
return JSON.parse(cached) as AkUserProfile
} catch (e) {
// 解析失败,重新加载
}
}
// 2. 缓存不存在,自动加载
try {
return await loadCurrentAkUser()
} catch (e) {
return null
}
}
/**
* 获取当前 Supabase Auth 用户 ID
*/
export function getCurrentAuthUserId(): string {
const cached = uni.getStorageSync(AUTH_USER_ID_KEY)
if (cached != null && String(cached).length > 0) {
return String(cached)
}
// 尝试从 session 获取
try {
const session = supa.getSession()
if (session != null && session.user != null) {
return session.user.getString('id') ?? ''
}
} catch (e) {}
return ''
}
/**
* 清除本地存储的 ak_user 信息
*/
export function clearAkUserCache(): void {
uni.removeStorageSync(AUTH_USER_ID_KEY)
uni.removeStorageSync(AK_USER_ID_KEY)
uni.removeStorageSync(AK_USER_PROFILE_KEY)
}
/**
* 调试输出:打印当前用户信息
*/
export function debugCurrentUser(): void {
console.log('[akUserMapping] ========== 当前用户调试信息 ==========')
console.log('[akUserMapping] authUserId:', uni.getStorageSync(AUTH_USER_ID_KEY))
console.log('[akUserMapping] akUserId:', uni.getStorageSync(AK_USER_ID_KEY))
const profileRaw = uni.getStorageSync(AK_USER_PROFILE_KEY)
if (profileRaw != null && String(profileRaw).length > 0) {
try {
const profile = JSON.parse(profileRaw) as AkUserProfile
console.log('[akUserMapping] profile:', profile)
} catch (e) {
console.log('[akUserMapping] profile (raw):', profileRaw)
}
} else {
console.log('[akUserMapping] profile: (未缓存)')
}
console.log('[akUserMapping] ======================================')
}