refactor: P4c 批次3 服务层改造(dept/city tree 型模块)

- add/update/delete/get_info 按 codemod 改造(get_info 委托 Repository)
- get_dept_list/get_city_list 手动 params 化返回 list
- city 删除函数 f-string R.failed → raise BizError
- router 取参包 R;新增 dept/city repository.py

【验收】flake8 零告警;应用启动;dept/city 服务运行时正常;12 passed

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DjangoAdmin Dev
2026-08-07 15:32:46 +08:00
co-authored by Claude
parent c2a4910d60
commit d97e456744
8 changed files with 229 additions and 256 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 @@
"""
City 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.city.models import City
class CityRepository(BaseRepository):
"""City 仓储"""
model = City
+29 -16
View File
@@ -55,21 +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.city import service as city
city_blue = Blueprint('city', __name__, url_prefix='/city')
from src.common.utils.request import parse_request_body
from src.core import response as R
city_blue = Blueprint("city", __name__, url_prefix="/city")
# ============================================================
# 城市分页查询
# ============================================================
@city_blue.route('/page', methods=["GET"])
@city_blue.route("/page", methods=["GET"])
@check_permission("sys:city:page")
def page():
"""
@@ -81,15 +84,15 @@ def page():
Returns:
JSON: 城市分页数据,包含 records(数据列表)、total(总记录数)等
"""
result = city.get_city_list()
return result
return R.ok(data=city.get_city_list(request.args))
# ============================================================
# 城市详情查询
# ============================================================
@city_blue.route('/detail/<int:id>', methods=["GET"])
@city_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:city:detail")
def detail(id):
"""
@@ -109,7 +112,8 @@ def detail(id):
# 城市添加
# ============================================================
@city_blue.route('/add', methods=["POST"])
@city_blue.route("/add", methods=["POST"])
@check_permission("sys:city:add")
@check_demo
def add():
@@ -122,15 +126,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = city.add_city()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
city.add_city(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 城市更新
# ============================================================
@city_blue.route('/update', methods=["PUT"])
@city_blue.route("/update", methods=["PUT"])
@check_permission("sys:city:update")
@check_demo
def update():
@@ -143,15 +151,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = city.update_city()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
city.update_city(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 城市删除
# ============================================================
@city_blue.route('/delete/<string:id>', methods=["DELETE"])
@city_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:city:delete")
@check_demo
def delete(id):
@@ -164,15 +176,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = city.delete_cities(id)
return result
count = city.delete_cities(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 子级城市查询
# ============================================================
@city_blue.route('/getCityList/<string:city_code>', methods=["GET"])
@city_blue.route("/getCityList/<string:city_code>", methods=["GET"])
def get_child_list(city_code):
"""
根据城市编码查询子级城市
+78 -117
View File
@@ -65,17 +65,16 @@
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from src.common.constants.dictionary_constants import CITY_LEVEL_LIST
from src.modules.city.schema import CityForm
from src.modules.city.models import City
from src.core.database import db
from src.core import response as R
from src.modules.city.repository import CityRepository
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.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.model_utils import models_to_list, model_to_dict
from src.core.security import get_username
@@ -87,38 +86,32 @@ logger = logging.getLogger(__name__)
# 城市列表查询
# ============================================================
def get_city_list():
def get_city_list(params: dict) -> list:
"""
查询城市列表
用于城市管理页面的列表展示,支持按父级ID和名称筛选。
Args:
request: Flask request对象,包含查询参数
params: 查询参数
- pid: 父级ID(可选,默认为0,查询顶级城市)
- name: 城市名称(可选,模糊查询)
Returns:
R: 包含城市列表的JSON响应
list: 城市字典列表
"""
try:
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 查询数据
city_list = query.all()
# 查询数据
city_list = query.all()
# 转换为字典列表并补充关联数据
records = _enrich_city_records(city_list)
return R.ok(data=records)
except Exception as e:
logger.error(f"查询城市列表异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 转换为字典列表并补充关联数据
return _enrich_city_records(city_list)
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -134,7 +127,7 @@ def _apply_filters():
query = City.query.filter(City.is_delete == 0)
# 父级ID精确筛选(默认为0,查询顶级城市)
pid = request.args.get('pid', 0)
pid = params.get("pid", 0)
if pid is not None:
try:
query = query.filter(City.pid == int(pid))
@@ -142,9 +135,9 @@ def _apply_filters():
pass
# 城市名称模糊筛选
name = request.args.get('name')
name = params.get("name")
if name:
query = query.filter(City.name.like(like_contains(name), escape='\\'))
query = query.filter(City.name.like(like_contains(name), escape="\\"))
# 按ID升序排列
return query.order_by(City.id.asc())
@@ -167,10 +160,10 @@ def _enrich_city_records(city_list):
for data in records:
# 城市级别名称
level = data.get('level')
data['level_name'] = CITY_LEVEL_LIST.get(level) if level else None
level = data.get("level")
data["level_name"] = CITY_LEVEL_LIST.get(level) if level else None
# 是否有子级(level < 3 表示还有下级)
data['have_child'] = data.get('level', 4) < 3 if data.get('level') else False
data["have_child"] = data.get("level", 4) < 3 if data.get("level") else False
return records
@@ -179,6 +172,7 @@ def _enrich_city_records(city_list):
# 城市详情查询
# ============================================================
def get_city_detail(city_id):
"""
根据ID查询城市详情(返回字典格式)
@@ -209,6 +203,7 @@ def get_city_detail(city_id):
# 城市信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(city_id: int):
"""
根据ID查询城市信息(返回模型对象)
@@ -234,26 +229,10 @@ def get_info(city_id: int):
if city:
print(city.name)
"""
# 参数有效性验证
if not city_id or city_id <= 0:
return None
try:
# 查询未删除的城市
city = City.query.filter(
and_(City.id == city_id, City.is_delete == 0)
).first()
return city
except Exception as e:
logger.error(f"查询城市信息异常,ID: {city_id}, 错误: {str(e)}")
return None
return CityRepository.get_by_id(city_id)
# ============================================================
# 城市添加
# ============================================================
def add_city():
def add_city(data: dict) -> None:
"""
添加城市
@@ -265,35 +244,24 @@ def add_city():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = CityForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = CityForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 创建城市
city = City(**form.data)
city.create_user = get_username()
city.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加城市异常: {str(e)}")
return R.failed(msg="添加城市失败,请稍后重试")
# 创建城市
city = City(**form.data)
city.create_user = get_username()
city.save()
# ============================================================
# 城市更新
# ============================================================
def update_city():
def update_city(data: dict) -> None:
"""
更新城市
@@ -306,52 +274,42 @@ def update_city():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = CityForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = CityForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取城市ID
city_id = form.data.get('id')
if not city_id or int(city_id) <= 0:
return R.failed("城市ID无效")
city_id = int(city_id)
# 获取城市ID
city_id = form.data.get("id")
if not city_id or int(city_id) <= 0:
raise BizError("城市ID无效")
city_id = int(city_id)
# 复用 get_info 方法验证城市是否存在
city = get_info(city_id)
if not city:
return R.failed("城市不存在")
# 复用 get_info 方法验证城市是否存在
city = get_info(city_id)
if not city:
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 = City.query.filter_by(id=city_id).update(form_data)
result = City.query.filter_by(id=city_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_cities(city_ids):
def delete_cities(city_ids: str) -> int:
"""
删除城市(批量删除)
@@ -365,28 +323,30 @@ def delete_cities(city_ids):
R: 操作结果
"""
if not city_ids:
return R.failed("记录ID不存在")
raise BizError("记录ID不存在")
# 解析ID列表
id_list = parse_id_list(city_ids)
if not id_list:
return R.failed("无效的记录ID")
raise BizError("无效的记录ID")
# 检查是否有子级城市
for city_id in id_list:
child_count = City.query.filter(
and_(City.pid == city_id, City.is_delete == 0)
).count()
child_count = City.query.filter(and_(City.pid == city_id, City.is_delete == 0)).count()
if child_count > 0:
return R.failed(f"城市ID {city_id} 存在子级城市,请先删除子级城市")
raise BizError(f"城市ID {city_id} 存在子级城市,请先删除子级城市")
return City.batch_delete_r(city_ids)
city_ids_list = parse_id_list(city_ids)
if not city_ids_list:
raise BizError("无效的记录ID")
return City.batch_delete(city_ids_list)
# ============================================================
# 子级城市查询
# ============================================================
def get_child_cities(city_code):
"""
根据城市编码获取子级城市列表
@@ -408,9 +368,9 @@ def get_child_cities(city_code):
try:
# 查询子级城市
child_list = City.query.filter(
and_(City.parent_code == city_code, City.is_delete == 0)
).order_by(City.id.asc()).all()
child_list = (
City.query.filter(and_(City.parent_code == city_code, City.is_delete == 0)).order_by(City.id.asc()).all()
)
# 构建返回数据
result = models_to_list(child_list)
@@ -425,6 +385,7 @@ def get_child_cities(city_code):
# 城市树形数据获取
# ============================================================
def get_city_tree():
"""
获取城市树形数据
@@ -468,16 +429,16 @@ def _build_city_tree(city_list, parent_id=0):
"""
tree = []
for city in city_list:
if city.get('pid') == parent_id:
if city.get("pid") == parent_id:
node = {
'id': city.get('id'),
'name': city.get('name'),
'code': city.get('area_code'),
'level': city.get('level'),
'children': _build_city_tree(city_list, city.get('id'))
"id": city.get("id"),
"name": city.get("name"),
"code": city.get("area_code"),
"level": city.get("level"),
"children": _build_city_tree(city_list, city.get("id")),
}
# 如果没有子节点,移除children字段
if not node['children']:
del node['children']
if not node["children"]:
del node["children"]
tree.append(node)
return tree
-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 @@
"""
Dept 仓储模块
=============================================================
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
"""
from src.common.base_repository import BaseRepository
from src.modules.system.dept.models import Dept
class DeptRepository(BaseRepository):
"""Dept 仓储"""
model = Dept
+29 -16
View File
@@ -55,21 +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.system.dept import service as dept
dept_blue = Blueprint('dept', __name__, url_prefix='/dept')
from src.common.utils.request import parse_request_body
from src.core import response as R
dept_blue = Blueprint("dept", __name__, url_prefix="/dept")
# ============================================================
# 部门列表查询
# ============================================================
@dept_blue.route('/list', methods=["GET"])
@dept_blue.route("/list", methods=["GET"])
@check_permission("sys:dept:list")
def list():
"""
@@ -81,15 +84,15 @@ def list():
Returns:
JSON: 部门列表数据
"""
result = dept.get_dept_list()
return result
return R.ok(data=dept.get_dept_list(request.args))
# ============================================================
# 部门详情查询
# ============================================================
@dept_blue.route('/detail/<int:id>', methods=["GET"])
@dept_blue.route("/detail/<int:id>", methods=["GET"])
@check_permission("sys:dept:detail")
def detail(id):
"""
@@ -109,7 +112,8 @@ def detail(id):
# 部门添加
# ============================================================
@dept_blue.route('/add', methods=["POST"])
@dept_blue.route("/add", methods=["POST"])
@check_permission("sys:dept:add")
@check_demo
def add():
@@ -122,15 +126,19 @@ def add():
Returns:
JSON: 操作结果
"""
result = dept.add_dept()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
dept.add_dept(json_data)
return R.ok(msg="添加成功")
# ============================================================
# 部门更新
# ============================================================
@dept_blue.route('/update', methods=["PUT"])
@dept_blue.route("/update", methods=["PUT"])
@check_permission("sys:dept:update")
@check_demo
def update():
@@ -143,15 +151,19 @@ def update():
Returns:
JSON: 操作结果
"""
result = dept.update_dept()
return result
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
dept.update_dept(json_data)
return R.ok(msg="更新成功")
# ============================================================
# 部门删除
# ============================================================
@dept_blue.route('/delete/<string:id>', methods=["DELETE"])
@dept_blue.route("/delete/<string:id>", methods=["DELETE"])
@check_permission("sys:dept:delete")
@check_demo
def delete(id):
@@ -164,15 +176,16 @@ def delete(id):
Returns:
JSON: 操作结果
"""
result = dept.delete_depts(id)
return result
count = dept.delete_depts(id)
return R.ok(msg=f"本次共删除{count}条数据")
# ============================================================
# 部门数据列表获取
# ============================================================
@dept_blue.route('/getDeptList', methods=["GET"])
@dept_blue.route("/getDeptList", methods=["GET"])
def get_dept_list():
"""
获取部门数据列表
+63 -105
View File
@@ -64,16 +64,15 @@
import logging
from datetime import datetime, timezone
from flask import request
from sqlalchemy import and_
from src.modules.system.dept.schema import DeptForm
from src.modules.system.dept.models import Dept
from src.core.database import db
from src.core import response as R
from src.modules.system.dept.repository import DeptRepository
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.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.model_utils import models_to_list, model_to_dict
from src.core.security import get_username
@@ -85,37 +84,31 @@ logger = logging.getLogger(__name__)
# 部门列表查询
# ============================================================
def get_dept_list():
def get_dept_list(params: dict) -> list:
"""
查询部门列表(无分页)
用于部门管理页面的列表展示,支持按名称模糊搜索。
Args:
request: Flask request对象,包含查询参数
params: 查询参数
- name: 部门名称(可选,模糊查询)
Returns:
R: 包含部门列表的JSON响应
list: 部门字典列表
"""
try:
# 应用筛选条件
query = _apply_filters()
# 应用筛选条件
query = _apply_filters(params)
# 查询数据
dept_list = query.all()
# 查询数据
dept_list = query.all()
# 转换为字典列表
records = models_to_list(dept_list)
return R.ok(data=records)
except Exception as e:
logger.error(f"查询部门列表异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
# 转换为字典列表
return models_to_list(dept_list)
def _apply_filters():
def _apply_filters(params: dict):
"""
应用查询筛选条件
@@ -130,9 +123,9 @@ def _apply_filters():
query = Dept.query.filter(Dept.is_delete == 0)
# 部门名称模糊筛选
name = request.args.get('name')
name = params.get("name")
if name:
query = query.filter(Dept.name.like(like_contains(name), escape='\\'))
query = query.filter(Dept.name.like(like_contains(name), escape="\\"))
# 按排序值升序排列
return query.order_by(Dept.sort.asc())
@@ -142,6 +135,7 @@ def _apply_filters():
# 部门详情查询
# ============================================================
def get_dept_detail(dept_id):
"""
根据ID查询部门详情(返回字典格式)
@@ -172,6 +166,7 @@ def get_dept_detail(dept_id):
# 部门信息查询(根据ID)- 核心查询方法
# ============================================================
def get_info(dept_id: int):
"""
根据ID查询部门信息(返回模型对象)
@@ -197,26 +192,10 @@ def get_info(dept_id: int):
if dept:
print(dept.name)
"""
# 参数有效性验证
if not dept_id or dept_id <= 0:
return None
try:
# 查询未删除的部门
dept = Dept.query.filter(
and_(Dept.id == dept_id, Dept.is_delete == 0)
).first()
return dept
except Exception as e:
logger.error(f"查询部门信息异常,ID: {dept_id}, 错误: {str(e)}")
return None
return DeptRepository.get_by_id(dept_id)
# ============================================================
# 部门添加
# ============================================================
def add_dept():
def add_dept(data: dict) -> None:
"""
添加部门
@@ -228,35 +207,24 @@ def add_dept():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = DeptForm(data=json_data)
if not form.validate():
err_msg = regular.get_err(form)
return R.failed(msg=err_msg)
# 表单验证
form = DeptForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 创建部门
dept = Dept(**form.data)
dept.create_user = get_username()
dept.save()
return R.ok(msg="添加成功")
except Exception as e:
logger.error(f"添加部门异常: {str(e)}")
return R.failed(msg="添加部门失败,请稍后重试")
# 创建部门
dept = Dept(**form.data)
dept.create_user = get_username()
dept.save()
# ============================================================
# 部门更新
# ============================================================
def update_dept():
def update_dept(data: dict) -> None:
"""
更新部门
@@ -269,52 +237,42 @@ def update_dept():
Returns:
R: 操作结果
"""
try:
# 获取参数
json_data, error = parse_request_body()
if error:
return R.failed(msg=error)
# 表单验证
form = DeptForm(data=json_data)
if not form.validate():
return R.failed(msg=regular.get_err(form))
# 表单验证
form = DeptForm(data=data)
if not form.validate():
raise BizError(regular.get_err(form))
# 获取部门ID
dept_id = form.data.get('id')
if not dept_id or int(dept_id) <= 0:
return R.failed("部门ID无效")
dept_id = int(dept_id)
# 获取部门ID
dept_id = form.data.get("id")
if not dept_id or int(dept_id) <= 0:
raise BizError("部门ID无效")
dept_id = int(dept_id)
# 复用 get_info 方法验证部门是否存在
dept = get_info(dept_id)
if not dept:
return R.failed("部门不存在")
# 复用 get_info 方法验证部门是否存在
dept = get_info(dept_id)
if not dept:
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 = Dept.query.filter_by(id=dept_id).update(form_data)
result = Dept.query.filter_by(id=dept_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_depts(dept_ids):
def delete_depts(dept_ids: str) -> int:
"""
删除部门(批量删除)
@@ -328,28 +286,30 @@ def delete_depts(dept_ids):
R: 操作结果
"""
if not dept_ids:
return R.failed("记录ID不存在")
raise BizError("记录ID不存在")
# 解析ID列表
id_list = parse_id_list(dept_ids)
if not id_list:
return R.failed("无效的记录ID")
raise BizError("无效的记录ID")
# 检查是否有子部门
for dept_id in id_list:
child_count = Dept.query.filter(
and_(Dept.parent_id == dept_id, Dept.is_delete == 0)
).count()
child_count = Dept.query.filter(and_(Dept.parent_id == dept_id, Dept.is_delete == 0)).count()
if child_count > 0:
return R.failed("存在子部门,请先删除子部门")
raise BizError("存在子部门,请先删除子部门")
return Dept.batch_delete_r(dept_ids)
dept_ids_list = parse_id_list(dept_ids)
if not dept_ids_list:
raise BizError("无效的记录ID")
return Dept.batch_delete(dept_ids_list)
# ============================================================
# 部门树形数据获取
# ============================================================
def get_dept_data():
"""
获取部门树形数据(无分页)
@@ -362,9 +322,7 @@ def get_dept_data():
"""
try:
# 查询所有未删除的部门
dept_list = Dept.query.filter(
and_(Dept.is_delete == 0, Dept.status == 1)
).order_by(Dept.sort.asc()).all()
dept_list = Dept.query.filter(and_(Dept.is_delete == 0, Dept.status == 1)).order_by(Dept.sort.asc()).all()
# 转换为字典列表
result = models_to_list(dept_list)