完成consumer端同步
This commit is contained in:
@@ -143,6 +143,7 @@
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
import { IS_TEST_MODE, PUSH_SERVER_URL } from '@/ak/config.uts'
|
||||
import { getCurrentUser, logout, setIsLoggedIn, setUserProfile } from '@/utils/store.uts'
|
||||
@@ -175,65 +176,101 @@ const TEST_ACCOUNT = 'test19@163.com'
|
||||
const TEST_PASSWORD = 'huang123456'
|
||||
|
||||
// ✅ account/password 直接以常量作初始值,上线/刷新立即生效,不再依赖 onMounted 延迟赋值
|
||||
const account = ref<string>(TEST_ACCOUNT)
|
||||
const password = ref<string>(TEST_PASSWORD)
|
||||
const account = ref<string>('')
|
||||
const password = ref<string>('')
|
||||
const captcha = ref<string>('')
|
||||
const isPasswordVisible = ref<boolean>(false)
|
||||
|
||||
const isLoading = ref<boolean>(false)
|
||||
const redirectPath = ref<string>('')
|
||||
const loginMode = ref<string>('consumer')
|
||||
|
||||
/**
|
||||
* 【核心函数】:登录成功后,多条件校验是否为商家角色
|
||||
* 优先级: session_uid (auth_id) -> id -> normalized email
|
||||
*/
|
||||
const checkAdminOrMerchantAccess = async (uid: string, rawEmail: string) : Promise<UTSJSONObject | null> => {
|
||||
const email = rawEmail.trim().toLowerCase()
|
||||
console.log(`🔍 开始校验后台或商家端角色 -> UID: ${uid}, Email: ${email}`)
|
||||
const getOptionText = (opts: UTSJSONObject, key: string): string => {
|
||||
try {
|
||||
const value = opts.getString(key)
|
||||
if (value != null && value !== '') {
|
||||
return value
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
const parseRoleData = (dataArray: any | null): UTSJSONObject | null => {
|
||||
if (Array.isArray(dataArray) && dataArray.length > 0) {
|
||||
const obj = dataArray[0] as UTSJSONObject
|
||||
const role = obj.getString('role')
|
||||
const id = obj.getString('id')
|
||||
console.log('✅ 匹配成功,role:', role)
|
||||
if ((role === 'merchant' || role === 'admin') && id != null) {
|
||||
return { id, role } as UTSJSONObject
|
||||
}
|
||||
}
|
||||
return null
|
||||
try {
|
||||
const rawValue = opts[key]
|
||||
if (rawValue != null) {
|
||||
return '' + rawValue
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
const isMerchantMode = (): boolean => {
|
||||
return loginMode.value === 'merchant'
|
||||
}
|
||||
|
||||
onLoad((opts) => {
|
||||
if (opts != null) {
|
||||
const optsObj = opts as UTSJSONObject
|
||||
redirectPath.value = getOptionText(optsObj, 'redirect')
|
||||
const parsedMode = getOptionText(optsObj, 'mode')
|
||||
if (parsedMode === 'merchant') {
|
||||
loginMode.value = 'merchant'
|
||||
uni.setNavigationBarTitle({ title: '商家登录' })
|
||||
} else {
|
||||
loginMode.value = 'consumer'
|
||||
uni.setNavigationBarTitle({ title: '用户登录' })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
type ParseRoleInput = UTSJSONObject | Array<UTSJSONObject> | string | null
|
||||
|
||||
const checkUserAccess = async (uid: string, rawEmail: string): Promise<UTSJSONObject | null> => {
|
||||
const email = rawEmail.trim().toLowerCase()
|
||||
|
||||
const parseRoleData = (dataArray: ParseRoleInput): UTSJSONObject | null => {
|
||||
if (Array.isArray(dataArray)) {
|
||||
const arr = dataArray as Array<UTSJSONObject>
|
||||
if (arr.length > 0) {
|
||||
const obj = arr[0]
|
||||
const role = obj.getString('role') ?? ''
|
||||
const id = obj.getString('id') ?? ''
|
||||
const allowed = isMerchantMode() ? (role === 'merchant' || role === 'admin') : (role === 'consumer' || role === 'customer')
|
||||
if (allowed && id !== '') {
|
||||
return { id, role } as UTSJSONObject
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 尝试按 auth_id 查询
|
||||
let res = await supa.from('ak_users').select('id, role').eq('auth_id', uid).execute()
|
||||
let parsed = parseRoleData(res.data)
|
||||
if (parsed != null) return parsed
|
||||
try {
|
||||
let res = await supa.from('ak_users').select('id, role').eq('auth_id', uid).execute()
|
||||
let parsed = parseRoleData(res.data)
|
||||
if (parsed != null) return parsed
|
||||
|
||||
// 2. 尝试按 id 查询 (兼容老数据)
|
||||
res = await supa.from('ak_users').select('id, role').eq('id', uid).execute()
|
||||
parsed = parseRoleData(res.data)
|
||||
if (parsed != null) return parsed
|
||||
res = await supa.from('ak_users').select('id, role').eq('id', uid).execute()
|
||||
parsed = parseRoleData(res.data)
|
||||
if (parsed != null) return parsed
|
||||
|
||||
// 3. 尝试按 email 兜底查询
|
||||
if (email !== '') {
|
||||
res = await supa.from('ak_users').select('id, role').eq('email', email).execute()
|
||||
const dataArray = res.data
|
||||
if (Array.isArray(dataArray) && dataArray.length > 1) {
|
||||
console.error('⚠️ 警告: 按 email 查到多条 ak_users 记录,取第一条校验。Email:', email)
|
||||
}
|
||||
parsed = parseRoleData(dataArray)
|
||||
if (parsed != null) return parsed
|
||||
}
|
||||
if (email !== '') {
|
||||
res = await supa.from('ak_users').select('id, role').eq('email', email).execute()
|
||||
parsed = parseRoleData(res.data)
|
||||
if (parsed != null) return parsed
|
||||
}
|
||||
|
||||
console.error('❌ 未能在 ak_users 中找到该用户的有效角色记录')
|
||||
throw new Error('NOT_REGISTERED')
|
||||
} catch (e) {
|
||||
console.error('❌ 查询角色过程异常:', e)
|
||||
if (e instanceof Error && e.message === 'NOT_REGISTERED') {
|
||||
throw new Error('该账户无后台或商家端权限,请联系管理员核对')
|
||||
}
|
||||
throw new Error('后台身份校验失败,请联系管理员检查用户数据')
|
||||
}
|
||||
throw new Error('NOT_REGISTERED')
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message === 'NOT_REGISTERED') {
|
||||
if (isMerchantMode()) {
|
||||
throw new Error('该账户不是商家/管理员,或数据库中不存在该账户')
|
||||
}
|
||||
throw new Error('该账户不是消费者,或数据库中不存在该账户')
|
||||
}
|
||||
if (isMerchantMode()) {
|
||||
throw new Error('商家身份校验失败,请联系管理员检查用户数据')
|
||||
}
|
||||
throw new Error('消费者身份校验失败,请联系管理员检查用户数据')
|
||||
}
|
||||
}
|
||||
|
||||
const codeDisabled = ref<boolean>(false)
|
||||
@@ -241,29 +278,54 @@ const codeText = ref<string>('获取验证码')
|
||||
const codeTimer = ref<number>(0)
|
||||
const codeCountdown = ref<number>(0)
|
||||
|
||||
const goConsumerHome = (): void => {
|
||||
uni.reLaunch({ url: '/pages/main/index' })
|
||||
}
|
||||
|
||||
const goTargetHome = (): void => {
|
||||
if (isMerchantMode()) {
|
||||
uni.reLaunch({ url: '/pages/mall/merchant/index' })
|
||||
} else {
|
||||
goConsumerHome()
|
||||
}
|
||||
}
|
||||
|
||||
const isTabBarPage = (url: string): boolean => {
|
||||
return url === '/pages/main/index'
|
||||
|| url === '/pages/main/category'
|
||||
|| url === '/pages/main/messages'
|
||||
|| url === '/pages/main/cart'
|
||||
|| url === '/pages/main/profile'
|
||||
}
|
||||
|
||||
const navigateToRedirect = (redirect: string): void => {
|
||||
const target = decodeURIComponent(redirect) ?? ''
|
||||
if (target === '') {
|
||||
goTargetHome()
|
||||
return
|
||||
}
|
||||
|
||||
if (isTabBarPage(target)) {
|
||||
uni.switchTab({ url: target })
|
||||
return
|
||||
}
|
||||
|
||||
uni.redirectTo({ url: target })
|
||||
}
|
||||
|
||||
const checkLoginStatus = (): void => {
|
||||
try {
|
||||
// 检查是否已有 Session
|
||||
const sessionInfo = supa.getSession()
|
||||
if (sessionInfo != null && sessionInfo.user != null) {
|
||||
const pages = getCurrentPages() as any[]
|
||||
const currentPage = pages.length > 0 ? pages[pages.length - 1] : null
|
||||
const opts = currentPage?.options as any
|
||||
const redirect = opts?.redirect as string | null
|
||||
|
||||
const storedId = uni.getStorageSync('user_id')
|
||||
const hasSession = sessionInfo != null && sessionInfo.user != null
|
||||
const hasStorage = storedId != null && (storedId as string) !== ''
|
||||
if (hasSession && hasStorage) {
|
||||
const redirect = redirectPath.value
|
||||
console.log('检测到已有会话, 执行重定向...')
|
||||
if (redirect != null && redirect.length > 0) {
|
||||
uni.redirectTo({ url: decodeURIComponent(redirect) })
|
||||
if (redirect !== '') {
|
||||
navigateToRedirect(redirect)
|
||||
} else {
|
||||
// #ifdef H5
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/merchant/index' })
|
||||
// #endif
|
||||
// #ifndef H5 || MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
goTargetHome()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -301,6 +363,11 @@ onMounted(() => {
|
||||
isDefault: account.value === TEST_ACCOUNT
|
||||
})
|
||||
|
||||
if (isMerchantMode()) {
|
||||
account.value = TEST_ACCOUNT
|
||||
password.value = TEST_PASSWORD
|
||||
}
|
||||
|
||||
checkLoginStatus()
|
||||
})
|
||||
|
||||
@@ -384,7 +451,7 @@ const handleLogin = async () => {
|
||||
bio: 'Administrator',
|
||||
avatar_url: '/static/logo.png',
|
||||
preferred_language: 'zh-CN',
|
||||
role: 'admin',
|
||||
role: isMerchantMode() ? 'admin' : 'consumer',
|
||||
school_id: '',
|
||||
grade_id: '',
|
||||
class_id: ''
|
||||
@@ -394,51 +461,36 @@ const handleLogin = async () => {
|
||||
|
||||
uni.showToast({ title: '管理员登录成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
// #ifdef H5
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/merchant/index' })
|
||||
// #endif
|
||||
// #ifndef H5 || MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
goTargetHome()
|
||||
}, 500)
|
||||
return
|
||||
}
|
||||
|
||||
// 演示模式:测试账号直接走本地 bypass,不请求后端(WeChat MP 禁止 HTTP/裸 IP 请求)
|
||||
if (IS_TEST_MODE && account.value === TEST_ACCOUNT && password.value === TEST_PASSWORD) {
|
||||
if (IS_TEST_MODE && isMerchantMode() && account.value === TEST_ACCOUNT && password.value === TEST_PASSWORD) {
|
||||
console.log('[LoginBypass] merchant test bypass hit')
|
||||
setIsLoggedIn(true)
|
||||
const merchantProfile = {
|
||||
id: 'demo-merchant-001',
|
||||
username: '演示机构',
|
||||
const demoProfile = {
|
||||
id: isMerchantMode() ? 'demo-merchant-001' : 'demo-consumer-001',
|
||||
username: isMerchantMode() ? '演示机构' : '演示用户',
|
||||
email: TEST_ACCOUNT,
|
||||
gender: 'unknown',
|
||||
birthday: '',
|
||||
height_cm: 0,
|
||||
weight_kg: 0,
|
||||
bio: '医养服务演示机构',
|
||||
bio: isMerchantMode() ? '医养服务演示机构' : '医养服务演示用户',
|
||||
avatar_url: '/static/logo.png',
|
||||
preferred_language: 'zh-CN',
|
||||
role: 'merchant',
|
||||
role: isMerchantMode() ? 'merchant' : 'consumer',
|
||||
school_id: '',
|
||||
grade_id: '',
|
||||
class_id: ''
|
||||
} as UserProfile
|
||||
setUserProfile(merchantProfile)
|
||||
uni.setStorageSync('user_id', 'demo-merchant-001')
|
||||
setUserProfile(demoProfile)
|
||||
uni.setStorageSync('user_id', demoProfile.id ?? '')
|
||||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
// #ifdef H5
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/merchant/index' })
|
||||
// #endif
|
||||
// #ifndef H5 || MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
goTargetHome()
|
||||
}, 500)
|
||||
return
|
||||
}
|
||||
@@ -481,27 +533,25 @@ const handleLogin = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 【核心逻辑】:执行商家端角色准入校验
|
||||
// 优先使用 session user id 来查询数据库里的真实 user 数据兜底校验
|
||||
// 2) 按当前登录模式校验消费者或商家身份
|
||||
const sessionUser = result.user
|
||||
let sessionUid = sessionUser?.getString('id') ?? ''
|
||||
|
||||
const accessData = await checkAdminOrMerchantAccess(sessionUid, account.value)
|
||||
const accessData = await checkUserAccess(sessionUid, account.value)
|
||||
if (accessData == null) {
|
||||
await supa.signOut()
|
||||
logout()
|
||||
throw new Error('该账户无后台或商家端权限')
|
||||
throw new Error(isMerchantMode() ? '该账户无商家端权限' : '该账户无消费者权限')
|
||||
}
|
||||
|
||||
const currRole = accessData.getString('role')
|
||||
const currId = accessData.getString('id')
|
||||
// uni.setStorageSync('adminRole', currRole) // 移除本地缓存依赖,强制按单例会话状态
|
||||
|
||||
if (currRole === 'merchant') {
|
||||
// uni.setStorageSync('merchant_id', currId) // 移除本地缓存依赖,强制按单例会话状态
|
||||
} else {
|
||||
uni.removeStorageSync('merchant_id')
|
||||
}
|
||||
if (isMerchantMode()) {
|
||||
if (currRole !== 'merchant') {
|
||||
uni.removeStorageSync('merchant_id')
|
||||
}
|
||||
} else {
|
||||
uni.removeStorageSync('merchant_id')
|
||||
}
|
||||
} else {
|
||||
uni.showToast({ title: '手机号密码登录功能开发中', icon: 'none' })
|
||||
return
|
||||
@@ -553,22 +603,11 @@ const handleLogin = async () => {
|
||||
|
||||
// 成功跳转逻辑
|
||||
setTimeout(() => {
|
||||
const pages = getCurrentPages() as any[]
|
||||
const currentPage = pages.length > 0 ? pages[pages.length - 1] : null
|
||||
const opts = currentPage?.options as any
|
||||
const redirect = opts?.redirect as string | null
|
||||
if (redirect != null && redirect.length > 0) {
|
||||
uni.redirectTo({ url: decodeURIComponent(redirect) })
|
||||
const redirect = redirectPath.value
|
||||
if (redirect !== '') {
|
||||
navigateToRedirect(redirect)
|
||||
} else {
|
||||
// #ifdef H5
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/merchant/index' })
|
||||
// #endif
|
||||
// #ifndef H5 || MP-WEIXIN
|
||||
uni.reLaunch({ url: '/pages/mall/admin/homePage/index' })
|
||||
// #endif
|
||||
goTargetHome()
|
||||
}
|
||||
}, 500)
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user