352 lines
11 KiB
JavaScript
352 lines
11 KiB
JavaScript
|
// API 管理文件
|
|||
|
class APIManager {
|
|||
|
constructor() {
|
|||
|
this.supabase = null;
|
|||
|
this.currentUser = null;
|
|||
|
this.init();
|
|||
|
}
|
|||
|
|
|||
|
// 初始化 Supabase 客户端
|
|||
|
async init() {
|
|||
|
try {
|
|||
|
// 加载 Supabase 客户端
|
|||
|
const { createClient } = supabase;
|
|||
|
this.supabase = createClient(CONFIG.SUPABASE.URL, CONFIG.SUPABASE.ANON_KEY);
|
|||
|
|
|||
|
// 检查用户登录状态
|
|||
|
await this.checkAuthStatus();
|
|||
|
|
|||
|
console.log('API Manager 初始化成功');
|
|||
|
} catch (error) {
|
|||
|
console.error('API Manager 初始化失败:', error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 检查认证状态
|
|||
|
async checkAuthStatus() {
|
|||
|
try {
|
|||
|
const { data: { user } } = await this.supabase.auth.getUser();
|
|||
|
this.currentUser = user;
|
|||
|
|
|||
|
if (user) {
|
|||
|
console.log('用户已登录:', user.email);
|
|||
|
this.updateUIForLoggedInUser(user);
|
|||
|
} else {
|
|||
|
console.log('用户未登录');
|
|||
|
this.updateUIForLoggedOutUser();
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('检查认证状态失败:', error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 用户注册
|
|||
|
async register(email, password, userData = {}) {
|
|||
|
try {
|
|||
|
const { data, error } = await this.supabase.auth.signUp({
|
|||
|
email: email,
|
|||
|
password: password,
|
|||
|
options: {
|
|||
|
data: {
|
|||
|
full_name: userData.fullName || '',
|
|||
|
phone: userData.phone || '',
|
|||
|
...userData
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
|
|||
|
// 创建用户档案
|
|||
|
if (data.user) {
|
|||
|
await this.createUserProfile(data.user, userData);
|
|||
|
}
|
|||
|
|
|||
|
return { success: true, data: data };
|
|||
|
} catch (error) {
|
|||
|
console.error('注册失败:', error);
|
|||
|
return { success: false, error: error.message };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 用户登录
|
|||
|
async login(email, password) {
|
|||
|
try {
|
|||
|
const { data, error } = await this.supabase.auth.signInWithPassword({
|
|||
|
email: email,
|
|||
|
password: password
|
|||
|
});
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
|
|||
|
this.currentUser = data.user;
|
|||
|
this.updateUIForLoggedInUser(data.user);
|
|||
|
|
|||
|
return { success: true, data: data };
|
|||
|
} catch (error) {
|
|||
|
console.error('登录失败:', error);
|
|||
|
return { success: false, error: error.message };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 用户登出
|
|||
|
async logout() {
|
|||
|
try {
|
|||
|
const { error } = await this.supabase.auth.signOut();
|
|||
|
if (error) throw error;
|
|||
|
|
|||
|
this.currentUser = null;
|
|||
|
this.updateUIForLoggedOutUser();
|
|||
|
|
|||
|
return { success: true };
|
|||
|
} catch (error) {
|
|||
|
console.error('登出失败:', error);
|
|||
|
return { success: false, error: error.message };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 创建用户档案
|
|||
|
async createUserProfile(user, userData) {
|
|||
|
try {
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('user_profiles')
|
|||
|
.insert([
|
|||
|
{
|
|||
|
id: user.id,
|
|||
|
email: user.email,
|
|||
|
full_name: userData.fullName || '',
|
|||
|
phone: userData.phone || '',
|
|||
|
avatar_url: userData.avatarUrl || '',
|
|||
|
created_at: new Date().toISOString(),
|
|||
|
updated_at: new Date().toISOString()
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('创建用户档案失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 获取用户档案
|
|||
|
async getUserProfile(userId = null) {
|
|||
|
try {
|
|||
|
const id = userId || this.currentUser?.id;
|
|||
|
if (!id) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('user_profiles')
|
|||
|
.select('*')
|
|||
|
.eq('id', id)
|
|||
|
.single();
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('获取用户档案失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 更新用户档案
|
|||
|
async updateUserProfile(updates) {
|
|||
|
try {
|
|||
|
if (!this.currentUser) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('user_profiles')
|
|||
|
.update({
|
|||
|
...updates,
|
|||
|
updated_at: new Date().toISOString()
|
|||
|
})
|
|||
|
.eq('id', this.currentUser.id);
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('更新用户档案失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 创建通话记录
|
|||
|
async createCallRecord(callData) {
|
|||
|
try {
|
|||
|
if (!this.currentUser) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('call_records')
|
|||
|
.insert([
|
|||
|
{
|
|||
|
user_id: this.currentUser.id,
|
|||
|
call_type: callData.type, // 'voice' or 'video'
|
|||
|
duration: callData.duration, // 通话时长(秒)
|
|||
|
has_translator: callData.hasTranslator || false,
|
|||
|
base_rate: callData.baseRate, // 基础费率
|
|||
|
translator_rate: callData.translatorRate || 0, // 翻译费率
|
|||
|
total_amount: callData.totalAmount, // 总金额
|
|||
|
status: callData.status || 'completed', // 'pending', 'completed', 'cancelled'
|
|||
|
created_at: new Date().toISOString()
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('创建通话记录失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 获取通话记录
|
|||
|
async getCallRecords(limit = 50) {
|
|||
|
try {
|
|||
|
if (!this.currentUser) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('call_records')
|
|||
|
.select('*')
|
|||
|
.eq('user_id', this.currentUser.id)
|
|||
|
.order('created_at', { ascending: false })
|
|||
|
.limit(limit);
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('获取通话记录失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 创建预约记录
|
|||
|
async createAppointment(appointmentData) {
|
|||
|
try {
|
|||
|
if (!this.currentUser) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('appointments')
|
|||
|
.insert([
|
|||
|
{
|
|||
|
user_id: this.currentUser.id,
|
|||
|
translator_id: appointmentData.translatorId,
|
|||
|
appointment_date: appointmentData.date,
|
|||
|
appointment_time: appointmentData.time,
|
|||
|
service_type: appointmentData.serviceType, // 'voice', 'video', 'document'
|
|||
|
language_pair: appointmentData.languagePair, // '中文-英文'
|
|||
|
duration: appointmentData.duration || 60, // 预约时长(分钟)
|
|||
|
notes: appointmentData.notes || '',
|
|||
|
status: 'pending', // 'pending', 'confirmed', 'completed', 'cancelled'
|
|||
|
created_at: new Date().toISOString()
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('创建预约失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 获取预约记录
|
|||
|
async getAppointments() {
|
|||
|
try {
|
|||
|
if (!this.currentUser) throw new Error('用户未登录');
|
|||
|
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('appointments')
|
|||
|
.select(`
|
|||
|
*,
|
|||
|
translator_profiles (
|
|||
|
full_name,
|
|||
|
avatar_url,
|
|||
|
languages,
|
|||
|
rating
|
|||
|
)
|
|||
|
`)
|
|||
|
.eq('user_id', this.currentUser.id)
|
|||
|
.order('appointment_date', { ascending: true });
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('获取预约记录失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 获取翻译员列表
|
|||
|
async getTranslators() {
|
|||
|
try {
|
|||
|
const { data, error } = await this.supabase
|
|||
|
.from('translator_profiles')
|
|||
|
.select('*')
|
|||
|
.eq('is_active', true)
|
|||
|
.order('rating', { ascending: false });
|
|||
|
|
|||
|
if (error) throw error;
|
|||
|
return data;
|
|||
|
} catch (error) {
|
|||
|
console.error('获取翻译员列表失败:', error);
|
|||
|
throw error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 更新UI - 已登录用户
|
|||
|
updateUIForLoggedInUser(user) {
|
|||
|
// 显示用户信息
|
|||
|
const userNameElements = document.querySelectorAll('.user-name');
|
|||
|
userNameElements.forEach(el => {
|
|||
|
el.textContent = user.user_metadata?.full_name || user.email;
|
|||
|
});
|
|||
|
|
|||
|
// 显示/隐藏相关元素
|
|||
|
const loginElements = document.querySelectorAll('.login-required');
|
|||
|
loginElements.forEach(el => el.style.display = 'none');
|
|||
|
|
|||
|
const userElements = document.querySelectorAll('.user-only');
|
|||
|
userElements.forEach(el => el.style.display = 'block');
|
|||
|
}
|
|||
|
|
|||
|
// 更新UI - 未登录用户
|
|||
|
updateUIForLoggedOutUser() {
|
|||
|
// 隐藏/显示相关元素
|
|||
|
const loginElements = document.querySelectorAll('.login-required');
|
|||
|
loginElements.forEach(el => el.style.display = 'block');
|
|||
|
|
|||
|
const userElements = document.querySelectorAll('.user-only');
|
|||
|
userElements.forEach(el => el.style.display = 'none');
|
|||
|
}
|
|||
|
|
|||
|
// Stripe 支付处理
|
|||
|
async processPayment(amount, callRecordId) {
|
|||
|
try {
|
|||
|
// 这里应该调用后端API来处理Stripe支付
|
|||
|
// 由于安全考虑,Stripe的secret key不应该在前端使用
|
|||
|
console.log('处理支付:', amount, callRecordId);
|
|||
|
|
|||
|
// 模拟支付成功
|
|||
|
return { success: true, paymentId: 'pi_test_' + Date.now() };
|
|||
|
} catch (error) {
|
|||
|
console.error('支付处理失败:', error);
|
|||
|
return { success: false, error: error.message };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Twilio 视频通话初始化
|
|||
|
async initVideoCall() {
|
|||
|
try {
|
|||
|
// 这里应该调用后端API获取Twilio访问令牌
|
|||
|
console.log('初始化视频通话');
|
|||
|
return { success: true, token: 'twilio_token_placeholder' };
|
|||
|
} catch (error) {
|
|||
|
console.error('视频通话初始化失败:', error);
|
|||
|
return { success: false, error: error.message };
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 创建全局API管理器实例
|
|||
|
window.apiManager = new APIManager();
|