- Update food, outfits, props, home-decor pages and components - Add permissions page and sidebar updates - Update API client and all API modules (auth, food, dances, etc.) - Add card model migrations for optional fields - Update Django views, serializers, and authentication - Add affinity level migrations and user app updates - Add project documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
272 lines
6.9 KiB
TypeScript
272 lines
6.9 KiB
TypeScript
// 基础API客户端配置
|
||
import axios from 'axios';
|
||
|
||
/**
|
||
* 基础API路径 - 从环境变量获取
|
||
* 环境变量配置文件优先级:
|
||
* .env.local > .env.development (开发环境) / .env.production (生产环境) > .env
|
||
*/
|
||
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000/api"
|
||
|
||
// 创建API客户端实例
|
||
export const apiClient = axios.create({
|
||
baseURL: API_BASE_URL,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
});
|
||
|
||
// 添加请求拦截器,自动为请求添加认证令牌
|
||
apiClient.interceptors.request.use((config) => {
|
||
if (typeof window !== 'undefined') {
|
||
const token = localStorage.getItem('auth_token');
|
||
console.log('🔍 Token检查:', token ? `存在 (${token.substring(0, 20)}...)` : '不存在');
|
||
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`;
|
||
console.log('✅ Token已添加到请求头');
|
||
} else {
|
||
console.warn('⚠️ 未找到auth_token,请求将不包含认证');
|
||
}
|
||
} else {
|
||
console.log('🔍 服务端渲染环境,跳过token检查');
|
||
}
|
||
|
||
console.log('📡 发送请求:', config.method?.toUpperCase(), config.url);
|
||
console.log('📋 请求头:', config.headers);
|
||
|
||
return config;
|
||
}, (error) => {
|
||
console.error('❌ 请求拦截器错误:', error);
|
||
return Promise.reject(error);
|
||
});
|
||
|
||
// 添加响应拦截器,用于处理认证错误
|
||
apiClient.interceptors.response.use(
|
||
(response) => {
|
||
console.log('✅ 响应成功:', response.status, response.config.url);
|
||
return response;
|
||
},
|
||
(error) => {
|
||
console.error('❌ 响应错误:', error.response?.status, error.config?.url);
|
||
|
||
// 处理401未授权错误(token过期或无效)
|
||
if (error.response?.status === 401) {
|
||
console.warn('🚫 认证失败,token可能过期或无效');
|
||
console.log('响应详情:', error.response?.data);
|
||
localStorage.removeItem('auth_token');
|
||
if (typeof window !== 'undefined' && window.location.pathname !== '/login') {
|
||
window.location.href = '/login';
|
||
}
|
||
}
|
||
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// 模拟网络延迟
|
||
export const simulateDelay = async (minMs = 300, maxMs = 800) => {
|
||
const delay = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs
|
||
await new Promise((resolve) => setTimeout(resolve, delay))
|
||
}
|
||
|
||
// 模拟API响应
|
||
export const mockResponse = async <T>(data: T, error?: string) => {
|
||
await simulateDelay()
|
||
|
||
// 随机模拟错误(概率约5%)
|
||
if (!error && Math.random() < 0.05) {
|
||
throw new Error("模拟随机网络错误,请重试")
|
||
}
|
||
|
||
// 如果提供了错误信息,则抛出错误
|
||
if (error) {
|
||
throw new Error(error)
|
||
}
|
||
|
||
return data
|
||
}
|
||
|
||
// API响应类型
|
||
export interface ApiResponse<T> {
|
||
success: boolean;
|
||
code: number;
|
||
message: string;
|
||
data: T;
|
||
}
|
||
|
||
// 分页响应类型
|
||
export interface PaginatedResponse<T> {
|
||
items: T[];
|
||
total: number;
|
||
page: number;
|
||
pageSize: number;
|
||
totalPages: number;
|
||
}
|
||
|
||
// 分页请求参数
|
||
export interface PaginationParams {
|
||
page?: number;
|
||
pageSize?: number;
|
||
search?: string;
|
||
}
|
||
|
||
// 获取当前环境
|
||
export const getCurrentEnvironment = (): 'development' | 'production' | 'test' => {
|
||
return (process.env.NODE_ENV as 'development' | 'production' | 'test') || 'development'
|
||
}
|
||
|
||
// 判断是否为开发环境
|
||
export const isDevelopment = (): boolean => getCurrentEnvironment() === 'development'
|
||
|
||
// 判断是否为生产环境
|
||
export const isProduction = (): boolean => getCurrentEnvironment() === 'production'
|
||
|
||
// 模拟用户数据
|
||
export const mockUsers = [
|
||
{
|
||
id: "1",
|
||
name: "管理员",
|
||
email: "admin@example.com",
|
||
role: "超级管理员",
|
||
status: "活跃",
|
||
registeredAt: "2023-01-01",
|
||
lastLogin: "今天 08:45",
|
||
phone: "13800138000",
|
||
address: "北京市海淀区中关村",
|
||
permissions: [
|
||
"仪表盘查看",
|
||
"用户管理",
|
||
"角色权限管理",
|
||
"AI模型管理",
|
||
"服装管理",
|
||
"道具管理",
|
||
"歌曲管理",
|
||
"系统设置",
|
||
],
|
||
loginHistory: [
|
||
{ date: "2023-05-15 08:45", ip: "192.168.1.1", device: "Chrome / Windows" },
|
||
{ date: "2023-05-14 15:30", ip: "192.168.1.1", device: "Chrome / Windows" },
|
||
{ date: "2023-05-13 09:20", ip: "192.168.1.1", device: "Chrome / Windows" },
|
||
],
|
||
},
|
||
{
|
||
id: "2",
|
||
name: "张三",
|
||
email: "zhangsan@example.com",
|
||
role: "内容管理员",
|
||
status: "活跃",
|
||
registeredAt: "2023-03-15",
|
||
lastLogin: "昨天 16:30",
|
||
phone: "13900139000",
|
||
permissions: ["仪表盘查看", "服装管理", "道具管理", "歌曲管理"],
|
||
loginHistory: [
|
||
{ date: "2023-05-14 16:30", ip: "192.168.1.2", device: "Safari / macOS" },
|
||
{ date: "2023-05-13 14:20", ip: "192.168.1.2", device: "Safari / macOS" },
|
||
],
|
||
},
|
||
{
|
||
id: "3",
|
||
name: "李四",
|
||
email: "lisi@example.com",
|
||
role: "AI模型管理员",
|
||
status: "活跃",
|
||
registeredAt: "2023-05-20",
|
||
lastLogin: "3天前",
|
||
permissions: ["仪表盘查看", "AI模型管理"],
|
||
loginHistory: [{ date: "2023-05-12 10:15", ip: "192.168.1.3", device: "Firefox / Ubuntu" }],
|
||
},
|
||
{
|
||
id: "4",
|
||
name: "王五",
|
||
email: "wangwu@example.com",
|
||
role: "卡牌管理员",
|
||
status: "活跃",
|
||
registeredAt: "2023-07-10",
|
||
lastLogin: "1周前",
|
||
permissions: ["仪表盘查看", "服装管理", "道具管理", "家居装饰管理"],
|
||
},
|
||
{
|
||
id: "5",
|
||
name: "赵六",
|
||
email: "zhaoliu@example.com",
|
||
role: "查看者",
|
||
status: "未激活",
|
||
registeredAt: "2023-09-05",
|
||
permissions: ["仪表盘查看"],
|
||
},
|
||
]
|
||
|
||
// 模拟角色数据
|
||
export const mockRoles = [
|
||
{
|
||
id: "1",
|
||
name: "超级管理员",
|
||
description: "拥有系统所有权限",
|
||
userCount: 1,
|
||
createdAt: "2023-01-01",
|
||
status: "系统角色",
|
||
permissions: {
|
||
dashboard: true,
|
||
users: true,
|
||
roles: true,
|
||
ai_models: true,
|
||
outfits: true,
|
||
props: true,
|
||
songs: true,
|
||
settings: true,
|
||
},
|
||
},
|
||
{
|
||
id: "2",
|
||
name: "内容管理员",
|
||
description: "管理系统内容",
|
||
userCount: 2,
|
||
createdAt: "2023-01-15",
|
||
status: "系统角色",
|
||
permissions: {
|
||
dashboard: true,
|
||
outfits: true,
|
||
props: true,
|
||
songs: true,
|
||
},
|
||
},
|
||
{
|
||
id: "3",
|
||
name: "AI模型管理员",
|
||
description: "管理AI模型",
|
||
userCount: 1,
|
||
createdAt: "2023-02-10",
|
||
status: "系统角色",
|
||
permissions: {
|
||
dashboard: true,
|
||
ai_models: true,
|
||
},
|
||
},
|
||
{
|
||
id: "4",
|
||
name: "卡牌管理员",
|
||
description: "管理卡牌内容",
|
||
userCount: 1,
|
||
createdAt: "2023-03-05",
|
||
status: "自定义角色",
|
||
permissions: {
|
||
dashboard: true,
|
||
outfits: true,
|
||
props: true,
|
||
home_decor: true,
|
||
},
|
||
},
|
||
{
|
||
id: "5",
|
||
name: "查看者",
|
||
description: "只能查看仪表盘",
|
||
userCount: 1,
|
||
createdAt: "2023-04-20",
|
||
status: "自定义角色",
|
||
permissions: {
|
||
dashboard: true,
|
||
},
|
||
},
|
||
]
|