test(base): 基础数据管理模块集成用例(48 例)与审计盲区修复
- 覆盖:401/引用校验/删除保护/子表全量替换/类别与组合唯一/归属变更留痕/审计双记录 - 缺陷修复:personnel/product/customer 三 service 的前置校验移入审计上下文, 校验失败(400)同样留痕(成功与失败都记的既定原则) - conftest 兜底清理扩展到 M2 五表(留痕无外键,按 operator_name + 前缀双轨清理) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
15de81dd2c
commit
ca88ee7a0b
12 files changed
+1090
-29
No files matched your search
@@ -166,10 +166,6 @@ def create_customer(
|
||||
:raises BadRequestError: 客户分类不合法 / 归属人员不存在或已离职 /
|
||||
「公司名称+负责人」已存在(含并发兜底)
|
||||
"""
|
||||
_ensure_customer_type(payload.customer_type)
|
||||
owner_name: str | None = None
|
||||
if payload.owner_personnel_id is not None:
|
||||
owner_name = _ensure_owner_personnel(session, payload.owner_personnel_id).personnel_name
|
||||
with audit_service.audit(
|
||||
session,
|
||||
operator=operator,
|
||||
@@ -181,6 +177,12 @@ def create_customer(
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
# 值域与归属校验在审计上下文内:失败(400)也要留痕 ——
|
||||
# 与 personnel_service._validate_refs 移入上下文的口径一致
|
||||
_ensure_customer_type(payload.customer_type)
|
||||
owner_name: str | None = None
|
||||
if payload.owner_personnel_id is not None:
|
||||
owner_name = _ensure_owner_personnel(session, payload.owner_personnel_id).personnel_name
|
||||
if (
|
||||
customer_repository.get_by_company_owner(
|
||||
session, payload.company_name, payload.owner_name
|
||||
@@ -233,7 +235,6 @@ def update_customer(
|
||||
:raises NotFoundError: 客户不存在
|
||||
:raises BadRequestError: 客户分类不合法 / 「公司名称+负责人」已存在(含并发兜底)
|
||||
"""
|
||||
_ensure_customer_type(payload.customer_type)
|
||||
# 只读预取在审计上下文之前:目标不存在(NotFound 失败)时也能留下可读记录,
|
||||
# 摘要与回包都要用客户行;归属姓名本端点不变更,一并带出
|
||||
row = customer_repository.get_with_owner_name(session, customer_id)
|
||||
@@ -249,6 +250,8 @@ def update_customer(
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
# 值域校验在审计上下文内:分类非法的 400 也要留痕(同 create 口径)
|
||||
_ensure_customer_type(payload.customer_type)
|
||||
if row is None:
|
||||
raise NotFoundError("客户不存在")
|
||||
customer, owner_name = row
|
||||
|
||||
@@ -67,7 +67,10 @@ def list_personnel(session: Session, query: PersonnelQuery) -> PageResult[Person
|
||||
business_role_id=query.business_role_id,
|
||||
)
|
||||
return PageResult[PersonnelOut](
|
||||
records=[_to_out(personnel, role_name, manager_name) for personnel, role_name, manager_name in rows],
|
||||
records=[
|
||||
_to_out(personnel, role_name, manager_name)
|
||||
for personnel, role_name, manager_name in rows
|
||||
],
|
||||
total=total,
|
||||
page_num=query.page_num,
|
||||
page_size=query.page_size,
|
||||
@@ -107,7 +110,6 @@ def create_personnel(
|
||||
|
||||
:raises BadRequestError: 岗位不存在或已停用 / 直属经理不合法
|
||||
"""
|
||||
role, manager_name = _validate_refs(session, payload.business_role_id, payload.manager_id, None)
|
||||
with audit_service.audit(
|
||||
session,
|
||||
operator=operator,
|
||||
@@ -119,6 +121,12 @@ def create_personnel(
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
# 引用校验放在审计上下文内:失败同样要留痕(与 update_personnel 口径
|
||||
# 一致)——「谁在何时试图把人挂到停用岗位/离职经理上」是审计对象,
|
||||
# 校验留在上下文外会让这类失败操作无据可查
|
||||
role, manager_name = _validate_refs(
|
||||
session, payload.business_role_id, payload.manager_id, None
|
||||
)
|
||||
personnel = Personnel(
|
||||
personnel_name=payload.personnel_name,
|
||||
business_role_id=payload.business_role_id,
|
||||
@@ -221,10 +229,9 @@ def delete_personnel(
|
||||
raise NotFoundError("人员不存在")
|
||||
|
||||
ids = [personnel_id]
|
||||
if (
|
||||
personnel_repository.count_team_members(session, ids)
|
||||
or personnel_repository.count_customer_ownerships(session, ids)
|
||||
):
|
||||
if personnel_repository.count_team_members(
|
||||
session, ids
|
||||
) or personnel_repository.count_customer_ownerships(session, ids):
|
||||
raise BadRequestError(_DELETE_BLOCKED)
|
||||
|
||||
name = personnel.personnel_name # 固化:对象随删除失效
|
||||
@@ -302,7 +309,9 @@ def replace_regions(
|
||||
if row is None:
|
||||
raise NotFoundError("人员不存在")
|
||||
rows = [
|
||||
PersonnelRegion(personnel_id=personnel_id, region_name=item.region_name, remark=item.remark)
|
||||
PersonnelRegion(
|
||||
personnel_id=personnel_id, region_name=item.region_name, remark=item.remark
|
||||
)
|
||||
for item in payload.items
|
||||
]
|
||||
personnel_repository.replace_regions(session, personnel_id, rows)
|
||||
|
||||
@@ -146,7 +146,6 @@ def create_product(
|
||||
|
||||
:raises BadRequestError: 产品类别不合法 / 已存在同名称同规格的产品
|
||||
"""
|
||||
_ensure_category(payload.product_category)
|
||||
with audit_service.audit(
|
||||
session,
|
||||
operator=operator,
|
||||
@@ -158,8 +157,13 @@ def create_product(
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
# 值域校验在审计上下文内:类别非法的 400 也要留痕(成功与失败都记)
|
||||
_ensure_category(payload.product_category)
|
||||
spec_model = payload.spec_model or "" # 归一空串:组合唯一键对 NULL 不去重
|
||||
if product_repository.get_by_name_spec(session, payload.product_name, spec_model) is not None:
|
||||
if (
|
||||
product_repository.get_by_name_spec(session, payload.product_name, spec_model)
|
||||
is not None
|
||||
):
|
||||
raise BadRequestError("已存在同名称同规格的产品")
|
||||
|
||||
product = Product(
|
||||
@@ -182,9 +186,7 @@ def create_product(
|
||||
session.rollback()
|
||||
_raise_if_duplicate(exc)
|
||||
|
||||
logger.info(
|
||||
"新增产品 | product_id=%s | category=%s", product.id, payload.product_category
|
||||
)
|
||||
logger.info("新增产品 | product_id=%s | category=%s", product.id, payload.product_category)
|
||||
return out
|
||||
|
||||
|
||||
@@ -202,7 +204,6 @@ def update_product(
|
||||
:raises BadRequestError: 产品类别不合法 / 已存在同名称同规格的产品
|
||||
"""
|
||||
product = product_repository.get_by_id(session, product_id)
|
||||
_ensure_category(payload.product_category)
|
||||
with audit_service.audit(
|
||||
session,
|
||||
operator=operator,
|
||||
@@ -214,6 +215,8 @@ def update_product(
|
||||
),
|
||||
meta=meta,
|
||||
):
|
||||
# 值域校验在审计上下文内:类别非法的 400 也要留痕(同 create 口径)
|
||||
_ensure_category(payload.product_category)
|
||||
if product is None:
|
||||
raise NotFoundError("产品不存在")
|
||||
|
||||
|
||||
Reference in new issue
Block a user