完成距离预校验

This commit is contained in:
2026-06-12 13:03:13 +08:00
parent 81f3e1d3b6
commit 72d29d4b68
7 changed files with 6060 additions and 3054 deletions

View File

@@ -29,10 +29,12 @@ const DELIVERY_RPC_DASHBOARD = 'rpc_delivery_dashboard'
const DELIVERY_RPC_ORDER_LIST = 'rpc_delivery_order_list'
const DELIVERY_RPC_ORDER_DETAIL = 'rpc_delivery_order_detail'
const DELIVERY_RPC_ACCEPT_ORDER = 'rpc_delivery_accept_order'
const DELIVERY_RPC_HOMECARE_ACCEPT_ASSIGNMENT_V2 = 'rpc_homecare_accept_assignment_v2'
const DELIVERY_RPC_REJECT_ORDER = 'rpc_delivery_reject_order'
const DELIVERY_RPC_START_DEPART = 'rpc_delivery_start_depart'
const DELIVERY_RPC_ARRIVE_ORDER = 'rpc_delivery_arrive_order'
const DELIVERY_RPC_CHECKIN_ORDER = 'rpc_delivery_checkin_order'
const DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT = 'rpc_homecare_checkin_submit'
const DELIVERY_RPC_START_SERVICE = 'rpc_delivery_start_service'
const DELIVERY_RPC_SAVE_PROGRESS = 'rpc_delivery_save_progress'
const DELIVERY_RPC_UPLOAD_EVIDENCE = 'rpc_delivery_upload_evidence'
@@ -2109,6 +2111,20 @@ export async function acceptDeliveryOrderById(orderId: string): Promise<Delivery
return await fallbackAcceptOrder(orderId)
}
/**
* 居家服务接单 RPCrpc_homecare_accept_assignment_v2
* 参数p_work_order_id, p_worker_id (akUserId)
*/
export async function acceptHomecareAssignmentV2(workOrderId: string, workerId: string): Promise<UTSJSONObject | null> {
console.warn('[HOMECARE ACCEPT] 调用 rpc_homecare_accept_assignment_v2, workOrderId=', workOrderId, ' workerId=', workerId)
const rpcData = await callDeliveryRpc(DELIVERY_RPC_HOMECARE_ACCEPT_ASSIGNMENT_V2, {
p_work_order_id: workOrderId,
p_worker_id: workerId
} as UTSJSONObject)
console.warn('[HOMECARE ACCEPT] rpc_homecare_accept_assignment_v2 result:', rpcData)
return rpcData as UTSJSONObject | null
}
export async function rejectDeliveryOrderById(orderId: string, reason: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_REJECT_ORDER, {
p_order_id: orderId,
@@ -2157,6 +2173,44 @@ export async function checkinOrderById(orderId: string, payload: DeliveryCheckin
return await fallbackCheckinOrder(orderId, payload)
}
/**
* 居家服务正式签到 RPCrpc_homecare_checkin_submit
* 参数p_work_order_id, p_worker_id, p_latitude, p_longitude, p_coordinate_type, p_accuracy, p_reported_at, p_evidence_file_ids, p_signature_payload, p_reason
*/
export async function submitHomecareCheckin(
workOrderId: string,
workerId: string,
latitude: number,
longitude: number,
coordinateType: string,
accuracy: number,
reportedAt: string,
evidenceFileIds: Array<string>,
signaturePayload: any | null = null,
reason: string = 'worker_arrived'
): Promise<UTSJSONObject | null> {
console.warn('[CHECKIN SUBMIT] 调用 rpc_homecare_checkin_submit')
console.warn('[CHECKIN SUBMIT] workOrderId=', workOrderId, ' workerId=', workerId)
console.warn('[CHECKIN SUBMIT] lat=', latitude, ' lng=', longitude, ' accuracy=', accuracy)
console.warn('[CHECKIN SUBMIT] evidenceFileIds=', evidenceFileIds)
const rpcParams = new UTSJSONObject()
rpcParams.set('p_work_order_id', workOrderId)
rpcParams.set('p_worker_id', workerId)
rpcParams.set('p_latitude', latitude)
rpcParams.set('p_longitude', longitude)
rpcParams.set('p_coordinate_type', coordinateType)
rpcParams.set('p_accuracy', accuracy)
rpcParams.set('p_reported_at', reportedAt)
rpcParams.set('p_evidence_file_ids', evidenceFileIds)
rpcParams.set('p_signature_payload', signaturePayload)
rpcParams.set('p_reason', reason)
const rpcData = await callDeliveryRpc(DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT, rpcParams)
console.warn('[CHECKIN SUBMIT] rpc_homecare_checkin_submit result:', rpcData)
return rpcData as UTSJSONObject | null
}
export async function startServiceById(orderId: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_START_SERVICE, {
p_order_id: orderId

View File

@@ -45,7 +45,8 @@ import { onLoad } from '@dcloudio/uni-app'
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
import ServicePanel from '@/components/homeService/ServicePanel.uvue'
import type { DeliveryLocationType, DeliveryOrderType } from '@/types/delivery.uts'
import { checkinOrder, getDeliveryOrderDetail } from '@/services/deliveryService.uts'
import supa from '@/components/supadb/aksupainstance.uts'
import { getDeliveryOrderDetail } from '@/services/deliveryService.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
import { getCurrentUserId } from '@/utils/store.uts'
import { getDeliveryRouteParam } from '@/utils/deliveryRoute.uts'
@@ -54,7 +55,7 @@ import {
checkinPrecheck,
getReasonText
} from '@/utils/homecareAuth.uts'
import { debugCurrentUser } from '@/utils/akUserMapping.uts'
import { debugCurrentUser, getCurrentAkUserId } from '@/utils/akUserMapping.uts'
const orderId = ref('')
const order = ref<DeliveryOrderType | null>(null)
@@ -62,6 +63,7 @@ const currentLocation = ref<DeliveryLocationType | null>(null)
const locationText = ref('未获取')
const accuracyText = ref('未知')
const photos = ref([] as Array<string>)
const evidenceFileIds = ref([] as Array<string>)
const note = ref('')
const submitting = ref(false)
@@ -77,19 +79,29 @@ const allowedRadiusText = ref('未校验')
const precheckStatusText = ref('未校验')
const reasonText = ref('')
function updateHomecareLoginStatus(): void {
console.warn('[CHECKIN DEBUG] updateHomecareLoginStatus: called')
// 改用 Supabase 当前用户判断是否已登录homecare 专属 token 已废弃,从未被写入)
const userId = getCurrentUserId()
console.warn('[CHECKIN DEBUG] updateHomecareLoginStatus: token length:', userId.length)
if (userId !== '') {
async function updateHomecareLoginStatus(): Promise<void> {
console.warn('[CHECKIN AUTH] ========== 登录状态检查 START ==========')
// 打印认证相关日志
const authUserId = getCurrentUserId()
const akUserId = await getCurrentAkUserId()
const storageAkUserId = uni.getStorageSync('ak_user_id')
const storageCurrentAkUser = uni.getStorageSync('current_ak_user')
console.warn('[CHECKIN AUTH] getCurrentUserId =', authUserId)
console.warn('[CHECKIN AUTH] getCurrentAkUserId =', akUserId)
console.warn('[CHECKIN AUTH] storage ak_user_id =', storageAkUserId)
console.warn('[CHECKIN AUTH] current_ak_user =', storageCurrentAkUser)
console.warn('[CHECKIN AUTH] ========== 登录状态检查 END ==========')
if (akUserId !== '') {
isHomecareLoggedIn.value = true
homecareUserEmail.value = '已登录 (uid: ' + userId.substring(0, 8) + '...)'
console.warn('[CHECKIN DEBUG] updateHomecareLoginStatus: logged in as', homecareUserEmail.value)
homecareUserEmail.value = '已登录 (uid: ' + akUserId.substring(0, 8) + '...)'
console.warn('[CHECKIN AUTH] 居家服务认证通过')
} else {
isHomecareLoggedIn.value = false
homecareUserEmail.value = ''
console.warn('[CHECKIN DEBUG] updateHomecareLoginStatus: not logged in')
console.warn('[CHECKIN AUTH] 居家服务认证失败akUserId 为空')
}
}
@@ -239,6 +251,72 @@ async function choosePhoto() {
try {
const selected = await wrapChooseImage()
photos.value = selected
// 上传现场图片并创建 hc_evidence_files 记录
if (selected.length > 0 && orderId.value != '') {
const akUserId = await getCurrentAkUserId()
console.warn('[CHOOSE PHOTO] akUserId:', akUserId)
if (akUserId == '') {
uni.showToast({ title: '未获取到业务用户 ID', icon: 'none' })
return
}
// 上传每张照片
for (let i = 0; i < selected.length; i++) {
const tempPath = selected[i]
try {
// 上传到 Supabase Storage
const { data: uploadData, error: uploadError } = await supa.storage
.from('evidence')
.upload('checkin/' + orderId.value + '/' + Date.now() + '_' + i + '.jpg', {
base64: tempPath,
contentType: 'image/jpeg'
})
if (uploadError == null && uploadData != null) {
// 获取公开 URL
const { data: urlData } = supa.storage
.from('evidence')
.getPublicUrl(uploadData.path)
if (urlData != null) {
const fileUrl = urlData.publicUrl
// 创建 hc_evidence_files 记录
const { data: evidenceRecord, error: evidenceError } = await supa
.from('hc_evidence_files')
.insert({
work_order_id: orderId.value,
uploader_id: akUserId,
file_url: fileUrl,
upload_status: 'UPLOADED',
file_type: 'image',
created_at: new Date().toISOString()
})
.select('id')
.single()
if (evidenceError == null && evidenceRecord != null) {
const evidenceId = evidenceRecord.getString('id') ?? ''
if (evidenceId != '') {
evidenceFileIds.value.push(evidenceId)
console.warn('[CHOOSE PHOTO] 证据记录创建成功, evidenceId:', evidenceId)
}
} else {
console.warn('[CHOOSE PHOTO] 证据记录创建失败:', evidenceError)
}
}
} else {
console.warn('[CHOOSE PHOTO] 图片上传失败:', uploadError)
}
} catch (uploadErr) {
console.warn('[CHOOSE PHOTO] 上传异常:', uploadErr)
}
}
console.warn('[CHOOSE PHOTO] 当前 evidenceFileIds:', evidenceFileIds.value)
}
} catch (error) {
uni.showToast({ title: '拍照失败,请重试', icon: 'none' })
}
@@ -259,6 +337,39 @@ async function handlePrecheck(): Promise<void> {
return
}
// 预校验前打印认证日志
const authUserId = getCurrentUserId()
let akUserId = await getCurrentAkUserId()
const storageAkUserId = uni.getStorageSync('ak_user_id')
const storageCurrentAkUser = uni.getStorageSync('current_ak_user')
console.warn('[CHECKIN AUTH] getCurrentUserId =', authUserId)
console.warn('[CHECKIN AUTH] getCurrentAkUserId =', akUserId)
console.warn('[CHECKIN AUTH] storage ak_user_id =', storageAkUserId)
console.warn('[CHECKIN AUTH] current_ak_user =', storageCurrentAkUser)
// 兜底逻辑:如果 akUserId 为空但 authUserId 非空,尝试写入缓存
if ((akUserId == '' || akUserId == null) && authUserId != null && String(authUserId).length > 0) {
console.warn('[CHECKIN AUTH] 兜底akUserId 为空但 authUserId 非空,尝试写入缓存')
console.warn('[CHECKIN AUTH] authUserId =', authUserId)
// 直接写入 authUserId 作为 ak_user_id
uni.setStorageSync('ak_user_id', authUserId)
uni.setStorageSync('current_ak_user', JSON.stringify({ id: authUserId }))
console.warn('[CHECKIN AUTH] 已写入缓存: ak_user_id =', authUserId)
// 重新读取
akUserId = await getCurrentAkUserId()
console.warn('[CHECKIN AUTH] 重新读取 akUserId =', akUserId)
}
// 如果 akUserId 仍然为空,提示重新登录
if (akUserId == '' || akUserId == null) {
uni.showToast({ title: '业务账号未初始化,请重新登录', icon: 'none' })
console.warn('[CHECKIN AUTH] akUserId 仍为空,阻止预校验')
return
}
console.warn('[CHECKIN PAGE] 开始预校验流程')
// 调试:打印当前 ak_user 信息
@@ -356,31 +467,41 @@ async function submitCheckin() {
return
}
// RPC 预校验已经做了距离判断,这里直接提交
// 保留坐标检查作为兜底,防止跳过预校验直接提交
if (order.value.latitude == 0 && order.value.longitude == 0) {
console.warn('[CHECKIN DEBUG] submitCheckin: order has no valid coordinates (lat=0, lng=0)')
uni.showToast({ title: '订单缺少服务地址坐标', icon: 'none' })
return
}
// 服务坐标以后端 precheck 返回为准,不再以前端 order.latitude/order.longitude 为准
// 删除了之前的坐标检查
const distance = calculateDistance(currentLocation.value.latitude, currentLocation.value.longitude, order.value.latitude, order.value.longitude)
console.warn('[CHECKIN DEBUG] submitCheckin: calculated distance:', distance, 'meters, allowedRadius:', order.value.allowCheckinRadiusMeters)
console.warn('[CHECKIN DEBUG] submitCheckin: proceeding with checkin, photos count:', photos.value.length)
console.warn('[CHECKIN DEBUG] submitCheckin: proceeding with checkin, photos count:', photos.value.length, ' evidenceFileIds:', evidenceFileIds.value)
doCheckin()
}
async function doCheckin() {
submitting.value = true
try {
await checkinOrder(orderId.value, {
location: currentLocation.value as DeliveryLocationType,
note: note.value,
photos: photos.value,
checkinMode: 'gps'
})
console.warn('[CHECKIN DEBUG] submitCheckin: checkinOrder succeeded')
// 获取 akUserId
const akUserId = await getCurrentAkUserId()
console.warn('[CHECKIN RPC] akUserId:', akUserId)
if (akUserId == '') {
uni.showToast({ title: '未获取到业务用户 ID请重新登录', icon: 'none' })
return
}
// 调用居家服务正式签到 RPCrpc_homecare_checkin_submit
const { submitHomecareCheckin } = await import('@/api/delivery.uts')
const result = await submitHomecareCheckin(
orderId.value,
akUserId,
currentLocation.value!.latitude,
currentLocation.value!.longitude,
'gcj02',
parseFloat(accuracyText.value) || 0,
new Date().toISOString(),
evidenceFileIds.value,
null,
'worker_arrived'
)
console.warn('[CHECKIN RPC] result:', result)
// 签到成功后显示等待消费者确认
uni.showModal({
@@ -388,7 +509,7 @@ async function doCheckin() {
content: '已提交到达签到,等待消费者确认',
showCancel: false,
success: () => {
// 返回订单列表或详情页
// 返回订单列表
uni.navigateBack()
}
})

View File

@@ -501,11 +501,69 @@ const handleLogin = async () => {
saveDeliverySession(result.token, result.userInfo, result.deliveryInfo)
// 加载 ak_users 映射Supabase Auth ID -> 业务用户 ID
// 优先从 result.userInfo.id 获取业务用户 ID直接写入缓存
// 只有 userInfo.id 不存在时,才调用 loadCurrentAkUser
let akUserIdFromLogin = ''
try {
await loadCurrentAkUser()
console.log('[Login] ak_users 映射加载成功')
} catch (akErr) {
console.warn('[Login] ⚠️ ak_users 映射加载失败(不影响登录):', akErr)
// 尝试从 userInfo.id 获取业务用户 ID
if (result.userInfo != null && result.userInfo.id != null && String(result.userInfo.id).length > 0) {
const candidateId = String(result.userInfo.id)
console.log('[Login] 从 result.userInfo.id 获取到 ID:', candidateId)
// 判断是否是 Supabase Auth ID通过长度和格式判断
// Supabase Auth ID 通常是 UUID 格式,业务用户 ID 也是 UUID 格式
// 最可靠的方式是尝试写入后验证
akUserIdFromLogin = candidateId
// 写入缓存
uni.setStorageSync('ak_user_id', akUserIdFromLogin)
uni.setStorageSync('user_id', akUserIdFromLogin)
uni.setStorageSync('current_ak_user', JSON.stringify({
id: akUserIdFromLogin,
auth_id: result.userInfo.auth_id ?? '',
username: result.userInfo.username ?? '',
email: result.userInfo.email ?? '',
role: result.userInfo.role ?? ''
}))
console.log('[Login] 从 userInfo.id 写入 ak_user_id 成功:', akUserIdFromLogin)
}
} catch (extractErr) {
console.warn('[Login] 从 userInfo.id 提取失败:', extractErr)
}
// 如果 userInfo.id 没有有效 ID才调用 loadCurrentAkUser
if (akUserIdFromLogin == null || String(akUserIdFromLogin).length == 0) {
try {
const profile = await loadCurrentAkUser()
akUserIdFromLogin = profile.id
console.log('[Login] ak_users 映射加载成功akUserId:', profile.id)
console.log('[Login] 缓存已写入: ak_user_id =', profile.id)
} catch (akErr) {
console.warn('[Login] ⚠️ ak_users 映射加载失败:', akErr)
// 如果 user_id 已有缓存,尝试用它兜底
const cachedUserId = uni.getStorageSync('user_id')
if (cachedUserId != null && String(cachedUserId).length > 0) {
console.log('[Login] 使用 user_id 缓存兜底:', cachedUserId)
uni.setStorageSync('ak_user_id', cachedUserId)
uni.setStorageSync('current_ak_user', JSON.stringify({ id: cachedUserId }))
akUserIdFromLogin = String(cachedUserId)
}
}
}
// 最终验证 ak_user_id 必须有值
const finalAkUserId = uni.getStorageSync('ak_user_id')
console.log('[Login] 最终 ak_user_id:', finalAkUserId)
console.log('[Login] 最终 current_ak_user:', uni.getStorageSync('current_ak_user'))
if (finalAkUserId == null || String(finalAkUserId).length == 0) {
console.error('[Login] ❌ ak_user_id 最终为空,输出详细调试信息')
console.error('[Login] result.userInfo:', JSON.stringify(result.userInfo))
console.error('[Login] result.deliveryInfo:', JSON.stringify(result.deliveryInfo))
console.error('[Login] user_id 缓存:', uni.getStorageSync('user_id'))
console.error('[Login] auth_user_id 缓存:', uni.getStorageSync('auth_user_id'))
console.error('[Login] user_info 缓存:', uni.getStorageSync('user_info'))
throw new Error('业务账号映射加载失败,请联系管理员')
}
const authResult = await requireDeliveryAuth({ redirectOnFail: false, toastOnFail: false })

View File

@@ -16,7 +16,13 @@ import {
getOrderDetail as getDirectServiceOrderDetail,
getOrdersByTab as getDirectOrdersByTab
} from '@/services/serviceOrderService.uts'
import { getUserInfo, requireDeliveryAuth, setDeliveryInfo } from '@/utils/deliveryAuth.uts'
import {
getDeliveryInfo,
getUserInfo,
requireDeliveryAuth,
setDeliveryInfo
} from '@/utils/deliveryAuth.uts'
import { getCurrentAkUserId as readCurrentAkUserId } from '@/utils/akUserMapping.uts'
import type {
DeliveryAbnormalReportType,
DeliveryCheckinPayloadType,
@@ -147,13 +153,17 @@ export async function checkDeliveryAuth(): Promise<boolean> {
}
async function getCurrentStaffId(): Promise<string> {
const profile = await getDeliveryProfile()
if (profile == null) {
return ''
}
return profile.id
// 旧 delivery RPCdashboard / order_list需要 ml_delivery_staff.id513449ca...
const deliveryInfo = getDeliveryInfo()
if (deliveryInfo != null && deliveryInfo.id != null && String(deliveryInfo.id) != '') {
console.warn('[deliveryService] getCurrentStaffId: 使用 deliveryStaffId =', String(deliveryInfo.id))
return String(deliveryInfo.id)
}
console.error('[deliveryService] getCurrentStaffId: 未获取到 deliveryStaffId')
return ''
}
function createEmptyLocation(): DeliveryLocationType {
return {
latitude: 0,
@@ -168,7 +178,9 @@ export async function getDeliveryDashboard(): Promise<DeliveryDashboardType> {
if (staffId != '') {
return await getDeliveryDashboardByStaffId(staffId)
}
return getDeliveryCareDashboard()
// staffId 为空时返回空 dashboard
console.error('[deliveryService] getDeliveryDashboard: staffId 为空,返回空数据')
return emptyDashboard()
}
export async function getDeliveryOrders(params: DeliveryOrderQueryType): Promise<Array<DeliveryOrderType>> {
@@ -176,13 +188,9 @@ export async function getDeliveryOrders(params: DeliveryOrderQueryType): Promise
if (staffId != '') {
return await getDeliveryOrdersByStaffId(staffId, params)
}
if (params.tab == 'pending' || params.tab == 'pending_assignment') {
return getPendingCareOrders()
}
if (params.tab == 'history' || params.tab == 'completed' || params.tab == 'archive') {
return getHistoryCareOrders()
}
return getTodayCareOrders()
// staffId 为空时返回空列表
console.error('[deliveryService] getDeliveryOrders: staffId 为空,返回空列表')
return [] as Array<DeliveryOrderType>
}
export async function getDeliveryOrderDetail(id: string): Promise<DeliveryOrderType | null> {
@@ -352,7 +360,60 @@ export async function getServiceOrderDetail(orderId: string): Promise<DeliveryOr
}
export async function acceptServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
return await acceptDeliveryOrder(orderId)
// 居家服务订单必须调用 rpc_homecare_accept_assignment_v2
// 不再调用 acceptDeliveryOrder / rpc_delivery_accept_order
const akUserId = await resolveCurrentAkUserIdForDelivery()
console.warn('[HOMECARE ACCEPT] acceptServiceOrder: orderId=', orderId, ' akUserId=', akUserId)
if (akUserId == '') {
uni.showToast({ title: '未获取到业务用户 ID请重新登录', icon: 'none' })
return null
}
// 调用居家服务接单 RPC
const { acceptHomecareAssignmentV2 } = await import('@/api/delivery.uts')
const result = await acceptHomecareAssignmentV2(orderId, akUserId)
console.warn('[HOMECARE ACCEPT] rpc_homecare_accept_assignment_v2 result:', result)
// 刷新订单列表
await loadData()
return null
}
/**
* 获取当前业务用户 ID从 akUserMapping 或缓存)
*/
async function resolveCurrentAkUserIdForDelivery(): Promise<string> {
try {
const id = await readCurrentAkUserId()
if (id != null && id != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from akUserMapping =', id)
return id
}
} catch (e) {
console.warn('[deliveryService] readCurrentAkUserId failed:', e)
}
try {
const cached = uni.getStorageSync('ak_user_id')
if (cached != null && String(cached) != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from storage ak_user_id =', String(cached))
return String(cached)
}
} catch (e) {}
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) != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from current_ak_user.id =', String(obj.id))
return String(obj.id)
}
}
} catch (e) {}
return ''
}
export async function rejectServiceOrder(orderId: string, reason: string): Promise<DeliveryOrderType | null> {

View File

@@ -26,7 +26,11 @@ export type AkUserProfile = {
/**
* 加载当前登录用户的业务账号信息
* 从 Supabase Auth 获取 authUserId然后查询 ak_users 表获取业务用户信息
* 查询顺序:
* 1. auth_id = session.user.id
* 2. id = uni.getStorageSync('user_id')
* 3. email = 当前登录邮箱
* 4. 最后才失败
*/
export async function loadCurrentAkUser(): Promise<AkUserProfile> {
// 1. 获取当前 Supabase Auth 用户
@@ -41,17 +45,68 @@ export async function loadCurrentAkUser(): Promise<AkUserProfile> {
}
// 2. 查询 ak_users 表,通过 auth_id 匹配
const { data: profiles, error } = await supa
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')
}
// 3. 存储到本地
// 5. 存储到本地
const profile: AkUserProfile = {
id: profiles.getString('id') ?? '',
auth_id: profiles.getString('auth_id') ?? '',
@@ -78,18 +133,192 @@ export async function loadCurrentAkUser(): Promise<AkUserProfile> {
/**
* 获取当前业务用户 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. 尝试从缓存读取
// 1. 尝试从 ak_user_id 缓存读取
const cachedAkUserId = uni.getStorageSync(AK_USER_ID_KEY)
if (cachedAkUserId != null && String(cachedAkUserId).length > 0) {
return String(cachedAkUserId)
const result = String(cachedAkUserId)
console.log('[akUserMapping] getCurrentAkUserId: 从 ak_user_id 缓存读取:', result)
return result
}
// 2. 缓存不存在,自动加载
const profile = await loadCurrentAkUser()
return profile.id
// 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 ''
}
/**

View File

@@ -140,12 +140,78 @@ export async function checkinPrecheck(
try {
// 获取当前业务用户 IDak_users.id不是 Supabase Auth ID
console.warn('[CHECKIN RPC] 步骤 1: 获取当前业务用户 ID')
const workerId = await getCurrentAkUserId()
// 打印认证相关日志
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] 未登录,返回 NOT_LOGGED_IN')
return { distanceMeters: null, allowedRadiusMeters: 0, canCheckin: false, reasonCode: 'NOT_LOGGED_IN' }
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 参数
@@ -157,6 +223,7 @@ export async function checkinPrecheck(
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))

File diff suppressed because one or more lines are too long