refactor: P4c 批次1 服务层改造(notice/param/link/level)

【服务层新范式批量铺开】4 个 uniform 模块:
- service:get_x_page(params)->dict、add/update/status(data)->None、
  delete(ids)->int、get_info 委托 Repository(新建 repository.py)
- 业务失败 raise BizError;router 取参包 R
- level 的 Excel import/export 暂保留 R 返回(后续统一改造)

【工具】codemod 函数级转换(try/except 退格移除、R.failed→BizError、
R.ok→return data)+ black 格式化 + 未用导入清理

【教训】清理脚本 startswith 前缀误删含多名字的 import 行(models_to_list,
model_to_dict 同行)导致 F821,已按 F821 精确补回

【验收】应用启动;4 服务分页查询运行时正常;12 passed;flake8 零告警

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DjangoAdmin Dev
2026-08-07 15:15:22 +08:00
co-authored by Claude
parent 63afa4e629
commit 3f55f62f0d
17 changed files with 570 additions and 656 deletions
-1
View File
@@ -50,7 +50,6 @@ from src.common.base_db import BaseDB
from src.common.base_model import BaseModel
from config import DB_PREFIX
from src.core.database import db
from src.common.utils.case_converter import convert_camel_to_snake
# ============================================================
+15
View File
@@ -0,0 +1,15 @@
"""
Link 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.cms.link.models import Link
class LinkRepository(BaseRepository):
"""Link 仓储"""
model = Link
+36 -20
View File
@@ -55,22 +55,24 @@
application.middleware.check_permission - 权限校验中间件
"""
from flask import Blueprint
from flask import Blueprint, request
from application.middleware.check_demo import check_demo
from application.middleware.check_permission import check_permission
from src.modules.cms.link import service as link
from src.common.utils.request import parse_batch_ids
link_blue = Blueprint('link', __name__, url_prefix='/link')
from src.common.utils.request import parse_batch_ids, parse_request_body
from src.core import response as R
link_blue = Blueprint("link", __name__, url_prefix="/link")
# ============================================================
# 友链分页查询
# ============================================================
@link_blue.route('/page', methods=["GET"])
@link_blue.route("/page", methods=["GET"])
@check_permission("sys:link:page")
def page():
"""
@@ -82,15 +84,15 @@ def page():
Returns:
JSON: 友链分页数据,包含 records(数据列表)、total(总记录数)等
"""
result = link.get_link_page()
return result
return R.ok(data=link.get_link_page(request.args))
# ============================================================
# 友链详情查询
# ============================================================
@link_blue.route('/detail/<int:id>', methods=["GET"])
@link_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:link:detail")
def detail(id):
"""
@@ -110,7 +112,8 @@ def detail(id):
# 友链添加
# ============================================================
@link_blue.route('/add', methods=["POST"])
@link_blue.route("/add", methods=["POST"])
@check_permission("sys:link:add")
@check_demo
def add():
@@ -123,15 +126,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = link.add_link()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
link.add_link(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 友链更新
# ============================================================
@link_blue.route('/update', methods=["PUT"])
@link_blue.route("/update", methods=["PUT"])
@check_permission("sys:link:update")
@check_demo
def update():
@@ -144,15 +151,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = link.update_link()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
link.update_link(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 友链删除
# ============================================================
@link_blue.route('/delete/<string:id>', methods=["DELETE"])
@link_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:link:delete")
@check_demo
def delete(id):
@@ -165,15 +176,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = link.delete_links(id)
return result
count = link.delete_links(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 友链批量删除
# ============================================================
@link_blue.route('/batchDelete', methods=["DELETE"])
@link_blue.route("/batchDelete", methods=["DELETE"])
@check_permission("sys:link:batch_delete")
@check_demo
def batch_delete():
@@ -199,7 +211,8 @@ def batch_delete():
# 友链状态设置
# ============================================================
@link_blue.route('/status', methods=["PUT"])
@link_blue.route("/status", methods=["PUT"])
@check_permission("sys:link:status")
@check_demo
def status():
@@ -212,5 +225,8 @@ def status():
Returns:
JSON: 操作结果
"""
result = link.update_link_status()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
link.update_link_status(json_data)
return R.ok(msg="更新成功")
+97 -152
View File
@@ -65,19 +65,17 @@
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from src.common.constants.common import PAGE_SIZE
from src.common.constants.dictionary_constants import LINK_TYPE_LIST, LINK_FORM_LIST
from src.modules.cms.link.schema import LinkForm, LinkStatusForm
from src.modules.cms.link.models import Link
from src.core.database import db
from src.core import response as R
from src.modules.cms.link.repository import LinkRepository
from src.core.exceptions import BizError
from src.common.utils import regular
from src.common.utils.case_converter import convert_camel_to_snake
from src.common.utils.model_utils import models_to_list, model_to_dict
from src.common.utils.request import parse_request_body, parse_id_list, like_contains
from src.common.utils.model_utils import model_to_dict
from src.common.utils.request import parse_id_list, like_contains
from src.common.utils.rich_text import get_file_url, save_file
from src.core.security import get_username
@@ -89,7 +87,8 @@ logger = logging.getLogger(__name__)
# 友链分页查询
# ============================================================
def get_link_page():
def get_link_page(params: dict) -> dict:
"""
查询友链分页数据
@@ -112,31 +111,26 @@ def get_link_page():
"pages": 总页数
}
"""
try:
# 获取分页参数
page_no = int(request.args.get('pageNo', 1))
page_size = int(request.args.get('pageSize', PAGE_SIZE))
# 获取分页参数
page_no = int(params.get("pageNo", 1))
page_size = int(params.get("pageSize", PAGE_SIZE))
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 分页查询
link_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(link_list, total, page_size, page_no, pages)
return R.ok(data=page_data)
except Exception as e:
logger.error(f"查询友链分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 分页查询
link_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(link_list, total, page_size, page_no, pages)
return page_data
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -156,22 +150,22 @@ def _apply_filters():
query = Link.query.filter(Link.is_delete == 0)
# 友链名称模糊筛选
name = request.args.get('name')
name = params.get("name")
if name:
query = query.filter(Link.name.like(like_contains(name), escape='\\'))
query = query.filter(Link.name.like(like_contains(name), escape="\\"))
# 友链类型精确筛选
link_type = request.args.get('type')
link_type = params.get("type")
if link_type:
query = query.filter(Link.type == link_type)
# 友链形式精确筛选
form = request.args.get('form')
form = params.get("form")
if form:
query = query.filter(Link.form == form)
# 友链状态精确筛选
status = request.args.get('status')
status = params.get("status")
if status:
query = query.filter(Link.status == status)
@@ -198,27 +192,22 @@ def _build_page_data(link_list, total, page_size, page_no, pages):
for item in link_list:
data = model_to_dict(item)
# 添加友链类型描述
data['typeName'] = LINK_TYPE_LIST.get(item.type)
data["typeName"] = LINK_TYPE_LIST.get(item.type)
# 添加友链形式描述
data['formName'] = LINK_FORM_LIST.get(item.form)
data["formName"] = LINK_FORM_LIST.get(item.form)
# 友链图片URL
if item.image:
data['image'] = get_file_url(item.image)
data["image"] = get_file_url(item.image)
records.append(data)
return {
'records': records,
'total': total,
'size': page_size,
'current': page_no,
'pages': pages
}
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
# ============================================================
# 友链详情查询
# ============================================================
def get_link_detail(link_id):
"""
根据ID查询友链详情(返回字典格式)
@@ -241,7 +230,7 @@ def get_link_detail(link_id):
data = model_to_dict(link)
# 友链图片URL
if link.image:
data['image'] = get_file_url(link.image)
data["image"] = get_file_url(link.image)
return data
except Exception as e:
@@ -253,6 +242,7 @@ def get_link_detail(link_id):
# 友链信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(link_id: int):
"""
根据ID查询友链信息(返回模型对象)
@@ -278,26 +268,10 @@ def get_info(link_id: int):
if link:
print(link.name)
"""
# 参数有效性验证
if not link_id or link_id <= 0:
return None
try:
# 查询未删除的友链
link = Link.query.filter(
and_(Link.id == link_id, Link.is_delete == 0)
).first()
return link
except Exception as e:
logger.error(f"查询友链信息异常,ID: {link_id}, 错误: {str(e)}")
return None
return LinkRepository.get_by_id(link_id)
# ============================================================
# 友链添加
# ============================================================
def add_link():
def add_link(data: dict) -> None:
"""
添加友链
@@ -310,40 +284,29 @@ def add_link():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LinkForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = LinkForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 处理友链图片
image = form.data.get('image')
if image:
form.image.data = save_file(image, "link")
# 处理友链图片
image = form.data.get("image")
if image:
form.image.data = save_file(image, "link")
# 创建友链
link = Link(**form.data)
link.create_user = get_username()
link.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加友链异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
# 创建友链
link = Link(**form.data)
link.create_user = get_username()
link.save()
# ============================================================
# 友链更新
# ============================================================
def update_link():
def update_link(data: dict) -> None:
"""
更新友链
@@ -357,57 +320,47 @@ def update_link():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LinkForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = LinkForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取友链ID
link_id = form.data.get('id')
if not link_id or int(link_id) <= 0:
return R.failed("友链ID无效")
link_id = int(link_id)
# 获取友链ID
link_id = form.data.get("id")
if not link_id or int(link_id) <= 0:
raise BizError("友链ID无效")
link_id = int(link_id)
# 复用 get_info 方法验证友链是否存在
link = get_info(link_id)
if not link:
return R.failed("友链不存在")
# 复用 get_info 方法验证友链是否存在
link = get_info(link_id)
if not link:
raise BizError("友链不存在")
# 处理友链图片
image = form.data.get('image')
if image:
form.image.data = save_file(image, "link")
# 处理友链图片
image = form.data.get("image")
if image:
form.image.data = save_file(image, "link")
# 更新友链信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop('id', None)
# 更新友链信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop("id", None)
form_data['update_time'] = datetime.now(tz=timezone.utc)
form_data['update_user'] = get_username()
form_data["update_time"] = datetime.now(tz=timezone.utc)
form_data["update_user"] = get_username()
result = Link.query.filter_by(id=link_id).update(form_data)
result = Link.query.filter_by(id=link_id).update(form_data)
if not result:
return R.failed("更新失败")
return R.ok(msg="更新成功")
except Exception as e:
logger.error(f"更新友链异常: {str(e)}")
return R.failed(msg="更新友链失败,请稍后重试")
if not result:
raise BizError("更新失败")
# ============================================================
# 友链删除
# ============================================================
def delete_links(link_ids):
def delete_links(link_ids: str) -> int:
"""
删除友链(批量删除)
@@ -420,14 +373,18 @@ def delete_links(link_ids):
Returns:
R: 操作结果
"""
return Link.batch_delete_r(link_ids)
link_ids_list = parse_id_list(link_ids)
if not link_ids_list:
raise BizError("无效的记录ID")
return Link.batch_delete(link_ids_list)
# ============================================================
# 友链状态设置
# ============================================================
def update_link_status():
def update_link_status(data: dict) -> None:
"""
设置友链状态
@@ -436,35 +393,23 @@ def update_link_status():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LinkStatusForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = LinkStatusForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
link_id = int(form.data['id'])
status = int(form.data['status'])
link_id = int(form.data["id"])
status = int(form.data["status"])
# 复用 get_info 方法验证友链是否存在
link = get_info(link_id)
if not link:
return R.failed("友链不存在")
# 复用 get_info 方法验证友链是否存在
link = get_info(link_id)
if not link:
raise BizError("友链不存在")
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Link.query.filter_by(id=link_id).update(update_data)
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Link.query.filter_by(id=link_id).update(update_data)
if not result:
return R.failed("设置失败")
return R.ok(msg="设置成功")
except Exception as e:
logger.error(f"设置友链状态异常: {str(e)}")
return R.failed(msg="设置友链状态失败,请稍后重试")
if not result:
raise BizError("设置失败")
-1
View File
@@ -50,7 +50,6 @@ from src.common.base_db import BaseDB
from src.common.base_model import BaseModel
from config import DB_PREFIX
from src.core.database import db
from src.common.utils.case_converter import convert_camel_to_snake
# ============================================================
+15
View File
@@ -0,0 +1,15 @@
"""
Notice 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.notice.models import Notice
class NoticeRepository(BaseRepository):
"""Notice 仓储"""
model = Notice
+29 -17
View File
@@ -53,22 +53,24 @@
依赖模块: application.services.notice - 通知业务逻辑层
application.middleware.check_permission - 权限校验中间件
"""
from flask import Blueprint
from flask import Blueprint, request
from application.middleware.check_demo import check_demo
from application.middleware.check_permission import check_permission
from src.modules.notice import service as notice
from src.common.utils.request import parse_batch_ids
notice_blue = Blueprint('notice', __name__, url_prefix='/notice')
from src.common.utils.request import parse_batch_ids, parse_request_body
from src.core import response as R
notice_blue = Blueprint("notice", __name__, url_prefix="/notice")
# ============================================================
# 通知分页查询
# ============================================================
@notice_blue.route('/page', methods=["GET"])
@notice_blue.route("/page", methods=["GET"])
@check_permission("sys:notice:page")
def page():
"""
@@ -80,15 +82,15 @@ def page():
Returns:
JSON: 通知分页数据,包含 records(数据列表)、total(总记录数)等
"""
result = notice.get_notice_page()
return result
return R.ok(data=notice.get_notice_page(request.args))
# ============================================================
# 通知详情查询
# ============================================================
@notice_blue.route('/detail/<int:id>', methods=["GET"])
@notice_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:notice:detail")
def detail(id):
"""
@@ -108,7 +110,8 @@ def detail(id):
# 通知添加
# ============================================================
@notice_blue.route('/add', methods=["POST"])
@notice_blue.route("/add", methods=["POST"])
@check_permission("sys:notice:add")
@check_demo
def add():
@@ -121,15 +124,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = notice.add_notice()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
notice.add_notice(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 通知更新
# ============================================================
@notice_blue.route('/update', methods=["PUT"])
@notice_blue.route("/update", methods=["PUT"])
@check_permission("sys:notice:update")
@check_demo
def update():
@@ -142,15 +149,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = notice.update_notice()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
notice.update_notice(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 通知删除
# ============================================================
@notice_blue.route('/delete/<string:id>', methods=["DELETE"])
@notice_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:notice:delete")
@check_demo
def delete(id):
@@ -163,15 +174,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = notice.delete_notices(id)
return result
count = notice.delete_notices(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 通知公告批量删除
# ============================================================
@notice_blue.route('/batchDelete', methods=["DELETE"])
@notice_blue.route("/batchDelete", methods=["DELETE"])
@check_permission("sys:notice:batch_delete")
@check_demo
def batch_delete():
+89 -133
View File
@@ -63,20 +63,18 @@
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from src.common.constants.common import PAGE_SIZE
from src.common.constants.dictionary_constants import NOTICE_TYPE_LIST
from src.modules.notice.schema import NoticeForm
from src.modules.notice.models import Notice
from src.modules.notice.repository import NoticeRepository
from src.core.exceptions import BizError
from config import FLASK_FILE_URL
from src.core.database import db
from src.core import response as R
from src.common.utils import regular
from src.common.utils.case_converter import convert_camel_to_snake
from src.common.utils.model_utils import model_to_dict
from src.common.utils.request import parse_request_body, parse_id_list, like_contains
from src.common.utils.request import parse_id_list, like_contains
from src.common.utils.rich_text import save_content, save_file
from src.core.security import get_username
@@ -88,7 +86,8 @@ logger = logging.getLogger(__name__)
# 通知公告分页查询
# ============================================================
def get_notice_page():
def get_notice_page(params: dict) -> dict:
"""
查询通知公告分页数据
@@ -110,31 +109,26 @@ def get_notice_page():
"pages": 总页数
}
"""
try:
# 获取分页参数
page_no = int(request.args.get('pageNo', 1))
page_size = int(request.args.get('pageSize', PAGE_SIZE))
# 获取分页参数
page_no = int(params.get("pageNo", 1))
page_size = int(params.get("pageSize", PAGE_SIZE))
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 分页查询
notice_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(notice_list, total, page_size, page_no, pages)
return R.ok(data=page_data)
except Exception as e:
logger.error(f"查询通知公告分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 分页查询
notice_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(notice_list, total, page_size, page_no, pages)
return page_data
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -152,17 +146,17 @@ def _apply_filters():
query = Notice.query.filter(Notice.is_delete == 0)
# 通知公告标题模糊筛选
title = request.args.get('title')
title = params.get("title")
if title:
query = query.filter(Notice.title.like(like_contains(title), escape='\\'))
query = query.filter(Notice.title.like(like_contains(title), escape="\\"))
# 通知类型精确筛选
type = request.args.get('type')
type = params.get("type")
if type:
query = query.filter(Notice.type == type)
# 通知状态精确筛选
status = request.args.get('status')
status = params.get("status")
if status:
query = query.filter(Notice.status == status)
@@ -189,22 +183,17 @@ def _build_page_data(notice_list, total, page_size, page_no, pages):
for notice in notice_list:
data = model_to_dict(notice)
# 添加通知来源描述
data['typeText'] = NOTICE_TYPE_LIST.get(notice.type)
data["typeText"] = NOTICE_TYPE_LIST.get(notice.type)
records.append(data)
return {
'records': records,
'total': total,
'size': page_size,
'current': page_no,
'pages': pages
}
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
# ============================================================
# 通知公告详情查询
# ============================================================
def get_notice_detail(notice_id):
"""
根据ID查询通知公告详情(返回字典格式)
@@ -227,9 +216,9 @@ def get_notice_detail(notice_id):
data = model_to_dict(notice)
# 富文本内容处理:替换图片URL占位符
content = notice.content.replace("[IMG_URL]", FLASK_FILE_URL) if notice.content else ""
data['content'] = content
data["content"] = content
# 通知来源描述
data['typeText'] = NOTICE_TYPE_LIST.get(notice.type)
data["typeText"] = NOTICE_TYPE_LIST.get(notice.type)
return data
except Exception as e:
@@ -241,6 +230,7 @@ def get_notice_detail(notice_id):
# 通知公告信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(notice_id: int):
"""
根据ID查询通知公告信息(返回模型对象)
@@ -265,26 +255,10 @@ def get_info(notice_id: int):
if notice:
print(notice.title)
"""
# 参数有效性验证
if not notice_id or notice_id <= 0:
return None
try:
# 查询未删除的通知公告
notice = Notice.query.filter(
and_(Notice.id == notice_id, Notice.is_delete == 0)
).first()
return notice
except Exception as e:
logger.error(f"查询通知公告信息异常,ID: {notice_id}, 错误: {str(e)}")
return None
return NoticeRepository.get_by_id(notice_id)
# ============================================================
# 通知公告添加
# ============================================================
def add_notice():
def add_notice(data: dict) -> None:
"""
添加通知公告
@@ -297,46 +271,35 @@ def add_notice():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = NoticeForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = NoticeForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 通知封面处理
cover = form.data.get('cover')
if cover:
cover = save_file(cover, "notice")
form.cover.data = cover
# 处理富文本内容
title = form.data.get('title', '')
content = form.data.get('content', '')
if content:
form.content.data = save_content(content, title, "notice")
# 通知封面处理
cover = form.data.get("cover")
if cover:
cover = save_file(cover, "notice")
form.cover.data = cover
# 处理富文本内容
title = form.data.get("title", "")
content = form.data.get("content", "")
if content:
form.content.data = save_content(content, title, "notice")
# 创建通知公告
notice = Notice(**form.data)
notice.create_user = get_username()
notice.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加通知公告异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
# 创建通知公告
notice = Notice(**form.data)
notice.create_user = get_username()
notice.save()
# ============================================================
# 通知公告更新
# ============================================================
def update_notice():
def update_notice(data: dict) -> None:
"""
更新通知公告
@@ -350,63 +313,53 @@ def update_notice():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = NoticeForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = NoticeForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取通知公告ID
notice_id = form.data.get('id')
if not notice_id or int(notice_id) <= 0:
return R.failed("通知公告ID无效")
notice_id = int(notice_id)
# 获取通知公告ID
notice_id = form.data.get("id")
if not notice_id or int(notice_id) <= 0:
raise BizError("通知公告ID无效")
notice_id = int(notice_id)
# 复用 get_info 方法验证通知公告是否存在
notice = get_info(notice_id)
if not notice:
return R.failed("通知公告不存在")
# 复用 get_info 方法验证通知公告是否存在
notice = get_info(notice_id)
if not notice:
raise BizError("通知公告不存在")
# 通知封面处理
cover = form.data.get('cover')
if cover:
cover = save_file(cover, "notice")
form.cover.data = cover
# 处理富文本内容
title = form.data.get('title', '')
content = form.data.get('content', '')
if content:
form.content.data = save_content(content, title, "notice")
# 通知封面处理
cover = form.data.get("cover")
if cover:
cover = save_file(cover, "notice")
form.cover.data = cover
# 处理富文本内容
title = form.data.get("title", "")
content = form.data.get("content", "")
if content:
form.content.data = save_content(content, title, "notice")
# 更新通知公告信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop('id', None)
# 更新通知公告信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop("id", None)
form_data['update_time'] = datetime.now(tz=timezone.utc)
form_data['update_user'] = get_username()
form_data["update_time"] = datetime.now(tz=timezone.utc)
form_data["update_user"] = get_username()
result = Notice.query.filter_by(id=notice_id).update(form_data)
result = Notice.query.filter_by(id=notice_id).update(form_data)
if not result:
return R.failed("更新失败")
return R.ok(msg="更新成功")
except Exception as e:
logger.error(f"更新通知公告异常: {str(e)}")
return R.failed(msg="更新通知公告失败,请稍后重试")
if not result:
raise BizError("更新失败")
# ============================================================
# 通知公告删除
# ============================================================
def delete_notices(notice_ids):
def delete_notices(notice_ids: str) -> int:
"""
删除通知公告(批量删除)
@@ -419,4 +372,7 @@ def delete_notices(notice_ids):
Returns:
R: 操作结果
"""
return Notice.batch_delete_r(notice_ids)
notice_ids_list = parse_id_list(notice_ids)
if not notice_ids_list:
raise BizError("无效的记录ID")
return Notice.batch_delete(notice_ids_list)
-1
View File
@@ -9,7 +9,6 @@ from src.common.base_db import BaseDB
from src.common.base_model import BaseModel
from config import DB_PREFIX
from src.core.database import db
from src.common.utils.case_converter import convert_camel_to_snake
# ============================================================
+15
View File
@@ -0,0 +1,15 @@
"""
Param 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.param.models import Param
class ParamRepository(BaseRepository):
"""Param 仓储"""
model = Param
+36 -20
View File
@@ -57,22 +57,24 @@
依赖模块: application.services.param - 参数业务逻辑层
application.middleware.check_permission - 权限校验中间件
"""
from flask import Blueprint
from flask import Blueprint, request
from application.middleware.check_demo import check_demo
from application.middleware.check_permission import check_permission
from src.modules.param import service as param
from src.common.utils.request import parse_batch_ids
param_blue = Blueprint('param', __name__, url_prefix='/param')
from src.common.utils.request import parse_batch_ids, parse_request_body
from src.core import response as R
param_blue = Blueprint("param", __name__, url_prefix="/param")
# ============================================================
# 参数分页查询
# ============================================================
@param_blue.route('/page', methods=["GET"])
@param_blue.route("/page", methods=["GET"])
@check_permission("sys:param:page")
def page():
"""
@@ -84,15 +86,15 @@ def page():
Returns:
JSON: 参数分页数据,包含 records(数据列表)、total(总记录数)等
"""
result = param.get_param_page()
return result
return R.ok(data=param.get_param_page(request.args))
# ============================================================
# 参数详情查询
# ============================================================
@param_blue.route('/detail/<int:id>', methods=["GET"])
@param_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:param:detail")
def detail(id):
"""
@@ -112,7 +114,8 @@ def detail(id):
# 参数添加
# ============================================================
@param_blue.route('/add', methods=["POST"])
@param_blue.route("/add", methods=["POST"])
@check_permission("sys:param:add")
@check_demo
def add():
@@ -125,15 +128,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = param.add_param()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
param.add_param(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 参数更新
# ============================================================
@param_blue.route('/update', methods=["PUT"])
@param_blue.route("/update", methods=["PUT"])
@check_permission("sys:param:update")
@check_demo
def update():
@@ -146,15 +153,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = param.update_param()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
param.update_param(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 参数删除
# ============================================================
@param_blue.route('/delete/<string:id>', methods=["DELETE"])
@param_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:param:delete")
@check_demo
def delete(id):
@@ -167,15 +178,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = param.delete_params(id)
return result
count = param.delete_params(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 参数批量删除
# ============================================================
@param_blue.route('/batchDelete', methods=["DELETE"])
@param_blue.route("/batchDelete", methods=["DELETE"])
@check_permission("sys:param:batch_delete")
@check_demo
def batch_delete():
@@ -201,7 +213,8 @@ def batch_delete():
# 参数状态设置
# ============================================================
@param_blue.route('/status', methods=["PUT"])
@param_blue.route("/status", methods=["PUT"])
@check_permission("sys:param:status")
@check_demo
def status():
@@ -214,5 +227,8 @@ def status():
Returns:
JSON: 操作结果
"""
result = param.update_param_status()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
param.update_param_status(json_data)
return R.ok(msg="更新成功")
+85 -140
View File
@@ -26,17 +26,15 @@
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from src.common.constants.common import PAGE_SIZE
from src.modules.param.schema import ParamForm, ParamStatusForm
from src.modules.param.models import Param
from src.core.database import db
from src.core import response as R
from src.modules.param.repository import ParamRepository
from src.core.exceptions import BizError
from src.common.utils import regular
from src.common.utils.model_utils import models_to_list, model_to_dict
from src.common.utils.request import parse_request_body, parse_id_list, like_contains
from src.common.utils.request import parse_id_list, like_contains
from src.core.security import get_username
# 获取日志记录器
@@ -47,7 +45,8 @@ logger = logging.getLogger(__name__)
# 参数分页查询
# ============================================================
def get_param_page():
def get_param_page(params: dict) -> dict:
"""
查询参数分页数据
@@ -70,31 +69,26 @@ def get_param_page():
"pages": 总页数
}
"""
try:
# 获取分页参数
page_no = int(request.args.get('pageNo', 1))
page_size = int(request.args.get('pageSize', PAGE_SIZE))
# 获取分页参数
page_no = int(params.get("pageNo", 1))
page_size = int(params.get("pageSize", PAGE_SIZE))
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 分页查询
param_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(param_list, total, page_size, page_no, pages)
return R.ok(data=page_data)
except Exception as e:
logger.error(f"查询参数分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 分页查询
param_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(param_list, total, page_size, page_no, pages)
return page_data
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -113,22 +107,22 @@ def _apply_filters():
query = Param.query.filter(Param.is_delete == 0)
# 参数名称模糊筛选
name = request.args.get('name')
name = params.get("name")
if name:
query = query.filter(Param.name.like(like_contains(name), escape='\\'))
query = query.filter(Param.name.like(like_contains(name), escape="\\"))
# 参数编码模糊筛选
code = request.args.get('code')
code = params.get("code")
if code:
query = query.filter(Param.code.like(like_contains(code), escape='\\'))
query = query.filter(Param.code.like(like_contains(code), escape="\\"))
# 参数类型精确筛选
param_type = request.args.get('type')
param_type = params.get("type")
if param_type:
query = query.filter(Param.type == param_type)
# 参数状态精确筛选
status = request.args.get('status')
status = params.get("status")
if status:
query = query.filter(Param.status == status)
@@ -153,19 +147,14 @@ def _build_page_data(param_list, total, page_size, page_no, pages):
# 将模型对象列表转换为字典列表
records = models_to_list(param_list)
return {
'records': records,
'total': total,
'size': page_size,
'current': page_no,
'pages': pages
}
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
# ============================================================
# 参数详情查询
# ============================================================
def get_param_detail(param_id):
"""
根据ID查询参数详情(返回字典格式)
@@ -196,6 +185,7 @@ def get_param_detail(param_id):
# 参数信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(param_id: int):
"""
根据ID查询参数信息(返回模型对象)
@@ -221,26 +211,10 @@ def get_info(param_id: int):
if param:
print(param.name)
"""
# 参数有效性验证
if not param_id or param_id <= 0:
return None
try:
# 查询未删除的参数
param = Param.query.filter(
and_(Param.id == param_id, Param.is_delete == 0)
).first()
return param
except Exception as e:
logger.error(f"查询参数信息异常,ID: {param_id}, 错误: {str(e)}")
return None
return ParamRepository.get_by_id(param_id)
# ============================================================
# 参数添加
# ============================================================
def add_param():
def add_param(data: dict) -> None:
"""
添加参数
@@ -252,35 +226,24 @@ def add_param():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = ParamForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = ParamForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 创建参数
param = Param(**form.data)
param.create_user = get_username()
param.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加参数异常: {str(e)}")
return R.failed(msg="添加参数失败,请稍后重试")
# 创建参数
param = Param(**form.data)
param.create_user = get_username()
param.save()
# ============================================================
# 参数更新
# ============================================================
def update_param():
def update_param(data: dict) -> None:
"""
更新参数
@@ -293,52 +256,42 @@ def update_param():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = ParamForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = ParamForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取参数ID
param_id = form.data.get('id')
if not param_id or int(param_id) <= 0:
return R.failed("参数ID无效")
param_id = int(param_id)
# 获取参数ID
param_id = form.data.get("id")
if not param_id or int(param_id) <= 0:
raise BizError("参数ID无效")
param_id = int(param_id)
# 复用 get_info 方法验证参数是否存在
param = get_info(param_id)
if not param:
return R.failed("参数不存在")
# 复用 get_info 方法验证参数是否存在
param = get_info(param_id)
if not param:
raise BizError("参数不存在")
# 更新参数信息(移除ID,避免更新主键)
form_data = form.data.copy()
form_data.pop('id', None)
# 更新参数信息(移除ID,避免更新主键)
form_data = form.data.copy()
form_data.pop("id", None)
form_data['update_time'] = datetime.now(tz=timezone.utc)
form_data['update_user'] = get_username()
form_data["update_time"] = datetime.now(tz=timezone.utc)
form_data["update_user"] = get_username()
result = Param.query.filter_by(id=param_id).update(form_data)
result = Param.query.filter_by(id=param_id).update(form_data)
if not result:
return R.failed("更新失败")
return R.ok(msg="更新成功")
except Exception as e:
logger.error(f"更新参数异常: {str(e)}")
return R.failed(msg="更新参数失败,请稍后重试")
if not result:
raise BizError("更新失败")
# ============================================================
# 参数删除
# ============================================================
def delete_params(param_ids):
def delete_params(param_ids: str) -> int:
"""
删除参数(批量删除)
@@ -351,14 +304,18 @@ def delete_params(param_ids):
Returns:
R: 操作结果
"""
return Param.batch_delete_r(param_ids)
param_ids_list = parse_id_list(param_ids)
if not param_ids_list:
raise BizError("无效的记录ID")
return Param.batch_delete(param_ids_list)
# ============================================================
# 参数状态设置
# ============================================================
def update_param_status():
def update_param_status(data: dict) -> None:
"""
设置参数状态
@@ -367,35 +324,23 @@ def update_param_status():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = ParamStatusForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = ParamStatusForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
param_id = int(form.data['id'])
status = int(form.data['status'])
param_id = int(form.data["id"])
status = int(form.data["status"])
# 复用 get_info 方法验证参数是否存在
param = get_info(param_id)
if not param:
return R.failed("参数不存在")
# 复用 get_info 方法验证参数是否存在
param = get_info(param_id)
if not param:
raise BizError("参数不存在")
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Param.query.filter_by(id=param_id).update(update_data)
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Param.query.filter_by(id=param_id).update(update_data)
if not result:
return R.failed("设置失败")
return R.ok(msg="设置成功")
except Exception as e:
logger.error(f"设置参数状态异常: {str(e)}")
return R.failed(msg="设置参数状态失败,请稍后重试")
if not result:
raise BizError("设置失败")
-1
View File
@@ -50,7 +50,6 @@ from src.common.base_db import BaseDB
from src.common.base_model import BaseModel
from config import DB_PREFIX
from src.core.database import db
from src.common.utils.case_converter import convert_camel_to_snake
# ============================================================
+15
View File
@@ -0,0 +1,15 @@
"""
Level 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.system.level.models import Level
class LevelRepository(BaseRepository):
"""Level 仓储"""
model = Level
+42 -23
View File
@@ -56,22 +56,24 @@
依赖模块: application.services.level - 职级业务逻辑层
application.middleware.check_permission - 权限校验中间件
"""
from flask import Blueprint
from flask import Blueprint, request
from application.middleware.check_demo import check_demo
from application.middleware.check_permission import check_permission
from src.modules.system.level import service as level
from src.common.utils.request import parse_batch_ids
level_blue = Blueprint('level', __name__, url_prefix='/level')
from src.common.utils.request import parse_batch_ids, parse_request_body
from src.core import response as R
level_blue = Blueprint("level", __name__, url_prefix="/level")
# ============================================================
# 职级分页查询
# ============================================================
@level_blue.route('/page', methods=["GET"])
@level_blue.route("/page", methods=["GET"])
@check_permission("sys:level:page")
def page():
"""
@@ -83,15 +85,15 @@ def page():
Returns:
JSON: 职级分页数据,包含 records(数据列表)、total(总记录数)等
"""
result = level.get_level_page()
return result
return R.ok(data=level.get_level_page(request.args))
# ============================================================
# 职级详情查询
# ============================================================
@level_blue.route('/detail/<int:id>', methods=["GET"])
@level_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:level:detail")
def detail(id):
"""
@@ -111,7 +113,8 @@ def detail(id):
# 职级添加
# ============================================================
@level_blue.route('/add', methods=["POST"])
@level_blue.route("/add", methods=["POST"])
@check_permission("sys:level:add")
@check_demo
def add():
@@ -124,15 +127,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = level.add_level()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
level.add_level(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 职级更新
# ============================================================
@level_blue.route('/update', methods=["PUT"])
@level_blue.route("/update", methods=["PUT"])
@check_permission("sys:level:update")
@check_demo
def update():
@@ -145,15 +152,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = level.update_level()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
level.update_level(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 职级删除
# ============================================================
@level_blue.route('/delete/<string:id>', methods=["DELETE"])
@level_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:level:delete")
@check_demo
def delete(id):
@@ -166,15 +177,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = level.delete_levels(id)
return result
count = level.delete_levels(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 职级批量删除
# ============================================================
@level_blue.route('/batchDelete', methods=["DELETE"])
@level_blue.route("/batchDelete", methods=["DELETE"])
@check_permission("sys:level:batch_delete")
@check_demo
def batch_delete():
@@ -200,7 +212,8 @@ def batch_delete():
# 职级状态设置
# ============================================================
@level_blue.route('/status', methods=["PUT"])
@level_blue.route("/status", methods=["PUT"])
@check_permission("sys:level:status")
@check_demo
def status():
@@ -213,15 +226,19 @@ def status():
Returns:
JSON: 操作结果
"""
result = level.update_level_status()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
level.update_level_status(json_data)
return R.ok(msg="更新成功")
# ============================================================
# Excel导入
# ============================================================
@level_blue.route('/import', methods=["POST"])
@level_blue.route("/import", methods=["POST"])
@check_permission("sys:level:import")
@check_demo
def imports():
@@ -242,7 +259,8 @@ def imports():
# Excel导出
# ============================================================
@level_blue.route('/export', methods=["POST"])
@level_blue.route("/export", methods=["POST"])
@check_permission("sys:level:export")
@check_demo
def export():
@@ -263,7 +281,8 @@ def export():
# 职级列表获取(无分页)
# ============================================================
@level_blue.route('/list', methods=["GET"])
@level_blue.route("/list", methods=["GET"])
def get_level_list():
"""
获取职级数据列表(无分页)
+95 -146
View File
@@ -71,24 +71,25 @@ import time
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from openpyxl.reader.excel import load_workbook
from openpyxl.workbook import Workbook
from sqlalchemy import and_
import utils
from src.common.constants.common import PAGE_SIZE
from src.modules.system.level.schema import LevelForm, LevelStatusForm
from src.modules.system.level.models import Level
from src.modules.system.level.repository import LevelRepository
from src.core import response as R
from src.core.database import db
from src.core.exceptions import BizError
from src.modules.upload import service as upload
from config import FLASK_TEMP_PATH, FLASK_UPLOAD_DIR
from src.core.database import db
from src.core import response as R
from src.common.utils import regular
from src.common.utils.case_converter import convert_camel_to_snake
from src.common.utils.model_utils import models_to_list, model_to_dict
from src.common.utils.request import parse_request_body, parse_id_list, like_contains
from src.common.utils.request import parse_id_list, like_contains
from src.common.utils.rich_text import get_file_url
from src.common.utils.file import mkdir
from src.core.security import get_username
@@ -101,7 +102,8 @@ logger = logging.getLogger(__name__)
# 职级分页查询
# ============================================================
def get_level_page():
def get_level_page(params: dict) -> dict:
"""
查询职级分页数据
@@ -122,31 +124,26 @@ def get_level_page():
"pages": 总页数
}
"""
try:
# 获取分页参数
page_no = int(request.args.get('pageNo', 1))
page_size = int(request.args.get('pageSize', PAGE_SIZE))
# 获取分页参数
page_no = int(params.get("pageNo", 1))
page_size = int(params.get("pageSize", PAGE_SIZE))
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 记录总数
total = query.count()
# 计算总页数
pages = (total + page_size - 1) // page_size if total > 0 else 0
# 分页查询
level_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(level_list, total, page_size, page_no, pages)
return R.ok(data=page_data)
except Exception as e:
logger.error(f"查询职级分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 分页查询
level_list = query.limit(page_size).offset((page_no - 1) * page_size).all()
# 构建返回数据
page_data = _build_page_data(level_list, total, page_size, page_no, pages)
return page_data
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -163,12 +160,12 @@ def _apply_filters():
query = Level.query.filter(Level.is_delete == 0)
# 职级名称模糊筛选
name = request.args.get('name')
name = params.get("name")
if name:
query = query.filter(Level.name.like(like_contains(name), escape='\\'))
query = query.filter(Level.name.like(like_contains(name), escape="\\"))
# 职级状态精确筛选
status = request.args.get('status')
status = params.get("status")
if status:
query = query.filter(Level.status == status)
@@ -193,19 +190,14 @@ def _build_page_data(level_list, total, page_size, page_no, pages):
# 将模型对象列表转换为字典列表
records = models_to_list(level_list)
return {
'records': records,
'total': total,
'size': page_size,
'current': page_no,
'pages': pages
}
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
# ============================================================
# 职级详情查询
# ============================================================
def get_level_detail(level_id):
"""
根据ID查询职级详情(返回字典格式)
@@ -236,6 +228,7 @@ def get_level_detail(level_id):
# 职级信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(level_id: int):
"""
根据ID查询职级信息(返回模型对象)
@@ -261,26 +254,10 @@ def get_info(level_id: int):
if level:
print(level.name)
"""
# 参数有效性验证
if not level_id or level_id <= 0:
return None
try:
# 查询未删除的职级
level = Level.query.filter(
and_(Level.id == level_id, Level.is_delete == 0)
).first()
return level
except Exception as e:
logger.error(f"查询职级信息异常,ID: {level_id}, 错误: {str(e)}")
return None
return LevelRepository.get_by_id(level_id)
# ============================================================
# 职级添加
# ============================================================
def add_level():
def add_level(data: dict) -> None:
"""
添加职级
@@ -292,35 +269,24 @@ def add_level():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LevelForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = LevelForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 创建职级
level = Level(**form.data)
level.create_user = get_username()
level.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加职级异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
# 创建职级
level = Level(**form.data)
level.create_user = get_username()
level.save()
# ============================================================
# 职级更新
# ============================================================
def update_level():
def update_level(data: dict) -> None:
"""
更新职级
@@ -333,52 +299,42 @@ def update_level():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LevelForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = LevelForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取职级ID
level_id = form.data.get('id')
if not level_id or int(level_id) <= 0:
return R.failed("职级ID无效")
level_id = int(level_id)
# 获取职级ID
level_id = form.data.get("id")
if not level_id or int(level_id) <= 0:
raise BizError("职级ID无效")
level_id = int(level_id)
# 复用 get_info 方法验证职级是否存在
level = get_info(level_id)
if not level:
return R.failed("职级不存在")
# 复用 get_info 方法验证职级是否存在
level = get_info(level_id)
if not level:
raise BizError("职级不存在")
# 更新职级信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop('id', None)
# 更新职级信息(移除ID,避免更新主键)
form_data = convert_camel_to_snake(form.data.copy())
form_data.pop("id", None)
form_data['update_time'] = datetime.now(tz=timezone.utc)
form_data['update_user'] = get_username()
form_data["update_time"] = datetime.now(tz=timezone.utc)
form_data["update_user"] = get_username()
result = Level.query.filter_by(id=level_id).update(form_data)
result = Level.query.filter_by(id=level_id).update(form_data)
if not result:
return R.failed("更新失败")
return R.ok(msg="更新成功")
except Exception as e:
logger.error(f"更新职级异常: {str(e)}")
return R.failed(msg="更新职级失败,请稍后重试")
if not result:
raise BizError("更新失败")
# ============================================================
# 职级删除
# ============================================================
def delete_levels(level_ids):
def delete_levels(level_ids: str) -> int:
"""
删除职级(批量删除)
@@ -391,14 +347,18 @@ def delete_levels(level_ids):
Returns:
R: 操作结果
"""
return Level.batch_delete_r(level_ids)
level_ids_list = parse_id_list(level_ids)
if not level_ids_list:
raise BizError("无效的记录ID")
return Level.batch_delete(level_ids_list)
# ============================================================
# 职级状态设置
# ============================================================
def update_level_status():
def update_level_status(data: dict) -> None:
"""
设置职级状态
@@ -407,44 +367,33 @@ def update_level_status():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = LevelStatusForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = LevelStatusForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
level_id = int(form.data['id'])
status = int(form.data['status'])
level_id = int(form.data["id"])
status = int(form.data["status"])
# 复用 get_info 方法验证职级是否存在
level = get_info(level_id)
if not level:
return R.failed("职级不存在")
# 复用 get_info 方法验证职级是否存在
level = get_info(level_id)
if not level:
raise BizError("职级不存在")
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Level.query.filter_by(id=level_id).update(update_data)
# 更新状态
update_data = {"status": status, "update_time": datetime.now(tz=timezone.utc), "update_user": get_username()}
result = Level.query.filter_by(id=level_id).update(update_data)
if not result:
return R.failed("设置失败")
return R.ok(msg="设置成功")
except Exception as e:
logger.error(f"设置职级状态异常: {str(e)}")
return R.failed(msg="设置职级状态失败,请稍后重试")
if not result:
raise BizError("设置失败")
# ============================================================
# Excel导入
# ============================================================
def import_levels():
"""
导入Excel文件批量导入职级
@@ -462,7 +411,7 @@ def import_levels():
try:
# 上传并读取Excel文件
file = upload.upload_file(".xls|.xlsx")
path = file['filePath']
path = file["filePath"]
wb = load_workbook(path)
ws = wb.worksheets[0]
@@ -502,8 +451,8 @@ def import_levels():
finally:
# 清理导入产生的临时Excel文件
try:
if 'path' in dir() and path:
abs_path = os.path.join(FLASK_UPLOAD_DIR, path.lstrip('/'))
if "path" in dir() and path:
abs_path = os.path.join(FLASK_UPLOAD_DIR, path.lstrip("/"))
if os.path.isfile(abs_path):
os.remove(abs_path)
except Exception:
@@ -514,6 +463,7 @@ def import_levels():
# Excel导出
# ============================================================
def export_levels():
"""
导出职级数据到Excel文件
@@ -533,17 +483,17 @@ def export_levels():
wb = Workbook()
ws = wb.worksheets[0]
# 写入表头
ws.append(['职级ID', '职级名称', '职级排序', '职级状态', '创建时间', '更新时间'])
ws.append(["职级ID", "职级名称", "职级排序", "职级状态", "创建时间", "更新时间"])
# 写入数据
for data in level_list:
ws.append(list(data.values()))
# 保存文件
save_path = FLASK_TEMP_PATH + "/" + time.strftime('%Y%m%d')
save_path = FLASK_TEMP_PATH + "/" + time.strftime("%Y%m%d")
mkdir(save_path)
file_name = time.strftime('%Y%m%d%H%M%S') + '%05d' % random.randint(0, 100) + ".xlsx"
file_name = time.strftime("%Y%m%d%H%M%S") + "%05d" % random.randint(0, 100) + ".xlsx"
file_path = save_path + "/" + file_name
wb.save(file_path)
@@ -562,6 +512,7 @@ def export_levels():
# 职级列表获取(无分页)
# ============================================================
def get_level_list():
"""
获取所有启用的职级列表(无分页)
@@ -574,9 +525,7 @@ def get_level_list():
"""
try:
# 查询所有启用的职级
level_list = Level.query.filter(
and_(Level.is_delete == 0, Level.status == 1)
).order_by(Level.sort.asc()).all()
level_list = Level.query.filter(and_(Level.is_delete == 0, Level.status == 1)).order_by(Level.sort.asc()).all()
# 转换为字典列表
result = []
+1 -1
View File
@@ -354,7 +354,7 @@ from src.core.database import db # 统一 db 单例
| **P1 工具链** | pyproject.toml / requirements-dev.txt / .flake8 / .pre-commit / Makefile / pytest 骨架 | 现有代码 `flake8` 通过(或登记存量告警);`pytest` 空跑通过。**已登记存量告警:154 条(F401×111 / E402×36 / 其余 7);pytest 5 passed** | 低 |
| **P2 core+common** | 建 `src/core/` `src/common/`;extensions/utils 打 shim;模型聚合器建 `src/models_registry.py` | 已验收:22 模型表全量识别且 DB 全部存在;src/ flake8 零告警;pytest 5 passed;应用启动;/login 表单校验与 /user/page 鉴权拦截冒烟正常。**基线修复**:application/models + middleware 原被嵌套 `.gitignore`(`*`)误排除,已补入。**登记既有缺陷**:① `flask db` CLI 因 .env 的 FLASK_VERSION 与 --version 冲突报错(Makefile 已加 FLASK_SKIP_DOTENV 规避);② 本机 Redis 未设密码但 REDIS_AUTH=True,/captcha 报 AUTH 错误(环境问题,非重构) | 高(依赖面最大,已完成) |
| **P3 模块搬迁** | 按 §2.5 顺序逐组迁移 23 个模块(models→forms→services→views),每批打 shim + 验证 | 已验收:22 模型→19 模块 models.py;23 表单→schema.py;29 服务→service.py(含 executor/scheduler_service/mail_service/sms_service);26 视图→router.py(login+index 合并入 auth)。应用启动、25 蓝图无重复注册(views/__init__.py 已加去重)、pytest 5 passed、接口冒烟(登录校验/鉴权 401)正常 | 中(已完成) |
| **P4 服务层改造** | 26 个服务文件改「入参数据/出参数据」;router 取参包 R;引入 base_service/base_repository + BizError | **P4a 地基已完成**:BizError/base_repository/base_service + 全局处理器接入。**P4b 标杆(position)已完成**:服务函数入参数据/出参数据、raise BizError、repository 层、7 个单测(无需 DB 可测)。**待铺开**:其余 ~25 个服务按 position 范式批量改造 + 单测 | 高(P4a/P4b 已完成,铺开中) |
| **P4 服务层改造** | 26 个服务文件改「入参数据/出参数据」;router 取参包 R;引入 base_service/base_repository + BizError | **P4a 地基 ✓**(BizError/base_repository/base_service)。**P4b 标杆 position ✓**(含单测)。**P4c 批次1 ✓**(notice/param/link/level 服务+router 改造,flake8 零告警,服务运行时验证通过)。**待铺开**:其余 ~20 个服务。注:level 的 Excel import/export 仍返回 R(暂保留 R 导入,后续统一改造) | 高(地基+标杆+批次1 完成,铺开中) |
| **P5 DI 注入** | core/permission.py + op_log 接口化;app 工厂注入 menu 服务 | `grep -r "from application" src/core/` 为空;权限/日志行为与现状一致 | 中 |
| **P6 API v1** | routes/v1.py + `/api/v1`;白名单/内部调用/前端前缀同步 | 全部接口路径 `curl /api/v1/...` 通过;`/login` 白名单放行 | 中 |
| **P7 切尾** | 删除 application/extensions/utils;`src/wsgi.py`;gunicorn.conf 指向 src.wsgi;更新 CLAUDE.md | `gunicorn -c gunicorn.conf.py src.wsgi:app` 启动;全量回归 | 高 |