import { AkReq } from '@/uni_modules/ak-req/index.uts' import supa from '@/components/supadb/aksupainstance.uts' import { getCurrentAkUserId } from '@/utils/akUserMapping.uts' const HOMECARE_API_BASE = 'http://localhost:4001' const HOMECARE_TOKEN_KEY = 'homecare_auth_token' const HOMECARE_USER_KEY = 'homecare_auth_user' export type HomecareLoginResult = { success: boolean message: string token: string | null user: UTSJSONObject | null } export type HomecarePrecheckResult = { success: boolean message: string distanceMeters: number | null allowedRadiusMeters: number canCheckin: boolean reasonCode: string workerLocationAccepted: boolean serviceLocationReady: boolean } export const reasonCodeMap: UTSJSONObject = new UTSJSONObject({ OK: '已进入允许签到范围', OUT_OF_RADIUS: '当前距离服务地点较远,请到达服务地址附近后再签到', SERVICE_LOCATION_MISSING: '当前工单缺少服务地址坐标,请联系管理员处理', WORK_ORDER_NOT_ASSIGNABLE: '当前工单未分配或状态不允许签到', WORKER_NOT_MATCHED: '当前账号不是该工单的服务人员', COORDINATE_TYPE_INVALID: '定位坐标类型异常,请重新定位', SLA_CONFIG_MISSING: '签到规则配置缺失,请联系管理员处理' }) export function getReasonText(code: string): string { const text = reasonCodeMap.getString(code) return text != null && text !== '' ? text : '未知原因: ' + code } export function getHomecareToken(): string { const stored = uni.getStorageSync(HOMECARE_TOKEN_KEY) as string | null return stored != null ? stored : '' } export function getHomecareUser(): UTSJSONObject | null { const raw = uni.getStorageSync(HOMECARE_USER_KEY) as string | null if (raw == null || raw === '') { return null } try { return JSON.parse(raw) as UTSJSONObject } catch (e) { return null } } export function saveHomecareToken(token: string): void { uni.setStorageSync(HOMECARE_TOKEN_KEY, token) } export function saveHomecareUser(user: UTSJSONObject): void { uni.setStorageSync(HOMECARE_USER_KEY, JSON.stringify(user)) } export function clearHomecareAuth(): void { uni.removeStorageSync(HOMECARE_TOKEN_KEY) uni.removeStorageSync(HOMECARE_USER_KEY) } export async function emailLogin(email: string, password: string): Promise { try { const reqData = new UTSJSONObject() reqData.set('email', email) reqData.set('password', password) const res = await AkReq.request({ url: HOMECARE_API_BASE + '/auth/email-login', method: 'POST', data: reqData, contentType: 'application/json' }) if (res.status >= 200 && res.status < 300 && res.data != null) { const dataObj = res.data as UTSJSONObject const dataInner = dataObj.getJSON('data') if (dataInner != null) { const token = dataInner.getString('token') const user = dataInner.getJSON('user') if (token != null && token !== '') { saveHomecareToken(token) if (user != null) { saveHomecareUser(user) } return { success: true, message: '登录成功', token: token, user: user } } } } let msg = '登录失败' if (res.data != null) { const dataObj = res.data as UTSJSONObject const m = dataObj.getString('msg') if (m != null && m !== '') { msg = m } } return { success: false, message: msg, token: null, user: null } } catch (error) { console.error('emailLogin error:', error) return { success: false, message: '网络错误,请检查网络连接', token: null, user: null } } } export async function checkinPrecheck( workOrderId: string, latitude: number, longitude: number, accuracy: number ): Promise { console.warn('[CHECKIN RPC] ========== checkinPrecheck START ==========') console.warn('[CHECKIN RPC] 参数: workOrderId=', workOrderId, ' lat=', latitude, ' lng=', longitude, ' accuracy=', accuracy) try { // 获取当前业务用户 ID(ak_users.id),不是 Supabase Auth ID console.warn('[CHECKIN RPC] 步骤 1: 获取当前业务用户 ID') // 打印认证相关日志 console.warn('[CHECKIN AUTH] storage ak_user_id =', uni.getStorageSync('ak_user_id')) console.warn('[CHECKIN AUTH] current_ak_user =', uni.getStorageSync('current_ak_user')) let workerId = await getCurrentAkUserId() console.warn('[CHECKIN RPC] workerId (ak_user_id):', workerId) if (workerId == '' || workerId == null) { console.warn('[CHECKIN RPC] workerId 为空,尝试从缓存恢复') // 按顺序从缓存恢复 // 1. ak_user_id try { const cached = uni.getStorageSync('ak_user_id') if (cached != null && String(cached) != '') { workerId = String(cached) console.warn('[CHECKIN RPC] 从 ak_user_id 恢复:', workerId) } } catch (e) {} // 2. current_ak_user.id if (workerId == '' || workerId == null) { try { const currentAkUser = uni.getStorageSync('current_ak_user') if (currentAkUser != null && String(currentAkUser) != '') { const obj = JSON.parse(String(currentAkUser)) as any if (obj != null && obj.id != null && String(obj.id) != '') { workerId = String(obj.id) console.warn('[CHECKIN RPC] 从 current_ak_user.id 恢复:', workerId) } } } catch (e) {} } // 3. user_id if (workerId == '' || workerId == null) { try { const userId = uni.getStorageSync('user_id') if (userId != null && String(userId) != '') { workerId = String(userId) console.warn('[CHECKIN RPC] 从 user_id 恢复:', workerId) } } catch (e) {} } // 4. user_info.id if (workerId == '' || workerId == null) { try { const cachedUserInfo = uni.getStorageSync('user_info') if (cachedUserInfo != null && String(cachedUserInfo) != '') { const userInfo = JSON.parse(String(cachedUserInfo)) as any if (userInfo != null && userInfo.id != null && String(userInfo.id) != '') { workerId = String(userInfo.id) console.warn('[CHECKIN RPC] 从 user_info.id 恢复:', workerId) } } } catch (e) {} } // 恢复成功后写回缓存 if (workerId != '' && workerId != null) { uni.setStorageSync('ak_user_id', workerId) uni.setStorageSync('current_ak_user', JSON.stringify({ id: workerId })) console.warn('[CHECKIN RPC] 已写回缓存: ak_user_id =', workerId) } // 如果仍然为空,返回错误 if (workerId == '' || workerId == null) { console.warn('[CHECKIN RPC] 所有恢复策略失败,返回 NOT_LOGGED_IN') return { distanceMeters: null, allowedRadiusMeters: 0, canCheckin: false, reasonCode: 'NOT_LOGGED_IN' } } } // 构建 RPC 参数 console.warn('[CHECKIN RPC] 步骤 2: 构建 RPC 参数') const rpcParams = { p_work_order_id: workOrderId, p_worker_id: workerId, p_latitude: latitude, p_longitude: longitude, p_coordinate_type: 'gcj02', p_accuracy: accuracy, p_reported_at: new Date().toISOString(), p_location_scene: 'CHECKIN_PRECHECK' } as UTSJSONObject console.warn('[CHECKIN RPC] RPC 参数:', JSON.stringify(rpcParams)) // 直接调用 Supabase RPC,不需要本地后端 console.warn('[CHECKIN RPC] 步骤 3: 调用 supa.rpc') const rpcResponse = await supa.rpc('rpc_homecare_checkin_precheck', rpcParams) console.warn('[CHECKIN RPC] RPC 原始返回类型:', rpcResponse.constructor.name) console.warn('[CHECKIN RPC] RPC 原始返回:', rpcResponse) // supa.rpc() 返回 AkReqResponse,需要从 data 字段提取 UTSJSONObject let result: UTSJSONObject | null = null if (rpcResponse != null) { // 检查是否是 AkReqResponse 包装 if ('data' in rpcResponse) { const respData = (rpcResponse as any).data console.warn('[CHECKIN RPC] 检测到 AkReqResponse,提取 data 字段') if (respData != null && respData.constructor.name == 'UTSJSONObject') { result = respData as UTSJSONObject console.warn('[CHECKIN RPC] 成功提取 UTSJSONObject') } else { console.warn('[CHECKIN RPC] data 字段不是 UTSJSONObject:', respData) // 尝试直接当作 UTSJSONObject if (respData != null) { result = respData as UTSJSONObject } } } else { // 直接就是 UTSJSONObject console.warn('[CHECKIN RPC] 直接当作 UTSJSONObject') result = rpcResponse as UTSJSONObject } } console.warn('[CHECKIN RPC] 最终 result:', result) if (result != null) { console.warn('[CHECKIN RPC] 解析结果字段') const distanceMetersRaw = result.getNumber('distanceMeters') const distanceMeters = distanceMetersRaw != null ? distanceMetersRaw : null const allowedRadiusMetersRaw = result.getNumber('allowedRadiusMeters') const allowedRadiusMeters = allowedRadiusMetersRaw != null ? allowedRadiusMetersRaw : 0 const canCheckinRaw = result.get('canCheckin') const canCheckin = canCheckinRaw === true const reasonCode = result.getString('reasonCode') ?? '' const workerLocationAcceptedRaw = result.get('workerLocationAccepted') const workerLocationAccepted = workerLocationAcceptedRaw === true const serviceLocationReadyRaw = result.get('serviceLocationReady') const serviceLocationReady = serviceLocationReadyRaw === true console.warn('[CHECKIN RPC] 解析结果: distance=', distanceMeters, ' radius=', allowedRadiusMeters, ' canCheckin=', canCheckin, ' reason=', reasonCode) return { success: canCheckin, message: getReasonText(reasonCode), distanceMeters: distanceMeters, allowedRadiusMeters: allowedRadiusMeters, canCheckin: canCheckin, reasonCode: reasonCode, workerLocationAccepted: workerLocationAccepted, serviceLocationReady: serviceLocationReady } } console.warn('[CHECKIN RPC] RPC 返回 null,返回默认错误') return { success: false, message: '预校验返回空结果', distanceMeters: null, allowedRadiusMeters: 0, canCheckin: false, reasonCode: 'UNKNOWN', workerLocationAccepted: false, serviceLocationReady: false } } catch (error) { console.warn('[CHECKIN RPC] ========== 捕获异常 ==========') console.warn('[CHECKIN RPC] 异常类型:', error instanceof Error ? 'Error' : typeof error) console.warn('[CHECKIN RPC] 异常信息:', error) console.warn('[CHECKIN RPC] ========== checkinPrecheck END (ERROR) ==========') // RPC 失败时返回详细错误 const errMsg = error instanceof Error ? error.message : String(error) return { success: false, message: '预校验失败: ' + errMsg, distanceMeters: null, allowedRadiusMeters: 0, canCheckin: false, reasonCode: 'RPC_ERROR', workerLocationAccepted: false, serviceLocationReady: false } } }