docs: add fix-double-finalize-and-bindings-api implementation plan
This commit is contained in:
@@ -0,0 +1,683 @@
|
||||
# Fix Double Finalize + Rule Type Bindings API Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Fix two blocking issues: (1) eliminate the duplicate `result_status` / `finished_at` write in `save_evaluation_results`, and (2) add full CRUD API for the `leaudit_rule_type_bindings` table.
|
||||
|
||||
**Architecture:** Fix 1 is a one-line removal in `storage_adapter.py` — strip the premature run summary UPDATE from `save_evaluation_results` so `finalize_run` is the single source of truth for terminal state. Fix 2 follows the existing RuleController → IRuleService → RuleServiceImpl layered pattern, adding DTO/VO types and 4 endpoints for binding management.
|
||||
|
||||
**Tech Stack:** Python, FastAPI, SQLAlchemy async, PostgreSQL
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|---|---|---|
|
||||
| `fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py` | Modify | Remove premature UPDATE from `save_evaluation_results` |
|
||||
| `fastapi_modules/fastapi_leaudit/domian/Dto/ruleBindingDto.py` | Create | `RuleBindingCreateDTO`, `RuleBindingUpdateDTO` |
|
||||
| `fastapi_modules/fastapi_leaudit/domian/vo/ruleVo.py` | Modify | Add `RuleBindingVO` |
|
||||
| `fastapi_modules/fastapi_leaudit/services/ruleService.py` | Modify | Add 4 abstract methods |
|
||||
| `fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py` | Modify | Add 4 method implementations |
|
||||
| `fastapi_modules/fastapi_leaudit/controllers/ruleController.py` | Modify | Add 4 endpoints |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Fix Double Finalize — Strip Premature UPDATE from save_evaluation_results
|
||||
|
||||
**Files:**
|
||||
- Modify: `fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py:167-181`
|
||||
|
||||
- [ ] **Step 1: Remove result_status and finished_at from the UPDATE clause**
|
||||
|
||||
Replace lines 167-181 of `storage_adapter.py`:
|
||||
|
||||
```python
|
||||
# Update audit_runs summary
|
||||
await session.execute(
|
||||
text("""UPDATE leaudit_audit_runs SET
|
||||
total_score = :ts, passed_count = :pc, failed_count = :fc,
|
||||
skipped_count = :sc, result_status = :rs, finished_at = now(), update_time = now()
|
||||
WHERE id = :rid"""),
|
||||
{
|
||||
"ts": evaluation.total_score,
|
||||
"pc": evaluation.passed_count,
|
||||
"fc": evaluation.failed_count,
|
||||
"sc": evaluation.skipped_count,
|
||||
"rs": "pass" if evaluation.failed_count == 0 else "fail",
|
||||
"rid": resolved_run_id,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
With:
|
||||
|
||||
```python
|
||||
# Update audit_runs summary (scores only — terminal state set by finalize_run)
|
||||
await session.execute(
|
||||
text("""UPDATE leaudit_audit_runs SET
|
||||
total_score = :ts, passed_count = :pc, failed_count = :fc,
|
||||
skipped_count = :sc, update_time = now()
|
||||
WHERE id = :rid"""),
|
||||
{
|
||||
"ts": evaluation.total_score,
|
||||
"pc": evaluation.passed_count,
|
||||
"fc": evaluation.failed_count,
|
||||
"sc": evaluation.skipped_count,
|
||||
"rid": resolved_run_id,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify finalize_run is still the last writer in persist_result**
|
||||
|
||||
Read `nativeRunner.py:149-157` to confirm `finalize_run` runs after all other persist steps, including `save_evaluation_results`. The order is:
|
||||
|
||||
```
|
||||
save_ocr_result → save_extraction_result → save_evaluation_results → save_run_errors → save_rescue_outcomes → save_run_metrics → finalize_run
|
||||
```
|
||||
|
||||
Confirmed: `finalize_run` is the LAST call in `persist_result()`, so it will always set the definitive terminal state.
|
||||
|
||||
- [ ] **Step 3: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py
|
||||
```
|
||||
Expected: Compile successful, no errors.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py
|
||||
git commit -m "fix: remove premature result_status/finished_at from save_evaluation_results
|
||||
|
||||
finalize_run() is the single source of truth for terminal run state.
|
||||
Previously save_evaluation_results wrote a binary pass/fail status and
|
||||
finished_at BEFORE rescue outcomes/metrics were saved, then finalize_run
|
||||
overwrote it. Now scores only are written here; terminal state is set
|
||||
once by finalize_run after all sub-results are persisted."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create RuleBinding DTOs
|
||||
|
||||
**Files:**
|
||||
- Create: `fastapi_modules/fastapi_leaudit/domian/Dto/ruleBindingDto.py`
|
||||
|
||||
- [ ] **Step 1: Create the DTO file**
|
||||
|
||||
```python
|
||||
"""规则类型绑定 DTO。"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
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")
|
||||
bindingMode: str = Field("explicit", description="绑定模式: explicit / wildcard / fallback")
|
||||
priority: int = Field(0, description="优先级(数值越大优先级越高)")
|
||||
note: str | None = Field(None, description="备注说明")
|
||||
|
||||
|
||||
class RuleBindingUpdateDTO(BaseModel):
|
||||
"""更新规则类型绑定请求。"""
|
||||
|
||||
isActive: bool | None = Field(None, description="是否激活")
|
||||
priority: int | None = Field(None, description="优先级")
|
||||
bindingMode: str | None = Field(None, description="绑定模式")
|
||||
note: str | None = Field(None, description="备注说明")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/domian/Dto/ruleBindingDto.py
|
||||
```
|
||||
Expected: Compile successful.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/domian/Dto/ruleBindingDto.py
|
||||
git commit -m "feat: add RuleBindingCreateDTO and RuleBindingUpdateDTO"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add RuleBindingVO to ruleVo.py
|
||||
|
||||
**Files:**
|
||||
- Modify: `fastapi_modules/fastapi_leaudit/domian/vo/ruleVo.py`
|
||||
|
||||
- [ ] **Step 1: Append RuleBindingVO class at end of file**
|
||||
|
||||
Add after line 49 (after the `RuleValidationVO` class):
|
||||
|
||||
```python
|
||||
|
||||
|
||||
class RuleBindingVO(BaseModel):
|
||||
"""规则类型绑定响应。"""
|
||||
|
||||
id: int = Field(..., description="绑定ID")
|
||||
docTypeId: int = Field(..., description="文档类型ID")
|
||||
docTypeCode: str | None = Field(None, description="文档类型编码")
|
||||
ruleSetId: int = Field(..., description="规则集ID")
|
||||
ruleType: str | None = Field(None, description="规则类型编码(来自关联查询)")
|
||||
ruleName: str | None = Field(None, description="规则集名称(来自关联查询)")
|
||||
bindingMode: str = Field(..., description="绑定模式: explicit / wildcard / fallback")
|
||||
priority: int = Field(0, description="优先级")
|
||||
isActive: bool = Field(True, description="是否激活")
|
||||
note: str | None = Field(None, description="备注说明")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/domian/vo/ruleVo.py
|
||||
```
|
||||
Expected: Compile successful.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/domian/vo/ruleVo.py
|
||||
git commit -m "feat: add RuleBindingVO for rule type bindings response"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Add Binding Methods to IRuleService Interface
|
||||
|
||||
**Files:**
|
||||
- Modify: `fastapi_modules/fastapi_leaudit/services/ruleService.py`
|
||||
|
||||
- [ ] **Step 1: Add import for RuleBindingVO at top of file**
|
||||
|
||||
Add `RuleBindingVO` to the existing import block (line 5-10):
|
||||
|
||||
```python
|
||||
from fastapi_modules.fastapi_leaudit.domian.vo.ruleVo import (
|
||||
RuleBindingVO,
|
||||
RuleContentVO,
|
||||
RuleSetVO,
|
||||
RuleValidationVO,
|
||||
RuleVersionVO,
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add 4 abstract methods before the closing of the class**
|
||||
|
||||
Add after the `Rollback` method (before the last blank line of the class):
|
||||
|
||||
```python
|
||||
@abstractmethod
|
||||
async def ListBindings(self, RuleType: str | None = None) -> list[RuleBindingVO]:
|
||||
"""列出规则类型绑定。可按规则类型过滤。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def CreateBinding(
|
||||
self,
|
||||
DocTypeId: int,
|
||||
RuleSetId: int,
|
||||
BindingMode: str = "explicit",
|
||||
Priority: int = 0,
|
||||
DocTypeCode: str | None = None,
|
||||
Note: str | None = None,
|
||||
) -> RuleBindingVO:
|
||||
"""创建规则类型绑定。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def UpdateBinding(
|
||||
self,
|
||||
BindingId: int,
|
||||
IsActive: bool | None = None,
|
||||
Priority: int | None = None,
|
||||
BindingMode: str | None = None,
|
||||
Note: str | None = None,
|
||||
) -> RuleBindingVO:
|
||||
"""更新规则类型绑定。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def DeleteBinding(self, BindingId: int) -> None:
|
||||
"""删除规则类型绑定。"""
|
||||
...
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/services/ruleService.py
|
||||
```
|
||||
Expected: Compile successful.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/services/ruleService.py
|
||||
git commit -m "feat: add binding CRUD methods to IRuleService interface"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Implement Binding Methods in RuleServiceImpl
|
||||
|
||||
**Files:**
|
||||
- Modify: `fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py`
|
||||
|
||||
- [ ] **Step 1: Add RuleBindingVO import**
|
||||
|
||||
Add `RuleBindingVO` to the import from `ruleVo` (line 12-17):
|
||||
|
||||
```python
|
||||
from fastapi_modules.fastapi_leaudit.domian.vo.ruleVo import (
|
||||
RuleBindingVO,
|
||||
RuleContentVO,
|
||||
RuleSetVO,
|
||||
RuleValidationVO,
|
||||
RuleVersionVO,
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add 4 method implementations after Rollback method (before _SwitchVersion)**
|
||||
|
||||
Insert before the `_SwitchVersion` method (before line 342):
|
||||
|
||||
```python
|
||||
async def ListBindings(self, RuleType: str | None = None) -> list[RuleBindingVO]:
|
||||
"""列出规则类型绑定,可按规则类型过滤。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
if 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.delete_time 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.delete_time IS NULL
|
||||
ORDER BY rs.rule_type, b.priority DESC, b.id DESC
|
||||
"""
|
||||
),
|
||||
)
|
||||
return [
|
||||
RuleBindingVO(
|
||||
id=int(Row["id"]),
|
||||
docTypeId=int(Row["doc_type_id"]),
|
||||
docTypeCode=Row["doc_type_code"],
|
||||
ruleSetId=int(Row["rule_set_id"]),
|
||||
ruleType=Row["rule_type"],
|
||||
ruleName=Row["rule_name"],
|
||||
bindingMode=Row["binding_mode"],
|
||||
priority=int(Row["priority"]),
|
||||
isActive=bool(Row["is_active"]),
|
||||
note=Row["note"],
|
||||
)
|
||||
for Row in Result.mappings().all()
|
||||
]
|
||||
|
||||
async def CreateBinding(
|
||||
self,
|
||||
DocTypeId: int,
|
||||
RuleSetId: int,
|
||||
BindingMode: str = "explicit",
|
||||
Priority: int = 0,
|
||||
DocTypeCode: str | None = None,
|
||||
Note: str | None = None,
|
||||
) -> RuleBindingVO:
|
||||
"""创建规则类型绑定。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
RuleSet = await Session.execute(
|
||||
text("SELECT id, rule_type, rule_name FROM leaudit_rule_sets WHERE id = :rid AND delete_time IS NULL LIMIT 1"),
|
||||
{"rid": RuleSetId},
|
||||
)
|
||||
if not RuleSet.mappings().first():
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "规则集不存在")
|
||||
|
||||
Existing = await Session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT id FROM leaudit_rule_type_bindings
|
||||
WHERE doc_type_id = :dtid AND rule_set_id = :rsid
|
||||
LIMIT 1
|
||||
"""
|
||||
),
|
||||
{"dtid": DocTypeId, "rsid": RuleSetId},
|
||||
)
|
||||
if Existing.mappings().first():
|
||||
raise LeauditException(StatusCodeEnum.HTTP_409_CONFLICT, "该文档类型已绑定此规则集")
|
||||
|
||||
Result = await Session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO leaudit_rule_type_bindings (
|
||||
doc_type_id,
|
||||
doc_type_code,
|
||||
rule_set_id,
|
||||
binding_mode,
|
||||
priority,
|
||||
is_active,
|
||||
note
|
||||
) VALUES (
|
||||
:doc_type_id,
|
||||
:doc_type_code,
|
||||
:rule_set_id,
|
||||
:binding_mode,
|
||||
:priority,
|
||||
true,
|
||||
:note
|
||||
)
|
||||
RETURNING id, doc_type_id, doc_type_code, rule_set_id,
|
||||
binding_mode, priority, is_active, note
|
||||
"""
|
||||
),
|
||||
{
|
||||
"doc_type_id": DocTypeId,
|
||||
"doc_type_code": DocTypeCode,
|
||||
"rule_set_id": RuleSetId,
|
||||
"binding_mode": BindingMode,
|
||||
"priority": Priority,
|
||||
"note": Note,
|
||||
},
|
||||
)
|
||||
await Session.commit()
|
||||
Row = Result.mappings().first()
|
||||
RsRow = RuleSet.mappings().first()
|
||||
return RuleBindingVO(
|
||||
id=int(Row["id"]),
|
||||
docTypeId=int(Row["doc_type_id"]),
|
||||
docTypeCode=Row["doc_type_code"],
|
||||
ruleSetId=int(Row["rule_set_id"]),
|
||||
ruleType=RsRow["rule_type"],
|
||||
ruleName=RsRow["rule_name"],
|
||||
bindingMode=Row["binding_mode"],
|
||||
priority=int(Row["priority"]),
|
||||
isActive=bool(Row["is_active"]),
|
||||
note=Row["note"],
|
||||
)
|
||||
|
||||
async def UpdateBinding(
|
||||
self,
|
||||
BindingId: int,
|
||||
IsActive: bool | None = None,
|
||||
Priority: int | None = None,
|
||||
BindingMode: str | None = None,
|
||||
Note: str | None = None,
|
||||
) -> RuleBindingVO:
|
||||
"""更新规则类型绑定。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
Existing = 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 b.id = :bid
|
||||
LIMIT 1
|
||||
"""
|
||||
),
|
||||
{"bid": BindingId},
|
||||
)
|
||||
Row = Existing.mappings().first()
|
||||
if not Row:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "绑定记录不存在")
|
||||
|
||||
SetClauses: list[str] = []
|
||||
Params: dict[str, object] = {"bid": BindingId}
|
||||
|
||||
if IsActive is not None:
|
||||
SetClauses.append("is_active = :is_active")
|
||||
Params["is_active"] = IsActive
|
||||
if Priority is not None:
|
||||
SetClauses.append("priority = :priority")
|
||||
Params["priority"] = Priority
|
||||
if BindingMode is not None:
|
||||
SetClauses.append("binding_mode = :binding_mode")
|
||||
Params["binding_mode"] = BindingMode
|
||||
if Note is not None:
|
||||
SetClauses.append("note = :note")
|
||||
Params["note"] = Note
|
||||
|
||||
if SetClauses:
|
||||
SetClauses.append("update_time = now()")
|
||||
await Session.execute(
|
||||
text(f"UPDATE leaudit_rule_type_bindings SET {', '.join(SetClauses)} WHERE id = :bid"),
|
||||
Params,
|
||||
)
|
||||
await Session.commit()
|
||||
|
||||
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 b.id = :bid
|
||||
LIMIT 1
|
||||
"""
|
||||
),
|
||||
{"bid": BindingId},
|
||||
)
|
||||
Row = Result.mappings().first()
|
||||
return RuleBindingVO(
|
||||
id=int(Row["id"]),
|
||||
docTypeId=int(Row["doc_type_id"]),
|
||||
docTypeCode=Row["doc_type_code"],
|
||||
ruleSetId=int(Row["rule_set_id"]),
|
||||
ruleType=Row["rule_type"],
|
||||
ruleName=Row["rule_name"],
|
||||
bindingMode=Row["binding_mode"],
|
||||
priority=int(Row["priority"]),
|
||||
isActive=bool(Row["is_active"]),
|
||||
note=Row["note"],
|
||||
)
|
||||
|
||||
async def DeleteBinding(self, BindingId: int) -> None:
|
||||
"""删除规则类型绑定。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
Result = await Session.execute(
|
||||
text("DELETE FROM leaudit_rule_type_bindings WHERE id = :bid"),
|
||||
{"bid": BindingId},
|
||||
)
|
||||
await Session.commit()
|
||||
if Result.rowcount == 0:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "绑定记录不存在")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py
|
||||
```
|
||||
Expected: Compile successful.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py
|
||||
git commit -m "feat: implement binding CRUD in RuleServiceImpl"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Add Binding Endpoints to RuleController
|
||||
|
||||
**Files:**
|
||||
- Modify: `fastapi_modules/fastapi_leaudit/controllers/ruleController.py`
|
||||
|
||||
- [ ] **Step 1: Add imports for new types**
|
||||
|
||||
Update the imports (lines 3-16) to include binding DTOs and VO:
|
||||
|
||||
```python
|
||||
"""规则管理控制器。"""
|
||||
|
||||
from fastapi_common.fastapi_common_web.controller import BaseController
|
||||
from fastapi_common.fastapi_common_web.domain.responses import Result
|
||||
|
||||
from fastapi_modules.fastapi_leaudit.domian.Dto.ruleBindingDto import (
|
||||
RuleBindingCreateDTO,
|
||||
RuleBindingUpdateDTO,
|
||||
)
|
||||
from fastapi_modules.fastapi_leaudit.domian.Dto.rulePublishDto import RulePublishDTO
|
||||
from fastapi_modules.fastapi_leaudit.domian.Dto.ruleValidateDto import RuleValidateDTO
|
||||
from fastapi_modules.fastapi_leaudit.domian.Dto.ruleVersionCreateDto import RuleVersionCreateDTO
|
||||
from fastapi_modules.fastapi_leaudit.domian.vo.ruleVo import (
|
||||
RuleBindingVO,
|
||||
RuleContentVO,
|
||||
RuleSetVO,
|
||||
RuleValidationVO,
|
||||
RuleVersionVO,
|
||||
)
|
||||
from fastapi_modules.fastapi_leaudit.services import IRuleService
|
||||
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import RuleServiceImpl
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add 4 endpoint definitions inside __init__**
|
||||
|
||||
Add after the `RollbackRuleVersion` endpoint (after line 82, before the closing of `__init__`):
|
||||
|
||||
```python
|
||||
# ── Rule Type Bindings ──────────────────────────────────────
|
||||
|
||||
@self.router.get("/bindings", response_model=Result[list[RuleBindingVO]])
|
||||
async def ListBindings(ruleType: str | None = None):
|
||||
"""列出规则类型绑定。可按规则类型过滤。"""
|
||||
Data = await self.RuleService.ListBindings(RuleType=ruleType)
|
||||
return Result.success(data=Data)
|
||||
|
||||
@self.router.post("/{RuleType}/bindings", response_model=Result[RuleBindingVO])
|
||||
async def CreateBinding(RuleType: str, body: RuleBindingCreateDTO):
|
||||
"""创建规则类型绑定。"""
|
||||
Data = await self.RuleService.CreateBinding(
|
||||
DocTypeId=body.docTypeId,
|
||||
RuleSetId=body.ruleSetId,
|
||||
BindingMode=body.bindingMode,
|
||||
Priority=body.priority,
|
||||
DocTypeCode=body.docTypeCode,
|
||||
Note=body.note,
|
||||
)
|
||||
return Result.success(data=Data)
|
||||
|
||||
@self.router.put("/bindings/{BindingId}", response_model=Result[RuleBindingVO])
|
||||
async def UpdateBinding(BindingId: int, body: RuleBindingUpdateDTO):
|
||||
"""更新规则类型绑定。"""
|
||||
Data = await self.RuleService.UpdateBinding(
|
||||
BindingId=BindingId,
|
||||
IsActive=body.isActive,
|
||||
Priority=body.priority,
|
||||
BindingMode=body.bindingMode,
|
||||
Note=body.note,
|
||||
)
|
||||
return Result.success(data=Data)
|
||||
|
||||
@self.router.delete("/bindings/{BindingId}", response_model=Result[None])
|
||||
async def DeleteBinding(BindingId: int):
|
||||
"""删除规则类型绑定。"""
|
||||
await self.RuleService.DeleteBinding(BindingId=BindingId)
|
||||
return Result.success()
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Syntax check**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -m compileall fastapi_modules/fastapi_leaudit/controllers/ruleController.py
|
||||
```
|
||||
Expected: Compile successful.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add fastapi_modules/fastapi_leaudit/controllers/ruleController.py
|
||||
git commit -m "feat: add rule type binding CRUD endpoints to RuleController"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Verification — Cross-Module Import Check
|
||||
|
||||
**Files:** None (verification only)
|
||||
|
||||
- [ ] **Step 1: Verify all modified modules compile together**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && python -c "
|
||||
from fastapi_modules.fastapi_leaudit.domian.Dto.ruleBindingDto import RuleBindingCreateDTO, RuleBindingUpdateDTO
|
||||
from fastapi_modules.fastapi_leaudit.domian.vo.ruleVo import RuleBindingVO
|
||||
from fastapi_modules.fastapi_leaudit.services.ruleService import IRuleService
|
||||
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import RuleServiceImpl
|
||||
from fastapi_modules.fastapi_leaudit.leaudit_bridge.storage_adapter import StorageAdapter
|
||||
print('All imports OK')
|
||||
"
|
||||
```
|
||||
Expected: `All imports OK`
|
||||
|
||||
- [ ] **Step 2: Verify the double finalize fix — confirm finalize_run is the only terminal state writer**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform && grep -n "result_status\|finished_at" fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py
|
||||
```
|
||||
Expected output should show `result_status` and `finished_at` ONLY in `finalize_run` and `fail_run`, NOT in `save_evaluation_results`.
|
||||
|
||||
- [ ] **Step 3: Commit final verification**
|
||||
|
||||
```bash
|
||||
cd /home/wren-dev/Porject/leaudit-platform
|
||||
git add -A
|
||||
git diff --cached --stat
|
||||
git commit -m "chore: verify cross-module imports and finalize consistency"
|
||||
```
|
||||
Reference in New Issue
Block a user