system_service_ext.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. """
  2. 系统管理服务层扩展 - 用户管理和应用管理
  3. """
  4. import sys
  5. import os
  6. # 添加src目录到Python路径
  7. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
  8. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..'))
  9. import logging
  10. import uuid
  11. import json
  12. import secrets
  13. from typing import Optional, List, Dict, Any, Tuple
  14. from datetime import datetime, timezone
  15. from app.base.async_mysql_connection import get_db_connection
  16. logger = logging.getLogger(__name__)
  17. class SystemServiceExt:
  18. """系统管理服务扩展类 - 用户管理和应用管理"""
  19. def __init__(self):
  20. """初始化服务"""
  21. pass
  22. # ==================== 用户管理 ====================
  23. async def get_user_detail(self, user_id: str) -> Optional[Dict[str, Any]]:
  24. """获取用户详情(包含角色信息)"""
  25. conn = get_db_connection()
  26. if not conn:
  27. return None
  28. cursor = conn.cursor()
  29. try:
  30. # 查询用户基本信息和详情
  31. cursor.execute("""
  32. SELECT u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
  33. u.last_login_at, u.created_time, up.real_name, up.company, up.department
  34. FROM t_sys_user u
  35. LEFT JOIN t_sys_user_profile up ON u.id = up.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.id, r.name, r.code
  44. FROM t_sys_user_role ur
  45. JOIN t_sys_role r ON ur.role_id = r.id
  46. WHERE ur.user_id = %s AND ur.is_active = 1
  47. """, (user_id,))
  48. roles = cursor.fetchall()
  49. role_ids = [role['id'] for role in roles]
  50. role_names = [role['name'] for role in roles]
  51. # 构建返回数据
  52. user_detail = {
  53. "id": user_data['id'],
  54. "username": user_data['username'],
  55. "email": user_data['email'],
  56. "phone": user_data['phone'],
  57. "is_active": bool(user_data['is_active']),
  58. "is_superuser": bool(user_data['is_superuser']),
  59. "last_login_at": user_data['last_login_at'].isoformat() if user_data['last_login_at'] else None,
  60. "created_time": user_data['created_time'].isoformat() if user_data['created_time'] else None,
  61. "real_name": user_data['real_name'],
  62. "company": user_data['company'],
  63. "department": user_data['department'],
  64. "role_ids": role_ids,
  65. "roles": ', '.join(role_names) if role_names else None
  66. }
  67. return user_detail
  68. finally:
  69. cursor.close()
  70. conn.close()
  71. async def get_users(self, page: int, page_size: int, keyword: Optional[str] = None) -> Tuple[List[Dict[str, Any]], int]:
  72. """获取用户列表"""
  73. conn = get_db_connection()
  74. if not conn:
  75. return [], 0
  76. cursor = conn.cursor()
  77. try:
  78. # 构建查询条件
  79. where_conditions = []
  80. params = []
  81. if keyword:
  82. where_conditions.append("(u.username LIKE %s OR u.email LIKE %s OR up.real_name LIKE %s)")
  83. params.extend([f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"])
  84. where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
  85. # 查询总数
  86. cursor.execute(f"SELECT COUNT(*) as count FROM t_sys_user u LEFT JOIN t_sys_user_profile up ON u.id = up.user_id WHERE {where_clause}", params)
  87. total = cursor.fetchone()['count']
  88. # 查询用户列表
  89. offset = (page - 1) * page_size
  90. cursor.execute(f"""
  91. SELECT u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
  92. u.last_login_at, u.created_time, u.updated_time, u.created_by, u.updated_by,
  93. up.real_name, up.company, up.department,
  94. GROUP_CONCAT(r.name) as roles,
  95. creator.username as created_by_name,
  96. updater.username as updated_by_name
  97. FROM t_sys_user u
  98. LEFT JOIN t_sys_user_profile up ON u.id = up.user_id
  99. LEFT JOIN t_sys_user_role ur ON u.id = ur.user_id AND ur.is_active = 1
  100. LEFT JOIN t_sys_role r ON ur.role_id = r.id
  101. LEFT JOIN t_sys_user creator ON u.created_by = creator.id
  102. LEFT JOIN t_sys_user updater ON u.updated_by = updater.id
  103. WHERE {where_clause}
  104. GROUP BY u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
  105. u.last_login_at, u.created_time, u.updated_time, u.created_by, u.updated_by,
  106. up.real_name, up.company, up.department, creator.username, updater.username
  107. ORDER BY u.created_time DESC
  108. LIMIT %s OFFSET %s
  109. """, params + [page_size, offset])
  110. users = []
  111. for row in cursor.fetchall():
  112. users.append({
  113. "id": row['id'],
  114. "username": row['username'],
  115. "email": row['email'],
  116. "phone": row['phone'],
  117. "is_active": bool(row['is_active']),
  118. "is_superuser": bool(row['is_superuser']),
  119. "last_login_at": row['last_login_at'].isoformat() if row['last_login_at'] else None,
  120. "created_time": row['created_time'].isoformat() if row['created_time'] else None,
  121. "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
  122. "created_by": row['created_by'],
  123. "updated_by": row['updated_by'],
  124. "created_by_name": row['created_by_name'],
  125. "updated_by_name": row['updated_by_name'],
  126. "real_name": row['real_name'],
  127. "company": row['company'],
  128. "department": row['department'],
  129. "roles": row['roles']
  130. })
  131. return users, total
  132. finally:
  133. cursor.close()
  134. conn.close()
  135. async def create_user(self, user_data: Dict[str, Any], password_hash: str, creator_id: str) -> Tuple[bool, str]:
  136. """创建用户"""
  137. conn = get_db_connection()
  138. if not conn:
  139. return False, "数据库连接失败"
  140. cursor = conn.cursor()
  141. try:
  142. # 检查用户名和邮箱是否已存在
  143. cursor.execute("SELECT id FROM t_sys_user WHERE username = %s OR email = %s",
  144. (user_data['username'], user_data['email']))
  145. if cursor.fetchone():
  146. return False, "用户名或邮箱已存在"
  147. # 生成用户ID
  148. user_id = str(uuid.uuid4())
  149. # 插入用户
  150. cursor.execute("""
  151. INSERT INTO t_sys_user (id, username, email, phone, password_hash, is_active, is_superuser, created_by, created_time, updated_time)
  152. VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
  153. """, (user_id, user_data['username'], user_data['email'], user_data.get('phone'),
  154. password_hash, user_data.get('is_active', True), user_data.get('is_superuser', False), creator_id))
  155. # 插入用户详情
  156. if any(key in user_data for key in ['real_name', 'company', 'department']):
  157. profile_id = str(uuid.uuid4())
  158. cursor.execute("""
  159. INSERT INTO t_sys_user_profile (id, user_id, real_name, company, department, created_by, created_time, updated_time)
  160. VALUES (%s, %s, %s, %s, %s, %s, NOW(), NOW())
  161. """, (profile_id, user_id, user_data.get('real_name'), user_data.get('company'), user_data.get('department'), creator_id))
  162. # 分配角色
  163. if 'role_ids' in user_data and user_data['role_ids']:
  164. for role_id in user_data['role_ids']:
  165. role_assignment_id = str(uuid.uuid4())
  166. cursor.execute("""
  167. INSERT INTO t_sys_user_role (id, user_id, role_id, assigned_by, created_time)
  168. VALUES (%s, %s, %s, %s, NOW())
  169. """, (role_assignment_id, user_id, role_id, creator_id))
  170. conn.commit()
  171. return True, "用户创建成功"
  172. except Exception as e:
  173. logger.exception("创建用户错误")
  174. conn.rollback()
  175. return False, "服务器内部错误"
  176. finally:
  177. cursor.close()
  178. conn.close()
  179. async def update_user(self, user_id: str, user_data: Dict[str, Any], updater_id: str) -> Tuple[bool, str]:
  180. """更新用户"""
  181. conn = get_db_connection()
  182. if not conn:
  183. return False, "数据库连接失败"
  184. cursor = conn.cursor()
  185. try:
  186. # 更新用户基本信息
  187. update_fields = []
  188. update_values = []
  189. # 处理密码字段 - 需要哈希
  190. if 'password' in user_data and user_data['password']:
  191. update_fields.append('password_hash = %s')
  192. update_values.append(user_data['password'])
  193. for field in ['email', 'phone', 'is_active', 'is_superuser']:
  194. if field in user_data:
  195. update_fields.append(f'{field} = %s')
  196. update_values.append(user_data[field])
  197. if update_fields:
  198. update_values.append(updater_id)
  199. update_values.append(user_id)
  200. cursor.execute(f"""
  201. UPDATE t_sys_user
  202. SET {', '.join(update_fields)}, updated_by = %s, updated_time = NOW()
  203. WHERE id = %s
  204. """, update_values)
  205. # 更新用户详情
  206. profile_fields = ['real_name', 'company', 'department']
  207. profile_updates = {k: v for k, v in user_data.items() if k in profile_fields}
  208. if profile_updates:
  209. # 检查是否已有记录
  210. cursor.execute("SELECT id FROM t_sys_user_profile WHERE user_id = %s", (user_id,))
  211. profile_exists = cursor.fetchone()
  212. if profile_exists:
  213. update_fields = []
  214. update_values = []
  215. for field, value in profile_updates.items():
  216. update_fields.append(f'{field} = %s')
  217. update_values.append(value)
  218. update_values.append(updater_id)
  219. update_values.append(user_id)
  220. cursor.execute(f"""
  221. UPDATE t_sys_user_profile
  222. SET {', '.join(update_fields)}, updated_by = %s, updated_time = NOW()
  223. WHERE user_id = %s
  224. """, update_values)
  225. else:
  226. profile_id = str(uuid.uuid4())
  227. fields = ['id', 'user_id'] + list(profile_updates.keys())
  228. values = [profile_id, user_id] + list(profile_updates.values())
  229. placeholders = ', '.join(['%s'] * len(values))
  230. cursor.execute(f"""
  231. INSERT INTO t_sys_user_profile ({', '.join(fields)}, created_by, created_time, updated_time)
  232. VALUES ({placeholders}, %s, NOW(), NOW())
  233. """, values + [updater_id])
  234. # 更新用户角色
  235. if 'role_ids' in user_data:
  236. # 删除现有角色
  237. cursor.execute("DELETE FROM t_sys_user_role WHERE user_id = %s", (user_id,))
  238. # 添加新角色
  239. for role_id in user_data['role_ids']:
  240. assignment_id = str(uuid.uuid4())
  241. cursor.execute("""
  242. INSERT INTO t_sys_user_role (id, user_id, role_id, assigned_by, created_time)
  243. VALUES (%s, %s, %s, %s, NOW())
  244. """, (assignment_id, user_id, role_id, updater_id))
  245. conn.commit()
  246. return True, "用户更新成功"
  247. except Exception as e:
  248. logger.exception("更新用户错误")
  249. conn.rollback()
  250. return False, "服务器内部错误"
  251. finally:
  252. cursor.close()
  253. conn.close()
  254. async def delete_user(self, user_id: str, current_user_id: str) -> Tuple[bool, str]:
  255. """删除用户"""
  256. conn = get_db_connection()
  257. if not conn:
  258. return False, "数据库连接失败"
  259. cursor = conn.cursor()
  260. try:
  261. # 不能删除自己
  262. if user_id == current_user_id:
  263. return False, "不能删除自己"
  264. # 检查是否为超级管理员
  265. cursor.execute("""
  266. SELECT COUNT(*) as count FROM t_sys_user_role ur
  267. JOIN t_sys_role r ON ur.role_id = r.id
  268. WHERE ur.user_id = %s AND r.name = 'super_admin' AND ur.is_active = 1
  269. """, (user_id,))
  270. if cursor.fetchone()['count'] > 0:
  271. return False, "不能删除超级管理员"
  272. # 删除相关数据
  273. cursor.execute("DELETE FROM t_sys_user_role WHERE user_id = %s", (user_id,))
  274. cursor.execute("DELETE FROM t_sys_user_profile WHERE user_id = %s", (user_id,))
  275. cursor.execute("DELETE FROM t_sys_user WHERE id = %s", (user_id,))
  276. conn.commit()
  277. return True, "用户删除成功"
  278. except Exception as e:
  279. logger.exception("删除用户错误")
  280. conn.rollback()
  281. return False, "服务器内部错误"
  282. finally:
  283. cursor.close()
  284. conn.close()
  285. # ==================== 应用管理 ====================
  286. async def get_apps(self, page: int, page_size: int, user_id: str, is_app_manager: bool,
  287. keyword: str = "", status: str = "") -> Tuple[List[Dict[str, Any]], int]:
  288. """获取应用列表"""
  289. conn = get_db_connection()
  290. if not conn:
  291. return [], 0
  292. cursor = conn.cursor()
  293. try:
  294. # 构建查询条件
  295. where_conditions = []
  296. params = []
  297. # 如果不是应用管理员,只显示自己创建的应用
  298. if not is_app_manager:
  299. where_conditions.append("created_by = %s")
  300. params.append(user_id)
  301. if keyword:
  302. where_conditions.append("(name LIKE %s OR description LIKE %s)")
  303. params.extend([f"%{keyword}%", f"%{keyword}%"])
  304. if status == "active":
  305. where_conditions.append("is_active = 1")
  306. elif status == "inactive":
  307. where_conditions.append("is_active = 0")
  308. where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
  309. # 查询总数
  310. cursor.execute(f"SELECT COUNT(*) as count FROM t_sys_app WHERE {where_clause}", params)
  311. total = cursor.fetchone()['count']
  312. # 查询应用列表
  313. offset = (page - 1) * page_size
  314. cursor.execute(f"""
  315. SELECT id, name, app_key, description, icon_url, redirect_uris, scope,
  316. is_active, is_trusted, access_token_expires, refresh_token_expires,
  317. created_time, updated_time
  318. FROM t_sys_app
  319. WHERE {where_clause}
  320. ORDER BY created_time DESC
  321. LIMIT %s OFFSET %s
  322. """, params + [page_size, offset])
  323. apps = []
  324. for row in cursor.fetchall():
  325. app = {
  326. "id": row['id'],
  327. "name": row['name'],
  328. "app_key": row['app_key'],
  329. "description": row['description'],
  330. "icon_url": row['icon_url'],
  331. "redirect_uris": json.loads(row['redirect_uris']) if row['redirect_uris'] else [],
  332. "scope": json.loads(row['scope']) if row['scope'] else [],
  333. "is_active": bool(row['is_active']),
  334. "is_trusted": bool(row['is_trusted']),
  335. "access_token_expires": row['access_token_expires'],
  336. "refresh_token_expires": row['refresh_token_expires'],
  337. "created_time": row['created_time'].isoformat() if row['created_time'] else None,
  338. "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
  339. # 模拟统计数据
  340. "today_requests": secrets.randbelow(1000),
  341. "active_users": secrets.randbelow(100)
  342. }
  343. apps.append(app)
  344. return apps, total
  345. finally:
  346. cursor.close()
  347. conn.close()
  348. async def get_app_detail(self, app_id: str, user_id: str) -> Optional[Dict[str, Any]]:
  349. """获取应用详情(包含密钥)"""
  350. conn = get_db_connection()
  351. if not conn:
  352. return None
  353. cursor = conn.cursor()
  354. try:
  355. # 查询应用详情(包含密钥)
  356. cursor.execute("""
  357. SELECT id, name, app_key, app_secret, description, icon_url,
  358. redirect_uris, scope, is_active, is_trusted,
  359. access_token_expires, refresh_token_expires,
  360. created_time, updated_time
  361. FROM t_sys_app
  362. WHERE id = %s AND created_by = %s
  363. """, (app_id, user_id))
  364. app_data = cursor.fetchone()
  365. if not app_data:
  366. return None
  367. app_detail = {
  368. "id": app_data["id"],
  369. "name": app_data["name"],
  370. "app_key": app_data["app_key"],
  371. "app_secret": app_data["app_secret"],
  372. "description": app_data["description"],
  373. "icon_url": app_data["icon_url"],
  374. "redirect_uris": json.loads(app_data["redirect_uris"]) if app_data["redirect_uris"] else [],
  375. "scope": json.loads(app_data["scope"]) if app_data["scope"] else [],
  376. "is_active": bool(app_data["is_active"]),
  377. "is_trusted": bool(app_data["is_trusted"]),
  378. "access_token_expires": app_data["access_token_expires"],
  379. "refresh_token_expires": app_data["refresh_token_expires"],
  380. "created_time": app_data["created_time"].isoformat() if app_data["created_time"] else None,
  381. "updated_time": app_data["updated_time"].isoformat() if app_data["updated_time"] else None
  382. }
  383. return app_detail
  384. finally:
  385. cursor.close()
  386. conn.close()
  387. async def create_app(self, app_data: Dict[str, Any], user_id: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
  388. """创建应用"""
  389. conn = get_db_connection()
  390. if not conn:
  391. return False, "数据库连接失败", None
  392. cursor = conn.cursor()
  393. try:
  394. # 生成应用ID和密钥
  395. app_id = str(uuid.uuid4())
  396. app_key = secrets.token_urlsafe(32)[:32]
  397. app_secret = secrets.token_urlsafe(64)[:64]
  398. # 插入应用记录
  399. cursor.execute("""
  400. INSERT INTO t_sys_app (
  401. id, name, app_key, app_secret, description, icon_url,
  402. redirect_uris, scope, is_active, is_trusted,
  403. access_token_expires, refresh_token_expires, created_by,
  404. created_time, updated_time
  405. ) VALUES (
  406. %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW()
  407. )
  408. """, (
  409. app_id,
  410. app_data['name'],
  411. app_key,
  412. app_secret,
  413. app_data.get('description', ''),
  414. app_data.get('icon_url', ''),
  415. json.dumps(app_data['redirect_uris']),
  416. json.dumps(app_data.get('scope', ['profile'])),
  417. True,
  418. app_data.get('is_trusted', False),
  419. app_data.get('access_token_expires', 7200),
  420. app_data.get('refresh_token_expires', 2592000),
  421. user_id
  422. ))
  423. conn.commit()
  424. # 返回创建的应用信息
  425. app_info = {
  426. "id": app_id,
  427. "name": app_data['name'],
  428. "app_key": app_key,
  429. "app_secret": app_secret,
  430. "description": app_data.get('description', ''),
  431. "redirect_uris": app_data['redirect_uris'],
  432. "scope": app_data.get('scope', ['profile']),
  433. "is_active": True,
  434. "is_trusted": app_data.get('is_trusted', False)
  435. }
  436. return True, "应用创建成功", app_info
  437. except Exception as e:
  438. logger.exception("创建应用错误")
  439. conn.rollback()
  440. return False, "服务器内部错误", None
  441. finally:
  442. cursor.close()
  443. conn.close()
  444. async def update_app(self, app_id: str, app_data: Dict[str, Any], user_id: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
  445. """更新应用信息"""
  446. conn = get_db_connection()
  447. if not conn:
  448. return False, "数据库连接失败", None
  449. cursor = conn.cursor()
  450. try:
  451. # 检查应用是否存在且属于当前用户
  452. cursor.execute("""
  453. SELECT id, name FROM t_sys_app
  454. WHERE id = %s AND created_by = %s
  455. """, (app_id, user_id))
  456. existing_app = cursor.fetchone()
  457. if not existing_app:
  458. return False, "应用不存在或无权限", None
  459. # 检查应用名称是否已被其他应用使用
  460. name = app_data.get('name', '').strip()
  461. if name:
  462. cursor.execute("""
  463. SELECT id FROM t_sys_app
  464. WHERE name = %s AND created_by = %s AND id != %s
  465. """, (name, user_id, app_id))
  466. if cursor.fetchone():
  467. return False, "应用名称已存在", None
  468. # 准备更新数据
  469. description = (app_data.get('description') or '').strip()
  470. icon_url = (app_data.get('icon_url') or '').strip()
  471. redirect_uris = app_data.get('redirect_uris', [])
  472. scope = app_data.get('scope', ['profile', 'email'])
  473. is_trusted = app_data.get('is_trusted', False)
  474. access_token_expires = app_data.get('access_token_expires', 7200)
  475. refresh_token_expires = app_data.get('refresh_token_expires', 2592000)
  476. # 更新应用信息
  477. cursor.execute("""
  478. UPDATE t_sys_app
  479. SET name = %s, description = %s, icon_url = %s,
  480. redirect_uris = %s, scope = %s, is_trusted = %s,
  481. access_token_expires = %s, refresh_token_expires = %s,
  482. updated_by = %s, updated_time = NOW()
  483. WHERE id = %s
  484. """, (
  485. name, description, icon_url,
  486. json.dumps(redirect_uris), json.dumps(scope), is_trusted,
  487. access_token_expires, refresh_token_expires, user_id, app_id
  488. ))
  489. conn.commit()
  490. # 获取更新后的应用信息
  491. cursor.execute("""
  492. SELECT id, name, app_key, description, icon_url,
  493. redirect_uris, scope, is_active, is_trusted,
  494. access_token_expires, refresh_token_expires,
  495. created_time, updated_time
  496. FROM t_sys_app
  497. WHERE id = %s
  498. """, (app_id,))
  499. app_info = cursor.fetchone()
  500. if app_info:
  501. app_result = {
  502. "id": app_info["id"],
  503. "name": app_info["name"],
  504. "app_key": app_info["app_key"],
  505. "description": app_info["description"],
  506. "icon_url": app_info["icon_url"],
  507. "redirect_uris": json.loads(app_info["redirect_uris"]) if app_info["redirect_uris"] else [],
  508. "scope": json.loads(app_info["scope"]) if app_info["scope"] else [],
  509. "is_active": bool(app_info["is_active"]),
  510. "is_trusted": bool(app_info["is_trusted"]),
  511. "access_token_expires": app_info["access_token_expires"],
  512. "refresh_token_expires": app_info["refresh_token_expires"],
  513. "created_time": app_info["created_time"].isoformat() if app_info["created_time"] else None,
  514. "updated_time": app_info["updated_time"].isoformat() if app_info["updated_time"] else None
  515. }
  516. return True, "应用更新成功", app_result
  517. return True, "应用更新成功", None
  518. except Exception as e:
  519. logger.exception("更新应用错误")
  520. conn.rollback()
  521. return False, "服务器内部错误", None
  522. finally:
  523. cursor.close()
  524. conn.close()
  525. async def toggle_app_status(self, app_id: str, is_active: bool, user_id: str) -> Tuple[bool, str]:
  526. """切换应用状态"""
  527. conn = get_db_connection()
  528. if not conn:
  529. return False, "数据库连接失败"
  530. cursor = conn.cursor()
  531. try:
  532. # 检查应用是否存在且属于当前用户
  533. cursor.execute("""
  534. SELECT id, name FROM t_sys_app
  535. WHERE id = %s AND created_by = %s
  536. """, (app_id, user_id))
  537. app_data = cursor.fetchone()
  538. if not app_data:
  539. return False, "应用不存在或无权限"
  540. # 更新应用状态
  541. cursor.execute("""
  542. UPDATE t_sys_app
  543. SET is_active = %s, updated_by = %s, updated_time = NOW()
  544. WHERE id = %s
  545. """, (is_active, user_id, app_id))
  546. conn.commit()
  547. action = "启用" if is_active else "禁用"
  548. logger.info(f"应用状态已更新: {app_data['name']} -> {action}")
  549. return True, f"应用已{action}"
  550. except Exception as e:
  551. logger.exception("切换应用状态错误")
  552. conn.rollback()
  553. return False, "服务器内部错误"
  554. finally:
  555. cursor.close()
  556. conn.close()
  557. async def check_user_app_manager_role(self, user_id: str) -> bool:
  558. """检查用户是否是应用管理员"""
  559. conn = get_db_connection()
  560. if not conn:
  561. return False
  562. cursor = conn.cursor()
  563. try:
  564. cursor.execute("""
  565. SELECT COUNT(*) as count FROM t_sys_user_role ur
  566. JOIN t_sys_role r ON ur.role_id = r.id
  567. WHERE ur.user_id = %s AND r.name IN ('super_admin', 'admin', 'app_manager') AND ur.is_active = 1
  568. """, (user_id,))
  569. return cursor.fetchone()['count'] > 0
  570. finally:
  571. cursor.close()
  572. conn.close()
  573. async def reset_app_secret(self, app_id: str, user_id: str) -> Tuple[bool, str, Optional[str]]:
  574. """重置应用密钥"""
  575. conn = get_db_connection()
  576. if not conn:
  577. return False, "数据库连接失败", None
  578. cursor = conn.cursor()
  579. try:
  580. # 检查应用是否存在且属于当前用户
  581. cursor.execute("""
  582. SELECT id, name FROM t_sys_app
  583. WHERE id = %s AND created_by = %s
  584. """, (app_id, user_id))
  585. app_data = cursor.fetchone()
  586. if not app_data:
  587. return False, "应用不存在或无权限", None
  588. # 生成新的应用密钥
  589. new_secret = secrets.token_urlsafe(64)[:64]
  590. # 更新应用密钥
  591. cursor.execute("""
  592. UPDATE t_sys_app
  593. SET app_secret = %s, updated_by = %s, updated_time = NOW()
  594. WHERE id = %s
  595. """, (new_secret, user_id, app_id))
  596. conn.commit()
  597. logger.info(f"应用密钥已重置: {app_data['name']}")
  598. return True, "应用密钥已重置", new_secret
  599. except Exception as e:
  600. logger.exception("重置应用密钥错误")
  601. conn.rollback()
  602. return False, "服务器内部错误", None
  603. finally:
  604. cursor.close()
  605. conn.close()
  606. async def delete_app_by_id(self, app_id: str, user_id: str) -> Tuple[bool, str]:
  607. """删除应用"""
  608. conn = get_db_connection()
  609. if not conn:
  610. return False, "数据库连接失败"
  611. cursor = conn.cursor()
  612. try:
  613. # 检查应用是否存在且属于当前用户
  614. cursor.execute("""
  615. SELECT id, name FROM t_sys_app
  616. WHERE id = %s AND created_by = %s
  617. """, (app_id, user_id))
  618. app_data = cursor.fetchone()
  619. if not app_data:
  620. return False, "应用不存在或无权限"
  621. # 删除应用(级联删除相关数据)
  622. cursor.execute("DELETE FROM t_sys_app WHERE id = %s", (app_id,))
  623. conn.commit()
  624. logger.info(f"应用已删除: {app_data['name']}")
  625. return True, "应用已删除"
  626. except Exception as e:
  627. logger.exception("删除应用错误")
  628. conn.rollback()
  629. return False, "服务器内部错误"
  630. finally:
  631. cursor.close()
  632. conn.close()