import { ReactNode } from 'react'
import { Navigate, useLocation } from 'react-router-dom'
import { useAuth } from '../auth'
import { extractTenantSlug } from '../tenant'
import { Result, Button } from 'antd'
import { LockOutlined } from '@ant-design/icons'
/**
* 路由守卫组件
*
* 检查顺序:
* 1. 检查是否已登录(未登录→携带租户标识重定向到登录页)
* 2. 检查模块权限(无权限→显示权限不足页面)
* 3. 有权限→渲染子组件
*
* @version 2026-03-14 V4.0:租户感知重定向
* - 期刊租户路径(/jtim/*)→ /jtim/login?redirect=/jtim/dashboard
* - 主平台路径(/rvw/*, /ai-qa/* 等)→ /login(行为不变)
* - extractTenantSlug 由 useTenantObserver 模块统一维护,自动排除所有注册模块路径
*/
/**
* 构造未登录时的登录跳转目标。
*
* 对齐 App.tsx 中已定义的路由:
*
* - 期刊租户下:/jtim/login?redirect=%2Fjtim%2Fdashboard
* - 主站下:/login(保持原有行为,向后兼容)
*/
function buildLoginRedirect(pathname: string): string {
const tenantSlug = extractTenantSlug(pathname)
if (!tenantSlug) return '/login'
const redirectParam = encodeURIComponent(pathname)
return `/${tenantSlug}/login?redirect=${redirectParam}`
}
interface RouteGuardProps {
/** 子组件 */
children: ReactNode
/** 所需模块代码(如 'AIA', 'PKB') */
requiredModule?: string
/** 模块名称(用于显示友好提示) */
moduleName?: string
/** 是否重定向到首页(默认显示无权限页面) */
redirectToHome?: boolean
/** 兼容旧参数(废弃) */
requiredVersion?: any
}
const RouteGuard = ({
children,
requiredModule,
moduleName,
redirectToHome = false,
}: RouteGuardProps) => {
const location = useLocation()
const { isAuthenticated, isLoading, user, hasModule } = useAuth()
// 加载中显示空白
if (isLoading) {
return null
}
// 1. 检查是否登录
if (!isAuthenticated) {
// V4.0 租户感知重定向:保留 tenantSlug,让登录页渲染对应期刊品牌
const loginTarget = buildLoginRedirect(location.pathname)
return
}
// 2. 检查模块权限(如果指定了 requiredModule)
if (requiredModule && !hasModule(requiredModule)) {
console.log('🔒 模块权限不足:', {
module: moduleName || requiredModule,
requiredModule,
userModules: user?.modules,
userId: user?.id,
timestamp: new Date().toISOString(),
})
if (redirectToHome) {
return
}
// 显示权限不足页面
return (
}
title="权限不足"
subTitle={`您暂无访问 "${moduleName || requiredModule}" 模块的权限,请联系管理员开通。`}
extra={
}
/>
)
}
// 3. 有权限,渲染子组件
return <>{children}>
}
export default RouteGuard
/**
* 🛡️ 路由守卫最佳实践:
*
* 1. 双重防护策略:
* - 第一道防线:TopNavigation(用户看不到无权限模块)
* - 第二道防线:RouteGuard(防止URL直接访问)
* - 为什么需要两道?防止用户通过浏览器直接输入URL绕过导航
*
* 2. 权限检查时机:
* ✅ 路由渲染前检查(RouteGuard)
* ✅ API请求前检查(后端)
* ✅ 组件渲染前检查(usePermission)
*
* 3. 无权限时的处理策略:
* 【推荐】显示PermissionDenied页面
* - 优点:引导用户升级,商业转化机会
* - 缺点:需要额外页面
* 【备选】重定向到首页
* - 优点:简单直接
* - 缺点:用户体验不好,不利于转化
*
* 4. 后续演进计划:
* 【Week 2 Day 8-9】对接后端JWT认证
* - 实现真实的登录流程
* - 从token解析用户权限
* - 处理token过期
*
* 【Week 3-4】ASL模块测试
* - ASL需要advanced权限
* - 测试权限控制是否生效
* - 优化无权限页面的转化率
*
* 【Week 5+】完善权限系统
* - 动态权限配置
* - 功能级权限控制(不仅是模块级)
* - 权限变更实时生效
*
* 5. 安全注意事项:
* ⚠️ 前端权限检查不是安全保障,仅用于用户体验
* ✅ 后端必须进行权限验证(真正的安全防线)
* ✅ 敏感数据不应该发送到前端
* ✅ API调用必须携带认证token
*/