文件

293 行
8.4 KiB
JavaScript

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

/**
* 全局双身份与角色守卫管理模块 (Role Guard Manager)
* 覆盖四大核心角色:
* 1. PLAYER: 普通买家/玩家
* 2. EXPERT: 陪玩大神/接单人
* 3. HOST: 车队房主/局长
* 4. ADMIN: 平台管理员/运营端
*/
export const ROLE_TYPES = {
PLAYER: 'PLAYER', // 发单人端(普通玩家/发起活动/找陪玩)
EXPERT: 'EXPERT', // 接单人端(大神接单/5km雷达定位/核销)
HOST: 'HOST', // 车队房主/局长(发起AA组局/动态调价/解散退款/到场核销)
ADMIN: 'ADMIN', // 平台管理员(资质审核/争议仲裁/大盘监管)
};
export const CERT_STATUS = {
NONE: 0, // 未提交
PENDING: 1, // 待审核
APPROVED: 2, // 审核通过
REJECTED: 3, // 驳回
};
const STORAGE_KEY_ROLE = 'PW_CURRENT_USER_ROLE';
const STORAGE_KEY_CERT = 'PW_EXPERT_CERT_STATUS';
export function getExpertCertStatus() {
try {
const saved = uni.getStorageSync(STORAGE_KEY_CERT);
if (saved !== '' && saved !== null && saved !== undefined) {
return Number(saved);
}
return CERT_STATUS.NONE;
} catch (e) {
return CERT_STATUS.NONE;
}
}
export function setExpertCertStatus(status) {
const target = Number(status);
try {
uni.setStorageSync(STORAGE_KEY_CERT, target);
} catch (e) {
console.error('Failed to persist cert status', e);
}
uni.$emit('cert-status-change', target);
if (target !== CERT_STATUS.APPROVED) {
setCurrentRole(ROLE_TYPES.PLAYER);
}
return target;
}
export function isExpertCertified() {
return getExpertCertStatus() === CERT_STATUS.APPROVED;
}
export function canSwitchToRole(targetRole) {
if (targetRole === ROLE_TYPES.EXPERT) {
if (!isExpertCertified()) {
return {
allowed: false,
reason: '尚未开通或通过大神实名认证,无法切换至接单模式',
};
}
}
return { allowed: true };
}
export function getCurrentRole() {
try {
const saved = uni.getStorageSync(STORAGE_KEY_ROLE);
if (saved === ROLE_TYPES.EXPERT) {
if (isExpertCertified()) {
return ROLE_TYPES.EXPERT;
}
return ROLE_TYPES.PLAYER;
}
return ROLE_TYPES.PLAYER;
} catch (e) {
return ROLE_TYPES.PLAYER;
}
}
export function setCurrentRole(role) {
let target = role === ROLE_TYPES.EXPERT ? ROLE_TYPES.EXPERT : ROLE_TYPES.PLAYER;
if (target === ROLE_TYPES.EXPERT && !isExpertCertified()) {
console.warn('[RoleGuard] 未认证大神禁止切换至接单端,已自动降级回玩家端');
target = ROLE_TYPES.PLAYER;
}
try {
uni.setStorageSync(STORAGE_KEY_ROLE, target);
} catch (e) {
console.error('Failed to persist user role', e);
}
uni.$emit('role-change', target);
return target;
}
export function toggleRole() {
const current = getCurrentRole();
if (current === ROLE_TYPES.PLAYER) {
if (!isExpertCertified()) {
return {
success: false,
currentRole: ROLE_TYPES.PLAYER,
message: '尚未通过大神实名认证,请先提交入驻认证',
};
}
setCurrentRole(ROLE_TYPES.EXPERT);
return {
success: true,
currentRole: ROLE_TYPES.EXPERT,
message: '已切换至大神接单模式',
};
} else {
setCurrentRole(ROLE_TYPES.PLAYER);
return {
success: true,
currentRole: ROLE_TYPES.PLAYER,
message: '已切回发单人端',
};
}
}
export function isUserLoggedIn() {
try {
if (typeof uni === 'undefined') return false;
const uid = uni.getStorageSync('userId');
const hasLoggedOut = uni.getStorageSync('user_has_logged_out');
if (hasLoggedOut === '1') return false;
return Boolean(uid && String(uid).trim() !== '' && String(uid) !== '0');
} catch (e) {
return false;
}
}
export function isUserBanned() {
try {
if (typeof uni === 'undefined') return false;
const uInfo = uni.getStorageSync('userInfo') || uni.getStorageSync('PW_USER_INFO') || uni.getStorageSync('USER_INFO');
return Boolean(uInfo && (uInfo.status === '已封禁' || uInfo.status === '封禁'));
} catch (e) {
return false;
}
}
// 必须登录才能访问的受保护业务页面清单 (未登录游客访问自动拦截并跳登录)
export const AUTH_GUARD_PAGES = [
'/pages/activity/create', // 发单/发起组队
'/pages/order/create', // 确认下单/预约
'/pages/order/list', // 我的订单中心
'/pages/activity/my-ticket', // 我的活动门票 (4位核销码)
'/pages/user/recharge', // 钱包充值
'/pages/user/withdraw', // 收益提现
'/pages/user/withdraw-records', // 提现审批明细
'/pages/user/bill-records', // 账单明细
'/pages/user/promote', // 推广赚钱
'/pages/user/favorites', // 我的收藏
'/pages/user/reports', // 我的举报与维权
'/pages/user/settings', // 系统与隐私设置
'/pages/expert/apply', // 大神入驻认证
'/pages/expert/workspace', // 大神接单工作台
'/pages/expert/radar', // 我要接单 (附近5km雷达)
'/pages/expert/hall' // 接单大厅
];
export function ensureLoginOrRedirect(redirectUrl = '') {
if (!isUserLoggedIn()) {
uni.showToast({
title: '请先登录账号',
icon: 'none',
duration: 1500
});
setTimeout(() => {
const target = redirectUrl ? `/pages/auth/login?redirect=${encodeURIComponent(redirectUrl)}` : '/pages/auth/login';
uni.navigateTo({
url: target,
fail: () => {
uni.switchTab({ url: '/pages/user/index' });
}
});
}, 350);
return false;
}
if (isUserBanned()) {
uni.showToast({
title: '账号已被封禁',
icon: 'none',
duration: 2000
});
return false;
}
return true;
}
// 非大神禁止访问的受保护页面清单
export const EXPERT_GUARD_PAGES = [
'/pages/expert/workspace',
'/pages/expert/radar',
'/pages/expert/hall',
];
export function checkRoleGuard(pageUrl) {
if (!pageUrl) return { allowed: true };
const pureUrl = pageUrl.split('?')[0];
// 1. 登录守卫拦截 (针对未登录游客)
if (AUTH_GUARD_PAGES.includes(pureUrl)) {
if (!isUserLoggedIn()) {
return {
allowed: false,
code: 'UNAUTHORIZED',
redirectUrl: `/pages/auth/login?redirect=${encodeURIComponent(pageUrl)}`,
message: '该功能需要登录账号,正在为您跳转登录...',
};
}
}
// 2. 大神身份守卫拦截 (针对已登录但非认证大神的普通玩家)
if (EXPERT_GUARD_PAGES.includes(pureUrl)) {
if (!isExpertCertified()) {
return {
allowed: false,
code: 'FORBIDDEN_NOT_EXPERT',
redirectUrl: '/pages/user/index',
message: '非大神禁止访问接单工作台与雷达,请先在个人中心完成实名认证',
};
}
}
return { allowed: true };
}
let guardInstalled = false;
export function installRoleGuard() {
if (guardInstalled) return;
guardInstalled = true;
const routeMethods = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'];
routeMethods.forEach((method) => {
try {
if (typeof uni !== 'undefined' && typeof uni.addInterceptor === 'function') {
uni.addInterceptor(method, {
invoke(args) {
if (!args || !args.url) return true;
const check = checkRoleGuard(args.url);
if (!check.allowed) {
uni.showToast({
title: check.message || '请先登录账号',
icon: 'none',
duration: 2000,
});
if (check.redirectUrl) {
setTimeout(() => {
uni.navigateTo({
url: check.redirectUrl,
fail: () => {
uni.switchTab({ url: '/pages/user/index' });
}
});
}, 350);
}
return false;
}
return true;
},
});
}
} catch (e) {}
});
}
let isRedirectingToExpert = false;
export function safeRedirectToExpertHall(target = '/pages/expert/hall') {
if (isRedirectingToExpert) {
return true;
}
if (getCurrentRole() === ROLE_TYPES.EXPERT && isExpertCertified()) {
isRedirectingToExpert = true;
uni.reLaunch({
url: target || '/pages/expert/hall',
complete: () => {
setTimeout(() => {
isRedirectingToExpert = false;
}, 500);
}
});
return true;
}
return false;
}