system.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. """系统管理接口"""
  2. import json
  3. from typing import Optional
  4. from fastapi import APIRouter, Depends, Query
  5. from sqlalchemy.orm import Session
  6. from sqlalchemy import desc, or_
  7. from app.core.database import get_db
  8. from app.core.auth import get_current_user
  9. from app.core.response import success_response
  10. from app.core.utils import generate_uuid
  11. from app.models.models import SysUpgradeLog, SysComment, BizSystemConfig
  12. router = APIRouter(prefix="/web/system", tags=["系统管理"])
  13. # =========================================
  14. # 更新日志管理
  15. # =========================================
  16. @router.get("/upgrade-logs", response_model=dict)
  17. async def get_upgrade_logs(
  18. page: int = Query(1, ge=1, description="页码"),
  19. pageSize: int = Query(20, ge=1, le=100, description="每页条数"),
  20. db: Session = Depends(get_db),
  21. current_user: dict = Depends(get_current_user)
  22. ):
  23. """
  24. 获取更新日志列表
  25. """
  26. # 构建查询
  27. query = db.query(SysUpgradeLog).filter(SysUpgradeLog.is_del == 0)
  28. # 获取总数
  29. total = query.count()
  30. # 分页查询
  31. offset = (page - 1) * pageSize
  32. logs = (
  33. query.order_by(desc(SysUpgradeLog.date))
  34. .offset(offset)
  35. .limit(pageSize)
  36. .all()
  37. )
  38. # 转换数据
  39. list_data = []
  40. for log in logs:
  41. detail_list = []
  42. if log.detail:
  43. try:
  44. detail_list = json.loads(log.detail)
  45. except json.JSONDecodeError:
  46. detail_list = []
  47. list_data.append({
  48. "id": log.id,
  49. "version": log.version,
  50. "title": log.title,
  51. "date": log.date.strftime("%Y-%m-%d") if log.date else "",
  52. "detail": detail_list,
  53. "requireReLogin": bool(log.require_re_login),
  54. "remark": log.remark,
  55. "createAt": log.create_at.strftime("%Y-%m-%d %H:%M:%S") if log.create_at else ""
  56. })
  57. return success_response({
  58. "list": list_data,
  59. "total": total,
  60. "page": page,
  61. "pageSize": pageSize
  62. })
  63. @router.get("/upgrade-logs/latest", response_model=dict)
  64. async def get_latest_upgrade_log(
  65. db: Session = Depends(get_db),
  66. current_user: dict = Depends(get_current_user)
  67. ):
  68. """
  69. 获取最新一条更新日志(用于升级检测)
  70. """
  71. log = (
  72. db.query(SysUpgradeLog)
  73. .filter(SysUpgradeLog.is_del == 0)
  74. .order_by(desc(SysUpgradeLog.date))
  75. .first()
  76. )
  77. if not log:
  78. return success_response(None)
  79. detail_list = []
  80. if log.detail:
  81. try:
  82. detail_list = json.loads(log.detail)
  83. except json.JSONDecodeError:
  84. detail_list = []
  85. return success_response({
  86. "version": log.version,
  87. "title": log.title,
  88. "date": log.date.strftime("%Y-%m-%d") if log.date else "",
  89. "detail": detail_list,
  90. "requireReLogin": bool(log.require_re_login),
  91. "remark": log.remark
  92. })
  93. @router.get("/upgrade-logs/all", response_model=dict)
  94. async def get_all_upgrade_logs(
  95. db: Session = Depends(get_db),
  96. current_user: dict = Depends(get_current_user)
  97. ):
  98. """
  99. 获取所有更新日志(用于变更日志页面展示)
  100. """
  101. logs = (
  102. db.query(SysUpgradeLog)
  103. .filter(SysUpgradeLog.is_del == 0)
  104. .order_by(desc(SysUpgradeLog.date))
  105. .all()
  106. )
  107. list_data = []
  108. for log in logs:
  109. detail_list = []
  110. if log.detail:
  111. try:
  112. detail_list = json.loads(log.detail)
  113. except json.JSONDecodeError:
  114. detail_list = []
  115. list_data.append({
  116. "version": log.version,
  117. "title": log.title,
  118. "date": log.date.strftime("%Y-%m-%d") if log.date else "",
  119. "detail": detail_list,
  120. "requireReLogin": bool(log.require_re_login),
  121. "remark": log.remark
  122. })
  123. return success_response(list_data)
  124. @router.post("/upgrade-logs", response_model=dict)
  125. async def create_upgrade_log(
  126. version: str,
  127. title: str,
  128. date: str,
  129. detail: Optional[str] = None,
  130. require_re_login: bool = False,
  131. remark: Optional[str] = None,
  132. db: Session = Depends(get_db),
  133. current_user: dict = Depends(get_current_user)
  134. ):
  135. """
  136. 创建更新日志
  137. """
  138. # 检查版本号是否已存在
  139. existing = db.query(SysUpgradeLog).filter(
  140. SysUpgradeLog.version == version,
  141. SysUpgradeLog.is_del == 0
  142. ).first()
  143. if existing:
  144. return {"code": 400, "message": "版本号已存在", "data": None}
  145. from datetime import datetime
  146. log = SysUpgradeLog(
  147. id=generate_uuid(),
  148. version=version,
  149. title=title,
  150. date=datetime.strptime(date, "%Y-%m-%d"),
  151. detail=detail,
  152. require_re_login=1 if require_re_login else 0,
  153. remark=remark,
  154. create_by=current_user.get("id"),
  155. create_name=current_user.get("username", "系统")
  156. )
  157. db.add(log)
  158. db.commit()
  159. return success_response({"id": log.id})
  160. @router.put("/upgrade-logs/{log_id}", response_model=dict)
  161. async def update_upgrade_log(
  162. log_id: str,
  163. version: Optional[str] = None,
  164. title: Optional[str] = None,
  165. date: Optional[str] = None,
  166. detail: Optional[str] = None,
  167. require_re_login: Optional[bool] = None,
  168. remark: Optional[str] = None,
  169. db: Session = Depends(get_db),
  170. current_user: dict = Depends(get_current_user)
  171. ):
  172. """
  173. 更新更新日志
  174. """
  175. log = db.query(SysUpgradeLog).filter(
  176. SysUpgradeLog.id == log_id,
  177. SysUpgradeLog.is_del == 0
  178. ).first()
  179. if not log:
  180. return {"code": 404, "message": "更新日志不存在", "data": None}
  181. if version:
  182. log.version = version
  183. if title:
  184. log.title = title
  185. if date:
  186. from datetime import datetime
  187. log.date = datetime.strptime(date, "%Y-%m-%d")
  188. if detail is not None:
  189. log.detail = detail
  190. if require_re_login is not None:
  191. log.require_re_login = 1 if require_re_login else 0
  192. if remark is not None:
  193. log.remark = remark
  194. log.update_by = current_user.get("id")
  195. db.commit()
  196. return success_response({"id": log.id})
  197. @router.delete("/upgrade-logs/{log_id}", response_model=dict)
  198. async def delete_upgrade_log(
  199. log_id: str,
  200. db: Session = Depends(get_db),
  201. current_user: dict = Depends(get_current_user)
  202. ):
  203. """
  204. 删除更新日志
  205. """
  206. log = db.query(SysUpgradeLog).filter(
  207. SysUpgradeLog.id == log_id,
  208. SysUpgradeLog.is_del == 0
  209. ).first()
  210. if not log:
  211. return {"code": 404, "message": "更新日志不存在", "data": None}
  212. log.is_del = 1
  213. log.update_by = current_user.get("id")
  214. db.commit()
  215. return success_response({"id": log.id})
  216. # =========================================
  217. # 评论管理
  218. # =========================================
  219. @router.get("/comments", response_model=dict)
  220. async def get_comments(
  221. target_type: Optional[str] = Query(None, description="目标类型"),
  222. target_id: Optional[str] = Query(None, description="目标ID"),
  223. page: int = Query(1, ge=1, description="页码"),
  224. pageSize: int = Query(20, ge=1, le=100, description="每页条数"),
  225. db: Session = Depends(get_db),
  226. current_user: dict = Depends(get_current_user)
  227. ):
  228. """
  229. 获取评论列表
  230. """
  231. query = db.query(SysComment).filter(SysComment.is_del == 0)
  232. if target_type:
  233. query = query.filter(SysComment.target_type == target_type)
  234. if target_id:
  235. query = query.filter(SysComment.target_id == target_id)
  236. total = query.count()
  237. offset = (page - 1) * pageSize
  238. comments = (
  239. query.order_by(desc(SysComment.create_at))
  240. .offset(offset)
  241. .limit(pageSize)
  242. .all()
  243. )
  244. # 构建树形结构
  245. comment_map = {}
  246. root_comments = []
  247. for c in comments:
  248. comment_map[c.id] = {
  249. "id": c.id,
  250. "parentId": c.parent_id,
  251. "author": c.author,
  252. "authorId": c.author_id,
  253. "content": c.content,
  254. "targetType": c.target_type,
  255. "targetId": c.target_id,
  256. "timestamp": c.create_at.strftime("%Y-%m-%d %H:%M") if c.create_at else "",
  257. "replies": []
  258. }
  259. for c in comments:
  260. if c.parent_id and c.parent_id in comment_map:
  261. comment_map[c.parent_id]["replies"].append(comment_map[c.id])
  262. elif not c.parent_id:
  263. root_comments.append(comment_map[c.id])
  264. return success_response({
  265. "list": root_comments,
  266. "total": total,
  267. "page": page,
  268. "pageSize": pageSize
  269. })
  270. @router.post("/comments", response_model=dict)
  271. async def create_comment(
  272. author: str,
  273. content: str,
  274. parent_id: Optional[str] = None,
  275. author_id: Optional[str] = None,
  276. target_type: Optional[str] = None,
  277. target_id: Optional[str] = None,
  278. db: Session = Depends(get_db),
  279. current_user: dict = Depends(get_current_user)
  280. ):
  281. """
  282. 创建评论
  283. """
  284. comment = SysComment(
  285. id=generate_uuid(),
  286. parent_id=parent_id,
  287. author=author,
  288. author_id=author_id or current_user.get("id"),
  289. content=content,
  290. target_type=target_type,
  291. target_id=target_id,
  292. create_by=current_user.get("id"),
  293. create_name=current_user.get("username", "匿名")
  294. )
  295. db.add(comment)
  296. db.commit()
  297. return success_response({
  298. "id": comment.id,
  299. "author": comment.author,
  300. "content": comment.content,
  301. "timestamp": comment.create_at.strftime("%Y-%m-%d %H:%M") if comment.create_at else "",
  302. "replies": []
  303. })
  304. @router.delete("/comments/{comment_id}", response_model=dict)
  305. async def delete_comment(
  306. comment_id: str,
  307. db: Session = Depends(get_db),
  308. current_user: dict = Depends(get_current_user)
  309. ):
  310. """
  311. 删除评论
  312. """
  313. comment = db.query(SysComment).filter(
  314. SysComment.id == comment_id,
  315. SysComment.is_del == 0
  316. ).first()
  317. if not comment:
  318. return {"code": 404, "message": "评论不存在", "data": None}
  319. comment.is_del = 1
  320. comment.update_by = current_user.get("id")
  321. db.commit()
  322. return success_response({"id": comment_id})
  323. # =========================================
  324. # 系统配置
  325. # =========================================
  326. @router.get("/china-map", response_model=dict)
  327. async def get_china_map_data(
  328. db: Session = Depends(get_db)
  329. ):
  330. """
  331. 获取中国地图GeoJSON数据
  332. 返回保存在系统配置中的地图数据
  333. """
  334. config = db.query(BizSystemConfig).filter(
  335. BizSystemConfig.config_key == "china_map_geojson",
  336. BizSystemConfig.is_del == 0
  337. ).first()
  338. if config and config.config_value:
  339. try:
  340. data = json.loads(config.config_value)
  341. return success_response(data)
  342. except json.JSONDecodeError:
  343. pass
  344. # 如果没有配置,返回空的GeoJSON结构
  345. return success_response({
  346. "type": "FeatureCollection",
  347. "features": []
  348. })
  349. @router.get("/config/{config_key}", response_model=dict)
  350. async def get_system_config(
  351. config_key: str,
  352. db: Session = Depends(get_db)
  353. ):
  354. """
  355. 获取系统配置
  356. """
  357. config = db.query(BizSystemConfig).filter(
  358. BizSystemConfig.config_key == config_key,
  359. BizSystemConfig.is_del == 0
  360. ).first()
  361. if not config:
  362. return success_response(None)
  363. # 根据配置类型解析值
  364. value = config.config_value
  365. if config.config_type == "number":
  366. try:
  367. value = float(value) if "." in str(value) else int(value)
  368. except (ValueError, TypeError):
  369. pass
  370. elif config.config_type == "boolean":
  371. value = value.lower() in ("true", "1", "yes") if value else False
  372. elif config.config_type == "json":
  373. try:
  374. value = json.loads(value)
  375. except json.JSONDecodeError:
  376. pass
  377. return success_response({
  378. "key": config.config_key,
  379. "value": value,
  380. "name": config.config_name,
  381. "type": config.config_type,
  382. "description": config.description
  383. })
  384. @router.get("/version", response_model=dict)
  385. async def get_current_version(
  386. db: Session = Depends(get_db)
  387. ):
  388. """
  389. 获取当前系统版本号
  390. """
  391. config = db.query(BizSystemConfig).filter(
  392. BizSystemConfig.config_key == "system_version",
  393. BizSystemConfig.is_del == 0
  394. ).first()
  395. version = "v1.0.0"
  396. if config and config.config_value:
  397. version = config.config_value
  398. return success_response({"version": version})