个股论坛重做
This commit is contained in:
@@ -324,9 +324,43 @@ def get_channel_admin_status(channel_id):
|
|||||||
@community_bp.route('/channels/<channel_id>/admins', methods=['GET'])
|
@community_bp.route('/channels/<channel_id>/admins', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def get_channel_admins(channel_id):
|
def get_channel_admins(channel_id):
|
||||||
"""获取频道管理员列表"""
|
"""获取频道管理员列表(包含超级管理员)"""
|
||||||
try:
|
try:
|
||||||
|
import json
|
||||||
|
admins = []
|
||||||
|
|
||||||
with get_db_engine().connect() as conn:
|
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("""
|
sql = text("""
|
||||||
SELECT cca.user_id, cca.role, cca.permissions, cca.created_at,
|
SELECT cca.user_id, cca.role, cca.permissions, cca.created_at,
|
||||||
u.username, u.avatar
|
u.username, u.avatar
|
||||||
@@ -342,9 +376,11 @@ def get_channel_admins(channel_id):
|
|||||||
""")
|
""")
|
||||||
result = conn.execute(sql, {'channel_id': channel_id}).fetchall()
|
result = conn.execute(sql, {'channel_id': channel_id}).fetchall()
|
||||||
|
|
||||||
admins = []
|
|
||||||
for row in result:
|
for row in result:
|
||||||
import json
|
# 跳过已在超级管理员列表中的用户
|
||||||
|
if str(row.user_id) in super_admin_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
permissions = row.permissions
|
permissions = row.permissions
|
||||||
if isinstance(permissions, str):
|
if isinstance(permissions, str):
|
||||||
permissions = json.loads(permissions)
|
permissions = json.loads(permissions)
|
||||||
@@ -354,6 +390,7 @@ def get_channel_admins(channel_id):
|
|||||||
'username': row.username,
|
'username': row.username,
|
||||||
'avatar': row.avatar,
|
'avatar': row.avatar,
|
||||||
'role': row.role,
|
'role': row.role,
|
||||||
|
'isSuperAdmin': False,
|
||||||
'permissions': permissions or {},
|
'permissions': permissions or {},
|
||||||
'createdAt': row.created_at.isoformat() if row.created_at else None
|
'createdAt': row.created_at.isoformat() if row.created_at else None
|
||||||
})
|
})
|
||||||
@@ -742,6 +779,27 @@ def send_message(channel_id):
|
|||||||
user = g.current_user
|
user = g.current_user
|
||||||
data = request.get_json()
|
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()
|
content = data.get('content', '').strip()
|
||||||
if not content:
|
if not content:
|
||||||
return api_error('消息内容不能为空')
|
return api_error('消息内容不能为空')
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ import ReactionBar from './ReactionBar';
|
|||||||
import StockEmbed from '../shared/StockEmbed';
|
import StockEmbed from '../shared/StockEmbed';
|
||||||
|
|
||||||
// 角色徽章配置
|
// 角色徽章配置
|
||||||
const ROLE_BADGE_CONFIG: Record<AdminRole | 'superAdmin', { label: string; bg: string; color: string }> = {
|
const ROLE_BADGE_CONFIG: Record<AdminRole, { label: string; bg: string; color: string }> = {
|
||||||
superAdmin: { label: '超管', bg: 'linear-gradient(135deg, rgba(239, 68, 68, 0.3), rgba(220, 38, 38, 0.3))', color: 'red.300' },
|
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' },
|
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' },
|
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' },
|
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<MessageItemProps> = ({
|
|||||||
// 管理员权限
|
// 管理员权限
|
||||||
const {
|
const {
|
||||||
getUserAdminRole,
|
getUserAdminRole,
|
||||||
isSuperAdmin: currentUserIsSuperAdmin,
|
|
||||||
canDeleteMessages,
|
canDeleteMessages,
|
||||||
canPinMessages,
|
canPinMessages,
|
||||||
globalAdmin
|
|
||||||
} = useAdmin();
|
} = useAdmin();
|
||||||
|
|
||||||
// 获取消息作者的管理员角色
|
// 获取消息作者的管理员角色(包括超级管理员)
|
||||||
const authorRole = getUserAdminRole(message.authorId);
|
const authorRole = getUserAdminRole(message.authorId);
|
||||||
// 判断作者是否是超级管理员(需要从全局管理员状态判断)
|
|
||||||
const authorIsSuperAdmin = globalAdmin?.isAdmin && globalAdmin?.role === 'admin' && message.authorId === '65';
|
|
||||||
|
|
||||||
// 获取显示的角色徽章
|
// 获取显示的角色徽章
|
||||||
const getRoleBadge = () => {
|
const roleBadge = authorRole ? ROLE_BADGE_CONFIG[authorRole] : null;
|
||||||
if (authorIsSuperAdmin) return ROLE_BADGE_CONFIG.superAdmin;
|
|
||||||
if (authorRole) return ROLE_BADGE_CONFIG[authorRole];
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
const roleBadge = getRoleBadge();
|
|
||||||
|
|
||||||
// 深色主题颜色(HeroUI 风格)
|
// 深色主题颜色(HeroUI 风格)
|
||||||
const hoverBg = 'rgba(255, 255, 255, 0.05)';
|
const hoverBg = 'rgba(255, 255, 255, 0.05)';
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ const TextChannel: React.FC<TextChannelProps> = ({
|
|||||||
loadMessages();
|
loadMessages();
|
||||||
// 加载管理员状态
|
// 加载管理员状态
|
||||||
loadChannelAdminStatus(channel.id);
|
loadChannelAdminStatus(channel.id);
|
||||||
// 加载频道管理员列表
|
// 加载频道管理员列表(包括超级管理员)
|
||||||
getChannelAdmins(channel.id).then(admins => {
|
getChannelAdmins(channel.id).then(admins => {
|
||||||
const adminMap = new Map<string, 'owner' | 'admin' | 'moderator'>();
|
const adminMap = new Map<string, 'super_admin' | 'owner' | 'admin' | 'moderator'>();
|
||||||
admins.forEach(admin => {
|
admins.forEach(admin => {
|
||||||
adminMap.set(admin.userId, admin.role);
|
adminMap.set(admin.userId, admin.role);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import { useAdmin } from '../../contexts/AdminContext';
|
|||||||
import { getChannelAdmins } from '../../services/communityService';
|
import { getChannelAdmins } from '../../services/communityService';
|
||||||
|
|
||||||
// 角色配置
|
// 角色配置
|
||||||
const ROLE_CONFIG: Record<AdminRole | 'superAdmin', { icon: any; label: string; color: string; bg: string }> = {
|
const ROLE_CONFIG: Record<AdminRole, { icon: any; label: string; color: string; bg: string }> = {
|
||||||
superAdmin: { icon: Crown, label: '超级管理员', color: 'red.300', bg: 'rgba(239, 68, 68, 0.2)' },
|
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)' },
|
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)' },
|
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)' },
|
moderator: { icon: UserCheck, label: '版主', color: 'green.300', bg: 'rgba(34, 197, 94, 0.2)' },
|
||||||
@@ -76,10 +76,8 @@ const MemberList: React.FC<MemberListProps> = ({ channelId }) => {
|
|||||||
}, [channelId]);
|
}, [channelId]);
|
||||||
|
|
||||||
// 获取成员的管理员角色
|
// 获取成员的管理员角色
|
||||||
const getMemberRole = (userId: string): AdminRole | 'superAdmin' | null => {
|
const getMemberRole = (userId: string): AdminRole | null => {
|
||||||
// 检查是否是超级管理员(ID=65)
|
// 从管理员列表中查找(已包含超级管理员)
|
||||||
if (userId === '65') return 'superAdmin';
|
|
||||||
// 从管理员列表中查找
|
|
||||||
return channelAdminList.get(userId) || null;
|
return channelAdminList.get(userId) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ export interface ApiResponse<T> {
|
|||||||
// 管理员相关
|
// 管理员相关
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export type AdminRole = 'owner' | 'admin' | 'moderator';
|
export type AdminRole = 'super_admin' | 'owner' | 'admin' | 'moderator';
|
||||||
|
|
||||||
export interface AdminPermissions {
|
export interface AdminPermissions {
|
||||||
delete_channel?: boolean;
|
delete_channel?: boolean;
|
||||||
@@ -311,6 +311,7 @@ export interface ChannelAdmin {
|
|||||||
username: string;
|
username: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
role: AdminRole;
|
role: AdminRole;
|
||||||
|
isSuperAdmin?: boolean;
|
||||||
permissions: AdminPermissions;
|
permissions: AdminPermissions;
|
||||||
createdAt: string;
|
createdAt?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user