refactor: region from document, not app config

- Add region column to leaudit_documents + LeauditDocument model
- AuditServiceImpl: read region from document.region, not APP_REGION
- RuleServiceImpl: ListBindings/CreateBinding accept Region parameter
- RuleBindingCreateDTO: add region field
- RuleController: pass region from query param/DTO to service
- APP_REGION removed from binding queries; region flows from document

Region is now per-document: each document carries its region at upload
time, and rules are matched to the document's region at run time.
This commit is contained in:
wren
2026-04-28 14:19:29 +08:00
parent e80e8febd8
commit c776af598a
6 changed files with 65 additions and 15 deletions
@@ -89,9 +89,9 @@ class RuleController(BaseController):
# ── 规则类型绑定 ──────────────────────────────────────────
@self.router.get("/bindings", response_model=Result[list[RuleBindingVO]])
async def ListBindings(ruleType: str | None = None):
"""列出规则类型绑定。可按规则类型过滤。"""
Data = await self.RuleService.ListBindings(RuleType=ruleType)
async def ListBindings(ruleType: str | None = None, region: str | None = None):
"""列出规则类型绑定。可按规则类型/地区过滤。"""
Data = await self.RuleService.ListBindings(RuleType=ruleType, Region=region)
return Result.success(data=Data)
@self.router.post("/{RuleType}/bindings", response_model=Result[RuleBindingVO])
@@ -100,6 +100,7 @@ class RuleController(BaseController):
Data = await self.RuleService.CreateBinding(
DocTypeId=body.docTypeId,
RuleSetId=body.ruleSetId,
Region=body.region,
BindingMode=body.bindingMode,
Priority=body.priority,
DocTypeCode=body.docTypeCode,
@@ -9,6 +9,7 @@ class RuleBindingCreateDTO(BaseModel):
docTypeId: int = Field(..., description="文档类型ID → leaudit_document_types.id")
docTypeCode: str | None = Field(None, description="文档类型编码(冗余快速匹配)")
ruleSetId: int = Field(..., description="规则集ID → leaudit_rule_sets.id")
region: str = Field("default", description="适用地区")
bindingMode: str = Field("explicit", description="绑定模式: explicit / wildcard / fallback")
priority: int = Field(0, description="优先级(数值越大优先级越高)")
note: str | None = Field(None, description="备注说明")
@@ -22,6 +22,7 @@ class LeauditDocument(BaseModel):
typeId: Mapped[int | None] = mapped_column(BigInteger, comment="文档类型ID")
processingStatus: Mapped[str | None] = mapped_column(String(64), default="waiting", comment="waiting/processing/completed/failed")
currentRunId: Mapped[int | None] = mapped_column(BigInteger, comment="最新有效 run id")
region: Mapped[str] = mapped_column(String(32), default="default", comment="所属地区: mz/yf/jy/cz/default")
@classmethod
async def get_by_biz_id(cls, session: AsyncSession, bizDocumentId: int) -> "LeauditDocument | None":
@@ -6,7 +6,6 @@
from datetime import datetime
from fastapi_admin.config import APP_REGION
from fastapi_common.fastapi_common_logger import logger
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
@@ -78,7 +77,7 @@ class AuditServiceImpl(IAuditService):
LIMIT 1
"""
),
{"doc_type_id": document.typeId, "region": APP_REGION},
{"doc_type_id": document.typeId, "region": document.region},
)
binding = bindingResult.mappings().first()
if not binding or not binding["rule_set_id"] or not binding["rule_version_id"]:
@@ -3,7 +3,6 @@
from __future__ import annotations
import hashlib
from fastapi_admin.config import APP_REGION
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
from fastapi_common.fastapi_common_web.exception.LeauditException import LeauditException
@@ -341,10 +340,11 @@ class RuleServiceImpl(IRuleService):
Rollback=True,
)
async def ListBindings(self, RuleType: str | None = None) -> list[RuleBindingVO]:
"""列出规则类型绑定,可按规则类型过滤。"""
async def ListBindings(self, RuleType: str | None = None, Region: str | None = None) -> list[RuleBindingVO]:
"""列出规则类型绑定,可按规则类型/地区过滤。"""
region = Region or ""
async with GetAsyncSession() as Session:
if RuleType:
if RuleType and region:
Result = await Session.execute(
text(
"""
@@ -367,9 +367,9 @@ class RuleServiceImpl(IRuleService):
ORDER BY b.priority DESC, b.id DESC
"""
),
{"rule_type": RuleType, "region": APP_REGION},
{"rule_type": RuleType, "region": region},
)
else:
elif region:
Result = await Session.execute(
text(
"""
@@ -391,7 +391,53 @@ class RuleServiceImpl(IRuleService):
ORDER BY rs.rule_type, b.priority DESC, b.id DESC
"""
),
{"region": APP_REGION},
{"region": region},
)
elif RuleType:
Result = await Session.execute(
text(
"""
SELECT
b.id,
b.doc_type_id,
b.doc_type_code,
b.rule_set_id,
b.binding_mode,
b.priority,
b.is_active,
b.note,
rs.rule_type,
rs.rule_name
FROM leaudit_rule_type_bindings b
JOIN leaudit_rule_sets rs ON rs.id = b.rule_set_id
WHERE rs.rule_type = :rule_type
AND rs.deleted_at IS NULL
ORDER BY b.priority DESC, b.id DESC
"""
),
{"rule_type": RuleType},
)
else:
Result = await Session.execute(
text(
"""
SELECT
b.id,
b.doc_type_id,
b.doc_type_code,
b.rule_set_id,
b.binding_mode,
b.priority,
b.is_active,
b.note,
rs.rule_type,
rs.rule_name
FROM leaudit_rule_type_bindings b
JOIN leaudit_rule_sets rs ON rs.id = b.rule_set_id
WHERE rs.deleted_at IS NULL
ORDER BY rs.rule_type, b.priority DESC, b.id DESC
"""
),
)
return [
RuleBindingVO(
@@ -413,6 +459,7 @@ class RuleServiceImpl(IRuleService):
self,
DocTypeId: int,
RuleSetId: int,
Region: str = "default",
BindingMode: str = "explicit",
Priority: int = 0,
DocTypeCode: str | None = None,
@@ -473,7 +520,7 @@ class RuleServiceImpl(IRuleService):
"rule_set_id": RuleSetId,
"binding_mode": BindingMode,
"priority": Priority,
"region": APP_REGION,
"region": Region,
"note": Note,
},
)
@@ -66,8 +66,8 @@ class IRuleService(ABC):
...
@abstractmethod
async def ListBindings(self, RuleType: str | None = None) -> list[RuleBindingVO]:
"""列出规则类型绑定。可按规则类型过滤。"""
async def ListBindings(self, RuleType: str | None = None, Region: str | None = None) -> list[RuleBindingVO]:
"""列出规则类型绑定。可按规则类型/地区过滤。"""
...
@abstractmethod
@@ -75,6 +75,7 @@ class IRuleService(ABC):
self,
DocTypeId: int,
RuleSetId: int,
Region: str = "default",
BindingMode: str = "explicit",
Priority: int = 0,
DocTypeCode: str | None = None,