# 需求文档：助教废除（Abolish）全链路清理

## 简介

上游 SaaS 系统提供了一个独立的"助教废除记录"API（`/AssistantPerformance/GetAbolitionAssistant`），
ETL 系统为此建立了完整的 ODS → DWD → DWS 数据链路。但经排查发现：

1. **废除表 `dwd_assistant_trash_event` 无法与服务记录表 `dwd_assistant_service_log` 做 1:1 关联**——废除表没有 `assistant_service_id` 外键，两个 ID 不同源。
2. **DWS 层已改用 `dwd_assistant_service_log_ex.is_trash` 字段**（来自 `assistant_service_records` API）直接判断服务是否被废除，不再依赖废除表做跨表匹配。
3. 废除表的 `_extract_trash_records` 和 `_build_trash_index` 虽然仍被调用，但 `trash_index` 实际上不再参与废除判断逻辑（仅"备查"），属于死代码。
4. `trashed_seconds` / `trashed_count` 等 DWS 字段的数据来源已从废除表切换为服务记录自身的 `income_seconds`，废除表数据不再被消费。

因此，整条 abolish 独立链路（API 抓取 → ODS 表 → DWD 表 → DWS 引用）可以安全移除，
同时保留 `assistant_service_records` 中已有的 `is_trash` / `trash_reason` / `trash_applicant_*` 字段作为废除判断的唯一数据源。

## 术语表

- **ETL_System**：飞球 ETL Connector（`apps/etl/connectors/feiqiu/`）
- **ODS_Layer**：原始数据层（`ods` schema），存储从上游 API 抓取的原始记录
- **DWD_Layer**：明细数据层（`dwd` schema），存储清洗后的事实表和维度表
- **DWS_Layer**：汇总数据层（`dws` schema），存储按业务粒度聚合的汇总表
- **Abolish_Chain**：助教废除独立链路，包括 `ODS_ASSISTANT_ABOLISH` 任务、`ods.assistant_cancellation_records` 表、`dwd.dwd_assistant_trash_event` / `_ex` 表，以及 DWS 层中引用这些表的代码
- **Service_Trash_Fields**：`assistant_service_records` API 中自带的废除标记字段（`is_trash`、`trash_reason`、`trash_applicant_id`、`trash_applicant_name`），已映射到 `dwd_assistant_service_log_ex` 表
- **FACT_MAPPINGS**：`dwd_load_task.py` 中定义的 ODS → DWD 字段映射字典
- **Task_Registry**：`orchestration/task_registry.py` 中的任务注册表

## 需求

### 需求 1：移除 ODS 层废除任务

**用户故事：** 作为 ETL 维护者，我希望移除不再使用的 ODS 抓取任务，以减少无效 API 调用和维护负担。

#### 验收标准

1. WHEN ETL_System 执行调度时，THE Task_Registry SHALL 不包含 `ODS_ASSISTANT_ABOLISH` 任务注册
2. WHEN ETL_System 加载 ODS 任务定义时，THE ETL_System SHALL 不包含 `OdsAssistantAbolishTask` 的 `OdsTaskSpec` 定义
3. WHEN ETL_System 构建默认执行序列时，THE ETL_System SHALL 不包含 `ODS_ASSISTANT_ABOLISH` 任务代码

### 需求 2：移除 DWD 层废除表映射

**用户故事：** 作为 ETL 维护者，我希望移除废除表的 FACT_MAPPINGS 和 DWD 加载配置，以消除死代码。

#### 验收标准

1. WHEN DWD_Layer 执行装载时，THE FACT_MAPPINGS SHALL 不包含 `dwd.dwd_assistant_trash_event` 的映射条目
2. WHEN DWD_Layer 执行装载时，THE FACT_MAPPINGS SHALL 不包含 `dwd.dwd_assistant_trash_event_ex` 的映射条目
3. WHEN DWD_Layer 构建 ODS→DWD 表映射时，THE ETL_System SHALL 不包含 `dwd.dwd_assistant_trash_event` 到 `ods.assistant_cancellation_records` 的映射关系
4. WHEN DWD_Layer 构建 ODS→DWD 表映射时，THE ETL_System SHALL 不包含 `dwd.dwd_assistant_trash_event_ex` 到 `ods.assistant_cancellation_records` 的映射关系

### 需求 3：清理 DWS 层废除表引用

**用户故事：** 作为 ETL 维护者，我希望移除 DWS 任务中对废除表的查询和索引构建代码，以消除死代码路径。

#### 验收标准

1. WHEN DWS_Layer 执行 `DWS_ASSISTANT_DAILY` 任务时，THE AssistantDailyTask SHALL 不调用 `_extract_trash_records` 方法
2. WHEN DWS_Layer 执行 `DWS_ASSISTANT_DAILY` 任务时，THE AssistantDailyTask SHALL 不调用 `_build_trash_index` 方法
3. WHEN DWS_Layer 执行 `DWS_ASSISTANT_DAILY` 任务时，THE AssistantDailyTask SHALL 不向 `_aggregate_by_assistant_date` 传递 `trash_index` 参数
4. WHEN DWS_Layer 聚合服务记录时，THE AssistantDailyTask SHALL 仅通过 `is_trash` 字段（来自 `dwd_assistant_service_log_ex` JOIN）判断服务是否被废除

### 需求 4：清理 DWD 验证器配置

**用户故事：** 作为 ETL 维护者，我希望移除验证器中对废除表的引用，以避免验证器尝试校验已不存在的表。

#### 验收标准

1. WHEN DWD_Layer 执行数据验证时，THE DwdVerifier SHALL 不包含 `dwd_assistant_trash_event` 的 ID 映射配置
2. WHEN DWD_Layer 执行数据验证时，THE DwdVerifier SHALL 不包含 `dwd_assistant_trash_event_ex` 的 ID 映射配置
3. WHEN DWD_Layer 执行数据验证时，THE DwdVerifier SHALL 不包含 `dwd_assistant_trash_event` 的时间字段映射配置
4. WHEN DWD_Layer 执行数据验证时，THE DwdVerifier SHALL 不包含 `dwd_assistant_trash_event_ex` 的时间字段映射配置

### 需求 5：创建数据库迁移脚本

**用户故事：** 作为数据库管理员，我希望通过迁移脚本安全地移除废除相关的数据库对象，以保持 schema 整洁。

#### 验收标准

1. WHEN 迁移脚本执行时，THE 迁移脚本 SHALL 删除 `dwd.dwd_assistant_trash_event` 表
2. WHEN 迁移脚本执行时，THE 迁移脚本 SHALL 删除 `dwd.dwd_assistant_trash_event_ex` 表
3. WHEN 迁移脚本执行时，THE 迁移脚本 SHALL 删除 `ods.assistant_cancellation_records` 表
4. WHEN 迁移脚本执行时，THE 迁移脚本 SHALL 在 DROP 前使用 `IF EXISTS` 防止重复执行报错
5. WHEN 迁移脚本执行时，THE 迁移脚本 SHALL 包含注释说明移除原因

### 需求 6：同步更新 DDL 文档

**用户故事：** 作为 ETL 维护者，我希望 DDL schema 文件与实际数据库结构保持一致。

#### 验收标准

1. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/dwd.sql` SHALL 不包含 `dwd_assistant_trash_event` 表的 CREATE TABLE 语句
2. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/dwd.sql` SHALL 不包含 `dwd_assistant_trash_event_ex` 表的 CREATE TABLE 语句
3. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/ods.sql` SHALL 不包含 `assistant_cancellation_records` 表的 CREATE TABLE 语句
4. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/schema_dwd_doc.sql` SHALL 不包含 `dwd_assistant_trash_event` 相关的 CREATE TABLE 和 COMMENT 语句
5. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/schema_ODS_doc.sql` SHALL 不包含 `assistant_cancellation_records` 相关的 CREATE TABLE 和 COMMENT 语句
6. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/dws.sql` 中 `dws_assistant_daily_detail` 的注释 SHALL 不再引用 `dwd_assistant_trash_event` 作为数据来源
7. WHEN DDL 文件更新后，THE `db/etl_feiqiu/schemas/schema_dws.sql` 中 `dws_assistant_daily_detail` 的注释 SHALL 不再引用 `dwd_assistant_trash_event` 作为数据来源

### 需求 7：更新属性测试

**用户故事：** 作为开发者，我希望属性测试反映清理后的实际状态，以确保测试的准确性。

#### 验收标准

1. WHEN 属性测试执行时，THE `test_property_1_fact_mappings.py` SHALL 不包含 `dwd.dwd_assistant_trash_event` 在 A 类表列表中
2. WHEN 属性测试执行时，THE `test_property_1_fact_mappings.py` SHALL 不包含 `assistant_cancellation_records → dwd_assistant_trash_event` 的映射期望（`_REQ3_EXPECTED`）
3. WHEN 属性测试执行后，THE 所有现有属性测试 SHALL 通过（无回归）

### 需求 8：更新运维脚本引用

**用户故事：** 作为运维人员，我希望运维脚本中不再引用已移除的表和任务，以避免脚本执行错误。

#### 验收标准

1. WHEN 运维脚本加载 ODS 任务映射时，THE 脚本 SHALL 不包含 `ODS_ASSISTANT_ABOLISH` 到 `assistant_cancellation_records` 的映射
2. WHEN 运维脚本加载 DWD 表映射时，THE 脚本 SHALL 不包含 `dwd.dwd_assistant_trash_event` 到 `ods.assistant_cancellation_records` 的映射
3. WHEN 运维脚本列举 ODS 表时，THE 脚本 SHALL 不包含 `assistant_cancellation_records`
4. WHEN 运维脚本列举 DWD 表时，THE 脚本 SHALL 不包含 `dwd_assistant_trash_event` 和 `dwd_assistant_trash_event_ex`

### 需求 9：保留 Service_Trash_Fields 不受影响

**用户故事：** 作为 ETL 维护者，我希望确认清理操作不会影响 `assistant_service_records` 中已有的废除标记字段。

#### 验收标准

1. WHILE 清理操作执行期间，THE `dwd_assistant_service_log_ex` 表 SHALL 保留 `is_trash`、`trash_reason`、`trash_applicant_id`、`trash_applicant_name` 字段不变
2. WHILE 清理操作执行期间，THE `ods.assistant_service_records` 表 SHALL 保留 `is_trash`、`trash_reason`、`trash_applicant_id`、`trash_applicant_name` 字段不变
3. WHILE 清理操作执行期间，THE AssistantDailyTask 中通过 `is_trash` 判断废除的逻辑 SHALL 保持正常工作
