feat(customer): 客户评级并入客户管理并落库
- customers 表新增 rating_score/rating_time(V13 迁移 + V10 SQL + init-dev 同步)
- 新端点 PUT /customers/{id}/rating:评分是档案属性,只存总分与评级时间(五项明细不落库)
- 列表排序 order_by=rating_score,未评分客户升序/降序都垫底,id desc 兜底稳定分页
- 前端 RatingCalculatorDialog 并入客户管理(评分列 + 评级计算按钮),删除独立页与路由
- 测试:评级保存/越界/排序垫底 3 例,test_customers.py 18 passed;schema 同步 216 passed
This commit is contained in:
1 parent
b9781f7d3f
commit
32b7940067
17 files changed
+917
-512
No files matched your search
@@ -15,6 +15,7 @@
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Row
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
@@ -38,6 +39,7 @@ from app.schemas.customer import (
|
||||
CustomerOut,
|
||||
CustomerOwnershipIn,
|
||||
CustomerQuery,
|
||||
CustomerRatingIn,
|
||||
CustomerUpdateIn,
|
||||
OwnershipChangeLogOut,
|
||||
)
|
||||
@@ -61,6 +63,8 @@ def _to_out(customer: Customer, owner_name: str | None) -> CustomerOut:
|
||||
owner_personnel_id=customer.owner_personnel_id,
|
||||
owner_personnel_name=owner_name,
|
||||
remark=customer.remark,
|
||||
rating_score=customer.rating_score,
|
||||
rating_time=customer.rating_time,
|
||||
)
|
||||
|
||||
|
||||
@@ -263,6 +267,53 @@ def update_customer(
|
||||
return out
|
||||
|
||||
|
||||
def rate_customer(
|
||||
session: Session,
|
||||
customer_id: int,
|
||||
payload: CustomerRatingIn,
|
||||
*,
|
||||
operator: User,
|
||||
meta: RequestMeta | None = None,
|
||||
) -> CustomerOut:
|
||||
"""保存客户评级(评级计算器「保存评分」)。
|
||||
|
||||
评分是客户档案属性而非审计事件:只存总分与评级时间,五项明细与所用
|
||||
等级配置不落库(对照表 #25 的定位口径)——等级映射是可变判定表,回溯
|
||||
「当时命中的等级」需要快照设计,超出档案属性的范围;评级时间已足够
|
||||
回答「这个分是什么时候打的」。
|
||||
|
||||
:raises NotFoundError: 客户不存在
|
||||
"""
|
||||
# 客户名进审计摘要,预取放在审计上下文之前(同 change_ownership 口径)
|
||||
row = customer_repository.get_with_owner_name(session, customer_id)
|
||||
customer_name = row[0].company_name if row else f"#{customer_id}"
|
||||
with audit_service.audit(
|
||||
session,
|
||||
operator=operator,
|
||||
record=AuditRecord(
|
||||
module=MODULE_CUSTOMER,
|
||||
action=ACTION_UPDATE,
|
||||
summary=f"保存客户评级 {customer_name}:{payload.score} 分",
|
||||
request_data=payload.model_dump(),
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
if row is None:
|
||||
raise NotFoundError("客户不存在")
|
||||
customer, owner_name = row
|
||||
|
||||
customer.rating_score = payload.score
|
||||
# 评级时间取应用层本地时间(库列 TZ=Asia/Shanghai,与 TimestampMixin
|
||||
# 的服务器默认值同一时区口径),不用数据库 NOW():行已在本会话
|
||||
customer.rating_time = datetime.now()
|
||||
|
||||
# 响应在 commit 之前构造(同 user_service 顶部说明)
|
||||
out = _to_out(customer, owner_name)
|
||||
session.commit()
|
||||
logger.info("保存客户评级 | customer_id=%s | score=%s", customer_id, payload.score)
|
||||
return out
|
||||
|
||||
|
||||
def delete_customer(
|
||||
session: Session,
|
||||
customer_id: int,
|
||||
|
||||
Reference in new issue
Block a user