system_config.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. """系统配置 API"""
  2. import re
  3. from typing import Optional, List
  4. from fastapi import APIRouter, Body, Depends
  5. from sqlalchemy.orm import Session
  6. from pydantic import BaseModel, Field, ConfigDict
  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, error_response
  10. from app.core.utils import generate_id
  11. from app.models.models import BizSystemConfig
  12. from app.crud.base import CRUDBase, get_model_attr
  13. router = APIRouter(prefix="/api/web/admin/system-config", tags=["系统配置"])
  14. def _resolve_payload_with_crypto_fallback(payload: dict, current_user: dict):
  15. """兼容明文/密文请求,返回业务 payload。失败时返回 error_response。"""
  16. req_dict = payload if isinstance(payload, dict) else {}
  17. if isinstance(req_dict, dict) and req_dict.get("enc") == 1 and req_dict.get("edata"):
  18. try:
  19. from app.core import api_crypto
  20. from app.core.config import settings
  21. ts = req_dict.get("ts")
  22. nonce = req_dict.get("nonce")
  23. edata = req_dict.get("edata")
  24. operator_user_id = current_user.get("user_id") if isinstance(current_user, dict) else None
  25. fixed_secret = getattr(settings, "API_ENCRYPT_FIXED_SECRET", "") or ""
  26. if not (fixed_secret and operator_user_id and ts is not None and nonce):
  27. return {}, error_response(400, "密文解析配置缺失")
  28. key = api_crypto.derive_key(
  29. fixed_secret=fixed_secret,
  30. user_id=str(operator_user_id),
  31. ts=int(ts),
  32. nonce=str(nonce),
  33. )
  34. plain = api_crypto.decrypt_json(str(edata), key)
  35. data = plain.get("data", plain) if isinstance(plain, dict) else {}
  36. return (data if isinstance(data, dict) else {}), None
  37. except Exception as exc:
  38. return {}, error_response(400, f"密文解密失败: {exc}")
  39. data = req_dict.get("data", req_dict) if isinstance(req_dict, dict) else {}
  40. return (data if isinstance(data, dict) else {}), None
  41. # 运营端「系统设置」仅存以下键(其余配置走 biz_system_config 但不在该界面维护)
  42. _SETTING_KEY_META = {
  43. "auto_cancel_minutes": {
  44. "response_key": "autoCancelMinutes",
  45. "default": 30,
  46. "min": 5,
  47. "max": 1440,
  48. "config_type": "number",
  49. "config_name": "订单自动取消时间",
  50. "config_group": "order",
  51. "description": "超时未支付自动取消订单(分钟)",
  52. },
  53. "default_region_expected_delivery_minutes": {
  54. "response_key": "defaultRegionExpectedDeliveryMinutes",
  55. "default": 600,
  56. "min": 1,
  57. "max": 10080,
  58. "config_type": "number",
  59. "config_name": "区域未配置时的默认预计配送时长",
  60. "config_group": "order",
  61. "description": "分配配送员时,若区域未设置预计配送(分钟),则配送截止时间=当前时间+本值(分钟)",
  62. },
  63. "delivery_timeout_warn_lead_minutes": {
  64. "response_key": "deliveryTimeoutWarnLeadMinutes",
  65. "default": 30,
  66. "min": 1,
  67. "max": 1440,
  68. "config_type": "number",
  69. "config_name": "配送超时预警提前时间",
  70. "config_group": "order",
  71. "description": "距离配送截止时间不足该分钟数时触发预警提示",
  72. },
  73. "auto_confirm_receive_days": {
  74. "response_key": "autoConfirmReceiveDays",
  75. "default": 7,
  76. "min": 1,
  77. "max": 365,
  78. "config_type": "number",
  79. "config_name": "自动确认收货期限",
  80. "config_group": "order",
  81. "description": "商品送达后,客户超过该天数未确认收货则系统自动确认(天)",
  82. },
  83. "customer_service_phone": {
  84. "response_key": "servicePhone",
  85. "default": "",
  86. "max_len": 32,
  87. "config_type": "string",
  88. "config_name": "客服电话",
  89. "config_group": "contact",
  90. "description": "小程序一键拨号展示的客服电话",
  91. },
  92. }
  93. # 运营端「系统设置」页涉及的配置键:首次读取/定时任务读取前若缺失则插入默认值,保证均以 biz_system_config 为准
  94. _WEB_SETTINGS_KEYS = (
  95. "auto_cancel_minutes",
  96. "default_region_expected_delivery_minutes",
  97. "delivery_timeout_warn_lead_minutes",
  98. "auto_confirm_receive_days",
  99. "customer_service_phone",
  100. )
  101. def ensure_web_system_settings_rows(db: Session, *, commit: bool = True) -> None:
  102. """若缺失则插入默认行(与 _SETTING_KEY_META 一致),便于统一从数据库读取。"""
  103. seeded = False
  104. for config_key in _WEB_SETTINGS_KEYS:
  105. if system_config_crud.get_by_key(db, config_key):
  106. continue
  107. meta = _SETTING_KEY_META[config_key]
  108. if meta["config_type"] == "string":
  109. val = "" if meta.get("default") in (None, "") else str(meta["default"])
  110. else:
  111. val = str(int(meta["default"]))
  112. system_config_crud.create_or_update(
  113. db,
  114. config_key,
  115. val,
  116. config_type=meta["config_type"],
  117. config_name=meta["config_name"],
  118. config_group=meta["config_group"],
  119. description=meta["description"],
  120. create_by="system",
  121. create_name="系统",
  122. commit=False,
  123. )
  124. seeded = True
  125. if seeded and commit:
  126. db.commit()
  127. def _parse_int_setting(raw: Optional[str], default: int) -> int:
  128. if raw is None or str(raw).strip() == "":
  129. return default
  130. try:
  131. return int(float(raw))
  132. except (TypeError, ValueError):
  133. return default
  134. class OrderSettingsUpdate(BaseModel):
  135. model_config = ConfigDict(populate_by_name=True)
  136. auto_cancel_minutes: Optional[int] = Field(None, alias="autoCancelMinutes", ge=5, le=1440)
  137. default_region_expected_delivery_minutes: Optional[int] = Field(
  138. None, alias="defaultRegionExpectedDeliveryMinutes", ge=1, le=10080
  139. )
  140. delivery_timeout_warn_lead_minutes: Optional[int] = Field(
  141. None, alias="deliveryTimeoutWarnLeadMinutes", ge=1, le=1440
  142. )
  143. auto_confirm_receive_days: Optional[int] = Field(
  144. None, alias="autoConfirmReceiveDays", ge=1, le=365
  145. )
  146. class ContactSettingsUpdate(BaseModel):
  147. model_config = ConfigDict(populate_by_name=True)
  148. service_phone: Optional[str] = Field(None, alias="servicePhone", max_length=32)
  149. class SystemSettingsUpdateBody(BaseModel):
  150. model_config = ConfigDict(populate_by_name=True)
  151. order: Optional[OrderSettingsUpdate] = None
  152. contact: Optional[ContactSettingsUpdate] = None
  153. class SystemConfigCRUD(CRUDBase[BizSystemConfig]):
  154. """系统配置CRUD"""
  155. def __init__(self):
  156. super().__init__(BizSystemConfig)
  157. def get_by_key(self, db: Session, config_key: str) -> Optional[BizSystemConfig]:
  158. """根据配置键获取配置"""
  159. return db.query(self.model).filter(
  160. self.model.config_key == config_key,
  161. self.model.is_del == 0
  162. ).first()
  163. def create_or_update(
  164. self,
  165. db: Session,
  166. config_key: str,
  167. config_value: str,
  168. config_type: str = "string",
  169. config_name: str = None,
  170. config_group: str = None,
  171. description: str = None,
  172. is_active: int = 1,
  173. create_by: str = None,
  174. create_name: str = None,
  175. update_by: str = None,
  176. commit: bool = True,
  177. ) -> BizSystemConfig:
  178. """创建或更新配置"""
  179. existing = db.query(self.model).filter(self.model.config_key == config_key).first()
  180. if existing:
  181. existing.config_value = config_value
  182. existing.config_type = config_type
  183. if config_name:
  184. existing.config_name = config_name
  185. if config_group:
  186. existing.config_group = config_group
  187. if description:
  188. existing.description = description
  189. existing.is_active = is_active
  190. if hasattr(existing, "is_del"):
  191. existing.is_del = 0
  192. if update_by is not None:
  193. existing.update_by = update_by
  194. if commit:
  195. db.commit()
  196. db.refresh(existing)
  197. else:
  198. db.flush()
  199. return existing
  200. else:
  201. data = {
  202. "id": generate_id(),
  203. "config_key": config_key,
  204. "config_value": config_value,
  205. "config_type": config_type,
  206. "config_name": config_name,
  207. "config_group": config_group,
  208. "description": description,
  209. "is_active": is_active,
  210. "create_by": create_by,
  211. "create_name": create_name
  212. }
  213. obj = self.model()
  214. for key, value in data.items():
  215. attr_name = get_model_attr(obj, key)
  216. if attr_name:
  217. setattr(obj, attr_name, value)
  218. if not obj.id:
  219. obj.id = generate_id()
  220. db.add(obj)
  221. if commit:
  222. db.commit()
  223. db.refresh(obj)
  224. else:
  225. db.flush()
  226. return obj
  227. def batch_update_status(
  228. self,
  229. db: Session,
  230. ids: List[str],
  231. is_active: int
  232. ) -> int:
  233. """批量更新状态"""
  234. count = db.query(self.model).filter(
  235. self.model.id.in_(ids)
  236. ).update({"is_active": is_active}, synchronize_session=False)
  237. db.commit()
  238. return count
  239. system_config_crud = SystemConfigCRUD()
  240. def _clamp_to_meta(value: int, meta: dict) -> int:
  241. return max(meta["min"], min(meta["max"], value))
  242. def _read_setting_int(db: Session, config_key: str) -> int:
  243. meta = _SETTING_KEY_META[config_key]
  244. row = system_config_crud.get_by_key(db, config_key)
  245. if not row:
  246. return meta["default"]
  247. parsed = _parse_int_setting(row.config_value, meta["default"])
  248. return _clamp_to_meta(parsed, meta)
  249. def _read_setting_str(db: Session, config_key: str) -> str:
  250. meta = _SETTING_KEY_META[config_key]
  251. default = meta.get("default", "")
  252. max_len = int(meta.get("max_len", 255))
  253. row = system_config_crud.get_by_key(db, config_key)
  254. if not row or row.config_value is None:
  255. s = "" if default is None else str(default)
  256. return s.strip()[:max_len]
  257. return str(row.config_value).strip()[:max_len]
  258. def _normalize_service_phone(value: Optional[str]) -> str:
  259. if value is None:
  260. return ""
  261. s = str(value).strip()
  262. if len(s) > 32:
  263. raise ValueError("客服电话长度不能超过32个字符")
  264. if not s:
  265. return ""
  266. if not re.fullmatch(r"[\d\-\+\s\(\)]+", s):
  267. raise ValueError("客服电话仅支持数字及 - + ( ) 与空格")
  268. return s
  269. def get_auto_cancel_minutes_for_job(db: Session) -> int:
  270. """供定时任务读取:线上单未支付自动取消的分钟数。"""
  271. ensure_web_system_settings_rows(db, commit=True)
  272. return _read_setting_int(db, "auto_cancel_minutes")
  273. def get_auto_confirm_receive_days_for_job(db: Session) -> int:
  274. """供定时任务读取:送达后自动确认收货的天数。"""
  275. ensure_web_system_settings_rows(db, commit=True)
  276. return _read_setting_int(db, "auto_confirm_receive_days")
  277. def get_default_region_expected_delivery_minutes(db: Session) -> int:
  278. """区域未配置预计配送时长时,分配配送员弹窗使用的默认分钟数。"""
  279. ensure_web_system_settings_rows(db, commit=True)
  280. return _read_setting_int(db, "default_region_expected_delivery_minutes")
  281. def read_customer_service_phone_for_mini(db: Session) -> str:
  282. """小程序端展示的客服电话(已清洗长度)。"""
  283. ensure_web_system_settings_rows(db, commit=True)
  284. return _read_setting_str(db, "customer_service_phone")
  285. @router.get("/settings", response_model=dict)
  286. def get_system_settings(
  287. db: Session = Depends(get_db),
  288. current_user: dict = Depends(get_current_user)
  289. ):
  290. """运营端系统设置 — 数据来自 biz_system_config(配送时效预警请在「区域」中配置)"""
  291. ensure_web_system_settings_rows(db, commit=True)
  292. order = {
  293. "autoCancelMinutes": _read_setting_int(db, "auto_cancel_minutes"),
  294. "defaultRegionExpectedDeliveryMinutes": _read_setting_int(
  295. db, "default_region_expected_delivery_minutes"
  296. ),
  297. "deliveryTimeoutWarnLeadMinutes": _read_setting_int(
  298. db, "delivery_timeout_warn_lead_minutes"
  299. ),
  300. "autoConfirmReceiveDays": _read_setting_int(db, "auto_confirm_receive_days"),
  301. }
  302. contact = {
  303. "servicePhone": _read_setting_str(db, "customer_service_phone"),
  304. }
  305. return success_response({
  306. "order": order,
  307. "contact": contact,
  308. })
  309. def _parse_drm_from_payload_order(order_raw: Optional[dict]) -> Optional[int]:
  310. """从原始 JSON 的 order 中解析 defaultRegionExpectedDeliveryMinutes(兼容解析异常)。"""
  311. if not isinstance(order_raw, dict):
  312. return None
  313. raw_v = order_raw.get("defaultRegionExpectedDeliveryMinutes")
  314. if raw_v is None:
  315. return None
  316. try:
  317. return int(float(raw_v))
  318. except (TypeError, ValueError):
  319. return None
  320. @router.put("/settings", response_model=dict)
  321. def update_system_settings(
  322. payload: dict = Body(...),
  323. db: Session = Depends(get_db),
  324. current_user: dict = Depends(get_current_user)
  325. ):
  326. """更新运营端系统设置(按模块部分更新);三条配置同一事务提交,避免只落库两条。"""
  327. payload, req_err = _resolve_payload_with_crypto_fallback(payload, current_user)
  328. if req_err is not None:
  329. return req_err
  330. try:
  331. body = SystemSettingsUpdateBody.model_validate(payload)
  332. except Exception as e:
  333. return error_response(400, f"参数无效: {str(e)}")
  334. if body.order is None and body.contact is None:
  335. return error_response(400, "请至少选择一项设置内容")
  336. uid = current_user.get("user_id")
  337. uname = current_user.get("real_name") or current_user.get("username") or ""
  338. order_raw = payload.get("order") if isinstance(payload.get("order"), dict) else None
  339. try:
  340. ensure_web_system_settings_rows(db, commit=False)
  341. updated = False
  342. if body.order is not None and body.order.auto_cancel_minutes is not None:
  343. k = "auto_cancel_minutes"
  344. meta = _SETTING_KEY_META[k]
  345. v = _clamp_to_meta(body.order.auto_cancel_minutes, meta)
  346. system_config_crud.create_or_update(
  347. db,
  348. k,
  349. str(v),
  350. config_type=meta["config_type"],
  351. config_name=meta["config_name"],
  352. config_group=meta["config_group"],
  353. description=meta["description"],
  354. create_by=uid,
  355. create_name=uname,
  356. update_by=uid,
  357. commit=False,
  358. )
  359. updated = True
  360. if body.order is not None and body.order.auto_confirm_receive_days is not None:
  361. k_acr = "auto_confirm_receive_days"
  362. meta_acr = _SETTING_KEY_META[k_acr]
  363. v_acr = _clamp_to_meta(body.order.auto_confirm_receive_days, meta_acr)
  364. system_config_crud.create_or_update(
  365. db,
  366. k_acr,
  367. str(v_acr),
  368. config_type=meta_acr["config_type"],
  369. config_name=meta_acr["config_name"],
  370. config_group=meta_acr["config_group"],
  371. description=meta_acr["description"],
  372. create_by=uid,
  373. create_name=uname,
  374. update_by=uid,
  375. commit=False,
  376. )
  377. updated = True
  378. if body.order is not None and body.order.delivery_timeout_warn_lead_minutes is not None:
  379. k_warn = "delivery_timeout_warn_lead_minutes"
  380. meta_warn = _SETTING_KEY_META[k_warn]
  381. v_warn = _clamp_to_meta(body.order.delivery_timeout_warn_lead_minutes, meta_warn)
  382. system_config_crud.create_or_update(
  383. db,
  384. k_warn,
  385. str(v_warn),
  386. config_type=meta_warn["config_type"],
  387. config_name=meta_warn["config_name"],
  388. config_group=meta_warn["config_group"],
  389. description=meta_warn["description"],
  390. create_by=uid,
  391. create_name=uname,
  392. update_by=uid,
  393. commit=False,
  394. )
  395. updated = True
  396. if body.order is not None:
  397. k_drm = "default_region_expected_delivery_minutes"
  398. meta_drm = _SETTING_KEY_META[k_drm]
  399. drm = body.order.default_region_expected_delivery_minutes
  400. if drm is None:
  401. drm = _parse_drm_from_payload_order(order_raw)
  402. if drm is None:
  403. drm = _read_setting_int(db, k_drm)
  404. v_drm = _clamp_to_meta(int(drm), meta_drm)
  405. system_config_crud.create_or_update(
  406. db,
  407. k_drm,
  408. str(v_drm),
  409. config_type=meta_drm["config_type"],
  410. config_name=meta_drm["config_name"],
  411. config_group=meta_drm["config_group"],
  412. description=meta_drm["description"],
  413. create_by=uid,
  414. create_name=uname,
  415. update_by=uid,
  416. commit=False,
  417. )
  418. updated = True
  419. if body.contact is not None and body.contact.service_phone is not None:
  420. try:
  421. phone_norm = _normalize_service_phone(body.contact.service_phone)
  422. except ValueError as e:
  423. return error_response(400, str(e))
  424. k = "customer_service_phone"
  425. meta = _SETTING_KEY_META[k]
  426. system_config_crud.create_or_update(
  427. db,
  428. k,
  429. phone_norm,
  430. config_type=meta["config_type"],
  431. config_name=meta["config_name"],
  432. config_group=meta["config_group"],
  433. description=meta["description"],
  434. create_by=uid,
  435. create_name=uname,
  436. update_by=uid,
  437. commit=False,
  438. )
  439. updated = True
  440. if not updated:
  441. return error_response(400, "没有可保存的字段")
  442. db.commit()
  443. return success_response({"message": "保存成功"})
  444. except Exception as e:
  445. db.rollback()
  446. return error_response(500, f"保存失败: {str(e)}")
  447. class SystemConfigCreateRequest(BaseModel):
  448. """创建系统配置请求"""
  449. config_key: str = Field(..., description="配置键")
  450. config_value: str = Field(..., description="配置值")
  451. config_type: str = Field("string", description="配置类型: string, number, boolean, json")
  452. config_name: str = Field(..., description="配置名称")
  453. config_group: str = Field(None, description="配置分组")
  454. description: str = Field(None, description="描述")
  455. is_active: int = Field(1, description="是否启用")
  456. @router.post("/create", response_model=dict)
  457. def create_config(
  458. request: SystemConfigCreateRequest,
  459. db: Session = Depends(get_db),
  460. current_user: dict = Depends(get_current_user)
  461. ):
  462. """创建系统配置"""
  463. try:
  464. # 检查配置键是否已存在
  465. existing = system_config_crud.get_by_key(db, request.config_key)
  466. if existing:
  467. return error_response(400, "配置键已存在")
  468. config = system_config_crud.create(
  469. db=db,
  470. data={
  471. "id": generate_id(),
  472. "config_key": request.config_key,
  473. "config_value": request.config_value,
  474. "config_type": request.config_type,
  475. "config_name": request.config_name,
  476. "config_group": request.config_group,
  477. "description": request.description,
  478. "is_active": request.is_active,
  479. "create_by": current_user.get("user_id"),
  480. "create_name": current_user.get("real_name")
  481. }
  482. )
  483. return success_response({
  484. "id": config.id,
  485. "message": "配置创建成功"
  486. })
  487. except Exception as e:
  488. return error_response(500, f"创建配置失败: {str(e)}")
  489. @router.post("/update/{config_id}", response_model=dict)
  490. def update_config(
  491. config_id: str,
  492. request: SystemConfigCreateRequest,
  493. db: Session = Depends(get_db),
  494. current_user: dict = Depends(get_current_user)
  495. ):
  496. """更新系统配置"""
  497. try:
  498. config = system_config_crud.get(db, config_id)
  499. if not config:
  500. return error_response(404, "配置不存在")
  501. if config.is_del == 1:
  502. return error_response(404, "配置已删除")
  503. # 如果修改了配置键,检查是否冲突
  504. if request.config_key != config.config_key:
  505. existing = system_config_crud.get_by_key(db, request.config_key)
  506. if existing:
  507. return error_response(400, "配置键已存在")
  508. config.config_key = request.config_key
  509. config.config_value = request.config_value
  510. config.config_type = request.config_type
  511. config.config_name = request.config_name
  512. config.config_group = request.config_group
  513. config.description = request.description
  514. config.is_active = request.is_active
  515. db.commit()
  516. db.refresh(config)
  517. return success_response({"message": "配置更新成功"})
  518. except Exception as e:
  519. return error_response(500, f"更新配置失败: {str(e)}")
  520. @router.get("/list", response_model=dict)
  521. def get_config_list(
  522. page: int = 1,
  523. page_size: int = 20,
  524. config_name: str = None,
  525. config_group: str = None,
  526. is_active: int = None,
  527. db: Session = Depends(get_db),
  528. current_user: dict = Depends(get_current_user)
  529. ):
  530. """获取系统配置列表"""
  531. try:
  532. query = db.query(system_config_crud.model).filter(system_config_crud.model.is_del == 0)
  533. if config_name:
  534. query = query.filter(system_config_crud.model.config_name.like(f"%{config_name}%"))
  535. if config_group:
  536. query = query.filter(system_config_crud.model.config_group == config_group)
  537. if is_active is not None:
  538. query = query.filter(system_config_crud.model.is_active == is_active)
  539. total = query.count()
  540. items = query.order_by(system_config_crud.model.config_group, system_config_crud.model.create_at.desc()).offset((page - 1) * page_size).limit(page_size).all()
  541. return success_response({
  542. "list": [_to_response(item) for item in items],
  543. "total": total,
  544. "page": page,
  545. "page_size": page_size
  546. })
  547. except Exception as e:
  548. return error_response(500, f"获取配置列表失败: {str(e)}")
  549. @router.get("/detail/{config_id}", response_model=dict)
  550. def get_config_detail(
  551. config_id: str,
  552. db: Session = Depends(get_db),
  553. current_user: dict = Depends(get_current_user)
  554. ):
  555. """获取配置详情"""
  556. try:
  557. config = system_config_crud.get(db, config_id)
  558. if not config:
  559. return error_response(404, "配置不存在")
  560. if config.is_del == 1:
  561. return error_response(404, "配置已删除")
  562. return success_response(_to_response(config))
  563. except Exception as e:
  564. return error_response(500, f"获取配置详情失败: {str(e)}")
  565. @router.get("/get/{config_key}", response_model=dict)
  566. def get_config_by_key(
  567. config_key: str,
  568. db: Session = Depends(get_db)
  569. ):
  570. """根据配置键获取配置值"""
  571. try:
  572. config = system_config_crud.get_by_key(db, config_key)
  573. if not config or config.is_del == 1:
  574. return error_response(404, "配置不存在")
  575. return success_response({
  576. "key": config.config_key,
  577. "value": config.config_value,
  578. "type": config.config_type
  579. })
  580. except Exception as e:
  581. return error_response(500, f"获取配置失败: {str(e)}")
  582. @router.post("/batch-status", response_model=dict)
  583. def batch_update_status(
  584. body: dict,
  585. db: Session = Depends(get_db),
  586. current_user: dict = Depends(get_current_user)
  587. ):
  588. """批量更新配置状态"""
  589. try:
  590. ids = body.get("ids", [])
  591. is_active = body.get("is_active")
  592. if not isinstance(ids, list) or len(ids) == 0:
  593. return error_response(400, "ids不能为空")
  594. if is_active is None:
  595. return error_response(400, "is_active不能为空")
  596. count = system_config_crud.batch_update_status(db, ids, is_active)
  597. return success_response({"count": count, "message": "批量更新成功"})
  598. except Exception as e:
  599. return error_response(500, f"批量更新失败: {str(e)}")
  600. @router.delete("/delete/{config_id}", response_model=dict)
  601. def delete_config(
  602. config_id: str,
  603. db: Session = Depends(get_db),
  604. current_user: dict = Depends(get_current_user)
  605. ):
  606. """删除配置"""
  607. try:
  608. config = system_config_crud.get(db, config_id)
  609. if not config:
  610. return error_response(404, "配置不存在")
  611. config.is_del = 1
  612. db.commit()
  613. return success_response({"message": "删除成功"})
  614. except Exception as e:
  615. return error_response(500, f"删除配置失败: {str(e)}")
  616. def _to_response(config: BizSystemConfig) -> dict:
  617. """转换为响应格式"""
  618. return {
  619. "id": config.id,
  620. "config_key": config.config_key,
  621. "config_value": config.config_value,
  622. "config_type": config.config_type,
  623. "config_name": config.config_name,
  624. "config_group": config.config_group,
  625. "description": config.description,
  626. "is_active": config.is_active,
  627. "create_at": str(config.create_at) if config.create_at else None,
  628. "update_at": str(config.update_at) if config.update_at else None
  629. }