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
@@ -376,3 +376,73 @@ class TestAudit:
|
||||
latest = logs[0]
|
||||
assert customer["company_name"] in latest.summary, "摘要应含客户名"
|
||||
assert "→" in latest.summary, "摘要应体现新旧归属走向"
|
||||
|
||||
|
||||
class TestRating:
|
||||
"""评级保存与列表排序 —— 评分是档案属性,只存总分与评级时间。
|
||||
|
||||
守的是:评分只走专属端点(编辑端点不含评分,旁路写评分会绕开口径);
|
||||
分值红线 0-100 在入口拦截;按评分排序时未评分客户必须垫底,不能因
|
||||
MySQL 把 NULL 视为最小值而在升序里压过打了低分的客户。
|
||||
"""
|
||||
|
||||
def test_保存评级_回显总分与时间_且编辑端点不含评分(
|
||||
self, client: TestClient, normal: dict
|
||||
) -> None:
|
||||
headers = normal["headers"]
|
||||
customer = _create_customer(client, headers)
|
||||
assert customer["rating_score"] is None, "新客户应无评分"
|
||||
|
||||
resp = client.put(
|
||||
f"{_CUSTOMERS}/{customer['id']}/rating", json={"score": 88}, headers=headers
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
body = resp.json()["data"]
|
||||
assert body["rating_score"] == 88
|
||||
assert body["rating_time"] is not None, "保存评分必须刷新评级时间"
|
||||
|
||||
# 覆盖式保存:新分数替换旧分数
|
||||
resp = client.put(
|
||||
f"{_CUSTOMERS}/{customer['id']}/rating", json={"score": 60}, headers=headers
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["data"]["rating_score"] == 60
|
||||
|
||||
def test_评分越界_400(self, client: TestClient, normal: dict) -> None:
|
||||
headers = normal["headers"]
|
||||
customer = _create_customer(client, headers)
|
||||
resp = client.put(
|
||||
f"{_CUSTOMERS}/{customer['id']}/rating", json={"score": 101}, headers=headers
|
||||
)
|
||||
assert resp.status_code == 400, resp.text
|
||||
|
||||
def test_按评分排序_未评分垫底_升序降序都不压过低分(
|
||||
self, client: TestClient, normal: dict
|
||||
) -> None:
|
||||
headers = normal["headers"]
|
||||
high = _create_customer(client, headers)
|
||||
low = _create_customer(client, headers)
|
||||
unrated = _create_customer(client, headers)
|
||||
for target, score in ((high, 80), (low, 60)):
|
||||
resp = client.put(
|
||||
f"{_CUSTOMERS}/{target['id']}/rating", json={"score": score}, headers=headers
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
|
||||
def _order_names(order: str) -> list[str]:
|
||||
resp = client.get(
|
||||
_CUSTOMERS,
|
||||
params={"pageNum": 1, "pageSize": 50, "order_by": "rating_score", "order": order},
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
names = [row["company_name"] for row in resp.json()["data"]["list"]]
|
||||
return names
|
||||
|
||||
# 降序:高分在前,未评分最后;升序:低分在前,未评分仍垫底
|
||||
desc_names = _order_names("desc")
|
||||
assert desc_names.index(high["company_name"]) < desc_names.index(low["company_name"])
|
||||
assert desc_names[-1] == unrated["company_name"]
|
||||
asc_names = _order_names("asc")
|
||||
assert asc_names.index(low["company_name"]) < asc_names.index(high["company_name"])
|
||||
assert asc_names[-1] == unrated["company_name"], "未评分必须垫底"
|
||||
Reference in new issue
Block a user