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

8.2 KiB
Raw Permalink Blame History

XLSXData 类技术文档

模块: xlsxdata.py
依赖库: openpyxl, json


概述

XLSXData 是一个用于从 Excel 文件(.xlsx)中读取结构化数据的 Python 类。它通过配置描述文件定义数据表的元信息,从而提取字段信息和实际数据记录。

该类适用于需要将 Excel 表格作为数据源的应用场景,例如后台管理系统中的数据导入、展示与分页查询。


文件格式说明(xlsxds 格式)

XLSXData 使用一个 JSON 格式的描述对象来指定 Excel 文件的结构布局。该描述对象包含以下字段:

{
  "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

class XLSXData:
    def __init__(self, path, desc)

构造函数:__init__(path, desc)

初始化 XLSX 数据读取器。

参数:

  • path (str): Excel 文件路径。
  • desc (dict): 符合上述 xlsxds 格式的描述字典。

功能:

  • 加载 Excel 文件;
  • 获取指定工作表;
  • 初始化内部状态。

示例:

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]

获取所有字段的基本元信息。

返回值:

[
  {
    "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(...) - 判断各类隐藏/冻结状态

示例输出:

[
  {
    "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 获取列名映射

示例:

data = xls_data.getPeriodData(7, 10)
# 返回第7~9行的数据

getData(ns: dict) → List[Dict]

获取全部数据记录。

参数:

  • ns (dict): 命名空间参数(未使用)

返回值:

  • data_from 行到末尾的所有数据。

相当于:

self.getPeriodData(data_from, ws.max_row + 1)

getPagingData(ns: dict) → Dict

实现分页数据查询。

参数:

  • ns (dict): 分页参数
    • page (int): 当前页码(从 1 开始),默认为 1
    • rows (int): 每页行数,默认为 50

返回值:

{
  "total": 100,
  "rows": [
    { "col1": "val1", "col2": "val2" },
    ...
  ]
}

计算逻辑:

  • 起始行:(page - 1) * rows + data_from
  • 结束行:page * rows + data_from + 1

示例请求参数:

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

修复建议:

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

使用示例

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

t = ws.cell(x,y).value  # ❌ 变量 y 未定义!应为 i

这会导致运行时报错 NameError: name 'y' is not defined

必须修复为

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