system_config.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. """
  2. 系统配置管理接口
  3. """
  4. from fastapi import APIRouter, Depends, Query, Request
  5. from sqlalchemy.orm import Session
  6. from pydantic import BaseModel
  7. from typing import Optional, Any
  8. from app.database import get_db
  9. from app.api.deps import get_current_admin
  10. from app.models.system_config import SystemConfig
  11. from app.utils.timezone import beijing_now
  12. router = APIRouter()
  13. class SystemConfigCreate(BaseModel):
  14. """创建系统配置请求"""
  15. config_key: str
  16. config_value: str
  17. config_type: str = 'string'
  18. app_name: str = 'backend-admin' # 所属应用:backend-admin、backend-client,多个用逗号拼接
  19. description: Optional[str] = None
  20. is_editable: bool = True
  21. class SystemConfigUpdate(BaseModel):
  22. """更新系统配置请求"""
  23. config_value: str
  24. config_type: Optional[str] = None
  25. app_name: Optional[str] = None # 支持修改所属应用
  26. description: Optional[str] = None
  27. is_editable: Optional[bool] = None
  28. def format_config_response(config: SystemConfig) -> dict:
  29. """格式化配置响应"""
  30. return {
  31. "id": config.id,
  32. "config_key": config.config_key,
  33. "config_value": config.config_value,
  34. "config_type": config.config_type,
  35. "app_name": config.app_name or '',
  36. "description": config.description,
  37. "is_system": bool(config.is_system),
  38. "is_editable": bool(config.is_editable),
  39. "created_at": config.created_at.isoformat() if config.created_at else None,
  40. "updated_at": config.updated_at.isoformat() if config.updated_at else None
  41. }
  42. @router.get("")
  43. async def get_system_config_list(
  44. page: int = Query(1, ge=1),
  45. page_size: int = Query(10, ge=1, le=100),
  46. keyword: Optional[str] = None,
  47. config_type: Optional[str] = None,
  48. app_name: Optional[str] = Query(None, description="所属应用过滤:backend-admin、backend-client"),
  49. db: Session = Depends(get_db),
  50. current_admin = Depends(get_current_admin)
  51. ):
  52. """获取系统配置列表"""
  53. query = db.query(SystemConfig).filter(SystemConfig.is_deleted == 0)
  54. # 搜索条件
  55. if keyword:
  56. query = query.filter(
  57. (SystemConfig.config_key.contains(keyword)) |
  58. (SystemConfig.description.contains(keyword))
  59. )
  60. if config_type:
  61. query = query.filter(SystemConfig.config_type == config_type)
  62. # 按所属应用过滤(支持模糊匹配,包含指定应用)
  63. if app_name:
  64. query = query.filter(SystemConfig.app_name.contains(app_name))
  65. # 计算总数
  66. total = query.count()
  67. # 分页
  68. configs = query.order_by(SystemConfig.id.desc()).offset((page - 1) * page_size).limit(page_size).all()
  69. return {
  70. "code": 200,
  71. "msg": "成功",
  72. "data": [format_config_response(c) for c in configs],
  73. "total": total,
  74. "page": page,
  75. "page_size": page_size
  76. }
  77. @router.get("/{config_id}")
  78. async def get_system_config_detail(
  79. config_id: int,
  80. db: Session = Depends(get_db),
  81. current_admin = Depends(get_current_admin)
  82. ):
  83. """获取系统配置详情"""
  84. config = db.query(SystemConfig).filter(
  85. SystemConfig.id == config_id,
  86. SystemConfig.is_deleted == 0
  87. ).first()
  88. if not config:
  89. return {"code": 404, "msg": "配置不存在"}
  90. return {
  91. "code": 200,
  92. "msg": "成功",
  93. "data": format_config_response(config)
  94. }
  95. @router.post("")
  96. async def create_system_config(
  97. req: SystemConfigCreate,
  98. request: Request,
  99. db: Session = Depends(get_db),
  100. current_admin = Depends(get_current_admin)
  101. ):
  102. """创建系统配置"""
  103. # 检查配置键是否已存在
  104. existing = db.query(SystemConfig).filter(
  105. SystemConfig.config_key == req.config_key,
  106. SystemConfig.is_deleted == 0
  107. ).first()
  108. if existing:
  109. return {"code": 400, "msg": "配置键已存在"}
  110. config = SystemConfig(
  111. config_key=req.config_key,
  112. config_value=req.config_value,
  113. config_type=req.config_type,
  114. app_name=req.app_name,
  115. description=req.description,
  116. is_system=0, # 用户创建的配置
  117. is_editable=1 if req.is_editable else 0,
  118. created_at=beijing_now(),
  119. updated_at=beijing_now()
  120. )
  121. db.add(config)
  122. db.flush()
  123. # 先写 his 再提交(创建时 his 在 flush 后写,保证有 id)
  124. from app.utils.history import create_system_config_history
  125. create_system_config_history(db, config, "INSERT")
  126. db.commit()
  127. db.refresh(config)
  128. return {
  129. "code": 200,
  130. "msg": "创建成功",
  131. "data": format_config_response(config)
  132. }
  133. @router.put("/{config_id}")
  134. async def update_system_config(
  135. config_id: int,
  136. req: SystemConfigUpdate,
  137. request: Request,
  138. db: Session = Depends(get_db),
  139. current_admin = Depends(get_current_admin)
  140. ):
  141. """更新系统配置"""
  142. config = db.query(SystemConfig).filter(
  143. SystemConfig.id == config_id,
  144. SystemConfig.is_deleted == 0
  145. ).first()
  146. if not config:
  147. return {"code": 404, "msg": "配置不存在"}
  148. # 检查是否可编辑
  149. if not config.is_editable:
  150. return {"code": 403, "msg": "系统配置不可编辑"}
  151. # 先写历史表,再改/删
  152. from app.utils.history import create_system_config_history
  153. create_system_config_history(db, config, "UPDATE")
  154. # 更新字段
  155. config.config_value = req.config_value
  156. if req.config_type:
  157. config.config_type = req.config_type
  158. if req.app_name is not None:
  159. config.app_name = req.app_name
  160. if req.description is not None:
  161. config.description = req.description
  162. if req.is_editable is not None:
  163. config.is_editable = 1 if req.is_editable else 0
  164. config.updated_at = beijing_now()
  165. db.commit()
  166. return {
  167. "code": 200,
  168. "msg": "更新成功",
  169. "data": format_config_response(config)
  170. }
  171. @router.delete("/{config_id}")
  172. async def delete_system_config(
  173. config_id: int,
  174. request: Request,
  175. db: Session = Depends(get_db),
  176. current_admin = Depends(get_current_admin)
  177. ):
  178. """删除系统配置(软删除)"""
  179. config = db.query(SystemConfig).filter(
  180. SystemConfig.id == config_id,
  181. SystemConfig.is_deleted == 0
  182. ).first()
  183. if not config:
  184. return {"code": 404, "msg": "配置不存在"}
  185. # 检查是否可删除
  186. if config.is_system:
  187. return {"code": 403, "msg": "系统配置不可删除"}
  188. # 先写历史表,再改/删
  189. from app.utils.history import create_system_config_history
  190. create_system_config_history(db, config, "DELETE")
  191. # 软删除
  192. config.is_deleted = 1
  193. config.updated_at = beijing_now()
  194. db.commit()
  195. return {"code": 200, "msg": "删除成功"}
  196. @router.put("/{config_id}/editable")
  197. async def toggle_config_editable(
  198. config_id: int,
  199. db: Session = Depends(get_db),
  200. current_admin = Depends(get_current_admin)
  201. ):
  202. """切换配置可编辑状态"""
  203. config = db.query(SystemConfig).filter(
  204. SystemConfig.id == config_id,
  205. SystemConfig.is_deleted == 0
  206. ).first()
  207. if not config:
  208. return {"code": 404, "msg": "配置不存在"}
  209. # 先写历史表,再改/删
  210. from app.utils.history import create_system_config_history
  211. create_system_config_history(db, config, "UPDATE")
  212. # 切换状态
  213. config.is_editable = 0 if config.is_editable else 1
  214. config.updated_at = beijing_now()
  215. db.commit()
  216. return {
  217. "code": 200,
  218. "msg": "更新成功",
  219. "data": {
  220. "id": config.id,
  221. "is_editable": bool(config.is_editable)
  222. }
  223. }