Twilioapp-admin/lib/api-service.ts
mars 211e0306b5 feat: 集成真实数据库连接和API服务
- 更新 .env.local 配置为真实的 Supabase 项目连接
- 创建完整的 API 服务层 (lib/api-service.ts)
- 创建数据库类型定义 (types/database.ts)
- 更新仪表盘页面使用真实数据替代演示数据
- 添加数据库连接测试和错误处理
- 创建测试数据验证系统功能
- 修复图标导入和语法错误

系统现在已连接到真实的 Supabase 数据库,可以正常显示统计数据和最近活动。
2025-07-03 13:12:54 +08:00

992 lines
23 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createClient } from '@supabase/supabase-js'
import { Database } from '../types/database'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)
// 用户相关接口
export interface User {
id: string;
name: string;
email: string;
phone: string;
userType: 'individual' | 'enterprise' | 'admin';
status: 'active' | 'inactive' | 'pending';
createdAt: string;
lastLogin?: string;
avatar?: string;
company?: string;
totalOrders: number;
}
// 仪表板统计数据接口
export interface DashboardStats {
totalUsers: number;
totalOrders: number;
totalRevenue: number;
activeInterpreters: number;
todayOrders: number;
pendingOrders: number;
completedOrders: number;
totalCalls: number;
}
// 最近活动接口
export interface RecentActivity {
id: string;
type: 'order' | 'call' | 'user' | 'payment';
title: string;
description: string;
time: string;
status: 'success' | 'pending' | 'warning' | 'error';
}
// API响应接口
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
message?: string;
}
// 用户过滤参数
export interface UserFilters {
search?: string;
userType?: 'all' | 'individual' | 'enterprise' | 'admin';
status?: 'all' | 'active' | 'inactive' | 'pending';
page?: number;
limit?: number;
}
class ApiService {
private baseUrl = '/api';
// 通用请求方法
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
try {
const token = localStorage.getItem('access_token');
const response = await fetch(`${this.baseUrl}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
...options.headers,
},
...options,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error(`API request failed: ${endpoint}`, error);
return {
success: false,
error: error instanceof Error ? error.message : '请求失败',
};
}
}
// 获取仪表板统计数据
async getDashboardStats(): Promise<ApiResponse<DashboardStats>> {
// 先尝试从真实API获取数据
const response = await this.request<DashboardStats>('/dashboard/stats');
// 如果API失败返回模拟数据
if (!response.success) {
return {
success: true,
data: {
totalUsers: 1248,
totalOrders: 3567,
totalRevenue: 245680,
activeInterpreters: 45,
todayOrders: 23,
pendingOrders: 12,
completedOrders: 3544,
totalCalls: 2890,
},
};
}
return response;
}
// 获取最近活动
async getRecentActivities(): Promise<ApiResponse<RecentActivity[]>> {
const response = await this.request<RecentActivity[]>('/dashboard/activities');
if (!response.success) {
return {
success: true,
data: [
{
id: '1',
type: 'order',
title: '新订单创建',
description: '用户张三创建了文档翻译订单',
time: '2分钟前',
status: 'success'
},
{
id: '2',
type: 'call',
title: '通话服务完成',
description: '英语口译通话服务已完成',
time: '5分钟前',
status: 'success'
},
{
id: '3',
type: 'user',
title: '新用户注册',
description: '企业用户ABC公司完成注册',
time: '10分钟前',
status: 'success'
},
{
id: '4',
type: 'payment',
title: '付款待处理',
description: '订单#1234的付款需要审核',
time: '15分钟前',
status: 'warning'
},
{
id: '5',
type: 'order',
title: '订单状态更新',
description: '文档翻译订单已交付',
time: '20分钟前',
status: 'success'
}
],
};
}
return response;
}
// 获取用户列表
async getUsers(filters: UserFilters = {}): Promise<ApiResponse<{ users: User[]; total: number }>> {
const queryParams = new URLSearchParams();
if (filters.search) queryParams.append('search', filters.search);
if (filters.userType && filters.userType !== 'all') queryParams.append('userType', filters.userType);
if (filters.status && filters.status !== 'all') queryParams.append('status', filters.status);
if (filters.page) queryParams.append('page', filters.page.toString());
if (filters.limit) queryParams.append('limit', filters.limit.toString());
const response = await this.request<{ users: User[]; total: number }>(
`/users?${queryParams.toString()}`
);
if (!response.success) {
// 返回模拟数据
const mockUsers: User[] = [
{
id: '1',
name: '张三',
email: 'zhangsan@example.com',
phone: '13800138001',
userType: 'individual',
status: 'active',
createdAt: '2024-01-15',
lastLogin: '2024-01-20 10:30',
totalOrders: 5
},
{
id: '2',
name: 'ABC公司',
email: 'contact@abc.com',
phone: '400-123-4567',
userType: 'enterprise',
status: 'active',
createdAt: '2024-01-10',
lastLogin: '2024-01-19 15:45',
company: 'ABC科技有限公司',
totalOrders: 23
},
{
id: '3',
name: '李四',
email: 'lisi@example.com',
phone: '13900139002',
userType: 'individual',
status: 'pending',
createdAt: '2024-01-18',
totalOrders: 0
},
{
id: '4',
name: '王五',
email: 'wangwu@example.com',
phone: '13700137003',
userType: 'individual',
status: 'inactive',
createdAt: '2024-01-12',
lastLogin: '2024-01-16 09:15',
totalOrders: 2
},
{
id: '5',
name: '管理员',
email: 'admin@system.com',
phone: '13600136004',
userType: 'admin',
status: 'active',
createdAt: '2024-01-01',
lastLogin: '2024-01-20 08:00',
totalOrders: 0
}
];
// 应用过滤器
let filteredUsers = mockUsers;
if (filters.search) {
const searchTerm = filters.search.toLowerCase();
filteredUsers = filteredUsers.filter(user =>
user.name.toLowerCase().includes(searchTerm) ||
user.email.toLowerCase().includes(searchTerm) ||
user.phone.includes(searchTerm)
);
}
if (filters.userType && filters.userType !== 'all') {
filteredUsers = filteredUsers.filter(user => user.userType === filters.userType);
}
if (filters.status && filters.status !== 'all') {
filteredUsers = filteredUsers.filter(user => user.status === filters.status);
}
return {
success: true,
data: {
users: filteredUsers,
total: filteredUsers.length
}
};
}
return response;
}
// 创建用户
async createUser(userData: Partial<User>): Promise<ApiResponse<User>> {
return this.request<User>('/users', {
method: 'POST',
body: JSON.stringify(userData),
});
}
// 更新用户
async updateUser(userId: string, userData: Partial<User>): Promise<ApiResponse<User>> {
return this.request<User>(`/users/${userId}`, {
method: 'PUT',
body: JSON.stringify(userData),
});
}
// 删除用户
async deleteUser(userId: string): Promise<ApiResponse<void>> {
return this.request<void>(`/users/${userId}`, {
method: 'DELETE',
});
}
// 批量删除用户
async deleteUsers(userIds: string[]): Promise<ApiResponse<void>> {
return this.request<void>('/users/batch-delete', {
method: 'POST',
body: JSON.stringify({ userIds }),
});
}
// 获取用户详情
async getUserDetail(userId: string): Promise<ApiResponse<User>> {
return this.request<User>(`/users/${userId}`);
}
// 检查服务状态
async checkServiceStatus(): Promise<ApiResponse<{ status: string; timestamp: string }>> {
return this.request<{ status: string; timestamp: string }>('/health');
}
}
// 导出单例实例
export const apiService = new ApiService();
// 导出默认实例
export default apiService;
// 用户相关API
export const userAPI = {
// 获取用户列表
async getUsers(params?: {
page?: number
limit?: number
search?: string
user_type?: string
status?: string
}) {
let query = supabase
.from('users')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`name.ilike.%${params.search}%,email.ilike.%${params.search}%`)
}
if (params?.user_type) {
query = query.eq('user_type', params.user_type)
}
if (params?.status) {
query = query.eq('status', params.status)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个用户
async getUser(id: string) {
const { data, error } = await supabase
.from('users')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建用户
async createUser(userData: any) {
const { data, error } = await supabase
.from('users')
.insert([userData])
.select()
.single()
if (error) throw error
return data
},
// 更新用户
async updateUser(id: string, userData: any) {
const { data, error } = await supabase
.from('users')
.update(userData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除用户
async deleteUser(id: string) {
const { error } = await supabase
.from('users')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 翻译员相关API
export const interpreterAPI = {
// 获取翻译员列表
async getInterpreters(params?: {
page?: number
limit?: number
search?: string
status?: string
language?: string
}) {
let query = supabase
.from('interpreters')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`name.ilike.%${params.search}%,email.ilike.%${params.search}%`)
}
if (params?.status) {
query = query.eq('status', params.status)
}
if (params?.language) {
query = query.contains('languages', [params.language])
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个翻译员
async getInterpreter(id: string) {
const { data, error } = await supabase
.from('interpreters')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建翻译员
async createInterpreter(interpreterData: any) {
const { data, error } = await supabase
.from('interpreters')
.insert([interpreterData])
.select()
.single()
if (error) throw error
return data
},
// 更新翻译员
async updateInterpreter(id: string, interpreterData: any) {
const { data, error } = await supabase
.from('interpreters')
.update(interpreterData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除翻译员
async deleteInterpreter(id: string) {
const { error } = await supabase
.from('interpreters')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 订单相关API
export const orderAPI = {
// 获取订单列表
async getOrders(params?: {
page?: number
limit?: number
search?: string
status?: string
service_type?: string
}) {
let query = supabase
.from('orders')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`order_number.ilike.%${params.search}%,user_name.ilike.%${params.search}%,user_email.ilike.%${params.search}%`)
}
if (params?.status) {
query = query.eq('status', params.status)
}
if (params?.service_type) {
query = query.eq('service_type', params.service_type)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个订单
async getOrder(id: string) {
const { data, error } = await supabase
.from('orders')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建订单
async createOrder(orderData: any) {
const { data, error } = await supabase
.from('orders')
.insert([orderData])
.select()
.single()
if (error) throw error
return data
},
// 更新订单
async updateOrder(id: string, orderData: any) {
const { data, error } = await supabase
.from('orders')
.update(orderData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除订单
async deleteOrder(id: string) {
const { error } = await supabase
.from('orders')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 通话记录相关API
export const callAPI = {
// 获取通话记录列表
async getCalls(params?: {
page?: number
limit?: number
search?: string
status?: string
service_type?: string
}) {
let query = supabase
.from('calls')
.select(`
*,
users(name, email),
interpreters(name, email)
`, { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.status) {
query = query.eq('status', params.status)
}
if (params?.service_type) {
query = query.eq('service_type', params.service_type)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个通话记录
async getCall(id: string) {
const { data, error } = await supabase
.from('calls')
.select(`
*,
users(name, email),
interpreters(name, email)
`)
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建通话记录
async createCall(callData: any) {
const { data, error } = await supabase
.from('calls')
.insert([callData])
.select()
.single()
if (error) throw error
return data
},
// 更新通话记录
async updateCall(id: string, callData: any) {
const { data, error } = await supabase
.from('calls')
.update(callData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
}
}
// 发票相关API
export const invoiceAPI = {
// 获取发票列表
async getInvoices(params?: {
page?: number
limit?: number
search?: string
status?: string
invoice_type?: string
}) {
let query = supabase
.from('invoices')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`invoice_number.ilike.%${params.search}%,user_name.ilike.%${params.search}%,user_email.ilike.%${params.search}%`)
}
if (params?.status) {
query = query.eq('status', params.status)
}
if (params?.invoice_type) {
query = query.eq('invoice_type', params.invoice_type)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个发票
async getInvoice(id: string) {
const { data, error } = await supabase
.from('invoices')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建发票
async createInvoice(invoiceData: any) {
const { data, error } = await supabase
.from('invoices')
.insert([invoiceData])
.select()
.single()
if (error) throw error
return data
},
// 更新发票
async updateInvoice(id: string, invoiceData: any) {
const { data, error } = await supabase
.from('invoices')
.update(invoiceData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除发票
async deleteInvoice(id: string) {
const { error } = await supabase
.from('invoices')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 文档相关API
export const documentAPI = {
// 获取文档列表
async getDocuments(params?: {
page?: number
limit?: number
search?: string
status?: string
file_type?: string
}) {
let query = supabase
.from('documents')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`filename.ilike.%${params.search}%,original_name.ilike.%${params.search}%`)
}
if (params?.status) {
query = query.eq('status', params.status)
}
if (params?.file_type) {
query = query.eq('file_type', params.file_type)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个文档
async getDocument(id: string) {
const { data, error } = await supabase
.from('documents')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建文档
async createDocument(documentData: any) {
const { data, error } = await supabase
.from('documents')
.insert([documentData])
.select()
.single()
if (error) throw error
return data
},
// 更新文档
async updateDocument(id: string, documentData: any) {
const { data, error } = await supabase
.from('documents')
.update(documentData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除文档
async deleteDocument(id: string) {
const { error } = await supabase
.from('documents')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 企业相关API
export const enterpriseAPI = {
// 获取企业列表
async getEnterprises(params?: {
page?: number
limit?: number
search?: string
status?: string
}) {
let query = supabase
.from('enterprises')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
if (params?.search) {
query = query.or(`name.ilike.%${params.search}%,contact_email.ilike.%${params.search}%`)
}
if (params?.status) {
query = query.eq('status', params.status)
}
const { data, error, count } = await query
.range(
((params?.page || 1) - 1) * (params?.limit || 10),
(params?.page || 1) * (params?.limit || 10) - 1
)
if (error) throw error
return { data, count }
},
// 获取单个企业
async getEnterprise(id: string) {
const { data, error } = await supabase
.from('enterprises')
.select('*')
.eq('id', id)
.single()
if (error) throw error
return data
},
// 创建企业
async createEnterprise(enterpriseData: any) {
const { data, error } = await supabase
.from('enterprises')
.insert([enterpriseData])
.select()
.single()
if (error) throw error
return data
},
// 更新企业
async updateEnterprise(id: string, enterpriseData: any) {
const { data, error } = await supabase
.from('enterprises')
.update(enterpriseData)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
// 删除企业
async deleteEnterprise(id: string) {
const { error } = await supabase
.from('enterprises')
.delete()
.eq('id', id)
if (error) throw error
}
}
// 统计数据API
export const statsAPI = {
// 获取仪表盘统计数据
async getDashboardStats() {
const [
{ count: totalUsers },
{ count: totalInterpreters },
{ count: totalOrders },
{ count: totalCalls },
{ count: activeUsers },
{ count: activeCalls },
{ data: recentOrders },
{ data: recentCalls }
] = await Promise.all([
supabase.from('users').select('*', { count: 'exact', head: true }),
supabase.from('interpreters').select('*', { count: 'exact', head: true }),
supabase.from('orders').select('*', { count: 'exact', head: true }),
supabase.from('calls').select('*', { count: 'exact', head: true }),
supabase.from('users').select('*', { count: 'exact', head: true }).eq('status', 'active'),
supabase.from('calls').select('*', { count: 'exact', head: true }).eq('status', 'connected'),
supabase.from('orders').select('*').order('created_at', { ascending: false }).limit(10),
supabase.from('calls').select(`
*,
users(name, email),
interpreters(name, email)
`).order('created_at', { ascending: false }).limit(10)
])
return {
totalUsers: totalUsers || 0,
totalInterpreters: totalInterpreters || 0,
totalOrders: totalOrders || 0,
totalCalls: totalCalls || 0,
activeUsers: activeUsers || 0,
activeCalls: activeCalls || 0,
recentOrders: recentOrders || [],
recentCalls: recentCalls || []
}
}
}
// 系统设置API
export const settingsAPI = {
// 获取系统设置
async getSettings() {
const { data, error } = await supabase
.from('system_settings')
.select('*')
.order('key')
if (error) throw error
return data
},
// 获取单个设置
async getSetting(key: string) {
const { data, error } = await supabase
.from('system_settings')
.select('*')
.eq('key', key)
.single()
if (error) throw error
return data
},
// 更新设置
async updateSetting(key: string, value: string, description?: string) {
const { data, error } = await supabase
.from('system_settings')
.upsert([{ key, value, description }])
.select()
.single()
if (error) throw error
return data
}
}