# 技术文档:`xlsx_data_processor.py`
---
## 概述
该模块提供了一个用于处理 Excel(`.xlsx`)文件和结构化数据的工具集,支持从 `.xlsx` 文件中读取数据并将其转换为结构化的字典对象。它特别适用于管理具有特定格式规范的 CRUD(创建、读取、更新、删除)配置表,并能自动解析字段类型、主键、外键、索引等元信息。
模块主要功能包括:
- 从 `.xlsx` 文件加载数据并按工作表组织。
- 支持自定义字段类型的自动转换(如 `int`, `float`, `json` 等)。
- 支持扩展的数据格式(如 `cruddata`, `xlsxdata`)通过字符串协议调用。
- 提供对特殊格式的 CRUD 配置文件的支持(包含 `summary`, `fields`, `validation` 等 sheet)。
- 命令行接口,可批量合并多个数据源(Excel 和参数)输出 JSON。
---
## 依赖库
```python
import os
import sys
from traceback import print_exc
from openpyxl import load_workbook
from appPublic.myjson import loadf, dumpf, dumps
from appPublic.dictObject import DictObject
```
> ⚠️ 注意:`appPublic` 是一个自定义公共库,需确保已安装或路径正确。
---
## 核心类与函数说明
### 1. `TypeConvert` 类
负责将原始值根据指定类型进行安全转换。
#### 方法列表:
| 方法 | 描述 |
|------|------|
| `conv(typ, v)` | 根据类型名 `typ` 调用对应的转换方法(如 `to_int`),若无则返回原值。 |
| `to_int(v)` | 转换为整数,失败返回 `0`。 |
| `to_float(v)` | 转换为浮点数,失败返回 `0.0`。 |
| `to_str(v)` | 转换为字符串,失败返回空串 `''`。 |
| `to_json(v)` | 将字符串解析为 JSON 对象,空字符串保持不变,失败返回原值。⚠️ 存在 bug:应使用 `loads` 但代码中写成了 `loads(v)` 实际未导入。 |
| `to_date(v)`, `to_time(v)`, `to_timestamp(v)` | 占位方法,直接返回原值(未来可扩展)。 |
| `to_cruddata(v)` | 解析形如 `"cruddata:\"filename\".property"` 的字符串,动态加载 `CRUDData` 并执行属性/方法访问。 |
| `to_xlsxdata(v)` | 类似 `to_cruddata`,但用于普通 `XLSXData` 对象。 |
> ✅ 示例输入:`"cruddata:\"user.xlsx\".users"` → 加载 `user.xlsx` 并获取其 `users` 表数据。
> ❗ Bug 提示:`to_json` 中使用了未定义的 `loads` 函数,应改为 `from json import loads` 或使用 `dumps` 所属模块中的 `loads`。
---
### 2. `CRUDException(Exception)` 类
自定义异常类,用于报告 `.xlsx` 文件处理过程中的错误。
#### 属性:
- `xlsfile`: 出错的文件名
- `errmsg`: 错误描述
#### `__str__()` 返回格式:
```
filename:<文件名> error:<错误信息>
```
---
### 3. `XLSXData` 类
通用 `.xlsx` 文件读取器,将每个工作表解析为记录列表,并封装成 `DictObject`。
#### 构造函数:`__init__(xlsxfile, book=None)`
- `xlsxfile`: Excel 文件路径
- `book`: 可选的已打开的 `Workbook` 对象(用于性能优化或复用)
#### 主要方法:
| 方法 | 功能 |
|------|------|
| `_read()` | 内部方法,遍历所有 sheet 并调用 `readRecords()` 构建 `self.data`。 |
| `get_data()` | 返回最终解析后的数据对象(`DictObject`)。 |
| `readRecords(name, sheet)` | 读取单个 sheet 数据:
- 第一行作为字段定义(格式:`字段名:类型`)
- 后续行为数据行
- 使用 `TypeConvert` 进行类型转换
- 返回 `{name: [DictObject(...), ...]}` |
| `getFieldNames(row)` | 处理首行字段:
- 若为空则命名为 `F_i`
- 分割 `name:type` 形式,缺失类型设为 `None`
- 返回 `[ [字段名, 类型], ... ]` |
> 📌 字段命名规则示例:
> - `id:int` → 名称 `id`,类型 `int`
> - `username` → 名称 `username`,类型 `None`
> - 空单元格 → 自动命名为 `F_0`, `F_1`...
---
### 4. `CRUDData(XLSXData)` 类
继承自 `XLSXData`,专用于处理符合 CRUD 元数据规范的 `.xlsx` 文件。
#### 规范要求:
必须包含以下三个工作表:
- `summary`: 表基本信息(如主键)
- `fields`: 字段定义列表
- `validation`: 验证规则(外键、索引等)
#### 类方法:`isMe(book)`
判断给定 workbook 是否满足 CRUDData 格式(检查是否存在上述三个 sheet)。
#### 重写 `_read()` 方法流程:
1. 调用父类 `_read()` 解析所有 sheet。
2. 验证 `summary`, `fields`, `validation` 存在且唯一。
3. 执行元数据转换:
- `convPrimary()`: 解析主键字段(逗号分隔)
- `convForeignkey()`: 解析外键规则(格式:`table:value:title`)
- `convIndex()`: 解析索引定义(格式:`idx_type:key1,key2`)
- `check_codes_fields()`: 验证 `codes` 表中的字段是否存在于 `fields`
#### 辅助方法:
| 方法 | 功能 |
|------|------|
| `check_field(fieldname)` | 检查字段是否在 `fields` 表中存在 |
| `getFieldByNmae(fields, name)` | 获取指定名称的字段定义(拼写错误:`Nmae` 应为 `Name`)❗ |
| `getFKs(validation)` | 提取所有外键规则(`oper == 'fk'`) |
| `getIDXs(validation)` | 提取所有索引规则(`oper == 'idx'`) |
> ⚠️ Bug 提示:
> - `getFieldByNmae` 函数名拼写错误,可能导致调用失败。
> - `getFKs` 中条件判断写成了 `'oepr'` 而非 `'oper'`,导致无法识别外键。
---
### 5. `xlsxFactory(xlsxfilename)` 函数
根据文件内容自动选择合适的类实例化(工厂模式)。
#### 流程:
1. 打开 `.xlsx` 文件。
2. 遍历 `XLSXData` 的所有子类(递归查找),调用 `isMe(book)` 判断是否匹配。
3. 匹配成功则返回 `CRUDData` 实例,否则返回默认 `XLSXData` 实例。
4. 异常捕获并打印堆栈。
> ✅ 支持插件式扩展:可通过新增子类实现其他专用格式识别。
---
### 6. `ValueConvert(s)` 函数
解析特殊前缀字符串,支持外部资源引用。
| 前缀 | 行为 |
|------|------|
| `xlsfile::path/to/file.xlsx` | 使用 `xlsxFactory` 加载并返回 `.get_data()` |
| `jsonfile::path/to/data.json` | 使用 `loadf()` 读取 JSON 文件 |
| 其他 | 直接返回原字符串 |
---
### 7. `paramentHandle(ns)` 函数
对参数字典中的每个值应用 `ValueConvert`,实现参数注入时的自动资源加载。
> ✅ 示例:
```bash
python script.py data=xlsfile::config.xlsx user=jsonfile::user.json
```
→ 自动加载 Excel 和 JSON 文件内容。
---
## 主程序入口(`if __name__ == '__main__':`)
支持命令行运行,用法如下:
### 参数格式:
- `key=value`:设置命名参数(支持 `xlsfile::`, `jsonfile::`)
- `filepath`:直接加载数据文件(仅限 `.xlsx`, `.xls`)
### 执行流程:
1. 解析命令行参数,分离 `ns`(命名空间)和 `datafiles`(文件列表)。
2. 处理 `ns` 中的值(调用 `paramentHandle`)。
3. 遍历 `datafiles`,使用 `xlsxFactory` 加载每个 Excel 文件。
4. 合并所有数据到 `retData`。
5. 输出合并后的 JSON 结果(使用 `dumps` 格式化)。
### 示例命令:
```bash
python xlsx_data_processor.py \
config=xlsfile::app_config.xlsx \
user=jsonfile::default_user.json \
roles.xlsx \
permissions.xlsx
```
输出结果为整合所有来源数据的 JSON 对象。
---
## 已知问题与改进建议
| 问题 | 描述 | 建议修复 |
|------|------|----------|
| `to_json` 使用 `loads` 未定义 | 导致 JSON 解析失败 | 改为 `from json import loads` 或使用 `dumps.loads()` |
| `getFieldByNmae` 拼写错误 | 方法名错误 | 更正为 `getFieldByName` |
| `getFKs` 条件判断错误 | `'oepr' == 'fk'` 永不成立 | 改为 `'oper'` |
| `check_field(f for f in ...)` | 语法错误,不能传生成器 | 改为循环逐一检查每个字段 |
| `vs < 3` 在 `split` 后无效 | `vs` 是 list,比较 `< 3` 不合理 | 应改为 `len(vs) < 3` |
| `to_cruddata` / `to_xlsxdata` 中 `vs[2] is None` | `split` 不会返回 `None` 元素 | 应改为 `len(vs) <= 2` 判断是否有命令部分 |
---
## 使用示例
### 示例 Excel 结构(`users.xlsx`)
**Sheet: users**
```
id:int,name:str,age:int
1,Alice,25
2,Bob,30
```
**Sheet: summary**
```
table:primary
users:id
```
**Sheet: fields**
```
name,type,desc
id,int,用户ID
name,str,用户名
age,int,年龄
```
### 调用方式:
```python
d = xlsxFactory('users.xlsx')
data = d.get_data()
print(data.users[0].name) # 输出: Alice
print(data.summary[0].primary) # 输出: ['id']
```
---
## 总结
本模块是一个轻量级但功能丰富的 `.xlsx` 数据处理器,适合用于配置中心、元数据管理、自动化脚本等场景。结合自定义前缀协议和工厂模式,具备良好的扩展性。
尽管存在少量 bug 和命名问题,整体设计清晰,层次分明,易于维护和二次开发。
---
> **作者**:Auto-generated Technical Documentation
> **版本**:1.0
> **最后更新**:2025-04-05