Files
DjangoAdmin_FastAPI_EleVue/apps/services/config.py
T
ClaudeandClaude dbcce79536 chore: 基线提交(重构前快照)
重构前保存现有 apps/ 结构基线,供后续各阶段回滚。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 09:43:18 +08:00

230 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
from datetime import datetime, timezone
from fastapi.logger import logger
from sqlalchemy import and_
from apps.constants.message import PAGE_SIZE
from apps.forms.config import ConfigForm
from apps.models.config import Config
from apps.models.config_item import ConfigItem
from config import FASTAPI_IMAGE_URL
from extensions import get_db_session
from utils import R
from utils.rich_text import get_file_url, save_file
from utils.request import like_contains
from utils.security import get_user_id
# 配置表单验证
def get_config_page(request):
db = get_db_session()
# 页码
page = int(request.query_params.get("pageNo", 1))
# 每页数
limit = int(request.query_params.get("pageSize", PAGE_SIZE))
# 实例化查询对象
query = db.query(Config).filter(Config.is_delete == 0)
# 配置名称
name = request.query_params.get('name')
if name:
query = query.filter(Config.name.like(like_contains(name), escape='\\'))
# 排序
query = query.order_by(Config.sort.asc())
# 记录总数
count = query.count()
# 分页查询
config_list = query.limit(limit).offset((page - 1) * limit).all()
# 若当前页超出末页(数据被并发删除),自动回退到第一页
if page > 1 and not config_list:
page = 1
count = query.count()
config_list = query.limit(limit).offset(0).all()
# 实例化结果
result = []
# 遍历数据源
if len(config_list) > 0:
for item in config_list:
# 对象转字典
data = item.to_dict()
# 加入列表
result.append(data)
# 返回结果
return R.ok(data=result, count=count)
# 根据ID获取配置详情
def get_config_detail(config_id):
db = get_db_session()
# 根据ID查询配置
config = db.query(Config).filter(and_(Config.id == config_id, Config.is_delete == 0)).first()
# 查询结果判空
if not config:
return None
# 对象转字典
data = config.to_dict()
# 返回结果
return data
# 添加配置
def add_config(request, data: ConfigForm):
db = get_db_session()
# 唯一性校验
if db.query(Config).filter(and_(Config.name == data.name, Config.is_delete == 0)).first():
return R.failed("配置名称不能重复")
# 表单数据赋值给对象
config = Config(**data.model_dump(exclude={'id'}))
config.create_user = get_user_id(request)
# 插入数据
config.save()
# 返回结果
return R.ok(msg="添加成功")
# 更新配置
def update_config(request, data: ConfigForm):
db = get_db_session()
# 记录ID判空
if not data.id or data.id <= 0:
return R.failed("记录ID不能为空")
# 唯一性校验(排除自身)
if db.query(Config).filter(and_(Config.name == data.name, Config.id != data.id, Config.is_delete == 0)).first():
return R.failed("配置名称不能重复")
# 根据ID查询记录
config = db.query(Config).filter(and_(Config.id == data.id, Config.is_delete == 0)).first()
# 查询结果判空
if not config:
return R.failed("记录不存在")
# 构造更新数据(排除 id)
updData = data.model_dump(exclude={'id'})
updData['update_user'] = get_user_id(request)
updData['update_time'] = datetime.now(tz=timezone.utc)
result = db.query(Config).filter_by(id=data.id).update(updData)
# 提交数据
if not result:
return R.failed("更新失败")
# 返回结果
return R.ok(msg="更新成功")
# 删除配置
def delete_config(config_id):
return Config.batch_delete_r(config_id)
# 解析配置项选项字符串为字典
def _parse_options(options_str):
"""将 'k1=v1,k2=v2' 格式的选项字符串解析为字典"""
if not options_str:
return {}
result = {}
for part in options_str.split(','):
parts = part.split('=', 1)
if len(parts) == 2:
result[parts[0]] = parts[1]
return result
# 获取网站配置信息
def get_config_info(request):
db = get_db_session()
# 一次性查询所有配置组
configList = db.query(Config).filter(Config.is_delete == 0).order_by(Config.sort.asc()).all()
if not configList:
return []
# 一次性查询所有活跃配置项,按config_id分组
config_ids = [c.id for c in configList]
all_items = db.query(ConfigItem).filter(
and_(ConfigItem.is_delete == 0, ConfigItem.status == 1, ConfigItem.config_id.in_(config_ids))
).order_by(ConfigItem.sort.asc()).all()
items_map = {}
for item in all_items:
items_map.setdefault(item.config_id, []).append(item)
config_list = []
for val in configList:
config = {
'config_id': val.id,
'config_name': val.name
}
data_list = []
for v in items_map.get(val.id, []):
data = {
"id": v.id,
"title": v.title,
"code": v.code,
"value": v.value,
"type": v.type,
}
item_type = v.type
if item_type == "checkbox":
data['optionsList'] = _parse_options(v.options)
data["value"] = v.value.split(',') if v.value else []
elif item_type in ("radio", "select"):
data['optionsList'] = _parse_options(v.options)
elif item_type == "image":
if v.value:
data["value"] = get_file_url(v.value)
elif item_type == "images":
if v.value:
data["value"] = [get_file_url(img) for img in v.value.split(',')]
data_list.append(data)
config['data_list'] = data_list
config_list.append(config)
return config_list
# 保存网站配置信息
async def save_config_info(request):
# 获取请求参数
req_data = await request.json()
if not req_data or not isinstance(req_data, dict):
return R.failed("请求参数异常")
def _save():
db = get_db_session()
# 预加载所有活跃配置项,仅允许修改这些项
valid_items = db.query(ConfigItem).filter(
and_(ConfigItem.is_delete == 0, ConfigItem.status == 1)
).all()
item_map = {item.code: item for item in valid_items}
# 数据源处理
for key, val in req_data.items():
# 仅允许修改已存在的活跃配置项,忽略非法key
config_item = item_map.get(key)
if not config_item:
continue
# 图片数据
item = []
if isinstance(val, list):
for v in val:
if isinstance(v, str) and (v.startswith("http://") or v.startswith("https://")):
url = save_file(v, "config")
item.append(url)
else:
item.append(v)
val = ','.join(item)
else:
if isinstance(val, str) and (val.startswith("http://") or val.startswith("https://")):
val = save_file(val, "config")
# 设置
config_item.value = val
# 同步DB操作与网络下载放入线程池,避免阻塞事件循环
await asyncio.to_thread(_save)
# 返回结果
return R.ok()