system_service.py.backup 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. """
  2. 系统管理服务层
  3. 从 system_view.py 提取的SQL查询逻辑
  4. """
  5. import logging
  6. import uuid
  7. import json
  8. import secrets
  9. from typing import Optional, List, Dict, Any, Tuple
  10. from datetime import datetime, timezone
  11. from app.base.async_mysql_connection import get_db_connection
  12. logger = logging.getLogger(__name__)
  13. def generate_random_string(length: int) -> str:
  14. """生成随机字符串"""
  15. return secrets.token_urlsafe(length)[:length]
  16. class SystemService:
  17. """系统管理服务类 - 使用 SQL 查询方式"""
  18. def __init__(self):
  19. """初始化服务"""
  20. pass
  21. # ==================== 用户资料管理 ====================
  22. async def get_user_profile(self, user_id: str) -> Optional[Dict[str, Any]]:
  23. """获取用户资料"""
  24. conn = get_db_connection()
  25. if not conn:
  26. return None
  27. cursor = conn.cursor()
  28. try:
  29. # 查找用户详细信息
  30. cursor.execute("""
  31. SELECT u.id, u.username, u.email, u.phone, u.avatar_url, u.is_active, u.is_superuser,
  32. u.last_login_at, u.created_at, u.updated_at,
  33. p.real_name, p.company, p.department, p.position
  34. FROM users u
  35. LEFT JOIN user_profiles p ON u.id = p.user_id
  36. WHERE u.id = %s
  37. """, (user_id,))
  38. user_data = cursor.fetchone()
  39. if not user_data:
  40. return None
  41. # 获取用户角色
  42. cursor.execute("""
  43. SELECT r.name
  44. FROM user_roles ur
  45. JOIN roles r ON ur.role_id = r.id
  46. WHERE ur.user_id = %s AND ur.is_active = 1
  47. """, (user_id,))
  48. roles = [row['name'] for row in cursor.fetchall()]
  49. # 构建用户信息
  50. user_info = {
  51. "id": user_data["id"],
  52. "username": user_data["username"],
  53. "email": user_data["email"],
  54. "phone": user_data["phone"],
  55. "avatar_url": user_data["avatar_url"],
  56. "is_active": user_data["is_active"],
  57. "is_superuser": user_data["is_superuser"],
  58. "last_login_at": user_data["last_login_at"].isoformat() if user_data["last_login_at"] else None,
  59. "created_at": user_data["created_at"].isoformat() if user_data["created_at"] else None,
  60. "updated_at": user_data["updated_at"].isoformat() if user_data["updated_at"] else None,
  61. "real_name": user_data["real_name"],
  62. "company": user_data["company"],
  63. "department": user_data["department"],
  64. "position": user_data["position"],
  65. "roles": roles
  66. }
  67. return user_info
  68. finally:
  69. cursor.close()
  70. conn.close()
  71. async def update_user_profile(self, user_id: str, profile_data: Dict[str, Any]) -> bool:
  72. """更新用户资料"""
  73. conn = get_db_connection()
  74. if not conn:
  75. return False
  76. cursor = conn.cursor()
  77. try:
  78. # 更新用户基本信息
  79. update_fields = []
  80. update_values = []
  81. if 'email' in profile_data:
  82. update_fields.append('email = %s')
  83. update_values.append(profile_data['email'])
  84. if 'phone' in profile_data:
  85. update_fields.append('phone = %s')
  86. update_values.append(profile_data['phone'])
  87. if update_fields:
  88. update_values.append(user_id)
  89. cursor.execute(f"""
  90. UPDATE users
  91. SET {', '.join(update_fields)}, updated_at = NOW()
  92. WHERE id = %s
  93. """, update_values)
  94. # 更新或插入用户详情
  95. profile_fields = ['real_name', 'company', 'department', 'position']
  96. profile_updates = {k: v for k, v in profile_data.items() if k in profile_fields}
  97. if profile_updates:
  98. # 检查是否已有记录
  99. cursor.execute("SELECT id FROM user_profiles WHERE user_id = %s", (user_id,))
  100. profile_exists = cursor.fetchone()
  101. if profile_exists:
  102. # 更新现有记录
  103. update_fields = []
  104. update_values = []
  105. for field, value in profile_updates.items():
  106. update_fields.append(f'{field} = %s')
  107. update_values.append(value)
  108. update_values.append(user_id)
  109. cursor.execute(f"""
  110. UPDATE user_profiles
  111. SET {', '.join(update_fields)}, updated_at = NOW()
  112. WHERE user_id = %s
  113. """, update_values)
  114. else:
  115. # 插入新记录
  116. fields = ['user_id'] + list(profile_updates.keys())
  117. values = [user_id] + list(profile_updates.values())
  118. placeholders = ', '.join(['%s'] * len(values))
  119. cursor.execute(f"""
  120. INSERT INTO user_profiles ({', '.join(fields)}, created_at, updated_at)
  121. VALUES ({placeholders}, NOW(), NOW())
  122. """, values)
  123. conn.commit()
  124. return True
  125. except Exception as e:
  126. logger.exception("更新用户资料错误")
  127. conn.rollback()
  128. return False
  129. finally:
  130. cursor.close()
  131. conn.close()
  132. async def verify_and_update_password(self, user_id: str, old_password_hash: str, new_password_hash: str) -> Tuple[bool, str]:
  133. """验证旧密码并更新为新密码"""
  134. conn = get_db_connection()
  135. if not conn:
  136. return False, "数据库连接失败"
  137. cursor = conn.cursor()
  138. try:
  139. # 验证当前密码
  140. cursor.execute("SELECT password_hash FROM users WHERE id = %s", (user_id,))
  141. result = cursor.fetchone()
  142. if not result:
  143. return False, "用户不存在"
  144. if result["password_hash"] != old_password_hash:
  145. return False, "当前密码错误"
  146. # 更新密码
  147. cursor.execute("""
  148. UPDATE users
  149. SET password_hash = %s, updated_at = NOW()
  150. WHERE id = %s
  151. """, (new_password_hash, user_id))
  152. conn.commit()
  153. return True, "密码修改成功"
  154. except Exception as e:
  155. logger.exception("修改密码错误")
  156. conn.rollback()
  157. return False, "服务器内部错误"
  158. finally:
  159. cursor.close()
  160. conn.close()
  161. # ==================== 菜单管理 ====================
  162. async def get_user_menus(self, user_id: str) -> List[Dict[str, Any]]:
  163. """获取用户菜单"""
  164. conn = get_db_connection()
  165. if not conn:
  166. return []
  167. cursor = conn.cursor()
  168. try:
  169. # 检查用户是否是超级管理员
  170. cursor.execute("""
  171. SELECT COUNT(*) as count FROM user_roles ur
  172. JOIN roles r ON ur.role_id = r.id
  173. WHERE ur.user_id = %s AND r.name = 'super_admin' AND ur.is_active = 1
  174. """, (user_id,))
  175. result = cursor.fetchone()
  176. is_super_admin = result['count'] > 0 if result else False
  177. if is_super_admin:
  178. # 超级管理员返回所有活跃菜单
  179. cursor.execute("""
  180. SELECT m.id, m.parent_id, m.name, m.title, m.path,
  181. m.component, m.icon, m.sort_order, m.menu_type,
  182. m.is_hidden, m.is_active
  183. FROM menus m
  184. WHERE m.is_active = 1
  185. ORDER BY m.sort_order
  186. """)
  187. else:
  188. # 普通用户根据角色权限获取菜单
  189. cursor.execute("""
  190. SELECT m.id, m.parent_id, m.name, m.title, m.path,
  191. m.component, m.icon, m.sort_order, m.menu_type,
  192. m.is_hidden, m.is_active
  193. FROM menus m
  194. JOIN role_menus rm ON m.id = rm.menu_id
  195. JOIN user_roles ur ON rm.role_id = ur.role_id
  196. WHERE ur.user_id = %s
  197. AND ur.is_active = 1
  198. AND m.is_active = 1
  199. GROUP BY m.id, m.parent_id, m.name, m.title, m.path,
  200. m.component, m.icon, m.sort_order, m.menu_type,
  201. m.is_hidden, m.is_active
  202. ORDER BY m.sort_order
  203. """, (user_id,))
  204. menus = []
  205. for row in cursor.fetchall():
  206. menu = {
  207. "id": row['id'],
  208. "parent_id": row['parent_id'],
  209. "name": row['name'],
  210. "title": row['title'],
  211. "path": row['path'],
  212. "component": row['component'],
  213. "icon": row['icon'],
  214. "sort_order": row['sort_order'],
  215. "menu_type": row['menu_type'],
  216. "is_hidden": bool(row['is_hidden']),
  217. "is_active": bool(row['is_active']),
  218. "children": []
  219. }
  220. menus.append(menu)
  221. return menus
  222. finally:
  223. cursor.close()
  224. conn.close()
  225. async def get_all_menus(self, page: int, page_size: int, keyword: Optional[str] = None) -> Tuple[List[Dict[str, Any]], int]:
  226. """获取所有菜单(管理员)"""
  227. conn = get_db_connection()
  228. if not conn:
  229. return [], 0
  230. cursor = conn.cursor()
  231. try:
  232. # 构建查询条件
  233. where_conditions = []
  234. params = []
  235. if keyword:
  236. where_conditions.append("(m.title LIKE %s OR m.name LIKE %s)")
  237. params.extend([f"%{keyword}%", f"%{keyword}%"])
  238. where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
  239. # 查询总数
  240. cursor.execute(f"SELECT COUNT(*) as count FROM menus m WHERE {where_clause}", params)
  241. total = cursor.fetchone()['count']
  242. # 查询菜单列表
  243. cursor.execute(f"""
  244. SELECT m.id, m.parent_id, m.name, m.title, m.path, m.component,
  245. m.icon, m.sort_order, m.menu_type, m.is_hidden, m.is_active,
  246. m.description, m.created_at, m.updated_at,
  247. pm.title as parent_title
  248. FROM menus m
  249. LEFT JOIN menus pm ON m.parent_id = pm.id
  250. WHERE {where_clause}
  251. ORDER BY
  252. CASE WHEN m.parent_id IS NULL THEN 0 ELSE 1 END,
  253. m.sort_order,
  254. CASE WHEN m.menu_type = 'menu' THEN 0 ELSE 1 END,
  255. m.created_at
  256. LIMIT %s OFFSET %s
  257. """, params + [page_size, (page - 1) * page_size])
  258. menus = []
  259. for row in cursor.fetchall():
  260. menu = {
  261. "id": row['id'],
  262. "parent_id": row['parent_id'],
  263. "name": row['name'],
  264. "title": row['title'],
  265. "path": row['path'],
  266. "component": row['component'],
  267. "icon": row['icon'],
  268. "sort_order": row['sort_order'],
  269. "menu_type": row['menu_type'],
  270. "is_hidden": bool(row['is_hidden']),
  271. "is_active": bool(row['is_active']),
  272. "description": row['description'],
  273. "created_at": row['created_at'].isoformat() if row['created_at'] else None,
  274. "updated_at": row['updated_at'].isoformat() if row['updated_at'] else None,
  275. "parent_title": row['parent_title']
  276. }
  277. menus.append(menu)
  278. return menus, total
  279. finally:
  280. cursor.close()
  281. conn.close()
  282. async def create_menu(self, menu_data: Dict[str, Any]) -> Tuple[bool, str]:
  283. """创建菜单"""
  284. conn = get_db_connection()
  285. if not conn:
  286. return False, "数据库连接失败"
  287. cursor = conn.cursor()
  288. try:
  289. # 检查菜单名是否已存在
  290. cursor.execute("SELECT id FROM menus WHERE name = %s", (menu_data['name'],))
  291. if cursor.fetchone():
  292. return False, "菜单标识已存在"
  293. # 创建菜单
  294. menu_id = str(uuid.uuid4())
  295. cursor.execute("""
  296. INSERT INTO menus (id, parent_id, name, title, path, component, icon,
  297. sort_order, menu_type, is_hidden, is_active, description, created_at, updated_at)
  298. VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
  299. """, (
  300. menu_id, menu_data.get('parent_id'), menu_data['name'], menu_data['title'],
  301. menu_data.get('path'), menu_data.get('component'), menu_data.get('icon'),
  302. menu_data.get('sort_order', 0), menu_data.get('menu_type', 'menu'),
  303. menu_data.get('is_hidden', False), menu_data.get('is_active', True),
  304. menu_data.get('description')
  305. ))
  306. conn.commit()
  307. return True, "菜单创建成功"
  308. except Exception as e:
  309. logger.exception("创建菜单错误")
  310. conn.rollback()
  311. return False, "服务器内部错误"
  312. finally:
  313. cursor.close()
  314. conn.close()
  315. async def update_menu(self, menu_id: str, menu_data: Dict[str, Any]) -> Tuple[bool, str]:
  316. """更新菜单"""
  317. conn = get_db_connection()
  318. if not conn:
  319. return False, "数据库连接失败"
  320. cursor = conn.cursor()
  321. try:
  322. # 更新菜单
  323. update_fields = []
  324. update_values = []
  325. for field in ['parent_id', 'title', 'path', 'component', 'icon', 'sort_order',
  326. 'menu_type', 'is_hidden', 'is_active', 'description']:
  327. if field in menu_data:
  328. update_fields.append(f'{field} = %s')
  329. update_values.append(menu_data[field])
  330. if update_fields:
  331. update_values.append(menu_id)
  332. cursor.execute(f"""
  333. UPDATE menus
  334. SET {', '.join(update_fields)}, updated_at = NOW()
  335. WHERE id = %s
  336. """, update_values)
  337. conn.commit()
  338. return True, "菜单更新成功"
  339. except Exception as e:
  340. logger.exception("更新菜单错误")
  341. conn.rollback()
  342. return False, "服务器内部错误"
  343. finally:
  344. cursor.close()
  345. conn.close()
  346. async def delete_menu(self, menu_id: str) -> Tuple[bool, str]:
  347. """删除菜单"""
  348. conn = get_db_connection()
  349. if not conn:
  350. return False, "数据库连接失败"
  351. cursor = conn.cursor()
  352. try:
  353. # 检查是否有子菜单
  354. cursor.execute("SELECT COUNT(*) as count FROM menus WHERE parent_id = %s", (menu_id,))
  355. if cursor.fetchone()['count'] > 0:
  356. return False, "该菜单下有子菜单,无法删除"
  357. # 删除菜单相关数据
  358. cursor.execute("DELETE FROM role_menus WHERE menu_id = %s", (menu_id,))
  359. cursor.execute("DELETE FROM menus WHERE id = %s", (menu_id,))
  360. conn.commit()
  361. return True, "菜单删除成功"
  362. except Exception as e:
  363. logger.exception("删除菜单错误")
  364. conn.rollback()
  365. return False, "服务器内部错误"
  366. finally:
  367. cursor.close()
  368. conn.close()
  369. # ==================== 角色管理 ====================
  370. async def get_all_roles(self, page: int, page_size: int, keyword: Optional[str] = None) -> Tuple[List[Dict[str, Any]], int]:
  371. """获取所有角色"""
  372. conn = get_db_connection()
  373. if not conn:
  374. return [], 0
  375. cursor = conn.cursor()
  376. try:
  377. # 构建查询条件
  378. where_conditions = []
  379. params = []
  380. if keyword:
  381. where_conditions.append("(r.display_name LIKE %s OR r.name LIKE %s)")
  382. params.extend([f"%{keyword}%", f"%{keyword}%"])
  383. where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
  384. # 查询总数
  385. cursor.execute(f"SELECT COUNT(*) as count FROM roles r WHERE {where_clause}", params)
  386. total = cursor.fetchone()['count']
  387. # 查询角色列表
  388. offset = (page - 1) * page_size
  389. cursor.execute(f"""
  390. SELECT r.id, r.name, r.display_name, r.description, r.is_active,
  391. r.is_system, r.created_at, r.updated_at,
  392. COUNT(ur.user_id) as user_count
  393. FROM roles r
  394. LEFT JOIN user_roles ur ON r.id = ur.role_id AND ur.is_active = 1
  395. WHERE {where_clause}
  396. GROUP BY r.id
  397. ORDER BY r.is_system DESC, r.created_at
  398. LIMIT %s OFFSET %s
  399. """, params + [page_size, offset])
  400. roles = []
  401. for row in cursor.fetchall():
  402. role = {
  403. "id": row['id'],
  404. "name": row['name'],
  405. "display_name": row['display_name'],
  406. "description": row['description'],
  407. "is_active": bool(row['is_active']),
  408. "is_system": bool(row['is_system']),
  409. "created_at": row['created_at'].isoformat() if row['created_at'] else None,
  410. "updated_at": row['updated_at'].isoformat() if row['updated_at'] else None,
  411. "user_count": row['user_count']
  412. }
  413. roles.append(role)
  414. return roles, total
  415. finally:
  416. cursor.close()
  417. conn.close()
  418. async def get_all_roles_simple(self) -> List[Dict[str, Any]]:
  419. """获取所有角色(简化版,用于下拉选择)"""
  420. conn = get_db_connection()
  421. if not conn:
  422. return []
  423. cursor = conn.cursor()
  424. try:
  425. cursor.execute("""
  426. SELECT id, name, display_name, is_system, is_active
  427. FROM roles
  428. WHERE is_active = 1
  429. ORDER BY is_system DESC, display_name
  430. """)
  431. roles = []
  432. for row in cursor.fetchall():
  433. roles.append({
  434. "id": row['id'],
  435. "name": row['name'],
  436. "display_name": row['display_name'],
  437. "is_system": bool(row['is_system']),
  438. "is_active": bool(row['is_active'])
  439. })
  440. return roles
  441. finally:
  442. cursor.close()
  443. conn.close()
  444. async def create_role(self, role_data: Dict[str, Any]) -> Tuple[bool, str]:
  445. """创建角色"""
  446. conn = get_db_connection()
  447. if not conn:
  448. return False, "数据库连接失败"
  449. cursor = conn.cursor()
  450. try:
  451. # 检查角色名是否已存在
  452. cursor.execute("SELECT id FROM roles WHERE name = %s", (role_data['name'],))
  453. if cursor.fetchone():
  454. return False, "角色名已存在"
  455. # 创建角色
  456. role_id = str(uuid.uuid4())
  457. cursor.execute("""
  458. INSERT INTO roles (id, name, display_name, description, is_active, is_system, created_at, updated_at)
  459. VALUES (%s, %s, %s, %s, %s, %s, NOW(), NOW())
  460. """, (role_id, role_data['name'], role_data['display_name'], role_data.get('description'),
  461. role_data.get('is_active', True), False))
  462. conn.commit()
  463. return True, "角色创建成功"
  464. except Exception as e:
  465. logger.exception("创建角色错误")
  466. conn.rollback()
  467. return False, "服务器内部错误"
  468. finally:
  469. cursor.close()
  470. conn.close()
  471. async def update_role(self, role_id: str, role_data: Dict[str, Any]) -> Tuple[bool, str]:
  472. """更新角色"""
  473. conn = get_db_connection()
  474. if not conn:
  475. return False, "数据库连接失败"
  476. cursor = conn.cursor()
  477. try:
  478. # 检查是否为系统角色
  479. cursor.execute("SELECT is_system FROM roles WHERE id = %s", (role_id,))
  480. role = cursor.fetchone()
  481. if not role:
  482. return False, "角色不存在"
  483. if role["is_system"]:
  484. return False, "不能修改系统角色"
  485. # 更新角色
  486. update_fields = []
  487. update_values = []
  488. for field in ['display_name', 'description', 'is_active']:
  489. if field in role_data:
  490. update_fields.append(f'{field} = %s')
  491. update_values.append(role_data[field])
  492. if update_fields:
  493. update_values.append(role_id)
  494. cursor.execute(f"""
  495. UPDATE roles
  496. SET {', '.join(update_fields)}, updated_at = NOW()
  497. WHERE id = %s
  498. """, update_values)
  499. conn.commit()
  500. return True, "角色更新成功"
  501. except Exception as e:
  502. logger.exception("更新角色错误")
  503. conn.rollback()
  504. return False, "服务器内部错误"
  505. finally:
  506. cursor.close()
  507. conn.close()
  508. async def delete_role(self, role_id: str) -> Tuple[bool, str]:
  509. """删除角色"""
  510. conn = get_db_connection()
  511. if not conn:
  512. return False, "数据库连接失败"
  513. cursor = conn.cursor()
  514. try:
  515. # 检查是否为系统角色
  516. cursor.execute("SELECT is_system FROM roles WHERE id = %s", (role_id,))
  517. role = cursor.fetchone()
  518. if not role:
  519. return False, "角色不存在"
  520. if role["is_system"]:
  521. return False, "不能删除系统角色"
  522. # 检查是否有用户使用此角色
  523. cursor.execute("SELECT COUNT(*) as count FROM user_roles WHERE role_id = %s", (role_id,))
  524. if cursor.fetchone()['count'] > 0:
  525. return False, "该角色正在被使用,无法删除"
  526. # 删除角色相关数据
  527. cursor.execute("DELETE FROM role_permissions WHERE role_id = %s", (role_id,))
  528. cursor.execute("DELETE FROM role_menus WHERE role_id = %s", (role_id,))
  529. cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,))
  530. conn.commit()
  531. return True, "角色删除成功"
  532. except Exception as e:
  533. logger.exception("删除角色错误")
  534. conn.rollback()
  535. return False, "服务器内部错误"
  536. finally:
  537. cursor.close()
  538. conn.close()
  539. async def get_role_menus(self, role_id: str) -> Tuple[Optional[Dict[str, Any]], List[str], List[Dict[str, Any]]]:
  540. """获取角色的菜单权限"""
  541. conn = get_db_connection()
  542. if not conn:
  543. return None, [], []
  544. cursor = conn.cursor()
  545. try:
  546. # 检查角色是否存在
  547. cursor.execute("SELECT id, name FROM roles WHERE id = %s", (role_id,))
  548. role = cursor.fetchone()
  549. if not role:
  550. return None, [], []
  551. # 检查是否为超级管理员角色
  552. role_name = role["name"]
  553. is_super_admin_role = role_name == "super_admin"
  554. if is_super_admin_role:
  555. # 超级管理员默认拥有所有菜单权限
  556. cursor.execute("""
  557. SELECT id, name, title, parent_id, menu_type
  558. FROM menus
  559. WHERE is_active = 1
  560. ORDER BY sort_order
  561. """)
  562. menu_permissions = cursor.fetchall()
  563. else:
  564. # 普通角色查询已分配的菜单权限
  565. cursor.execute("""
  566. SELECT m.id, m.name, m.title, m.parent_id, m.menu_type
  567. FROM role_menus rm
  568. JOIN menus m ON rm.menu_id = m.id
  569. WHERE rm.role_id = %s AND m.is_active = 1
  570. ORDER BY m.sort_order
  571. """, (role_id,))
  572. menu_permissions = cursor.fetchall()
  573. # 构建返回数据
  574. menu_ids = [menu["id"] for menu in menu_permissions]
  575. menu_details = []
  576. for menu in menu_permissions:
  577. menu_details.append({
  578. "id": menu["id"],
  579. "name": menu["name"],
  580. "title": menu["title"],
  581. "parent_id": menu["parent_id"],
  582. "menu_type": menu["menu_type"]
  583. })
  584. role_info = {
  585. "id": role["id"],
  586. "name": role["name"]
  587. }
  588. return role_info, menu_ids, menu_details
  589. finally:
  590. cursor.close()
  591. conn.close()
  592. async def update_role_menus(self, role_id: str, menu_ids: List[str]) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
  593. """更新角色的菜单权限"""
  594. conn = get_db_connection()
  595. if not conn:
  596. return False, "数据库连接失败", None
  597. cursor = conn.cursor()
  598. try:
  599. # 检查角色是否存在
  600. cursor.execute("SELECT id, name FROM roles WHERE id = %s", (role_id,))
  601. role = cursor.fetchone()
  602. if not role:
  603. return False, "角色不存在", None
  604. # 检查是否为超级管理员角色
  605. role_name = role["name"]
  606. is_super_admin_role = role_name == "super_admin"
  607. if is_super_admin_role:
  608. return False, "超级管理员角色拥有全部权限,无需修改", None
  609. # 验证菜单ID是否存在
  610. if menu_ids:
  611. placeholders = ','.join(['%s'] * len(menu_ids))
  612. cursor.execute(f"""
  613. SELECT id FROM menus
  614. WHERE id IN ({placeholders}) AND is_active = 1
  615. """, menu_ids)
  616. valid_menu_ids = [row['id'] for row in cursor.fetchall()]
  617. invalid_menu_ids = set(menu_ids) - set(valid_menu_ids)
  618. if invalid_menu_ids:
  619. return False, f"无效的菜单ID: {', '.join(invalid_menu_ids)}", None
  620. # 开始事务
  621. cursor.execute("START TRANSACTION")
  622. # 删除角色现有的菜单权限
  623. cursor.execute("DELETE FROM role_menus WHERE role_id = %s", (role_id,))
  624. # 添加新的菜单权限
  625. if menu_ids:
  626. values = [(role_id, menu_id) for menu_id in menu_ids]
  627. cursor.executemany("""
  628. INSERT INTO role_menus (role_id, menu_id, created_at)
  629. VALUES (%s, %s, NOW())
  630. """, values)
  631. # 提交事务
  632. conn.commit()
  633. result_data = {
  634. "role_id": role_id,
  635. "role_name": role["name"],
  636. "menu_ids": menu_ids,
  637. "updated_count": len(menu_ids)
  638. }
  639. return True, "角色菜单权限更新成功", result_data
  640. except Exception as e:
  641. logger.exception("更新角色菜单权限错误")
  642. conn.rollback()
  643. return False, "服务器内部错误", None
  644. finally:
  645. cursor.close()
  646. conn.close()
  647. async def get_user_permissions(self, user_id: str) -> List[Dict[str, Any]]:
  648. """获取用户权限"""
  649. conn = get_db_connection()
  650. if not conn:
  651. return []
  652. cursor = conn.cursor()
  653. try:
  654. # 获取用户权限
  655. cursor.execute("""
  656. SELECT DISTINCT p.name, p.resource, p.action
  657. FROM permissions p
  658. JOIN role_permissions rp ON p.id = rp.permission_id
  659. JOIN user_roles ur ON rp.role_id = ur.role_id
  660. WHERE ur.user_id = %s
  661. AND ur.is_active = 1
  662. AND p.is_active = 1
  663. """, (user_id,))
  664. permissions = []
  665. for row in cursor.fetchall():
  666. permissions.append({
  667. "name": row['name'],
  668. "resource": row['resource'],
  669. "action": row['action']
  670. })
  671. return permissions
  672. finally:
  673. cursor.close()
  674. conn.close()