在前后端开发联调前 的提交20260223

This commit is contained in:
Neo
2026-02-23 23:02:20 +08:00
parent 254ccb1e77
commit fafc95e64c
1142 changed files with 10366960 additions and 36957 deletions

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,354 @@
# 设计文档ETL 员工维度表staff_info
## 概述
为飞球 ETL 连接器新增员工维度表,从 `SearchSystemStaffInfo` API 抓取球房全体员工数据(店长、主管、教练、收银员、助教管理员等),经 ODS 落地后清洗装载至 DWD 层。员工表与现有助教表(`assistant_accounts_master`)是完全独立的实体。
## API 响应结构
```json
{
"data": {
"total": 15,
"staffProfiles": [
{
"id": 3020236636900101,
"cashierPointId": 2790685415443270,
"cashierPointName": "默认",
"job_num": "",
"staff_name": "葛芃",
"mobile": "13811638071",
"auth_code": "",
"avatar": "",
"create_time": "2025-12-24 00:03:37",
"entry_time": "2025-12-23 08:00:00",
"is_delete": 0,
"leave_status": 0,
"resign_time": "2225-12-24 00:03:37",
"site_id": 2790685415443269,
"staff_identity": 2,
"status": 1,
"system_role_id": 4,
"system_user_id": 3020236636293893,
"tenant_id": 2790683160709957,
"tenant_org_id": 2790685415443269,
"job": "店长",
"shop_name": "朗朗桌球",
"account_status": 1,
"is_reserve": 1,
"groupName": "",
"groupId": 0,
"alias_name": "葛芃",
"staff_profile_id": 0,
"site_label": "",
"rank_id": -1,
"ding_talk_synced": 1,
"new_rank_id": 0,
"new_staff_identity": 0,
"salary_grant_enabled": 2,
"rankName": "无职级",
"entry_type": 1,
"userRoles": [],
"entry_sign_status": 0,
"resign_sign_status": 0,
"criticism_status": 1,
"gender": 3
}
]
},
"code": 0
}
```
## 1. ODS 层设计
### 1.1 ODS 任务规格
```python
OdsTaskSpec(
code="ODS_STAFF_INFO",
class_name="OdsStaffInfoTask",
table_name="ods.staff_info_master",
endpoint="/PersonnelManagement/SearchSystemStaffInfo",
data_path=("data",),
list_key="staffProfiles",
pk_columns=(_int_col("id", "id", required=True),),
extra_params={
"workStatusEnum": 0,
"dingTalkSynced": 0,
"staffIdentity": 0,
"rankId": 0,
"criticismStatus": 0,
"signStatus": -1,
},
include_source_endpoint=False,
include_fetched_at=False,
include_record_index=True,
requires_window=False,
time_fields=None,
snapshot_mode=SnapshotMode.FULL_TABLE,
description="员工档案 ODSSearchSystemStaffInfo -> staffProfiles 原始 JSON",
)
```
### 1.2 ODS 表 DDL`ods.staff_info_master`
```sql
CREATE TABLE ods.staff_info_master (
id BIGINT NOT NULL,
tenant_id BIGINT,
site_id BIGINT,
tenant_org_id BIGINT,
system_user_id BIGINT,
staff_name TEXT,
alias_name TEXT,
mobile TEXT,
avatar TEXT,
gender INTEGER,
job TEXT,
job_num TEXT,
staff_identity INTEGER,
status INTEGER,
account_status INTEGER,
system_role_id INTEGER,
rank_id INTEGER,
rank_name TEXT,
new_rank_id INTEGER,
new_staff_identity INTEGER,
leave_status INTEGER,
entry_time TIMESTAMP WITHOUT TIME ZONE,
resign_time TIMESTAMP WITHOUT TIME ZONE,
create_time TIMESTAMP WITHOUT TIME ZONE,
is_delete INTEGER,
is_reserve INTEGER,
shop_name TEXT,
site_label TEXT,
cashier_point_id BIGINT,
cashier_point_name TEXT,
group_id BIGINT,
group_name TEXT,
staff_profile_id BIGINT,
auth_code TEXT,
auth_code_create TIMESTAMP WITHOUT TIME ZONE,
ding_talk_synced INTEGER,
salary_grant_enabled INTEGER,
entry_type INTEGER,
entry_sign_status INTEGER,
resign_sign_status INTEGER,
criticism_status INTEGER,
user_roles JSONB,
-- ETL 元数据
content_hash TEXT NOT NULL,
source_file TEXT,
fetched_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
payload JSONB NOT NULL
);
COMMENT ON TABLE ods.staff_info_master IS '员工档案主数据来源SearchSystemStaffInfo API';
```
### 1.3 ODS 列名映射说明
API 返回的驼峰字段在 ODS 层统一转为蛇形命名(由 BaseOdsTask 自动处理):
- `cashierPointId``cashier_point_id`
- `cashierPointName``cashier_point_name`
- `staffName` / `staff_name``staff_name`API 已是蛇形)
- `systemUserId` / `system_user_id``system_user_id`
- `tenantOrgId` / `tenant_org_id``tenant_org_id`
- `groupName``group_name`注意API 返回驼峰 `groupName`
- `groupId``group_id`API 返回驼峰 `groupId`
- `rankName``rank_name`API 返回驼峰 `rankName`
- `userRoles``user_roles`(数组,存为 JSONB
- `authCodeCreate` / `auth_code_create``auth_code_create`
## 2. DWD 层设计
### 2.1 主表 DDL`dwd.dim_staff`
核心业务字段,高频查询使用。
```sql
CREATE TABLE dwd.dim_staff (
staff_id BIGINT NOT NULL,
staff_name TEXT,
alias_name TEXT,
mobile TEXT,
gender INTEGER,
job TEXT,
tenant_id BIGINT,
site_id BIGINT,
system_role_id INTEGER,
staff_identity INTEGER,
status INTEGER,
leave_status INTEGER,
entry_time TIMESTAMP WITH TIME ZONE,
resign_time TIMESTAMP WITH TIME ZONE,
is_delete INTEGER,
-- SCD2
scd2_start_time TIMESTAMP WITH TIME ZONE NOT NULL,
scd2_end_time TIMESTAMP WITH TIME ZONE,
scd2_is_current INTEGER,
scd2_version INTEGER,
PRIMARY KEY (staff_id, scd2_start_time)
);
COMMENT ON TABLE dwd.dim_staff IS '员工档案维度主表SCD2';
```
### 2.2 扩展表 DDL`dwd.dim_staff_ex`
次要/低频变更字段。
```sql
CREATE TABLE dwd.dim_staff_ex (
staff_id BIGINT NOT NULL,
avatar TEXT,
job_num TEXT,
account_status INTEGER,
rank_id INTEGER,
rank_name TEXT,
new_rank_id INTEGER,
new_staff_identity INTEGER,
is_reserve INTEGER,
shop_name TEXT,
site_label TEXT,
tenant_org_id BIGINT,
system_user_id BIGINT,
cashier_point_id BIGINT,
cashier_point_name TEXT,
group_id BIGINT,
group_name TEXT,
staff_profile_id BIGINT,
auth_code TEXT,
auth_code_create TIMESTAMP WITH TIME ZONE,
ding_talk_synced INTEGER,
salary_grant_enabled INTEGER,
entry_type INTEGER,
entry_sign_status INTEGER,
resign_sign_status INTEGER,
criticism_status INTEGER,
create_time TIMESTAMP WITH TIME ZONE,
user_roles JSONB,
-- SCD2
scd2_start_time TIMESTAMP WITH TIME ZONE NOT NULL,
scd2_end_time TIMESTAMP WITH TIME ZONE,
scd2_is_current INTEGER,
scd2_version INTEGER,
PRIMARY KEY (staff_id, scd2_start_time)
);
COMMENT ON TABLE dwd.dim_staff_ex IS '员工档案维度扩展表SCD2';
```
### 2.3 TABLE_MAP 映射
```python
# 在 DwdLoadTask.TABLE_MAP 中新增:
"dwd.dim_staff": "ods.staff_info_master",
"dwd.dim_staff_ex": "ods.staff_info_master",
```
### 2.4 FACT_MAPPINGS 字段映射
```python
# dim_staff 主表映射
"dwd.dim_staff": [
("staff_id", "id", None),
("entry_time", "entry_time", "timestamptz"),
("resign_time", "resign_time", "timestamptz"),
],
# dim_staff_ex 扩展表映射
"dwd.dim_staff_ex": [
("staff_id", "id", None),
("rank_name", "rankname", None),
("cashier_point_id", "cashierpointid", "bigint"),
("cashier_point_name", "cashierpointname", None),
("group_id", "groupid", "bigint"),
("group_name", "groupname", None),
("system_user_id", "systemuserid", "bigint"),
("tenant_org_id", "tenantorgid", "bigint"),
("auth_code_create", "auth_code_create", "timestamptz"),
("create_time", "create_time", "timestamptz"),
("user_roles", "userroles", "jsonb"),
],
```
说明:
- ODS 层的列名由 BaseOdsTask 自动从 API 驼峰转为蛇形(如 `cashierPointId``cashierpointid`,注意 PG 列名全小写无下划线)
- DWD 主表中 `staff_name``alias_name``mobile` 等与 ODS 同名列自动映射,无需显式配置
- `staff_id` 映射自 ODS 的 `id`
## 3. 数据流概览
```
API: SearchSystemStaffInfo
↓ (POST, 分页, extra_params 筛选)
ODS: ods.staff_info_master
↓ (SCD2 合并, FULL_TABLE 快照)
DWD: dwd.dim_staff + dwd.dim_staff_ex
```
## 4. 测试框架
- 测试框架:`pytest` + `hypothesis`
- 单元测试使用 `FakeDB` / `FakeAPI``tests/unit/task_test_utils.py`
## 5. 正确性属性
### P1ODS 任务规格完整性
对于 `ODS_STAFF_INFO` 任务规格,以下属性必须成立:
- `code == "ODS_STAFF_INFO"`
- `table_name == "ods.staff_info_master"`
- `endpoint == "/PersonnelManagement/SearchSystemStaffInfo"`
- `list_key == "staffProfiles"`
- `snapshot_mode == SnapshotMode.FULL_TABLE`
- `requires_window == False`
- `time_fields is None`
- `"staffProfiles"` 存在于 `DEFAULT_LIST_KEYS`
- `"ODS_STAFF_INFO"` 存在于 `ENABLED_ODS_CODES`
验证方式:单元测试直接断言
### P2DWD 映射完整性
对于 DWD 装载配置,以下属性必须成立:
- `TABLE_MAP["dwd.dim_staff"] == "ods.staff_info_master"`
- `TABLE_MAP["dwd.dim_staff_ex"] == "ods.staff_info_master"`
- `FACT_MAPPINGS["dwd.dim_staff"]` 包含 `staff_id``id` 的映射
- `FACT_MAPPINGS["dwd.dim_staff_ex"]` 包含 `staff_id``id` 的映射
验证方式:单元测试直接断言
### P3ODS 列名提取一致性(属性测试)
对于任意 API 返回的员工记录(含驼峰和蛇形混合字段名),经 BaseOdsTask 处理后:
- 所有字段名转为小写蛇形
- `id` 字段不为空且为正整数
- `payload` 字段包含完整原始 JSON
验证方式hypothesis 属性测试,生成随机员工记录验证转换一致性
## 6. 文件变更清单
### 代码变更
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `apps/etl/connectors/feiqiu/api/client.py` | 修改 | `DEFAULT_LIST_KEYS` 添加 `"staffProfiles"` |
| `apps/etl/connectors/feiqiu/tasks/ods/ods_tasks.py` | 修改 | 新增 `ODS_STAFF_INFO` 任务规格 + 注册到 `ENABLED_ODS_CODES` |
| `apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py` | 修改 | `TABLE_MAP``FACT_MAPPINGS` 新增 dim_staff/dim_staff_ex 映射 |
### DDL / 迁移
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `db/etl_feiqiu/migrations/2026-02-22__add_staff_info_tables.sql` | 新增 | ODS + DWD 建表迁移脚本 |
| `docs/database/ddl/etl_feiqiu__ods.sql` | 修改 | 追加 `ods.staff_info_master` DDL |
| `docs/database/ddl/etl_feiqiu__dwd.sql` | 修改 | 追加 `dwd.dim_staff` + `dwd.dim_staff_ex` DDL |
### 文档
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `apps/etl/connectors/feiqiu/docs/database/ODS/mappings/mapping_SearchSystemStaffInfo_staff_info_master.md` | 新增 | API→ODS 字段映射文档 |
| `apps/etl/connectors/feiqiu/docs/database/ODS/main/BD_manual_staff_info_master.md` | 新增 | ODS 表 BD 手册 |
| `apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff.md` | 新增 | DWD 主表 BD 手册 |
| `apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff_ex.md` | 新增 | DWD 扩展表 BD 手册 |
| `apps/etl/connectors/feiqiu/docs/database/README.md` | 修改 | 增加员工表条目 |
| `apps/etl/connectors/feiqiu/docs/etl_tasks/ods_tasks.md` | 修改 | 增加 ODS_STAFF_INFO 任务说明 |
| `docs/database/README.md` | 修改 | 增加员工相关表条目 |

View File

@@ -0,0 +1,82 @@
# 需求文档ETL 员工维度表staff_info
## 概述
为飞球 ETL 连接器新增"员工"维度表,从上游 `SearchSystemStaffInfo` API 抓取球房员工数据,经 ODS 落地后清洗装载至 DWD 层,走 SCD2 缓慢变化维度。
## 用户故事
### US-1API 对接与 ODS 落地
作为 ETL 开发者,我需要将 `SearchSystemStaffInfo` API 融入现有 API 请求框架,并将原始数据落地到 ODS 层,以便后续清洗使用。
验收标准:
- 1.1 在 `ODS_TASK_SPECS` 中新增 `ODS_STAFF_INFO` 任务规格endpoint 为 `/PersonnelManagement/SearchSystemStaffInfo`
- 1.2 API 请求体包含必要的筛选参数(`workStatusEnum``staffIdentity` 等),使用现有 `APIClient.iter_paginated` 分页机制
- 1.3 ODS 表 `ods.staff_info_master` 包含 API 返回的所有业务字段 + ETL 元数据列(`content_hash``source_file``fetched_at``payload`
- 1.4 任务配置为 `snapshot_mode=FULL_TABLE`(全量快照,无时间窗口),`requires_window=False`
- 1.5 在 `DEFAULT_LIST_KEYS` 中添加 `staffProfiles`API 响应的列表键名)
- 1.6 在 `ENABLED_ODS_CODES` 中注册 `ODS_STAFF_INFO`
### US-2DWD 维度表设计与 SCD2 装载
作为数据分析师,我需要一张清洗后的员工维度表,以便在 DWS 汇总层关联员工信息。
验收标准:
- 2.1 创建 `dwd.dim_staff` 主表,包含核心业务字段(员工 ID、姓名、手机号、角色、门店、在职状态等+ SCD2 列
- 2.2 创建 `dwd.dim_staff_ex` 扩展表,包含次要/低频变更字段 + SCD2 列
- 2.3 在 `DwdLoadTask.TABLE_MAP` 中注册 `dwd.dim_staff``ods.staff_info_master``dwd.dim_staff_ex``ods.staff_info_master` 的映射
- 2.4 在 `DwdLoadTask.FACT_MAPPINGS` 中配置字段映射ODS 列名 → DWD 列名,含必要的类型转换)
- 2.5 DWD 装载走 SCD2 合并路径,变更检测正常工作
### US-3DDL 创建与归档
作为 DBA我需要 ODS 和 DWD 层的 DDL 被正确创建并归档到项目文档中。
验收标准:
- 3.1 编写 ODS 层 DDL`ods.staff_info_master`),在测试库执行
- 3.2 编写 DWD 层 DDL`dwd.dim_staff``dwd.dim_staff_ex`),在测试库执行
- 3.3 DDL 归档至 `docs/database/ddl/etl_feiqiu__ods.sql``docs/database/ddl/etl_feiqiu__dwd.sql`
- 3.4 编写迁移脚本至 `db/etl_feiqiu/migrations/`(日期前缀)
### US-4文档增补
作为团队成员,我需要所有相关文档同步更新,以便理解新增的数据流。
验收标准:
- 4.1 新增 ODS mapping 文档:`apps/etl/connectors/feiqiu/docs/database/ODS/mappings/mapping_SearchSystemStaffInfo_staff_info_master.md`
- 4.2 新增 ODS BD_manual 文档:`apps/etl/connectors/feiqiu/docs/database/ODS/main/BD_manual_staff_info_master.md`
- 4.3 新增 DWD BD_manual 文档:`apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff.md`
- 4.4 更新 `apps/etl/connectors/feiqiu/docs/database/README.md`,增加员工表条目
- 4.5 更新 `apps/etl/connectors/feiqiu/docs/etl_tasks/ods_tasks.md`,增加 ODS_STAFF_INFO 任务说明
- 4.6 更新 `docs/database/README.md`,增加员工相关表的条目
- 4.7 新增 DWD BD_manual 扩展表文档:`apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff_ex.md`(如有扩展表)
## API 信息
| 属性 | 值 |
|------|-----|
| URL | `https://pc.ficoo.vip/apiprod/admin/v1/PersonnelManagement/SearchSystemStaffInfo` |
| 方法 | POST |
| 端点路径 | `/PersonnelManagement/SearchSystemStaffInfo` |
| 认证 | Bearer Token标准飞球 API 认证) |
| 分页 | `page` + `limit`(与现有接口一致) |
### 请求体参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| workStatusEnum | int | 0 | 在职状态筛选0=全部) |
| dingTalkSynced | int | 0 | 钉钉同步状态0=全部) |
| staffIdentity | int | 0 | 员工身份筛选0=全部) |
| rankId | int | 0 | 职级筛选0=全部) |
| criticismStatus | int | 0 | 批评状态0=全部) |
| signStatus | int | -1 | 签约状态(-1=全部) |
| page | int | 1 | 页码 |
| limit | int | 50 | 每页条数 |
## 技术约束
- 员工表为维度表DWD 层走 SCD2
- ODS 使用 `SnapshotMode.FULL_TABLE`(全量快照软删除)
- 不需要时间窗口(`requires_window=False``time_fields=None`
- 主键为 `id`(员工 ID
- API 响应结构已确认:`data.staffProfiles` 为列表键,`data.total` 为总数
- 员工表与助教表(`assistant_accounts_master`)是完全独立的实体
- DWD 层拆分为主表(`dim_staff`+ 扩展表(`dim_staff_ex`

View File

@@ -0,0 +1,33 @@
# 任务列表ETL 员工维度表staff_info
## 任务
- [x] 1. DDL 创建与数据库执行
- [x] 1.1 编写迁移脚本 `db/etl_feiqiu/migrations/2026-02-22__add_staff_info_tables.sql`,包含 ODS + DWD 三张表的 CREATE TABLE 语句
- [x] 1.2 在测试库test_etl_feiqiu执行迁移脚本创建 `ods.staff_info_master``dwd.dim_staff``dwd.dim_staff_ex`
- [x] 1.3 将 DDL 归档追加至 `docs/database/ddl/etl_feiqiu__ods.sql``docs/database/ddl/etl_feiqiu__dwd.sql`
- [x] 2. ODS 任务注册
- [x] 2.1 在 `apps/etl/connectors/feiqiu/api/client.py``DEFAULT_LIST_KEYS` 中添加 `"staffProfiles"`
- [x] 2.2 在 `apps/etl/connectors/feiqiu/tasks/ods/ods_tasks.py``ODS_TASK_SPECS` 中新增 `ODS_STAFF_INFO` 任务规格
- [x] 2.3 在 `ENABLED_ODS_CODES` 集合中注册 `"ODS_STAFF_INFO"`
- [x] 3. DWD 映射注册
- [x] 3.1 在 `apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py``TABLE_MAP` 中新增 `dwd.dim_staff``dwd.dim_staff_ex` 的映射
- [x] 3.2 在 `FACT_MAPPINGS` 中新增 `dwd.dim_staff``dwd.dim_staff_ex` 的字段映射配置
- [x] 4. 单元测试
- [x] 4.1 编写 ODS 任务规格完整性测试(验证 P1 属性)
- [x] 4.2 编写 DWD 映射完整性测试(验证 P2 属性)
- [x] 5. 属性测试
- [x] 5.1 [PBT] 编写 ODS 列名提取一致性属性测试(验证 P3 属性):对于任意员工记录,字段名转换和 payload 保留正确
- [x] 6. 文档增补
- [x] 6.1 新增 ODS mapping 文档:`apps/etl/connectors/feiqiu/docs/database/ODS/mappings/mapping_SearchSystemStaffInfo_staff_info_master.md`
- [x] 6.2 新增 ODS BD_manual 文档:`apps/etl/connectors/feiqiu/docs/database/ODS/main/BD_manual_staff_info_master.md`
- [x] 6.3 新增 DWD BD_manual 主表文档:`apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff.md`
- [x] 6.4 新增 DWD BD_manual 扩展表文档:`apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dim_staff_ex.md`
- [x] 6.5 更新 `apps/etl/connectors/feiqiu/docs/database/README.md`,增加员工表条目
- [x] 6.6 更新 `apps/etl/connectors/feiqiu/docs/etl_tasks/ods_tasks.md`,增加 ODS_STAFF_INFO 任务说明
- [x] 6.7 更新 `docs/database/README.md`,增加员工相关表条目