diff --git a/community_api.py b/community_api.py index c7f743a6..9fbab0c0 100644 --- a/community_api.py +++ b/community_api.py @@ -324,9 +324,43 @@ def get_channel_admin_status(channel_id): @community_bp.route('/channels//admins', methods=['GET']) @login_required def get_channel_admins(channel_id): - """获取频道管理员列表""" + """获取频道管理员列表(包含超级管理员)""" try: + import json + admins = [] + with get_db_engine().connect() as conn: + # 1. 先获取全局超级管理员 + super_admin_sql = text(""" + SELECT ca.user_id, u.username, u.avatar + FROM community_admins ca + LEFT JOIN users u ON ca.user_id = u.id + WHERE ca.role = 'admin' + """) + super_admins = conn.execute(super_admin_sql).fetchall() + + for row in super_admins: + admins.append({ + 'userId': str(row.user_id), + 'username': row.username, + 'avatar': row.avatar, + 'role': 'super_admin', # 特殊标记 + 'isSuperAdmin': True, + 'permissions': { + 'delete_channel': True, + 'edit_channel': True, + 'manage_admins': True, + 'pin_messages': True, + 'delete_messages': True, + 'slow_mode': True, + 'lock_channel': True + }, + 'createdAt': None + }) + + # 2. 获取频道管理员(排除已在超级管理员列表中的用户) + super_admin_ids = [str(row.user_id) for row in super_admins] + sql = text(""" SELECT cca.user_id, cca.role, cca.permissions, cca.created_at, u.username, u.avatar @@ -342,9 +376,11 @@ def get_channel_admins(channel_id): """) result = conn.execute(sql, {'channel_id': channel_id}).fetchall() - admins = [] for row in result: - import json + # 跳过已在超级管理员列表中的用户 + if str(row.user_id) in super_admin_ids: + continue + permissions = row.permissions if isinstance(permissions, str): permissions = json.loads(permissions) @@ -354,6 +390,7 @@ def get_channel_admins(channel_id): 'username': row.username, 'avatar': row.avatar, 'role': row.role, + 'isSuperAdmin': False, 'permissions': permissions or {}, 'createdAt': row.created_at.isoformat() if row.created_at else None }) @@ -742,6 +779,27 @@ def send_message(channel_id): user = g.current_user data = request.get_json() + # 检查频道类型和权限 + with get_db_engine().connect() as conn: + channel_sql = text(""" + SELECT type, is_readonly FROM community_channels WHERE id = :channel_id + """) + channel = conn.execute(channel_sql, {'channel_id': channel_id}).fetchone() + if not channel: + return api_error('频道不存在', 404) + + # 公告频道只有管理员可以发消息 + if channel.type == 'announcement': + admin_info = get_channel_admin_info(channel_id, user['id']) + if not admin_info or not admin_info.get('isAdmin'): + return api_error('只有管理员可以在公告频道发送消息', 403) + + # 只读频道不能发消息 + if channel.is_readonly: + admin_info = get_channel_admin_info(channel_id, user['id']) + if not admin_info or not admin_info.get('isAdmin'): + return api_error('该频道为只读频道', 403) + content = data.get('content', '').strip() if not content: return api_error('消息内容不能为空') diff --git a/src/views/StockCommunity/components/MessageArea/TextChannel/MessageItem.tsx b/src/views/StockCommunity/components/MessageArea/TextChannel/MessageItem.tsx index 8793dcda..fba20028 100644 --- a/src/views/StockCommunity/components/MessageArea/TextChannel/MessageItem.tsx +++ b/src/views/StockCommunity/components/MessageArea/TextChannel/MessageItem.tsx @@ -37,8 +37,8 @@ import ReactionBar from './ReactionBar'; import StockEmbed from '../shared/StockEmbed'; // 角色徽章配置 -const ROLE_BADGE_CONFIG: Record = { - superAdmin: { label: '超管', bg: 'linear-gradient(135deg, rgba(239, 68, 68, 0.3), rgba(220, 38, 38, 0.3))', color: 'red.300' }, +const ROLE_BADGE_CONFIG: Record = { + super_admin: { label: '超管', bg: 'linear-gradient(135deg, rgba(239, 68, 68, 0.3), rgba(220, 38, 38, 0.3))', color: 'red.300' }, owner: { label: '频主', bg: 'linear-gradient(135deg, rgba(251, 191, 36, 0.3), rgba(245, 158, 11, 0.3))', color: 'yellow.300' }, admin: { label: '管理', bg: 'linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(37, 99, 235, 0.3))', color: 'blue.300' }, moderator: { label: '版主', bg: 'linear-gradient(135deg, rgba(34, 197, 94, 0.3), rgba(22, 163, 74, 0.3))', color: 'green.300' }, @@ -64,24 +64,15 @@ const MessageItem: React.FC = ({ // 管理员权限 const { getUserAdminRole, - isSuperAdmin: currentUserIsSuperAdmin, canDeleteMessages, canPinMessages, - globalAdmin } = useAdmin(); - // 获取消息作者的管理员角色 + // 获取消息作者的管理员角色(包括超级管理员) const authorRole = getUserAdminRole(message.authorId); - // 判断作者是否是超级管理员(需要从全局管理员状态判断) - const authorIsSuperAdmin = globalAdmin?.isAdmin && globalAdmin?.role === 'admin' && message.authorId === '65'; // 获取显示的角色徽章 - const getRoleBadge = () => { - if (authorIsSuperAdmin) return ROLE_BADGE_CONFIG.superAdmin; - if (authorRole) return ROLE_BADGE_CONFIG[authorRole]; - return null; - }; - const roleBadge = getRoleBadge(); + const roleBadge = authorRole ? ROLE_BADGE_CONFIG[authorRole] : null; // 深色主题颜色(HeroUI 风格) const hoverBg = 'rgba(255, 255, 255, 0.05)'; diff --git a/src/views/StockCommunity/components/MessageArea/TextChannel/index.tsx b/src/views/StockCommunity/components/MessageArea/TextChannel/index.tsx index b9bfa641..13db39c3 100644 --- a/src/views/StockCommunity/components/MessageArea/TextChannel/index.tsx +++ b/src/views/StockCommunity/components/MessageArea/TextChannel/index.tsx @@ -86,9 +86,9 @@ const TextChannel: React.FC = ({ loadMessages(); // 加载管理员状态 loadChannelAdminStatus(channel.id); - // 加载频道管理员列表 + // 加载频道管理员列表(包括超级管理员) getChannelAdmins(channel.id).then(admins => { - const adminMap = new Map(); + const adminMap = new Map(); admins.forEach(admin => { adminMap.set(admin.userId, admin.role); }); diff --git a/src/views/StockCommunity/components/RightPanel/MemberList.tsx b/src/views/StockCommunity/components/RightPanel/MemberList.tsx index 0368726b..333de70e 100644 --- a/src/views/StockCommunity/components/RightPanel/MemberList.tsx +++ b/src/views/StockCommunity/components/RightPanel/MemberList.tsx @@ -23,8 +23,8 @@ import { useAdmin } from '../../contexts/AdminContext'; import { getChannelAdmins } from '../../services/communityService'; // 角色配置 -const ROLE_CONFIG: Record = { - superAdmin: { icon: Crown, label: '超级管理员', color: 'red.300', bg: 'rgba(239, 68, 68, 0.2)' }, +const ROLE_CONFIG: Record = { + super_admin: { icon: Crown, label: '超级管理员', color: 'red.300', bg: 'rgba(239, 68, 68, 0.2)' }, owner: { icon: Crown, label: '频道创建者', color: 'yellow.300', bg: 'rgba(251, 191, 36, 0.2)' }, admin: { icon: Shield, label: '管理员', color: 'blue.300', bg: 'rgba(59, 130, 246, 0.2)' }, moderator: { icon: UserCheck, label: '版主', color: 'green.300', bg: 'rgba(34, 197, 94, 0.2)' }, @@ -76,10 +76,8 @@ const MemberList: React.FC = ({ channelId }) => { }, [channelId]); // 获取成员的管理员角色 - const getMemberRole = (userId: string): AdminRole | 'superAdmin' | null => { - // 检查是否是超级管理员(ID=65) - if (userId === '65') return 'superAdmin'; - // 从管理员列表中查找 + const getMemberRole = (userId: string): AdminRole | null => { + // 从管理员列表中查找(已包含超级管理员) return channelAdminList.get(userId) || null; }; diff --git a/src/views/StockCommunity/types/index.ts b/src/views/StockCommunity/types/index.ts index dee83fb7..8698a726 100644 --- a/src/views/StockCommunity/types/index.ts +++ b/src/views/StockCommunity/types/index.ts @@ -280,7 +280,7 @@ export interface ApiResponse { // 管理员相关 // ============================================================ -export type AdminRole = 'owner' | 'admin' | 'moderator'; +export type AdminRole = 'super_admin' | 'owner' | 'admin' | 'moderator'; export interface AdminPermissions { delete_channel?: boolean; @@ -311,6 +311,7 @@ export interface ChannelAdmin { username: string; avatar?: string; role: AdminRole; + isSuperAdmin?: boolean; permissions: AdminPermissions; - createdAt: string; + createdAt?: string; }