refactor(repos): 归属留痕共享数据访问,人员在职校验收口单点
- ownership_log_repository:客户/业务两侧逐行同构的 list/count/add 收口(owner_type 与引用列参数化),双键倒序与被删人员兜底口径 单点维护;两侧 repository 变薄包装,service 层零改动 - PERSONNEL_STATUS_ACTIVE 单点导出于 models/personnel(对照 BUSINESS_STATUS_* 先例);业务/客户两侧的「存在且在职」校验收口 于 personnel_service.ensure_active——此前两份常量+校验仅靠注释 维持同步,在职判定调整时会两入口口径分叉;校验放 service 层遵守 「repositories 不抛业务异常」红线 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
409e26614f
commit
60f6af6494
7 files changed
+119
-85
No files matched your search
@@ -49,7 +49,7 @@ from app.models.business import (
|
||||
BUSINESS_STATUS_DONE,
|
||||
)
|
||||
from app.models.customer import OWNER_TYPE_BUSINESS
|
||||
from app.repositories import business_repository, customer_repository, personnel_repository
|
||||
from app.repositories import business_repository, customer_repository
|
||||
from app.schemas.business import (
|
||||
BusinessCreateIn,
|
||||
BusinessDetailOut,
|
||||
@@ -66,7 +66,7 @@ from app.schemas.business import (
|
||||
GrossProfitRowOut,
|
||||
OwnershipChangeLogOut,
|
||||
)
|
||||
from app.services import audit_service, param_service
|
||||
from app.services import audit_service, param_service, personnel_service
|
||||
from app.services.audit_service import AuditRecord, RequestMeta
|
||||
from app.services.formula import converted_profit, profit_rate
|
||||
from app.services.no_generator import next_no
|
||||
@@ -77,9 +77,6 @@ logger = logging.getLogger(__name__)
|
||||
# 重试是为极端场景(锁等待超时后重入)兜底 —— 无限重试反而掩盖死锁类缺陷
|
||||
_NO_RETRY = 1
|
||||
|
||||
# personnel.status 的在职值(与 customer_service 同口径,注释见彼处)
|
||||
_PERSONNEL_STATUS_ACTIVE = 1
|
||||
|
||||
|
||||
def _to_out(row: tuple) -> BusinessOut:
|
||||
"""(business, company_name, owner_name, assistant_name) → 响应模型。
|
||||
@@ -214,11 +211,8 @@ def _generate_no(session: Session) -> str:
|
||||
|
||||
|
||||
def _ensure_personnel_active(session: Session, personnel_id: int, label: str) -> Personnel:
|
||||
"""归属人员须存在且在职(人员侧复用校验文案口径)。"""
|
||||
person = personnel_repository.get_by_id(session, personnel_id)
|
||||
if person is None or person.status != _PERSONNEL_STATUS_ACTIVE:
|
||||
raise BadRequestError(f"{label}人员不存在或已离职")
|
||||
return person
|
||||
"""归属人员须存在且在职(共享校验收口于 personnel_service.ensure_active)。"""
|
||||
return personnel_service.ensure_active(session, personnel_id, label=label)
|
||||
|
||||
|
||||
def _ensure_customer(session: Session, customer_id: int) -> Customer:
|
||||
|
||||
@@ -40,15 +40,11 @@ from app.schemas.customer import (
|
||||
CustomerUpdateIn,
|
||||
OwnershipChangeLogOut,
|
||||
)
|
||||
from app.services import audit_service
|
||||
from app.services import audit_service, personnel_service
|
||||
from app.services.audit_service import AuditRecord, RequestMeta
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# personnel.status 的在职值(列注释口径:1在职 0离职)。离职人员不可再被
|
||||
# 客户归属引用;人员侧抽出共享常量后此处替换
|
||||
_PERSONNEL_STATUS_ACTIVE = 1
|
||||
|
||||
|
||||
def _to_out(customer: Customer, owner_name: str | None) -> CustomerOut:
|
||||
"""ORM 对象转响应模型。禁止把 ORM 对象直接当 response_model。"""
|
||||
@@ -95,17 +91,8 @@ def _ensure_customer_type(customer_type: str) -> None:
|
||||
|
||||
|
||||
def _ensure_owner_personnel(session: Session, personnel_id: int) -> Personnel:
|
||||
"""归属人员必须存在且在职,返回人员行。
|
||||
|
||||
「不存在」与「已离职」合并为同一文案:人员 ID 是否有效不构成需要
|
||||
向调用方区分的信息(同登录防枚举的思路)。
|
||||
|
||||
:raises BadRequestError: 归属人员不存在或已离职
|
||||
"""
|
||||
person = personnel_repository.get_by_id(session, personnel_id)
|
||||
if person is None or person.status != _PERSONNEL_STATUS_ACTIVE:
|
||||
raise BadRequestError("归属人员不存在或已离职")
|
||||
return person
|
||||
"""归属人员必须存在且在职(共享校验收口于 personnel_service.ensure_active)。"""
|
||||
return personnel_service.ensure_active(session, personnel_id, label="归属")
|
||||
|
||||
|
||||
def _is_duplicate_entry(exc: IntegrityError) -> bool:
|
||||
|
||||
@@ -30,6 +30,7 @@ from app.core.constants import (
|
||||
from app.core.exceptions import BadRequestError, NotFoundError
|
||||
from app.core.response import PageResult
|
||||
from app.models import BusinessRole, Personnel, PersonnelPlan, PersonnelRegion, User
|
||||
from app.models.personnel import PERSONNEL_STATUS_ACTIVE
|
||||
from app.repositories import business_role_repository, personnel_repository
|
||||
from app.schemas.personnel import (
|
||||
PersonnelCreateIn,
|
||||
@@ -51,6 +52,22 @@ logger = logging.getLogger(__name__)
|
||||
_DELETE_BLOCKED = "存在团队成员、客户归属或业务归属引用,请改为离职而非删除"
|
||||
|
||||
|
||||
def ensure_active(session: Session, personnel_id: int, *, label: str) -> Personnel:
|
||||
"""归属人员必须存在且在职,返回人员行(业务/客户两侧归属校验共用)。
|
||||
|
||||
「不存在」与「已离职」合并为同一文案:人员 ID 是否有效不构成需要向
|
||||
调用方区分的信息(同登录防枚举的思路);label 落文案(负责/协助/归属)。
|
||||
收口单点:在职状态值取 models.personnel.PERSONNEL_STATUS_ACTIVE,
|
||||
此前 business/customer 两 service 各持常量与校验,仅靠注释维持同步。
|
||||
|
||||
:raises BadRequestError: {label}人员不存在或已离职
|
||||
"""
|
||||
person = personnel_repository.get_by_id(session, personnel_id)
|
||||
if person is None or person.status != PERSONNEL_STATUS_ACTIVE:
|
||||
raise BadRequestError(f"{label}人员不存在或已离职")
|
||||
return person
|
||||
|
||||
|
||||
def list_personnel(session: Session, query: PersonnelQuery) -> PageResult[PersonnelOut]:
|
||||
"""分页查询人员(含离职)。"""
|
||||
rows = personnel_repository.list_personnel(
|
||||
|
||||
Reference in new issue
Block a user