feat(business): 客户评级等级映射配置接口(区间重叠判定/名称唯一)
- 判定=按 sort_order 升序首个 min_score≤总分,边界相邻不算重叠 - 新增同样执行重叠判定(判定表语义下插出重叠即脏配置) - 等级配置无下游引用(评级为纯计算器,对照表 #25),允许清空 Co-Authored-By: Claude Code <noreply@anthropic.com>
这个提交包含在:
1 父节点
899bf3afa5
当前提交
1625907380
5 个文件已更改
+498
没有与搜索条件相匹配的文件
@@ -0,0 +1,8 @@
|
||||
"""业务数据管理接口(需求 2.2)。
|
||||
|
||||
挂载于 `/api/v1/business/*`,含业务单据维护、客户评级等级配置等业务数据入口。
|
||||
|
||||
与 `api/v1/system` 的权限分工:业务数据是**业务操作**,登录即可访问
|
||||
(不要求管理员);写操作同样经 `RequestMetaDep` 带入请求元信息,
|
||||
由 service 层写入操作日志。
|
||||
"""
|
||||
@@ -0,0 +1,74 @@
|
||||
"""客户评级等级配置接口(需求 2.2.5)。
|
||||
|
||||
评级计算器的等级映射判定表:判定按 sort_order 升序取首个 min_score ≤ 总分
|
||||
的等级,max_score 仅展示。区间合法(0≤min≤max≤100)与互不重叠由后端校验
|
||||
—— 前端表单只是展示约束,后端校验是唯一防线。
|
||||
|
||||
列表不分页全量返回:等级映射是判定表,数量个位数,分页无意义。
|
||||
业务数据属业务操作,登录用户即可访问(区别于系统管理模块的 AdminUser);
|
||||
写操作经 RequestMetaDep 带入请求元信息,由 service 层写入操作日志。
|
||||
"""
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Path
|
||||
|
||||
from app.api.deps import DbSession, RequestMetaDep, get_current_user
|
||||
from app.core.response import ApiResponse, ok
|
||||
from app.models import User
|
||||
from app.schemas.rating import RatingGradeCreateIn, RatingGradeOut, RatingGradeUpdateIn
|
||||
from app.services import rating_service
|
||||
|
||||
CurrentUser = Annotated[User, Depends(get_current_user)]
|
||||
|
||||
router = APIRouter(prefix="/rating-grades", tags=["客户评级"])
|
||||
|
||||
|
||||
@router.get("", response_model=ApiResponse[list[RatingGradeOut]], summary="评级等级列表")
|
||||
def list_grades(
|
||||
session: DbSession,
|
||||
_: CurrentUser,
|
||||
) -> ApiResponse[list[RatingGradeOut]]:
|
||||
"""全量等级列表,按判定顺序(sort_order 升序)返回,不分页。"""
|
||||
return ok(rating_service.list_grades(session))
|
||||
|
||||
|
||||
@router.post("", response_model=ApiResponse[RatingGradeOut], summary="新增评级等级")
|
||||
def create_grade(
|
||||
session: DbSession,
|
||||
current_user: CurrentUser,
|
||||
payload: RatingGradeCreateIn,
|
||||
meta: RequestMetaDep,
|
||||
) -> ApiResponse[RatingGradeOut]:
|
||||
"""新增评级等级。区间须 0≤min≤max≤100 且与既有等级不重叠。"""
|
||||
return ok(
|
||||
rating_service.create_grade(session, payload, operator=current_user, meta=meta),
|
||||
message="评级等级已创建",
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{grade_id}", response_model=ApiResponse[RatingGradeOut], summary="编辑评级等级")
|
||||
def update_grade(
|
||||
session: DbSession,
|
||||
current_user: CurrentUser,
|
||||
grade_id: Annotated[int, Path(ge=1)],
|
||||
payload: RatingGradeUpdateIn,
|
||||
meta: RequestMetaDep,
|
||||
) -> ApiResponse[RatingGradeOut]:
|
||||
"""编辑评级等级(全量替换)。区间须合法且与其他等级不重叠。"""
|
||||
return ok(
|
||||
rating_service.update_grade(session, grade_id, payload, operator=current_user, meta=meta),
|
||||
message="已保存",
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{grade_id}", response_model=ApiResponse[None], summary="删除评级等级")
|
||||
def delete_grade(
|
||||
session: DbSession,
|
||||
current_user: CurrentUser,
|
||||
grade_id: Annotated[int, Path(ge=1)],
|
||||
meta: RequestMetaDep,
|
||||
) -> ApiResponse[None]:
|
||||
"""物理删除评级等级(等级表无下游引用,允许清空,理由见 service 说明)。"""
|
||||
rating_service.delete_grade(session, grade_id, operator=current_user, meta=meta)
|
||||
return ok(message="评级等级已删除")
|
||||
在新工单中引用
屏蔽一个用户