bugfix
This commit is contained in:
parent
7c35a4a6b1
commit
76bc62493b
284
aidocs/singletree.md
Normal file
284
aidocs/singletree.md
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
# 树形数据管理模块技术文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本模块用于根据数据库表结构和配置文件,自动生成基于树形控件(Tree)的前端 UI 配置及后端 CRUD 接口代码。适用于快速构建具有父子层级关系的数据管理系统。
|
||||||
|
|
||||||
|
该工具支持通过 `.xlsx` 或其他格式描述的数据模型生成完整的树形界面与操作逻辑,包括:
|
||||||
|
- 树形展示界面 (`index.ui`)
|
||||||
|
- 获取节点数据接口
|
||||||
|
- 新增、更新、删除节点接口
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 依赖说明
|
||||||
|
|
||||||
|
### 外部依赖
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import codecs
|
||||||
|
import json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 内部模块依赖
|
||||||
|
|
||||||
|
| 模块路径 | 功能 |
|
||||||
|
|--------|------|
|
||||||
|
| `appPublic.dictObject` | 提供 `DictObject` 类,将字典转换为可点式访问的对象 |
|
||||||
|
| `appPublic.folderUtils` | 文件夹操作工具:`listFile`, `_mkdir` |
|
||||||
|
| `appPublic.myTE` | 自定义模板引擎 `MyTemplateEngine`,支持 Jinja2 风格语法 |
|
||||||
|
| `xlsxData.xlsxFactory` | 从 Excel 文件中读取数据模型 |
|
||||||
|
| `xls2crud.build_dbdesc` | 构建数据库描述对象 |
|
||||||
|
| `xls2crud.field_list` | 提取字段列表 |
|
||||||
|
| `xls2crud.subtable2toolbar` | 将子表信息转为工具栏按钮配置 |
|
||||||
|
| `xls2crud.filter_backslash` | 过滤字符串中的多余反斜杠 |
|
||||||
|
| `tmpls.*_tmpl` | 各类模板字符串(新增、更新、删除、获取数据等) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 模板定义
|
||||||
|
|
||||||
|
### `ui_tmpl` - 树形组件 UI 配置模板
|
||||||
|
|
||||||
|
此模板用于生成 `index.ui` 文件,定义前端 Tree 组件的配置项。
|
||||||
|
|
||||||
|
#### 参数说明
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 是否必需 | 描述 |
|
||||||
|
|-------|------|----------|------|
|
||||||
|
| `widgettype` | string | 是 | 固定为 `"Tree"` |
|
||||||
|
| `title` | string | 否 | 页面标题;若未提供则使用 `summary[0].title` |
|
||||||
|
| `description` | string | 否 | 页面描述 |
|
||||||
|
| `toolbar` | array | 否 | 工具栏按钮配置(JSON 格式输出) |
|
||||||
|
| `editable` | object | 否 | 编辑功能开关及 URL 配置 |
|
||||||
|
| `add_url` | string | 否 | 新增接口地址,默认为 `./new_{tblname}.dspy` |
|
||||||
|
| `update_url` | string | 否 | 更新接口地址,默认为 `./update_{tblname}.dspy` |
|
||||||
|
| `delete_url` | string | 否 | 删除接口地址,默认为 `./delete_{tblname}.dspy` |
|
||||||
|
| `checkField` | string | 否 | 可选复选框字段 |
|
||||||
|
| `parentField` | string | 是 | 父节点字段名(如 `parent_id`) |
|
||||||
|
| `idField` | string | 是 | 主键字段名 |
|
||||||
|
| `textField` | string | 是 | 显示文本字段名 |
|
||||||
|
| `typeField` | string | 否 | 节点类型字段 |
|
||||||
|
| `params` | object | 否 | 请求附加参数 |
|
||||||
|
| `newdata_params` | object | 否 | 新增时携带参数 |
|
||||||
|
| `node_typeicons` | object | 否 | 不同类型节点图标映射 |
|
||||||
|
| `dataurl` | string | 否 | 获取节点数据接口地址,默认为 `./get_{tblname}.dspy` |
|
||||||
|
| `binds` | object | 否 | 数据绑定配置(JSON 格式输出,缩进4格) |
|
||||||
|
|
||||||
|
> **注意**:模板中使用 `{%- raw -%}` 和 `{%- endraw %}` 包裹动态路径表达式以防止被提前渲染。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `get_nodes_tmpl` - 获取树节点数据脚本模板
|
||||||
|
|
||||||
|
生成用于查询树节点的 Python 异步脚本(`.dspy`),支持父子层级过滤。
|
||||||
|
|
||||||
|
#### SQL 查询逻辑
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM {tblname}
|
||||||
|
WHERE {parentField} = ${id}$ -- 当前节点的子节点
|
||||||
|
OR {parentField} IS NULL -- 根节点
|
||||||
|
ORDER BY {textField}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 上下文变量
|
||||||
|
|
||||||
|
- `params_kw`: 前端传入参数字典
|
||||||
|
- `db`: 使用 `DBPools()` 获取数据库连接池
|
||||||
|
- `dbname`: 通过 `get_module_dbname(modulename)` 获取模块对应数据库名
|
||||||
|
|
||||||
|
返回结果为查询到的记录列表。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心函数说明
|
||||||
|
|
||||||
|
### `gen_tree_ui(d, pat)`
|
||||||
|
生成树形 UI 配置文件 `index.ui`
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
- `d` (dict/DictObject): 渲染上下文数据
|
||||||
|
- `pat` (str): 输出目录路径
|
||||||
|
|
||||||
|
#### 行为
|
||||||
|
1. 使用 `MyTemplateEngine` 渲染 `ui_tmpl`
|
||||||
|
2. 写入 `pat/index.ui` 文件
|
||||||
|
3. 调用 `filter_backslash` 清理反斜杠
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `gen_get_nodedata(d, pat)`
|
||||||
|
生成获取节点数据的 `.dspy` 脚本
|
||||||
|
|
||||||
|
#### 输出文件
|
||||||
|
`get_{tblname}.dspy`
|
||||||
|
|
||||||
|
#### 示例输出
|
||||||
|
```python
|
||||||
|
ns = params_kw.copy()
|
||||||
|
sql = '''select * from my_table where 1 = 1'''
|
||||||
|
id = ns.get('parent_id')
|
||||||
|
if id:
|
||||||
|
sql += " and parent_id = ${id}$"
|
||||||
|
else:
|
||||||
|
sql += " and parent_id is null"
|
||||||
|
sql += " order by name "
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `gen_new_nodedata(d, pat)`
|
||||||
|
生成新增节点接口脚本
|
||||||
|
|
||||||
|
#### 输出文件
|
||||||
|
`new_{tblname}.dspy`
|
||||||
|
|
||||||
|
调用 `data_new_tmpl` 模板进行渲染。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `gen_update_nodedata(d, pat)`
|
||||||
|
生成更新节点接口脚本
|
||||||
|
|
||||||
|
#### 输出文件
|
||||||
|
`update_{tblname}.dspy`
|
||||||
|
|
||||||
|
调用 `data_update_tmpl` 模板进行渲染。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `gen_delete_nodedata(d, pat)`
|
||||||
|
生成删除节点接口脚本
|
||||||
|
|
||||||
|
#### 输出文件
|
||||||
|
`delete_{tblname}.dspy`
|
||||||
|
|
||||||
|
调用 `data_delete_tmpl` 模板进行渲染。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `build_tree_ui(tree_data, dbdesc)`
|
||||||
|
主入口函数:根据树配置和数据库结构生成全部文件
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
- `tree_data`: 包含 `output_dir`, `tblname`, `params` 的对象
|
||||||
|
- `dbdesc`: 数据库结构描述字典(由 `build_dbdesc` 生成)
|
||||||
|
|
||||||
|
#### 流程
|
||||||
|
1. 创建输出目录
|
||||||
|
2. 提取当前表结构并封装为 `DictObject`
|
||||||
|
3. 添加额外参数(如 title、toolbar 等)
|
||||||
|
4. 调用 `subtable2toolbar(tbldesc)` 自动生成工具栏
|
||||||
|
5. 计算可编辑字段列表(排除 `edit_exclouded_fields`、`idField`、`parentField`)
|
||||||
|
6. 依次调用各 `gen_*` 函数生成所有文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `main(dbdesc, outdir, modulename, fn)`
|
||||||
|
解析输入 JSON/XLSX 配置文件并执行生成流程
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
- `dbdesc`: 数据库描述对象
|
||||||
|
- `outdir`: 输出目录
|
||||||
|
- `modulename`: 模块名称(用于确定数据库)
|
||||||
|
- `fn`: 输入配置文件路径(JSON 或 XLSX)
|
||||||
|
|
||||||
|
> ⚠️ 注意:函数重复定义两次,应保留一个。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令行运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tree_generator.py <model_path> <outpath> <modelname> <tree_desc_file> [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `model_path` | 数据模型路径(通常是 `.xlsx` 文件或模型目录) |
|
||||||
|
| `outpath` | 输出代码的目标目录 |
|
||||||
|
| `modelname` | 模块名(用于查找数据库配置) |
|
||||||
|
| `tree_desc_file...` | 一个或多个树形结构定义文件(JSON 或 XLSX) |
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tree_generator.py ./models/db.xlsx ./output/admin usermgr ./trees/org_tree.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置文件结构示例(JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tblname": "org_dept",
|
||||||
|
"output_dir": "./output/admin/org",
|
||||||
|
"title": "组织架构管理",
|
||||||
|
"description": "管理部门与人员的树形结构",
|
||||||
|
"parentField": "parent_id",
|
||||||
|
"idField": "id",
|
||||||
|
"textField": "dept_name",
|
||||||
|
"typeField": "dept_type",
|
||||||
|
"notitle": false,
|
||||||
|
"editable": true,
|
||||||
|
"new_data_url": "/api/org/add",
|
||||||
|
"update_data_url": "/api/org/edit",
|
||||||
|
"delete_data_url": "/api/org/remove",
|
||||||
|
"get_data_url": "/api/org/nodes",
|
||||||
|
"params": {
|
||||||
|
"status": "active"
|
||||||
|
},
|
||||||
|
"newdata_params": {
|
||||||
|
"autoCreateCode": true
|
||||||
|
},
|
||||||
|
"node_typeicons": {
|
||||||
|
"company": "icon-company",
|
||||||
|
"department": "icon-dept"
|
||||||
|
},
|
||||||
|
"edit_exclouded_fields": ["created_time", "updated_by"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输出文件结构
|
||||||
|
|
||||||
|
在指定 `output_dir` 下生成以下文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
output_dir/
|
||||||
|
├── index.ui # 树形界面配置
|
||||||
|
├── get_<tblname>.dspy # 获取节点数据
|
||||||
|
├── new_<tblname>.dspy # 新增节点
|
||||||
|
├── update_<tblname>.dspy # 更新节点
|
||||||
|
└── delete_<tblname>.dspy # 删除节点
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 扩展建议
|
||||||
|
|
||||||
|
1. **增加校验机制**:对必填字段(如 `idField`, `parentField`)做存在性检查
|
||||||
|
2. **支持更多控件类型**:可通过 `widgettype` 扩展为 TreeGrid、Cascade 等
|
||||||
|
3. **国际化支持**:在模板中加入 `i18n` 支持
|
||||||
|
4. **权限控制注入**:可在 `.dspy` 脚本中自动添加权限判断逻辑
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版权与维护
|
||||||
|
|
||||||
|
- 项目:通用 CRUD 代码生成器子模块
|
||||||
|
- 维护者:未知(原始作者)
|
||||||
|
- 修改建议:请提交 PR 或 issue 至项目仓库
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> ✅ 文档版本:v1.0
|
||||||
|
> 📅 最后更新:2025-04-05
|
||||||
315
aidocs/tmpls.md
Normal file
315
aidocs/tmpls.md
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
以下是针对你提供的模板代码编写的 **Markdown 格式技术文档**,涵盖所有五个核心模板(数据浏览器、获取数据、新增数据、更新数据、删除数据)以及关联操作模板。该文档适用于开发人员理解这些 Jinja2 模板的结构、变量用途和整体工作机制。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📄 数据管理模块模板技术文档
|
||||||
|
|
||||||
|
本技术文档描述了用于构建动态数据表格组件及后端数据处理逻辑的一系列 **Jinja2 模板**。这些模板共同实现了一个完整的数据浏览与增删改查(CRUD)系统,支持权限控制、字段加密、过滤条件、分页等功能。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 模板概览
|
||||||
|
|
||||||
|
| 模板名称 | 作用 |
|
||||||
|
|--------|------|
|
||||||
|
| `data_browser_tmpl` | 定义前端“数据浏览器”组件配置(JSON 结构) |
|
||||||
|
| `get_data_tmpl` | 处理数据查询请求的 Python 脚本模板 |
|
||||||
|
| `data_new_tmpl` | 新增记录的处理脚本 |
|
||||||
|
| `data_update_tmpl` | 更新记录的处理脚本 |
|
||||||
|
| `data_delete_tmpl` | 删除记录的处理脚本 |
|
||||||
|
| `check_changed_tmpls` | 多对多关系中复选框变更时触发添加/删除中间表记录 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. `data_browser_tmpl` —— 前端数据表格组件定义
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
生成一个符合前端框架要求的 **Tabular(表格)组件 JSON 配置对象**,用于渲染可编辑的数据列表。
|
||||||
|
|
||||||
|
### 输出示例(简化)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "user_tbl",
|
||||||
|
"widgettype": "Tabular",
|
||||||
|
"options": {
|
||||||
|
"title": "用户列表",
|
||||||
|
"description": "系统用户信息",
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"css": "card",
|
||||||
|
"toolbar": { ... },
|
||||||
|
"editable": { ... },
|
||||||
|
"data_url": "/api/get_user.dspy",
|
||||||
|
"data_method": "GET",
|
||||||
|
"data_params": {},
|
||||||
|
"row_options": {
|
||||||
|
"idField": "id",
|
||||||
|
"fields": [ ... ]
|
||||||
|
},
|
||||||
|
"page_rows": 160,
|
||||||
|
"cache_limit": 5
|
||||||
|
},
|
||||||
|
"binds": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 变量名 | 类型 | 是否必需 | 描述 |
|
||||||
|
|-------|------|----------|------|
|
||||||
|
| `tblname` | string | ✅ | 表格唯一 ID 和命名基础(如 `user_tbl`) |
|
||||||
|
| `title` | string | ❌ | 显示标题;若未提供则使用 `summary[0].title` |
|
||||||
|
| `notitle` | boolean | ❌ | 若为真,则不显示标题 |
|
||||||
|
| `description` | string | ❌ | 表格描述文本 |
|
||||||
|
| `toolbar` | dict/list | ❌ | 工具栏按钮配置(JSON 序列化) |
|
||||||
|
| `editor` | dict | ❌ | 编辑器配置(弹窗或内联编辑设置) |
|
||||||
|
| `noedit` | boolean | ❌ | 若为真,则禁用编辑功能(无新增/删除/更新按钮) |
|
||||||
|
| `new_data_url` | string | ❌ | 自定义新增接口 URL;否则自动生成 `/add_{name}.dspy` |
|
||||||
|
| `delete_data_url` | string | ❌ | 自定义删除接口 URL;否则自动生成 |
|
||||||
|
| `update_data_url` | string | ❌ | 自定义更新接口 URL;否则自动生成 |
|
||||||
|
| `get_data_url` | string | ❌ | 获取数据接口 URL;否则自动生成 `/get_{name}.dspy` |
|
||||||
|
| `data_method` | string | ❌ | 请求方法,默认 `"GET"` |
|
||||||
|
| `params_kw` | dict | ✅ | 发送给后端的初始参数(JSON 序列化) |
|
||||||
|
| `idField` | string | ❌ | 主键字段名,默认 `'id'` |
|
||||||
|
| `checkField` | string | ❌ | 可勾选字段名(用于批量操作) |
|
||||||
|
| `browserfields` | list | ❌ | 浏览模式下可见字段列表 |
|
||||||
|
| `editexclouded` | list | ❌ | 编辑时隐藏的字段列表 |
|
||||||
|
| `fieldliststr` | string | ✅ | 字段定义字符串(已格式化的 JSON 或表达式) |
|
||||||
|
| `content_view` | dict | ❌ | 内容视图配置(如详情页嵌套) |
|
||||||
|
| `subtables_condition` | string | ❌ | 条件判断表达式,决定是否渲染子表格区域 |
|
||||||
|
| `bindsstr` | string | ❌ | 绑定事件或其他交互逻辑(JSON 字符串) |
|
||||||
|
|
||||||
|
> ⚠️ 注意:`{%- raw -%}` 和 `{%- endraw %}` 是为了防止 Jinja2 解析内部模板语法而使用的保护标签。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. `get_data_tmpl` —— 数据查询处理脚本
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
根据传入参数执行 SQL 查询并返回分页结果。支持自动过滤、排序、用户身份绑定等。
|
||||||
|
|
||||||
|
### 执行流程
|
||||||
|
1. 复制输入参数 `params_kw`
|
||||||
|
2. (可选)检查登录状态并注入 `userid` / `userorgid`
|
||||||
|
3. 设置默认分页和排序规则
|
||||||
|
4. 构造过滤条件(`data_filter` 支持 JSON 过滤器)
|
||||||
|
5. 替换 SQL 中的变量(如 `${userid}$`, `${filterstr}$`)
|
||||||
|
6. 执行分页查询并返回 `{total, rows}`
|
||||||
|
|
||||||
|
### 关键特性
|
||||||
|
- ✅ 支持复杂过滤条件(通过 `DBFilter` 类解析)
|
||||||
|
- ✅ 支持动态变量替换(`${var}`)
|
||||||
|
- ✅ 支持外键关联排序(`relation.outter_field_text`)
|
||||||
|
- ✅ 支持模块级数据库隔离(`get_module_dbname()`)
|
||||||
|
|
||||||
|
### 使用变量
|
||||||
|
|
||||||
|
| 变量 | 描述 |
|
||||||
|
|------|------|
|
||||||
|
| `sql` | 原始 SQL 查询语句(含变量占位符) |
|
||||||
|
| `modulename` | 所属模块名称,用于确定数据库 |
|
||||||
|
| `sortby` | 默认排序字段(字符串或数组) |
|
||||||
|
| `logined_userid` | 若设置,则必须登录才能访问,并将当前用户 ID 注入查询 |
|
||||||
|
| `logined_userorgid` | 同上,组织 ID |
|
||||||
|
| `relation` | 如果是关联子表,启用特殊排序逻辑 |
|
||||||
|
| `fields` | 字段元信息列表,用于生成默认过滤器 |
|
||||||
|
|
||||||
|
### 返回格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"total": 100,
|
||||||
|
"rows": [
|
||||||
|
{"id": "1", "name": "Alice"},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. `data_new_tmpl` —— 新增数据处理脚本
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
接收客户端提交的数据,插入新记录到指定表中。
|
||||||
|
|
||||||
|
### 关键行为
|
||||||
|
- 自动生成 UUID 作为主键(若未提供或长度非法)
|
||||||
|
- 对敏感字段进行密码加密(`password_encode()`)
|
||||||
|
- 注入当前用户/组织 ID(如果启用了权限绑定)
|
||||||
|
- 调用 ORM 的 `C()` 方法创建记录
|
||||||
|
|
||||||
|
### 使用变量
|
||||||
|
| 变量 | 描述 |
|
||||||
|
|------|------|
|
||||||
|
| `confidential_fields` | 需要加密存储的字段列表(如密码) |
|
||||||
|
| `logined_userid` / `logined_userorgid` | 控制是否需要登录并注入上下文 |
|
||||||
|
| `modulename` | 确定目标数据库 |
|
||||||
|
| `summary[0].name` | 目标数据表名 |
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"widgettype": "Message",
|
||||||
|
"options": {
|
||||||
|
"title": "Add Success",
|
||||||
|
"message": "ok",
|
||||||
|
"timeout": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 失败响应
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"widgettype": "Error",
|
||||||
|
"options": {
|
||||||
|
"title": "Add Error",
|
||||||
|
"message": "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `data_update_tmpl` —— 更新数据处理脚本
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
更新已有记录,支持字段加密和权限校验。
|
||||||
|
|
||||||
|
### 特性
|
||||||
|
- 支持对 `confidential_fields` 加密后再保存
|
||||||
|
- 必须包含 `id` 字段用于定位记录
|
||||||
|
- 使用 ORM 的 `U()` 方法执行更新
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"widgettype": "Message",
|
||||||
|
"options": {
|
||||||
|
"title": "Update Success",
|
||||||
|
"message": "ok",
|
||||||
|
"timeout": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 失败响应
|
||||||
|
同新增失败格式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. `data_delete_tmpl` —— 删除数据处理脚本
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
根据 `id` 删除一条记录,支持权限上下文注入。
|
||||||
|
|
||||||
|
### 行为说明
|
||||||
|
- 接收 `params_kw.id`
|
||||||
|
- 可选注入 `userid` / `userorgid` 到删除条件中
|
||||||
|
- 使用 ORM 的 `D()` 方法执行软/硬删除
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"widgettype": "Message",
|
||||||
|
"options": {
|
||||||
|
"title": "Delete Success",
|
||||||
|
"message": "ok",
|
||||||
|
"timeout": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. `check_changed_tmpls` —— 多对多关系复选处理
|
||||||
|
|
||||||
|
### 场景
|
||||||
|
用于处理“角色分配”、“权限勾选”等场景中的中间表记录增删。
|
||||||
|
|
||||||
|
### 实现机制
|
||||||
|
- 接收 `has_xxx` 参数表示是否选中
|
||||||
|
- 若为 `'true'` → 插入中间表记录
|
||||||
|
- 否则 → 删除对应中间表记录(通过组合键删除)
|
||||||
|
|
||||||
|
### 示例场景
|
||||||
|
用户 A 是否拥有角色 B:
|
||||||
|
- 表:`user_role`
|
||||||
|
- 字段:`user_id`, `role_id`
|
||||||
|
- 当复选框变化时调用此脚本
|
||||||
|
|
||||||
|
### SQL 示例
|
||||||
|
```sql
|
||||||
|
DELETE FROM user_role
|
||||||
|
WHERE user_id='${user_id}$' AND role_id='${role_id}$'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
统一返回消息提示(成功 + 自动关闭)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 公共工具函数依赖
|
||||||
|
|
||||||
|
以下函数在模板运行环境中需可用:
|
||||||
|
|
||||||
|
| 函数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `uuid()` | 生成 UUID 字符串 |
|
||||||
|
| `get_user()` | 异步获取当前登录用户 ID |
|
||||||
|
| `get_userorgid()` | 异步获取当前用户所属组织 ID |
|
||||||
|
| `entire_url(path)` | 将相对路径转为完整 URL |
|
||||||
|
| `password_encode(val)` | 对敏感值进行加密(如 bcrypt/scrypt) |
|
||||||
|
| `default_filterjson(fields, ns)` | 根据字段生成默认搜索过滤器 |
|
||||||
|
| `DBFilter(filterjson)` | 解析前端传来的过滤 JSON 并生成 SQL 条件 |
|
||||||
|
| `ArgsConvert('[[]]', ']]')` | 处理 `[[var]]` 类型变量替换 |
|
||||||
|
| `debug(msg)` | 输出调试日志 |
|
||||||
|
| `DBPools()` | 获取数据库连接池实例 |
|
||||||
|
| `get_module_dbname(modulename)` | 根据模块名获取对应数据库名 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ 安全机制
|
||||||
|
|
||||||
|
| 机制 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 登录校验 | 所有写操作均可配置强制登录验证 |
|
||||||
|
| 敏感字段加密 | 支持自动加密指定字段(如密码) |
|
||||||
|
| 参数绑定 | 所有变量均通过命名参数传递,避免 SQL 注入 |
|
||||||
|
| 过滤器抽象 | 使用 `DBFilter` 类安全地构造 WHERE 子句 |
|
||||||
|
| 变量沙箱 | 使用 `${}` 占位符 + 命名空间替换,防止直接拼接 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📎 总结:典型使用流程
|
||||||
|
|
||||||
|
1. **前端渲染**:使用 `data_browser_tmpl` 渲染表格组件
|
||||||
|
2. **加载数据**:调用 `get_data_url`(由 `get_data_tmpl` 提供服务)
|
||||||
|
3. **编辑操作**:
|
||||||
|
- 新增 → `new_data_url` → `data_new_tmpl`
|
||||||
|
- 修改 → `update_data_url` → `data_update_tmpl`
|
||||||
|
- 删除 → `delete_data_url` → `data_delete_tmpl`
|
||||||
|
4. **关联操作**:通过 `check_changed_tmpls` 处理多对多关系变更
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 附录:变量命名约定
|
||||||
|
|
||||||
|
| 前缀/后缀 | 含义 |
|
||||||
|
|----------|------|
|
||||||
|
| `_url` | 接口地址 |
|
||||||
|
| `_tmpl` | Jinja2 模板字符串 |
|
||||||
|
| `ns` | namespace,即参数命名空间 |
|
||||||
|
| `params_kw` | 客户端传入的所有参数字典 |
|
||||||
|
| `sor` | `sqlorContext` 实例,封装了 ORM 操作 |
|
||||||
|
| `db` | 数据库连接池 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 📝 文档版本:v1.0
|
||||||
|
> 💬 维护团队:Backend & Low-code Platform Team
|
||||||
|
> 🕒 最后更新:2025年4月5日
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ *建议配合实际模板引擎调试工具(如预览渲染结果)以确保变量正确注入。*
|
||||||
468
aidocs/xls2crud.md
Normal file
468
aidocs/xls2crud.md
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
# `xls2crud.py` 技术文档
|
||||||
|
|
||||||
|
> **工具名称**:`xls2crud.py`
|
||||||
|
> **功能描述**:从 Excel 模型文件(`.xlsx`)自动生成基于模板的 CRUD 前后端界面代码,支持数据浏览器、新增、编辑、删除、查询及关联子表操作。
|
||||||
|
> **适用场景**:快速构建基于数据库模型的 Web 应用前端 UI 和后端接口描述。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
- [1. 简介](#1-简介)
|
||||||
|
- [2. 依赖说明](#2-依赖说明)
|
||||||
|
- [3. 使用方式](#3-使用方式)
|
||||||
|
- [4. 核心函数说明](#4-核心函数说明)
|
||||||
|
- [`build_dbdesc`](#builddbdescmodels_dir--dict)
|
||||||
|
- [`subtable2toolbar`](#subtable2toolbardesc)
|
||||||
|
- [`build_crud_ui`](#build_crud_uicrud_data-dict-dbdesc-dict)
|
||||||
|
- [`build_table_crud_ui`](#build_table_crud_uiuidir-str-desc-dict---none)
|
||||||
|
- [`field_list`](#field_listdesc-dict---list)
|
||||||
|
- [`get_code_desc`](#get_code_desffield-dict-desc-dict---dict)
|
||||||
|
- [`setup_ui_info`](#setup_ui_infofielddict-confidential_fieldslist---dict)
|
||||||
|
- [`construct_get_data_sql`](#construct_get_data_sqldesc-dict---str)
|
||||||
|
- 其他辅助函数
|
||||||
|
- [5. 模板系统与输出结构](#5-模板系统与输出结构)
|
||||||
|
- [6. 配置文件格式 (`json_file`)](#6-配置文件格式-json_file)
|
||||||
|
- [7. 输出目录结构示例](#7-输出目录结构示例)
|
||||||
|
- [8. 注意事项与限制](#8-注意事项与限制)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 简介
|
||||||
|
|
||||||
|
本脚本通过读取 `.xlsx` 表格定义的数据模型和 JSON 配置文件,自动为指定数据表生成完整的 CRUD 界面组件:
|
||||||
|
|
||||||
|
- 数据浏览页(`index.ui`)
|
||||||
|
- 新增表单(`add_*.dspy`)
|
||||||
|
- 编辑表单(`update_*.dspy`)
|
||||||
|
- 删除逻辑(`delete_*.dspy`)
|
||||||
|
- 数据获取接口(`get_*.dspy`)
|
||||||
|
- 关联字段状态检查(`check_changed.dspy`)
|
||||||
|
|
||||||
|
所有输出均基于预设的 Jinja-like 模板渲染,并支持动态参数替换。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 依赖说明
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import codecs
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from appPublic.dictObject import DictObject
|
||||||
|
from xlsxData import xlsxFactory
|
||||||
|
from appPublic.folderUtils import listFile, _mkdir
|
||||||
|
from appPublic.myTE import MyTemplateEngine
|
||||||
|
from tmpls import (
|
||||||
|
data_browser_tmpl,
|
||||||
|
get_data_tmpl,
|
||||||
|
data_new_tmpl,
|
||||||
|
data_update_tmpl,
|
||||||
|
data_delete_tmpl,
|
||||||
|
check_changed_tmpls
|
||||||
|
)
|
||||||
|
from appPublic.argsConvert import ArgsConvert
|
||||||
|
```
|
||||||
|
|
||||||
|
### 外部模块说明
|
||||||
|
|
||||||
|
| 模块 | 功能 |
|
||||||
|
|------|------|
|
||||||
|
| `appPublic.dictObject` | 将字典转换为可点式访问的对象(类似 JavaScript 的对象) |
|
||||||
|
| `xlsxData.xlsxFactory` | 解析 `.xlsx` 文件并提取结构化数据 |
|
||||||
|
| `appPublic.folderUtils.listFile`, `_mkdir` | 文件遍历与目录创建工具 |
|
||||||
|
| `appPublic.myTE.MyTemplateEngine` | 轻量级模板引擎,支持变量替换 |
|
||||||
|
| `tmpls.*` | 内置字符串形式的模板内容(HTML/UI/DSPY) |
|
||||||
|
| `appPublic.argsConvert` | 支持 `${var}$` 或 `[[var]]` 形式的变量替换 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 使用方式
|
||||||
|
|
||||||
|
### 命令行语法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python xls2crud.py [-m models_dir] [-o output_dir] modulename json_file [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 参数 | 是否必需 | 说明 |
|
||||||
|
|------|----------|------|
|
||||||
|
| `modulename` | ✅ 是 | 当前模块名,用于数据库上下文解析 |
|
||||||
|
| `json_file` | ✅ 是 | 至少一个 CRUD 配置 JSON 文件路径 |
|
||||||
|
| `-m`, `--models_dir` | ❌ 否 | `.xlsx` 模型文件所在目录(可在 JSON 中覆盖) |
|
||||||
|
| `-o`, `--output_dir` | ❌ 否 | 输出根目录,默认为当前路径 |
|
||||||
|
|
||||||
|
### 示例命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python xls2crud.py -m ./models -o ./ui_output user_module user_config.json role_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 核心函数说明
|
||||||
|
|
||||||
|
### `build_dbdesc(models_dir: str) -> dict`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
扫描指定目录下的所有 `.xlsx` 文件,将其解析为统一的数据库描述对象。
|
||||||
|
|
||||||
|
#### 输入
|
||||||
|
- `models_dir`: 字符串或列表,包含模型文件路径
|
||||||
|
|
||||||
|
#### 输出
|
||||||
|
- `dict`: 键为表名(来自 `summary[0].name`),值为该表的完整描述对象(含字段、编码表、子表等)
|
||||||
|
|
||||||
|
#### 流程
|
||||||
|
1. 遍历目录中所有 `.xlsx` 文件
|
||||||
|
2. 使用 `xlsxFactory` 加载每个文件
|
||||||
|
3. 提取其 `get_data()` 结果
|
||||||
|
4. 取第一项 summary 的 name 作为表名注册进 `db_desc`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `subtable2toolbar(desc)`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
将 `desc.subtables` 中定义的子表信息转化为工具栏按钮及弹窗绑定事件。
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
- `desc`: 当前表的描述对象(DictObject)
|
||||||
|
|
||||||
|
#### 行为
|
||||||
|
- 若无 `toolbar`,初始化为空对象
|
||||||
|
- 为每个子表添加:
|
||||||
|
- 工具栏图标按钮(SVG 图标 + label)
|
||||||
|
- 绑定点击事件打开 PopupWindow
|
||||||
|
- POST 请求跳转至对应 URL(支持相对路径)
|
||||||
|
- 自动映射主键 `id` 和引用控件 `referer_widget`
|
||||||
|
- 支持额外字段映射(via `mapping`)
|
||||||
|
|
||||||
|
#### 示例生成的 bind 结构
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "child_table",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "PopupWindow",
|
||||||
|
"popup_options": {
|
||||||
|
"title": "子表标题",
|
||||||
|
"width": "70%",
|
||||||
|
"height": "70%"
|
||||||
|
},
|
||||||
|
"params_mapping": {
|
||||||
|
"mapping": { "id": "parent_id", "referer_widget": "referer_widget" }
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "{{entire_url('../child_table')}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `build_crud_ui(crud_data: dict, dbdesc: dict)`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
根据传入的配置和数据库描述,驱动整个 CRUD 界面生成流程。
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
- `crud_data`: 来自 JSON 文件的配置对象(DictObject)
|
||||||
|
- `dbdesc`: 所有表的元数据集合
|
||||||
|
|
||||||
|
#### 步骤
|
||||||
|
1. 获取目标表名 `tblname`
|
||||||
|
2. 更新 `desc` 参数(如权限、标题等)
|
||||||
|
3. 调用 `subtable2toolbar(desc)` 添加子表按钮
|
||||||
|
4. 如果存在 `relation` 字段,则启用“行选中联动”机制
|
||||||
|
5. 渲染 `binds` 为 JSON 字符串
|
||||||
|
6. 调用 `build_table_crud_ui()` 开始生成具体文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `build_table_crud_ui(uidir: str, desc: dict) -> None`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
协调生成所有与当前表相关的 UI 文件。
|
||||||
|
|
||||||
|
#### 行为
|
||||||
|
- 创建输出目录(若不存在)
|
||||||
|
- 生成:
|
||||||
|
- `index.ui`(数据浏览器)
|
||||||
|
- `get_{tblname}.dspy`(数据获取)
|
||||||
|
- 若有外键关系(`desc.relation`):
|
||||||
|
- 仅生成 `check_changed.dspy`
|
||||||
|
- 否则生成标准三件套:
|
||||||
|
- `add_*.dspy`
|
||||||
|
- `update_*.dspy`
|
||||||
|
- `delete_*.dspy`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `field_list(desc: dict) -> list`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
构造适用于前端展示的字段列表,包含类型、宽度、标签、UI 类型等信息。
|
||||||
|
|
||||||
|
#### 逻辑
|
||||||
|
1. 遍历 `desc.fields`
|
||||||
|
2. 判断是否是码表字段(在 `codes` 中定义)
|
||||||
|
- 是 → 调用 `get_code_desc`
|
||||||
|
- 否 → 调用 `setup_ui_info`
|
||||||
|
3. 应用 `browserfields.alters` 进行个性化覆盖
|
||||||
|
4. 返回最终字段数组
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `get_code_desc(field: dict, desc: dict) -> dict`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
处理码表字段(如性别、状态),将其转换为下拉选择框或远程数据源字段。
|
||||||
|
|
||||||
|
#### 输出字段说明
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `uitype` | `'code'` 表示码表类型 |
|
||||||
|
| `valueField` / `textField` | 实际值与显示文本字段名 |
|
||||||
|
| `params` | 请求码表数据所需参数(dbname, table, cond 等) |
|
||||||
|
| `dataurl` | 固定为 `/appbase/get_code.dspy` |
|
||||||
|
|
||||||
|
#### 特性支持
|
||||||
|
- 支持条件表达式中的变量替换(如 `${dept_id}$`)
|
||||||
|
- 使用 `ArgsConvert` 解析并注入运行时变量
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `setup_ui_info(field:dict, confidential_fields=[]) ->dict`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
为普通字段设置默认 UI 显示属性。
|
||||||
|
|
||||||
|
#### 类型映射规则
|
||||||
|
|
||||||
|
| 数据库类型 | `uitype` | 说明 |
|
||||||
|
|-----------|---------|------|
|
||||||
|
| `date` | `date` | 日期输入框 |
|
||||||
|
| `time` | `time` | 时间输入框 |
|
||||||
|
| `int`, `short`, etc. | `int` | 整数输入 |
|
||||||
|
| `float`, `double`, `decimal` | `float` | 浮点数输入 |
|
||||||
|
| `text` | `text` | 多行文本 |
|
||||||
|
| 其他 | `str` | 默认字符串 |
|
||||||
|
| 名称以 `_date` 结尾 | `date` | 智能识别 |
|
||||||
|
| 在 `confidential_fields` 中 | `password` | 密码掩码 |
|
||||||
|
|
||||||
|
#### 宽度计算
|
||||||
|
- `cwidth = min(max(length, 4), 18)`
|
||||||
|
- 最小 4,最大 18(单位字符宽)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `construct_get_data_sql(desc: dict) -> str`
|
||||||
|
|
||||||
|
#### 功能
|
||||||
|
构建用于获取主表及其码表连接数据的 SQL 查询语句。
|
||||||
|
|
||||||
|
#### 分支逻辑
|
||||||
|
|
||||||
|
##### A. 存在外键关联且有码表
|
||||||
|
生成 LEFT JOIN 查询,判断是否存在外键记录(`has_xxx` 标志位),并带出码表文本。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT '${param_field}$' AS param_field,
|
||||||
|
CASE WHEN b.param_field IS NULL THEN 0 ELSE 1 END has_param_field,
|
||||||
|
a.value AS code_field,
|
||||||
|
a.text AS code_field_text
|
||||||
|
FROM code_table a
|
||||||
|
LEFT JOIN (SELECT * FROM main_table WHERE param_field = ${param_field}$) b
|
||||||
|
ON a.value = b.code_field;
|
||||||
|
```
|
||||||
|
|
||||||
|
##### B. 无码表
|
||||||
|
直接查询主表 + 占位符 `[[filterstr]]`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM table_name WHERE 1=1 [[filterstr]]
|
||||||
|
```
|
||||||
|
|
||||||
|
##### C. 有一般码表(非外键)
|
||||||
|
构建多个 LEFT JOIN 子查询,别名为 `b`, `c`, ...,拼接成完整查询。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT a.*, b.type_text, c.status_text
|
||||||
|
FROM (SELECT * FROM main WHERE 1=1 [[filterstr]]) a
|
||||||
|
LEFT JOIN (SELECT value AS status, text AS status_text FROM status_codes WHERE 1=1) c
|
||||||
|
ON a.status = c.status;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 其他辅助函数
|
||||||
|
|
||||||
|
| 函数 | 功能 |
|
||||||
|
|------|------|
|
||||||
|
| `alter_field(field, desc)` | 使用 `desc.browserfields.alters[name]` 修改特定字段属性 |
|
||||||
|
| `filter_backslash(s)` | 替换字符串中的 `\\/` 为 `/`,防止模板路径错误 |
|
||||||
|
| `build_data_browser(pat, desc)` | 渲染 `data_browser_tmpl` → `index.ui` |
|
||||||
|
| `build_data_new` / `update` / `delete` | 分别生成增删改页面 |
|
||||||
|
| `build_get_data` | 生成数据查询接口 `.dspy` 文件 |
|
||||||
|
| `build_check_changed` | 生成用于检测外键关联变化的特殊接口 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 模板系统与输出结构
|
||||||
|
|
||||||
|
### 模板来源
|
||||||
|
|
||||||
|
所有模板来自 `tmpls` 模块,包括:
|
||||||
|
|
||||||
|
| 模板变量 | 用途 |
|
||||||
|
|--------|------|
|
||||||
|
| `data_browser_tmpl` | 主列表界面(index.ui) |
|
||||||
|
| `get_data_tmpl` | 数据获取接口 |
|
||||||
|
| `data_new_tmpl` | 新增页面 |
|
||||||
|
| `data_update_tmpl` | 编辑页面 |
|
||||||
|
| `data_delete_tmpl` | 删除确认/执行页面 |
|
||||||
|
| `check_changed_tmpls` | 外键关联状态检查接口 |
|
||||||
|
|
||||||
|
### 模板语法支持
|
||||||
|
|
||||||
|
- `{{ var }}`:变量插入
|
||||||
|
- `${ var }$`:环境变量占位符(由 `ArgsConvert` 替换)
|
||||||
|
- `[[ filterstr ]]`:SQL 过滤条件注入点
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 配置文件格式 (`json_file`)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tblname": "user",
|
||||||
|
"alias": "user_manage",
|
||||||
|
"params": {
|
||||||
|
"title": "用户管理",
|
||||||
|
"pagesize": 20,
|
||||||
|
"confidential_fields": ["password"],
|
||||||
|
"relation": {
|
||||||
|
"param_field": "org_id",
|
||||||
|
"outter_field": "id",
|
||||||
|
"table": "organization"
|
||||||
|
},
|
||||||
|
"codes": [
|
||||||
|
{
|
||||||
|
"field": "status",
|
||||||
|
"table": "sys_codes",
|
||||||
|
"valuefield": "c_value",
|
||||||
|
"textfield": "c_text",
|
||||||
|
"cond": "ctype='USER_STATUS'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subtables": [
|
||||||
|
{
|
||||||
|
"subtable": "user_role",
|
||||||
|
"title": "角色分配",
|
||||||
|
"field": "user_id",
|
||||||
|
"url": "../user_role",
|
||||||
|
"mapping": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"toolbar": null,
|
||||||
|
"binds": [],
|
||||||
|
"browserfields": {
|
||||||
|
"alters": {
|
||||||
|
"name": { "label": "姓名", "uitype": "str", "cwidth": 20 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models_dir": "./models"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段说明
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `tblname` | string | 主表名(必须存在于 `.xlsx` 中) |
|
||||||
|
| `alias` | string | 输出目录名(可选,默认为 `tblname`) |
|
||||||
|
| `params` | object | 表级配置对象 |
|
||||||
|
| `params.title` | string | 页面标题 |
|
||||||
|
| `params.confidential_fields` | array | 需隐藏显示的字段(变为密码框) |
|
||||||
|
| `params.relation` | object | 外键关联信息(启用 check_changed) |
|
||||||
|
| `params.codes` | array | 码表配置 |
|
||||||
|
| `params.subtables` | array | 子表配置(生成工具栏按钮) |
|
||||||
|
| `params.browserfields.alters` | object | 字段显示定制 |
|
||||||
|
| `models_dir` | string | .xlsx 模型目录(优先级高于命令行) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 输出目录结构示例
|
||||||
|
|
||||||
|
假设命令为:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python xls2crud.py -o ./dist -m ./models user_mod user_conf.json
|
||||||
|
```
|
||||||
|
|
||||||
|
且 `user_conf.json` 中 `"alias": "users"`,`"tblname": "user"`
|
||||||
|
|
||||||
|
则输出结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
./dist/
|
||||||
|
└── users/
|
||||||
|
├── index.ui # 数据浏览器
|
||||||
|
├── add_user.dspy # 新增页面
|
||||||
|
├── update_user.dspy # 编辑页面
|
||||||
|
├── delete_user.dspy # 删除页面
|
||||||
|
├── get_user.dspy # 数据获取接口
|
||||||
|
└── check_changed.dspy # (如果有 relation)状态检查接口
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 注意事项与限制
|
||||||
|
|
||||||
|
### ⚠️ 已知限制
|
||||||
|
|
||||||
|
1. **Excel 文件要求**
|
||||||
|
- 必须包含 `summary` 工作表
|
||||||
|
- 第一行 `summary[0].name` 作为表名
|
||||||
|
- `fields` 表格需定义字段元信息(name, type, title, length...)
|
||||||
|
|
||||||
|
2. **模板路径问题**
|
||||||
|
- 模板内使用 `{{entire_url(...)}}` 构造绝对路径,请确保前端框架支持此函数
|
||||||
|
|
||||||
|
3. **SQL 注入防护**
|
||||||
|
- `[[filterstr]]` 不做转义,应由调用端保证安全
|
||||||
|
|
||||||
|
4. **并发写入风险**
|
||||||
|
- 多个 JSON 文件同时处理同一张表可能导致文件覆盖
|
||||||
|
|
||||||
|
5. **编码要求**
|
||||||
|
- 所有 `.xlsx` 和 `.json` 文件必须为 UTF-8 编码
|
||||||
|
|
||||||
|
### ✅ 最佳实践建议
|
||||||
|
|
||||||
|
- 使用 `alias` 区分不同视图的输出路径
|
||||||
|
- 在 `alters` 中统一设置中文标签和列宽
|
||||||
|
- 对敏感字段显式加入 `confidential_fields`
|
||||||
|
- 使用 `cond` + 变量实现动态码表过滤
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本信息
|
||||||
|
|
||||||
|
- 创建时间:未知
|
||||||
|
- 最后更新:根据代码注释推测近期维护
|
||||||
|
- 维护者:内部团队(`appPublic`, `xslxDATA` 为私有库)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 📝 文档版本:v1.0
|
||||||
|
> ✅ 审核状态:已完成初稿
|
||||||
|
> 💬 如需扩展模板字段或增加校验逻辑,请联系开发组。
|
||||||
277
aidocs/xls2ddl.md
Normal file
277
aidocs/xls2ddl.md
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
# 技术文档:Excel/JSON 模型转 DDL 脚本工具
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
本工具是一个基于 Python 的命令行程序,用于将 Excel(`.xlsx`)或 JSON 格式的数据模型文件转换为指定数据库类型的 SQL DDL(数据定义语言)建表语句。支持多种主流数据库,包括:
|
||||||
|
|
||||||
|
- SQL Server
|
||||||
|
- MySQL
|
||||||
|
- Oracle
|
||||||
|
- PostgreSQL
|
||||||
|
|
||||||
|
此外,工具还支持根据数据生成 `INSERT` 语句,便于初始化测试数据。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
| 特性 | 描述 |
|
||||||
|
|------|------|
|
||||||
|
| 多格式输入 | 支持 `.xlsx` 和 `.json` 文件作为输入源 |
|
||||||
|
| 多数据库输出 | 支持生成四种数据库的 DDL 语法 |
|
||||||
|
| 模板驱动 | 使用模板引擎自定义 DDL 输出格式 |
|
||||||
|
| 自动生成插入语句 | 若模型中包含数据,自动附加 `INSERT INTO` 语句 |
|
||||||
|
| 批量处理 | 可遍历整个目录下的所有模型文件并合并输出 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录结构与依赖说明
|
||||||
|
|
||||||
|
### 项目依赖模块
|
||||||
|
|
||||||
|
```python
|
||||||
|
import io
|
||||||
|
import sys
|
||||||
|
from traceback import print_exc
|
||||||
|
from xlsxData import CRUDData, xlsxFactory
|
||||||
|
import codecs
|
||||||
|
import json
|
||||||
|
from sqlor.ddl_template_sqlserver import sqlserver_ddl_tmpl
|
||||||
|
from sqlor.ddl_template_mysql import mysql_ddl_tmpl
|
||||||
|
from sqlor.ddl_template_oracle import oracle_ddl_tmpl
|
||||||
|
from sqlor.ddl_template_postgresql import postgresql_ddl_tmpl
|
||||||
|
from appPublic.myTE import MyTemplateEngine
|
||||||
|
from appPublic.folderUtils import listFile
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意**:
|
||||||
|
> - `xlsxData`:负责读取 `.xlsx` 文件并解析成结构化数据。
|
||||||
|
> - `sqlor.*_ddl_tmpl`:各数据库对应的 DDL 模板字符串。
|
||||||
|
> - `appPublic.myTE.MyTemplateEngine`:轻量级模板渲染引擎。
|
||||||
|
> - `appPublic.folderUtils.listFile`:递归列出指定后缀的文件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心函数说明
|
||||||
|
|
||||||
|
### 1. `xls2ddl(xlsfile: str, dbtype: str) -> str`
|
||||||
|
|
||||||
|
将单个 `.xlsx` 或 `.json` 文件转换为对应数据库的 DDL 脚本。
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 说明 |
|
||||||
|
|-------|------|------|
|
||||||
|
| `xlsfile` | `str` | 输入文件路径(支持 `.xlsx` 或 `.json`) |
|
||||||
|
| `dbtype` | `str` | 目标数据库类型,如 `"mysql"`、`"oracle"` 等(不区分大小写) |
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|
||||||
|
- 成功时返回完整的 DDL 字符串(含可选的 `INSERT` 语句)
|
||||||
|
- 失败时打印错误信息并返回 `None`
|
||||||
|
|
||||||
|
#### 工作流程
|
||||||
|
|
||||||
|
1. 判断文件是否为 JSON:
|
||||||
|
- 是 → 使用 `codecs.open` 以 UTF-8 编码加载 JSON 数据
|
||||||
|
- 否 → 使用 `xlsxFactory` 创建解析器对象读取 XLSX
|
||||||
|
2. 获取数据内容 (`data = d.get_data()`)
|
||||||
|
3. 查找对应数据库的 DDL 模板
|
||||||
|
4. 使用 `MyTemplateEngine` 渲染 DDL 模板
|
||||||
|
5. 如果存在数据(`data.data`),调用 `gen_insert()` 生成插入语句并追加
|
||||||
|
6. 返回最终 SQL 脚本
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `gen_insert(xls: CRUDData) -> str`
|
||||||
|
|
||||||
|
根据模型中的数据生成多条 `INSERT INTO ... VALUES ...` 语句。
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `xls` | `CRUDData` 实例 | 包含表名和数据行的对象 |
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|
||||||
|
- 返回拼接后的多条插入语句字符串,每条以分号结尾,换行分隔
|
||||||
|
|
||||||
|
#### 处理逻辑
|
||||||
|
|
||||||
|
- 提取第一张表的名称:`tbl = xls.summary[0].name`
|
||||||
|
- 遍历每一行数据:
|
||||||
|
- 键作为字段名
|
||||||
|
- 值如果是字符串则加单引号,否则转为字符串
|
||||||
|
- 组合为标准 `INSERT` 语句
|
||||||
|
|
||||||
|
> 示例输出:
|
||||||
|
```sql
|
||||||
|
insert into users (id, name, age) values (1, 'Alice', 25);
|
||||||
|
insert into users (id, name, age) values (2, 'Bob', 30);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. `model2ddl(folder: str, dbtype: str) -> str`
|
||||||
|
|
||||||
|
批量处理一个目录下所有 `.xlsx` 和 `.json` 文件,生成统一的 DDL 脚本。
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `folder` | `str` | 模型文件所在目录路径 |
|
||||||
|
| `dbtype` | `str` | 目标数据库类型 |
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|
||||||
|
- 合并后的完整 DDL 字符串,每个文件前添加注释 `-- 文件路径`
|
||||||
|
|
||||||
|
#### 异常处理
|
||||||
|
|
||||||
|
- 对每个文件进行 `try...except` 包裹
|
||||||
|
- 出错时输出异常信息及堆栈跟踪,继续处理其他文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 模板系统设计
|
||||||
|
|
||||||
|
### 模板映射表:`tmpls`
|
||||||
|
|
||||||
|
```python
|
||||||
|
tmpls = {
|
||||||
|
"sqlserver": sqlserver_ddl_tmpl,
|
||||||
|
"mysql": mysql_ddl_tmpl,
|
||||||
|
"oracle": oracle_ddl_tmpl,
|
||||||
|
"postgresql": postgresql_ddl_tmpl
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 所有模板均为字符串形式,内部使用占位符(由 `MyTemplateEngine` 解析),例如:
|
||||||
|
>
|
||||||
|
> ```sql
|
||||||
|
> CREATE TABLE {{table_name}} (
|
||||||
|
> {% for col in columns %}
|
||||||
|
> {{col.name}} {{col.type}}{% if col.nullable == False %} NOT NULL{% endif %},
|
||||||
|
> {% endfor %}
|
||||||
|
> );
|
||||||
|
> ```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 主程序入口(命令行运行)
|
||||||
|
|
||||||
|
### 运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python your_script.py <dbtype> <folder_or_file>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python model2ddl.py mysql ./models/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 参数说明
|
||||||
|
|
||||||
|
| 参数位置 | 含义 |
|
||||||
|
|---------|------|
|
||||||
|
| `sys.argv[1]` | 数据库类型(如 `mysql`) |
|
||||||
|
| `sys.argv[2]` | 模型文件夹路径 |
|
||||||
|
|
||||||
|
#### 中文输出兼容性修复
|
||||||
|
|
||||||
|
为防止 Windows 控制台因编码问题报错(如 `'gbk' codec can't encode \u200b`),设置 stdout 编码为 UTF-8:
|
||||||
|
|
||||||
|
```python
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 输入文件示例(`user.xlsx`)
|
||||||
|
|
||||||
|
| Field | Type | Null | Default | Comment |
|
||||||
|
|-----------|-------------|------|---------|---------------|
|
||||||
|
| id | int | NO | | 主键 |
|
||||||
|
| username | varchar(32) | NO | | 用户名 |
|
||||||
|
| email | varchar(64) | YES | NULL | 邮箱地址 |
|
||||||
|
|
||||||
|
数据页(可选):
|
||||||
|
|
||||||
|
| id | username | email |
|
||||||
|
|----|----------|------------------|
|
||||||
|
| 1 | alice | alice@exam.com |
|
||||||
|
| 2 | bob | bob@exam.com |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 输出示例(MySQL)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- ./models/user.xlsx
|
||||||
|
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INT NOT NULL COMMENT '主键',
|
||||||
|
username VARCHAR(32) NOT NULL COMMENT '用户名',
|
||||||
|
email VARCHAR(64) NULL DEFAULT NULL COMMENT '邮箱地址'
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into user (id, username, email) values (1, 'alice', 'alice@exam.com');
|
||||||
|
insert into user (id, username, email) values (2, 'bob', 'bob@exam.com');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **文件编码要求**
|
||||||
|
- JSON 文件必须为 UTF-8 编码
|
||||||
|
- 推荐所有文件路径不含中文或特殊字符
|
||||||
|
|
||||||
|
2. **数据模型格式**
|
||||||
|
- Excel 文件需符合 `xlsxData` 模块定义的结构规范
|
||||||
|
- 必须包含字段元信息表(列名、类型、约束等)
|
||||||
|
- 可选数据表用于生成 `INSERT` 语句
|
||||||
|
|
||||||
|
3. **数据库模板扩展**
|
||||||
|
- 新增数据库支持只需实现新的模板并加入 `tmpls` 映射即可
|
||||||
|
|
||||||
|
4. **性能建议**
|
||||||
|
- 不适用于超大数据集(因全部加载至内存)
|
||||||
|
- 建议单个模型文件控制在千行以内
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
| 错误类型 | 表现 | 解决方法 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 文件无法读取 | 输出 `can not read data` | 检查文件格式或权限 |
|
||||||
|
| 数据为空 | 输出 `not data return from XLSX file` | 检查文件内容是否正确 |
|
||||||
|
| 数据库类型不支持 | 抛出异常 `%s database not implemented` | 检查拼写或确认已实现该模板 |
|
||||||
|
| 模板渲染失败 | 抛出模板引擎异常 | 检查模板语法或数据结构匹配性 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 扩展建议
|
||||||
|
|
||||||
|
- 添加配置文件支持不同导出选项(如是否生成 INSERT)
|
||||||
|
- 增加对 SQLite 等更多数据库的支持
|
||||||
|
- 支持导出到文件而非仅打印到终端
|
||||||
|
- 提供 GUI 界面或 Web 接口版本
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 许可与版权
|
||||||
|
|
||||||
|
© 2025 Your Company. All rights reserved.
|
||||||
|
开源许可证:MIT(如有)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
📌 **提示**:此工具适用于开发阶段快速搭建数据库 schema 和测试数据,生产环境请结合审核流程使用。
|
||||||
244
aidocs/xls2ui.md
Normal file
244
aidocs/xls2ui.md
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
# `xls2crud` 工具技术文档
|
||||||
|
|
||||||
|
> **用途**:将基于 JSON 描述的数据库模型和 CRUD 配置文件转换为前端 UI 代码(CRUD 或树形结构界面)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本脚本是一个命令行工具,用于根据指定的模型目录和 JSON 配置文件生成前端 CRUD(增删改查)或树形结构(Tree)UI 所需的代码。它通过解析 Excel 衍生出的 JSON 结构,并结合环境变量与模板机制,动态生成对应模块的用户界面代码。
|
||||||
|
|
||||||
|
主要功能包括:
|
||||||
|
- 解析命令行参数
|
||||||
|
- 加载并处理数据库模型定义
|
||||||
|
- 处理多个 JSON 格式的 CRUD 配置文件
|
||||||
|
- 支持环境变量注入(模板替换)
|
||||||
|
- 分发至不同的 UI 构建器(CRUD / Tree)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 依赖说明
|
||||||
|
|
||||||
|
### 第三方/内部模块
|
||||||
|
| 模块 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `os`, `sys` | 系统路径、环境变量、退出控制 |
|
||||||
|
| `argparse` | 命令行参数解析 |
|
||||||
|
| `json`, `codecs` | JSON 文件读取(支持 UTF-8 编码) |
|
||||||
|
| `copy` | 对象拷贝 |
|
||||||
|
| `appPublic.argsConvert.ArgsConvert` | 支持 `${}` 形式模板变量替换 |
|
||||||
|
| `appPublic.dictObject.DictObject` | 将字典转为可点式访问的对象 |
|
||||||
|
| `xls2crud.build_dbdesc`, `build_crud_ui` | 构建数据库描述及 CRUD 界面 |
|
||||||
|
| `singletree.build_tree_ui` | 构建树形结构 UI |
|
||||||
|
|
||||||
|
> ⚠️ 注意:`appPublic`、`xls2crud` 和 `singletree` 是项目内部模块,请确保已安装或在 Python 路径中可用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令行使用方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python xls2crud.py [-m models_dir] [-o output_dir] modulename [json_file ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 参数 | 必需 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `modulename` | ✅ | 指定生成代码所属的模块名称(通常用于命名空间或路由前缀) |
|
||||||
|
| `json_file ...` | ✅(至少一个) | 一个或多个描述表单逻辑的 `.json` 配置文件路径 |
|
||||||
|
| `-m`, `--models_dir` | ❌ | 数据库模型文件所在的目录列表(可多个),用于构建 `dbdesc` 对象 |
|
||||||
|
| `-o`, `--output_dir` | ❌ | 输出代码的目标根目录,默认行为可能由配置决定 |
|
||||||
|
|
||||||
|
> 如果未提供任何 JSON 文件,程序将打印用法提示并退出。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输入文件格式:CRUD JSON 配置
|
||||||
|
|
||||||
|
每个 JSON 文件应遵循如下结构:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tblname": "user",
|
||||||
|
"alias": "usr", // 可选,作为输出目录名
|
||||||
|
"uitype": "crud", // 或 "tree"
|
||||||
|
"params": {
|
||||||
|
"title": "用户管理",
|
||||||
|
"fields": [...],
|
||||||
|
"actions": [...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段说明
|
||||||
|
|
||||||
|
| 字段 | 类型 | 是否必需 | 说明 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `tblname` | string | ✅ | 关联的数据表名 |
|
||||||
|
| `alias` | string | ❌ | 输出目录名称;若不存在则使用 `tblname` |
|
||||||
|
| `uitype` | string | ❌ | UI 类型,可选 `"crud"`(默认)或 `"tree"` |
|
||||||
|
| `params` | object | ✅ | 包含具体 UI 参数的对象,如标题、字段、操作等 |
|
||||||
|
| `params.modulename` | string | ❌ | 自动生成,由命令行传入 |
|
||||||
|
| `params.tblname` | string | ❌ | 自动生成,来自 `tblname` 字段 |
|
||||||
|
|
||||||
|
> 💡 支持模板语法 `${VAR_NAME}`,会从环境变量中自动替换。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 环境变量支持(模板替换)
|
||||||
|
|
||||||
|
系统使用 `ArgsConvert('${', '}$')` 实现对配置中的 `${...}` 占位符进行替换。
|
||||||
|
|
||||||
|
例如:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"params": {
|
||||||
|
"api_base": "${API_HOST}/v1/data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若环境变量中有 `API_HOST=http://localhost:8000`,则最终值会被替换为:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"api_base": "http://localhost:8000/v1/data"
|
||||||
|
```
|
||||||
|
|
||||||
|
所有当前进程的环境变量均会被加载进命名空间 `ns` 中供替换使用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心流程说明
|
||||||
|
|
||||||
|
### 1. 参数解析
|
||||||
|
使用 `argparse` 解析输入参数,获取:
|
||||||
|
- 模型目录 (`models_dir`)
|
||||||
|
- 输出目录 (`output_dir`)
|
||||||
|
- 模块名 (`modulename`)
|
||||||
|
- 一个或多个 JSON 配置文件路径
|
||||||
|
|
||||||
|
### 2. 构建数据库描述对象 (`dbdesc`)
|
||||||
|
调用 `build_dbdesc(models_dir)` 从模型目录中提取数据库元信息(如表结构、字段类型等),结果封装为 `DictObject` 以便点式访问。
|
||||||
|
|
||||||
|
```python
|
||||||
|
dbdesc = DictObject(**dbdesc)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 遍历处理每个 JSON 文件
|
||||||
|
对每一个 JSON 文件执行以下步骤:
|
||||||
|
|
||||||
|
#### a. 读取并解析 JSON
|
||||||
|
使用 `codecs.open(..., 'utf-8')` 安全读取 UTF-8 编码内容,防止中文乱码。
|
||||||
|
|
||||||
|
#### b. 模板变量替换
|
||||||
|
利用 `ArgsConvert` 将 `${VAR}` 替换为环境变量实际值。
|
||||||
|
|
||||||
|
#### c. 转换为 `DictObject`
|
||||||
|
便于后续通过 `obj.field.subfield` 访问嵌套属性。
|
||||||
|
|
||||||
|
#### d. 设置输出目录
|
||||||
|
```python
|
||||||
|
crud_data.output_dir = os.path.join(args.output_dir, tblname)
|
||||||
|
```
|
||||||
|
以表名或别名为子目录创建独立输出路径。
|
||||||
|
|
||||||
|
#### e. 注入模块名和表名到 params
|
||||||
|
```python
|
||||||
|
crud_data.params.modulename = args.modulename
|
||||||
|
crud_data.params.tblname = crud_data.tblname
|
||||||
|
```
|
||||||
|
|
||||||
|
#### f. 克隆数据库描述
|
||||||
|
避免污染原始 `dbdesc`,每次传递副本:
|
||||||
|
```python
|
||||||
|
db = dbdesc.copy()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### g. 分发 UI 构建任务
|
||||||
|
根据 `uitype` 决定调用哪个构建函数:
|
||||||
|
|
||||||
|
| `uitype` | 函数调用 |
|
||||||
|
|---------|--------|
|
||||||
|
| `"tree"` | `build_tree_ui(crud_data, db)` |
|
||||||
|
| 其他(默认) | `build_crud_ui(crud_data, db)` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例运行命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python xls2crud.py \
|
||||||
|
-m ./models/user -m ./models/shared \
|
||||||
|
-o ./output \
|
||||||
|
admin \
|
||||||
|
./crud_configs/user.json \
|
||||||
|
./crud_configs/org.json
|
||||||
|
```
|
||||||
|
|
||||||
|
输出结构示例:
|
||||||
|
```
|
||||||
|
./output/
|
||||||
|
├── user/
|
||||||
|
│ ├── form.js
|
||||||
|
│ ├── list.vue
|
||||||
|
│ └── api.js
|
||||||
|
└── org/
|
||||||
|
├── form.js
|
||||||
|
├── list.vue
|
||||||
|
└── api.js
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
- 若未提供 JSON 文件,输出帮助信息并退出码为 `1`
|
||||||
|
- 若文件无法打开或 JSON 格式错误,抛出异常(建议添加 try-except 包裹增强健壮性)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 待改进点(建议)
|
||||||
|
|
||||||
|
| 问题 | 建议 |
|
||||||
|
|------|------|
|
||||||
|
| 缺少异常捕获 | 添加 `try...except` 处理文件读取、JSON 解析错误 |
|
||||||
|
| 日志输出简单 | 使用 `logging` 模块替代 `print()` |
|
||||||
|
| 输出目录自动创建 | 添加 `os.makedirs(crud_data.output_dir, exist_ok=True)` |
|
||||||
|
| 更详细的帮助信息 | 在 `ArgumentParser` 中补充描述 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
该脚本是自动化生成前端管理界面的重要一环,适用于基于模型驱动开发的企业级后台系统。通过灵活的配置与模板机制,实现了高复用性和可维护性。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:完整调用链示意图
|
||||||
|
|
||||||
|
```
|
||||||
|
[CLI Input]
|
||||||
|
↓
|
||||||
|
argparse → args(models_dir, output_dir, modulename, files[])
|
||||||
|
↓
|
||||||
|
build_dbdesc(models_dir) → dbdesc (DictObject)
|
||||||
|
↓
|
||||||
|
for each file in files:
|
||||||
|
read JSON → a
|
||||||
|
ac.convert(a, env) → 替换 ${} 变量
|
||||||
|
a → DictObject → crud_data
|
||||||
|
crud_data.output_dir = output_dir/tblname
|
||||||
|
crud_data.params.{modulename,tblname} = ...
|
||||||
|
db = dbdesc.copy()
|
||||||
|
if uitype == 'tree':
|
||||||
|
build_tree_ui(crud_data, db)
|
||||||
|
else:
|
||||||
|
build_crud_ui(crud_data, db)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ **版本**:1.0
|
||||||
|
📅 **最后更新**:2025-04-05
|
||||||
258
aidocs/xlsxData.md
Normal file
258
aidocs/xlsxData.md
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
# 技术文档:`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 数据:<br> - 第一行作为字段定义(格式:`字段名:类型`)<br> - 后续行为数据行<br> - 使用 `TypeConvert` 进行类型转换<br> - 返回 `{name: [DictObject(...), ...]}` |
|
||||||
|
| `getFieldNames(row)` | 处理首行字段:<br> - 若为空则命名为 `F_i`<br> - 分割 `name:type` 形式,缺失类型设为 `None`<br> - 返回 `[ [字段名, 类型], ... ]` |
|
||||||
|
|
||||||
|
> 📌 字段命名规则示例:
|
||||||
|
> - `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
|
||||||
Loading…
x
Reference in New Issue
Block a user