system_config.py 24 KB

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