ahserver/aidocs/xlsxData.md
2025-10-05 12:07:12 +08:00

337 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `XLSXData` 类技术文档
> **模块**: `xlsxdata.py`
> **依赖库**: `openpyxl`, `json`
---
## 概述
`XLSXData` 是一个用于从 Excel 文件(`.xlsx`)中读取结构化数据的 Python 类。它通过配置描述文件定义数据表的元信息,从而提取字段信息和实际数据记录。
该类适用于需要将 Excel 表格作为数据源的应用场景,例如后台管理系统中的数据导入、展示与分页查询。
---
## 文件格式说明(`xlsxds` 格式)
`XLSXData` 使用一个 JSON 格式的描述对象来指定 Excel 文件的结构布局。该描述对象包含以下字段:
```json
{
"xlsxfile": "./data.xlsx",
"data_from": 7,
"data_sheet": "Sheet1",
"label_at": 1,
"name_at": null,
"datatype_at": 2,
"ioattrs_at": 3,
"listhide_at": 4,
"inputhide_at": 5,
"frozen_at": 6
}
```
### 字段解释
| 字段名 | 类型 | 描述 |
|----------------|------|------|
| `xlsxfile` | str | Excel 文件路径 |
| `data_from` | int | 数据起始行号(默认为 2表示从哪一行开始读取数据记录 |
| `data_sheet` | str | 工作表名称,默认为 `"Sheet1"` |
| `label_at` | int or null | 标签所在行号(用于显示名称),若为 `null` 则使用默认名 |
| `name_at` | int or null | 字段英文名所在行号,若为 `null` 则生成为 `f1`, `f2`, ... |
| `datatype_at` | int or null | 数据类型所在行号(如 `str`, `int`, `float` 等) |
| `ioattrs_at` | int or null | 输入输出属性JSON 字符串)所在行号 |
| `listhide_at` | int or null | 控制列表是否隐藏的标志行('Y'/'y' 表示隐藏) |
| `inputhide_at` | int or null | 控制输入界面是否隐藏的标志行 |
| `frozen_at` | int or null | 控制列是否冻结的标志行 |
> ⚠️ 所有 `_at` 后缀字段均为行号(从 1 开始计数),若设为 `null` 或不存在,则对应功能无效或使用默认值。
---
## 类定义:`XLSXData`
```python
class XLSXData:
def __init__(self, path, desc)
```
### 构造函数:`__init__(path, desc)`
初始化 XLSX 数据读取器。
#### 参数:
- `path` (str): Excel 文件路径。
- `desc` (dict): 符合上述 `xlsxds` 格式的描述字典。
#### 功能:
- 加载 Excel 文件;
- 获取指定工作表;
- 初始化内部状态。
#### 示例:
```python
desc = {
"xlsxfile": "./data.xlsx",
"data_sheet": "Sheet1",
"data_from": 7,
"label_at": 1,
"datatype_at": 2,
...
}
xls_data = XLSXData("./data.xlsx", desc)
```
---
## 方法列表
---
### `getBaseFieldsInfo() → List[Dict]`
获取所有字段的基本元信息。
#### 返回值:
```python
[
{
"name": "字段英文名",
"label": "显示标签",
"type": "数据类型",
"listhide": True/False,
"inputhide": True/False,
"frozen": True/False,
**其他 IO 属性来自 ioattrs**
},
...
]
```
#### 内部调用方法:
- `_fieldName(ws, col)` - 获取字段名
- `_fieldLabel(ws, col)` - 获取显示标签
- `_fieldType(ws, col)` - 获取数据类型
- `_fieldIOattrs(ws, col)` - 解析并返回额外属性JSON
- `_isListHide(...)`, `_isInputHide(...)`, `_isFrozen(...)` - 判断各类隐藏/冻结状态
#### 示例输出:
```json
[
{
"name": "user_id",
"label": "用户ID",
"type": "int",
"listhide": false,
"inputhide": true,
"frozen": true,
"width": 100,
"editor": "numberbox"
}
]
```
> 💡 若 `ioattrs_at` 行的内容是合法 JSON 字符串,其键值对会合并到结果中。
---
### `getPeriodData(min_r: int, max_r: int) → List[Dict]`
读取指定行范围内的数据记录。
#### 参数:
- `min_r` (int): 起始行号(含)
- `max_r` (int): 结束行号(不含)
#### 返回值:
- 列表形式的数据记录,每条记录是以字段名为键的字典。
#### 注意事项:
- 自动断言 `min_r >= data_from`
-`max_r > 最大行数`,则自动截断
- 使用 `_fieldName` 获取列名映射
#### 示例:
```python
data = xls_data.getPeriodData(7, 10)
# 返回第7~9行的数据
```
---
### `getData(ns: dict) → List[Dict]`
获取全部数据记录。
#### 参数:
- `ns` (dict): 命名空间参数(未使用)
#### 返回值:
-`data_from` 行到末尾的所有数据。
#### 相当于:
```python
self.getPeriodData(data_from, ws.max_row + 1)
```
---
### `getPagingData(ns: dict) → Dict`
实现分页数据查询。
#### 参数:
- `ns` (dict): 分页参数
- `page` (int): 当前页码(从 1 开始),默认为 1
- `rows` (int): 每页行数,默认为 50
#### 返回值:
```json
{
"total": 100,
"rows": [
{ "col1": "val1", "col2": "val2" },
...
]
}
```
#### 计算逻辑:
- 起始行:`(page - 1) * rows + data_from`
- 结束行:`page * rows + data_from + 1`
#### 示例请求参数:
```python
ns = {'page': 2, 'rows': 20}
result = xls_data.getPagingData(ns)
```
---
### `getArgumentsDesc(ns, request) → None`
预留方法,用于获取参数描述(当前未实现)。
> ✅ 当前返回 `None`,可用于扩展接口文档生成功能。
---
## 私有方法详解
这些方法仅供内部使用,负责解析某一列的特定属性。
| 方法 | 说明 |
|------|------|
| `_fieldName(ws, i)` | 若 `name_at` 存在,取该行第 i 列值;否则返回 `'f'+i` |
| `_fieldLabel(ws, i)` | 取 `label_at` 行值,若无则同上 |
| `_fieldType(ws, i)` | 取 `datatype_at` 行值,缺省为 `'str'` |
| `_fieldIOattrs(ws, i)` | 读取 `ioattrs_at` 行内容,并尝试 `json.loads` 解析,失败时打印错误并返回 `{}` |
| `_isListHide(ws, i)` | 检查 `listhide_at` 是否为 'Y'/'y' |
| `_isInputHide(ws, i)` | 检查 `inputhide_at` 是否为 'Y'/'y' |
| `_isFrozen(ws, i)` | 检查 `frozen_at` 是否为 'Y'/'y' |
> 🔴 **Bug 提示**:在 `_isFrozen()` 方法中存在变量错误!应为 `ws.cell(x, i).value` 而不是 `ws.cell(x, y).value`
#### 修复建议:
```python
def _isFrozen(self, ws, i):
x = self.desc.get('frozen_at')
if x is not None:
t = ws.cell(x, i).value # 原代码误写成 y
if t == 'Y' or t == 'y':
return True
return False
```
---
## 使用示例
```python
from xlsxdata import XLSXData
desc = {
"xlsxfile": "./employees.xlsx",
"data_sheet": "Staff",
"data_from": 2,
"label_at": 1,
"name_at": None,
"datatype_at": None,
"ioattrs_at": 3,
"listhide_at": 4,
"inputhide_at": 5,
"frozen_at": 6
}
xls = XLSXData("./employees.xlsx", desc)
# 获取字段信息
fields = xls.getBaseFieldsInfo()
print(json.dumps(fields, ensure_ascii=False, indent=2))
# 获取全部数据
all_data = xls.getData({})
print(f"共加载 {len(all_data)} 条记录")
# 分页获取第2页每页10条
paged = xls.getPagingData({'page': 2, 'rows': 10})
print(f"当前页数据条数: {len(paged['rows'])}, 总计: {paged['total']}")
```
---
## 异常处理与注意事项
-`ioattrs` 内容非合法 JSON会捕获异常并打印错误日志不影响主流程。
- 所有行索引基于 1与 Excel 一致),程序内部无需转换。
- 不支持多 sheet 联合分析。
- 不修改原始 Excel 文件(只读模式)。
---
## 已知问题Bug
⚠️ 在方法 `_isFrozen` 中存在 **严重 Bug**
```python
t = ws.cell(x,y).value # ❌ 变量 y 未定义!应为 i
```
这会导致运行时报错 `NameError: name 'y' is not defined`
**必须修复为**
```python
t = ws.cell(x, i).value
```
---
## 总结
| 特性 | 支持情况 |
|------|----------|
| 读取 Excel 数据 | ✅ |
| 元数据驱动配置 | ✅ |
| 字段级属性控制 | ✅(隐藏、冻结、编辑属性等) |
| 分页支持 | ✅ |
| JSON 属性嵌入 | ✅ |
| 错误容忍机制 | ✅(部分) |
| 安全性 | ⚠️ 需注意 JSON 解析风险 |
| 可维护性 | ⚠️ 存在一个关键 bug |
---
## 建议改进方向
1. 🛠 修复 `_isFrozen` 中的变量引用错误;
2. 🔒 添加 `read_only=True` 提升性能(如无需写操作);
3. 📦 将 `desc` 验证封装成独立方法;
4. 🧪 增加单元测试覆盖核心方法;
5. 📄 支持 `.xls` 或其他格式可通过抽象接口扩展。
---
📝 文档版本v1.0
📅 更新时间2025-04-05