610 lines
17 KiB
JavaScript
610 lines
17 KiB
JavaScript
/**
|
||
* Supabase 消息系统客户端 - 完整角色管理版本
|
||
* 包含角色管理、权限检查、消息操作等功能
|
||
*/
|
||
|
||
import { createClient } from '@supabase/supabase-js'
|
||
|
||
// Supabase 配置
|
||
const supabaseUrl = 'YOUR_SUPABASE_URL'
|
||
const supabaseKey = 'YOUR_SUPABASE_ANON_KEY'
|
||
const supabase = createClient(supabaseUrl, supabaseKey)
|
||
|
||
/**
|
||
* 用户角色管理类
|
||
*/
|
||
class UserRoleManager {
|
||
constructor(supabaseClient) {
|
||
this.supabase = supabaseClient
|
||
this.currentUser = null
|
||
this.currentRole = null
|
||
this.permissions = {}
|
||
}
|
||
|
||
/**
|
||
* 初始化用户角色信息
|
||
*/
|
||
async initialize() {
|
||
try {
|
||
const { data: { user } } = await this.supabase.auth.getUser()
|
||
|
||
if (user) {
|
||
this.currentUser = user
|
||
this.currentRole = await this.getUserRole(user.id)
|
||
this.permissions = await this.getUserPermissions(user.id)
|
||
|
||
console.log('✅ 用户角色初始化完成:', {
|
||
userId: user.id,
|
||
email: user.email,
|
||
role: this.currentRole,
|
||
permissions: this.permissions
|
||
})
|
||
}
|
||
|
||
return {
|
||
user: this.currentUser,
|
||
role: this.currentRole,
|
||
permissions: this.permissions
|
||
}
|
||
} catch (error) {
|
||
console.error('❌ 用户角色初始化失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户角色
|
||
* @param {string} userId - 用户ID
|
||
* @returns {Promise<string>} 用户角色
|
||
*/
|
||
async getUserRole(userId = null) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('get_user_role', { target_user_id: userId })
|
||
|
||
if (error) throw error
|
||
|
||
return data || 'student'
|
||
} catch (error) {
|
||
console.error('获取用户角色失败:', error)
|
||
return 'student'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户权限
|
||
* @param {string} userId - 用户ID
|
||
* @returns {Promise<Object>} 用户权限对象
|
||
*/
|
||
async getUserPermissions(userId = null) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.from('user_roles')
|
||
.select('permissions')
|
||
.eq('user_id', userId || this.currentUser?.id)
|
||
.eq('is_active', true)
|
||
.single()
|
||
|
||
if (error && error.code !== 'PGRST116') throw error
|
||
|
||
return data?.permissions || {}
|
||
} catch (error) {
|
||
console.error('获取用户权限失败:', error)
|
||
return {}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否有特定权限
|
||
* @param {string} permission - 权限名称
|
||
* @param {string} userId - 用户ID(可选)
|
||
* @returns {Promise<boolean>} 是否有权限
|
||
*/
|
||
async hasPermission(permission, userId = null) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('user_has_permission', {
|
||
permission_name: permission,
|
||
target_user_id: userId
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
return data === true
|
||
} catch (error) {
|
||
console.error('权限检查失败:', error)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查是否可以访问资源
|
||
* @param {string} resourceType - 资源类型
|
||
* @param {string} resourceId - 资源ID
|
||
* @param {string} accessType - 访问类型
|
||
* @returns {Promise<boolean>} 是否可以访问
|
||
*/
|
||
async canAccessResource(resourceType, resourceId, accessType = 'read') {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('can_access_resource', {
|
||
resource_type: resourceType,
|
||
resource_id: resourceId,
|
||
access_type: accessType
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
return data === true
|
||
} catch (error) {
|
||
console.error('资源访问检查失败:', error)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新用户角色(仅管理员)
|
||
* @param {string} targetUserId - 目标用户ID
|
||
* @param {string} newRole - 新角色
|
||
* @param {Object} additionalData - 额外数据
|
||
* @returns {Promise<boolean>} 是否更新成功
|
||
*/
|
||
async updateUserRole(targetUserId, newRole, additionalData = {}) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('update_user_role', {
|
||
target_user_id: targetUserId,
|
||
new_role: newRole,
|
||
additional_data: additionalData
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
console.log('✅ 用户角色更新成功:', { targetUserId, newRole })
|
||
return data === true
|
||
} catch (error) {
|
||
console.error('❌ 用户角色更新失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量更新用户角色
|
||
* @param {Array} roleUpdates - 角色更新数组
|
||
* @returns {Promise<Array>} 更新结果
|
||
*/
|
||
async batchUpdateUserRoles(roleUpdates) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('batch_update_user_roles', {
|
||
role_updates: roleUpdates
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
console.log('✅ 批量角色更新完成:', data)
|
||
return data
|
||
} catch (error) {
|
||
console.error('❌ 批量角色更新失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户详细信息(管理员视图)
|
||
*/
|
||
async getUsersWithRoles() {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.from('user_roles_detailed')
|
||
.select('*')
|
||
.order('created_at', { ascending: false })
|
||
|
||
if (error) throw error
|
||
|
||
return data
|
||
} catch (error) {
|
||
console.error('获取用户角色列表失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试权限系统
|
||
* @param {string} testUserId - 测试用户ID
|
||
*/
|
||
async testPermissions(testUserId = null) {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('test_message_permissions', {
|
||
test_user_id: testUserId || this.currentUser?.id
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
console.log('🧪 权限测试结果:', data)
|
||
return data
|
||
} catch (error) {
|
||
console.error('权限测试失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 消息系统管理类
|
||
*/
|
||
class MessageManager {
|
||
constructor(supabaseClient, roleManager) {
|
||
this.supabase = supabaseClient
|
||
this.roleManager = roleManager
|
||
}
|
||
|
||
/**
|
||
* 发送安全消息
|
||
* @param {Object} messageData - 消息数据
|
||
* @returns {Promise<string>} 消息ID
|
||
*/
|
||
async sendSecureMessage(messageData) {
|
||
try {
|
||
const {
|
||
messageTypeId,
|
||
receiverType,
|
||
receiverId,
|
||
title,
|
||
content,
|
||
metadata = {}
|
||
} = messageData
|
||
|
||
const { data, error } = await this.supabase
|
||
.rpc('send_secure_message', {
|
||
message_type_id: messageTypeId,
|
||
receiver_type: receiverType,
|
||
receiver_id: receiverId,
|
||
title,
|
||
content,
|
||
metadata_json: metadata
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
console.log('✅ 消息发送成功:', data)
|
||
return data
|
||
} catch (error) {
|
||
console.error('❌ 消息发送失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户可访问的消息列表
|
||
* @param {Object} options - 查询选项
|
||
* @returns {Promise<Array>} 消息列表
|
||
*/
|
||
async getAccessibleMessages(options = {}) {
|
||
try {
|
||
const {
|
||
limit = 50,
|
||
offset = 0,
|
||
messageType = null,
|
||
unreadOnly = false
|
||
} = options
|
||
|
||
let query = this.supabase
|
||
.from('ak_messages')
|
||
.select(`
|
||
*,
|
||
ak_message_types(type_name, display_name),
|
||
ak_message_recipients(
|
||
read_at,
|
||
replied_at,
|
||
status
|
||
)
|
||
`)
|
||
.order('created_at', { ascending: false })
|
||
.range(offset, offset + limit - 1)
|
||
|
||
if (messageType) {
|
||
query = query.eq('ak_message_types.type_name', messageType)
|
||
}
|
||
|
||
if (unreadOnly) {
|
||
query = query.is('ak_message_recipients.read_at', null)
|
||
}
|
||
|
||
const { data, error } = await query
|
||
|
||
if (error) throw error
|
||
|
||
return data
|
||
} catch (error) {
|
||
console.error('获取消息列表失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 标记消息为已读
|
||
* @param {string} messageId - 消息ID
|
||
*/
|
||
async markMessageAsRead(messageId) {
|
||
try {
|
||
const { error } = await this.supabase
|
||
.from('ak_message_recipients')
|
||
.update({
|
||
read_at: new Date().toISOString(),
|
||
status: 'read'
|
||
})
|
||
.eq('message_id', messageId)
|
||
.eq('user_id', this.roleManager.currentUser?.id)
|
||
|
||
if (error) throw error
|
||
|
||
console.log('✅ 消息已标记为已读:', messageId)
|
||
} catch (error) {
|
||
console.error('标记消息已读失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取消息统计
|
||
*/
|
||
async getMessageStats() {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.from('ak_message_stats')
|
||
.select('*')
|
||
.eq('entity_type', 'user')
|
||
.eq('entity_id', this.roleManager.currentUser?.id)
|
||
|
||
if (error) throw error
|
||
|
||
return data
|
||
} catch (error) {
|
||
console.error('获取消息统计失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加入消息群组
|
||
* @param {string} groupId - 群组ID
|
||
* @param {string} joinMessage - 加入消息
|
||
*/
|
||
async joinMessageGroup(groupId, joinMessage = '') {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.rpc('join_message_group', {
|
||
target_group_id: groupId,
|
||
join_message: joinMessage
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
if (data === true) {
|
||
console.log('✅ 成功加入群组:', groupId)
|
||
} else {
|
||
console.log('⏳ 申请已提交,等待审批:', groupId)
|
||
}
|
||
|
||
return data
|
||
} catch (error) {
|
||
console.error('加入群组失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户可访问的群组列表
|
||
*/
|
||
async getAccessibleGroups() {
|
||
try {
|
||
const { data, error } = await this.supabase
|
||
.from('ak_message_groups')
|
||
.select(`
|
||
*,
|
||
ak_message_group_members!inner(
|
||
status,
|
||
joined_at,
|
||
role
|
||
)
|
||
`)
|
||
.eq('ak_message_group_members.user_id', this.roleManager.currentUser?.id)
|
||
.eq('ak_message_group_members.status', 'active')
|
||
.order('created_at', { ascending: false })
|
||
|
||
if (error) throw error
|
||
|
||
return data
|
||
} catch (error) {
|
||
console.error('获取群组列表失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主应用类
|
||
*/
|
||
class MessageApp {
|
||
constructor() {
|
||
this.supabase = supabase
|
||
this.roleManager = new UserRoleManager(this.supabase)
|
||
this.messageManager = new MessageManager(this.supabase, this.roleManager)
|
||
this.initialized = false
|
||
}
|
||
|
||
/**
|
||
* 初始化应用
|
||
*/
|
||
async initialize() {
|
||
try {
|
||
await this.roleManager.initialize()
|
||
this.initialized = true
|
||
|
||
// 监听认证状态变化
|
||
this.supabase.auth.onAuthStateChange(async (event, session) => {
|
||
console.log('🔄 认证状态变化:', event)
|
||
|
||
if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
|
||
await this.roleManager.initialize()
|
||
} else if (event === 'SIGNED_OUT') {
|
||
this.roleManager.currentUser = null
|
||
this.roleManager.currentRole = null
|
||
this.roleManager.permissions = {}
|
||
}
|
||
})
|
||
|
||
console.log('✅ 消息应用初始化完成')
|
||
return true
|
||
} catch (error) {
|
||
console.error('❌ 消息应用初始化失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 用户登录
|
||
* @param {string} email - 邮箱
|
||
* @param {string} password - 密码
|
||
*/
|
||
async signIn(email, password) {
|
||
try {
|
||
const { data, error } = await this.supabase.auth.signInWithPassword({
|
||
email,
|
||
password
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
await this.roleManager.initialize()
|
||
console.log('✅ 登录成功')
|
||
return data
|
||
} catch (error) {
|
||
console.error('❌ 登录失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 用户注册
|
||
* @param {string} email - 邮箱
|
||
* @param {string} password - 密码
|
||
* @param {Object} metadata - 额外元数据
|
||
*/
|
||
async signUp(email, password, metadata = {}) {
|
||
try {
|
||
const { data, error } = await this.supabase.auth.signUp({
|
||
email,
|
||
password,
|
||
options: {
|
||
data: metadata
|
||
}
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
console.log('✅ 注册成功,请检查邮箱验证')
|
||
return data
|
||
} catch (error) {
|
||
console.error('❌ 注册失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 用户退出
|
||
*/
|
||
async signOut() {
|
||
try {
|
||
const { error } = await this.supabase.auth.signOut()
|
||
if (error) throw error
|
||
|
||
console.log('✅ 退出成功')
|
||
} catch (error) {
|
||
console.error('❌ 退出失败:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前用户信息
|
||
*/
|
||
getCurrentUserInfo() {
|
||
return {
|
||
user: this.roleManager.currentUser,
|
||
role: this.roleManager.currentRole,
|
||
permissions: this.roleManager.permissions,
|
||
initialized: this.initialized
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 角色管理器
|
||
*/
|
||
get roles() {
|
||
return this.roleManager
|
||
}
|
||
|
||
/**
|
||
* 消息管理器
|
||
*/
|
||
get messages() {
|
||
return this.messageManager
|
||
}
|
||
}
|
||
|
||
// 创建全局实例
|
||
const messageApp = new MessageApp()
|
||
|
||
// 导出
|
||
export { MessageApp, UserRoleManager, MessageManager, messageApp }
|
||
|
||
// 使用示例
|
||
/*
|
||
// 1. 初始化应用
|
||
await messageApp.initialize()
|
||
|
||
// 2. 用户登录
|
||
await messageApp.signIn('teacher@example.com', 'password123')
|
||
|
||
// 3. 检查用户角色和权限
|
||
const userInfo = messageApp.getCurrentUserInfo()
|
||
console.log('当前用户:', userInfo)
|
||
|
||
// 4. 检查特定权限
|
||
const canSendBroadcast = await messageApp.roles.hasPermission('can_send_broadcasts')
|
||
console.log('可以发送广播:', canSendBroadcast)
|
||
|
||
// 5. 发送消息
|
||
const messageId = await messageApp.messages.sendSecureMessage({
|
||
messageTypeId: 'some-type-id',
|
||
receiverType: 'user',
|
||
receiverId: 'student-user-id',
|
||
title: '作业通知',
|
||
content: '请完成本周的作业',
|
||
metadata: { priority: 'high' }
|
||
})
|
||
|
||
// 6. 获取消息列表
|
||
const messages = await messageApp.messages.getAccessibleMessages({
|
||
limit: 20,
|
||
unreadOnly: true
|
||
})
|
||
|
||
// 7. 加入群组
|
||
const joinResult = await messageApp.messages.joinMessageGroup('group-id')
|
||
|
||
// 8. 管理员操作:更新用户角色
|
||
if (userInfo.role === 'admin') {
|
||
await messageApp.roles.updateUserRole('user-id', 'teacher', {
|
||
department: 'Mathematics',
|
||
class_id: 'class-123'
|
||
})
|
||
}
|
||
|
||
// 9. 测试权限系统
|
||
await messageApp.roles.testPermissions()
|
||
*/
|