This commit is contained in:
yumoqing 2026-01-15 17:14:16 +08:00
parent f0993e8e7b
commit 40e20e2bae
5 changed files with 428 additions and 1 deletions

View File

@ -98,7 +98,7 @@ bricks.register('ExtContainer', bricks.ExtContainer); /* 注册新控件 */
##### subwidgets ##### subwidgets
数组属性,定义容器控件的子控件,每个元素定义一个子控件,子控件遵守控件的数据要素要求 数组属性,定义容器控件的子控件,每个元素定义一个子控件,子控件遵守控件的数据要素要求
## 应用开发开发 ## 应用开发
使用存放在服务器后台的.ui后缀的json格式文件来开发每个.ui文件定义一个控件 支持基本控件和容器空间。 使用存放在服务器后台的.ui后缀的json格式文件来开发每个.ui文件定义一个控件 支持基本控件和容器空间。
关于如何书写ui文件请参考[UI文件格式](descjson.md) 关于如何书写ui文件请参考[UI文件格式](descjson.md)

94
kdb/crud.md Normal file
View File

@ -0,0 +1,94 @@
# 支持CRUD
用一个json文件定义一张数据表的增删改查操作框架支持两种显示形式的crud 树形表和列表表的crud
当一张数据表的记录存在父子关系一个记录一个字段值指向另一个记录的id使用树状展示的CRUD 否则使用列表方式的CRUD
## 列表形式的数据表crud定义JSON规范
表形机构表的crud的json说明
{
"tblname" # 表名
"alias" # 别名, 可选可用alias来生成份不同的功能
"title" # 标题,如果不给定,使用数据表定义中的表标题
"params":{ # crud 的参数
"sortby" # 指定排序字段,可以多个字段,用["a desc", "b"]>形式给出按照多个字段排序,"desc“表示倒排不给定就是正排
"logined_userorgid" # 可选如果表中有机构编码id并且需要按照机构过滤
"logined_userid" # 可选如果表中有用户id并且需要登录用户过滤
"confidential_fields" # 可选,敏感字段名数组,如果表中有敏感字段,要填写
"editor":{ # 给定编辑时form初始化参数
# 需要参考bricks的Form控件
"binds":[ # 比如添加事件处理
{
"wid":"province_id", # 指定form的事件名
"event":"changed", # 事件,比如数据变化
"actiontype":"script", # 事件处理类型
"target":"city_id", # 目标控件
"script": # 脚本内容规定用js脚本
}
]
},
"browserfields": { # 列表显示的参数
"exclouded": ["id"], # 不显示的字段列表
"alters": { # 需要改变的字段属性
"field1":{ # 例子将字段field1改为选项输入
"uitype":"code", # 设置输入类型为"code"
# uitype类型也可以用dataurl设置一个url从服务器获取数据
# 如果设置了打他URL datamethod和dataparams可选
"data":[ # 设置选项数据内容
{
"value":"v1", # value是数据值
"text": "显示项"# text是此值对应的显示内容
},
...
]
}
}
},
"editexclouded": [ # 编辑时不包含的字段列表
],
"subtables":[ # 可选,外键清单(表中存在字段指向本表主键记录)
{
"field" # 外键表字段
"title" # 可选,不选用外键表的表标题
"url" # 当alias定义时需要用{{entire_url(...)方式定义
指向表的url
"subtable" # 外键表名称
}
]
}
}
## 树形结构的数据表crud
当一张数据表中存在一个父节点的字段指向本表中另条记录的主键id,那么这个表就是一个树形>结构的表
树形机构表的crud的json数据说明
{
"tblname" # 表名
"alias" # 别名当需多个crud界面时可用alias来生成份不同的功能
"uitype": "tree"# 给定uitype为"tree"指定用树状结构数据crud
"title" # 标题,如果不给定,使用数据表定义中的表标题
"params":{ # crud 的参数
"idField": # 必须指定树节点的id字段
"textField" # 指定树节点显示内容字段
"sortby" # 指定排序字段,可以多个字段,用[a desc, b]形式>给出按照多个字段排序,"desc“表示倒排不给定就是正排
"confidential_fields" # 可选,敏感字段名数组,如果表中有敏感字段,要填写
"browserfields": { # 列表显示的参数
"alters": {} # 需要改变的字段属性
}
"logined_userorgid" # 可选如果表中有机构编码id并且需要按照机构过滤
"logined_userid" # 可选如果表中有用户id并且需要登录用户过滤
"editable" # true代表可编辑false表示不可编辑通常给定true
"edit_exclouded_fields" # 设置不参与编辑(新增和修改)字段,数组
"parentField" # 父节点字段
"subtables":[ # 外键清单(表中存在字段指向本表主键记录)
{
"field" # 外键表字段
"title" # 可选,不选用外键表的表标题
"url" # 当alias定义时需要用{{entire_url(...)方式定义
指向表的url
"subtable" # 外键表名称
}
],
}
}

124
kdb/module.md Normal file
View File

@ -0,0 +1,124 @@
# 模块开发规范
假设需开发的模块名为“mymodule”
一个模块如果使用数据库存储数据则需要按照数据表规范生成数据表和相应的crud定义
## 模块目录结构
```
+--mymodule目录 # 存放模块的主要逻辑py代码
+--init.py # 模块初始化脚本需要定义一个load_mymodule()函数,此函>数需要将在ui和dspy文件中用到的函数通过ServerEnv实例传过去
+--__init__.py #python模块所需
|
| +--其他源码.py # 模块中需要的其他py源码文件
+--wwwroot目录 # web服务脚本文件以.ui和.dspy结束ui文件支持jinja2模>板前端控件文件内容为json格式的控件描述文本 dspy是ahserver支持的受限python脚本>,可以按照需要设置下级目录
+--models目录 # 模块使用的数据表以.xlsx后缀存放在此目录中
+--json目录 # 存放json文件定义数据表的CRUD逻辑
+--pyproject.toml文件 # pip打包文件, 其中项目名字与mymodule相同
+--README.md文件 # 模块自说明文件
+--init目录 # 模块初始化目录
+--data.json # 初始化数据
+--script.py # 初始化脚本
```
## mymodule/init.py的主要内容
```
from ahserver.serverenv import ServerEnv
from appPublic.worker import awaitify
from .x import a, b, c # ab, c是协程
from .y import x, y # x, y是普通函数
def load_mymodule(): # mymodule需替换为实际的模块名字
env = ServerEnv()
env.a = a # 脚本中用"a" 调用"a"
env.b = b # 脚本中用"b"调用"b"
env.cc = c # 脚本中用"cc"调用"c"
env.y = awaitify(y) # 将函数包装为协程
env.x = awaitify(x) # 将函数包装为协程
```
注释这里的load_mymodule中的mymodule是模块名字而不是“mymodule”本身
## mymodule/*.py
模块中的主要逻辑用py源码
## models/*.json
每个数据表一个文件,存放按照数据表定义规范生成的数据表定义
## json/*.json
为数据表定义crud功能 每个表至少一个
## wwwroot/*.ui
模块前端ui代码文件内容遵守json格式要求支持jinja2模板
## wwwroot/*.dspy
模块后端脚本代码
## init/data.json
文件保存需要初始化的数据,格式如下
{
"table1":[
{
"field1":"value1",
...
},
...
{
"field1":"valuen",
...
}
]
}
## init/script.py
脚本主要任务是将init/data.json中的数据导入到数据库中
* 客户端业务功能放在wwwroot目录下以.ui和.dspy后缀结束用.ui文件遵守jinja2规范, 而.dspy文件是一个受控的脚本
* 返回客户端数据的功能放在wwwroot目录下以.dspy后缀结束
## ui, dspy可以直接使用的变量
### request
aiohttp.Request实例每个客户端请求有一个独立的Request实例
request._run_ns可以获得所有在.ui和.dspy源码中可以使用的变量通过ahserver.ServerEnv 传递
### params_kw
DictObjectdict子类, 支持a.b方式获取属性实例接收到客户端传来的数据如果有文件>文件都保存在服务器指定的位置params_kw中属性名保存的是其相对路径可用FileStorage().realPath(params_kw.myfile)来获得文件在服务器中的实际路径。
### get_user()
来自rbac模块协程函数获得当前登录用户如果用户没有登录返回None
### get_userorgid()
来自rbac模块协程函数获得当前登录用户的机构id如果用户没有登录返回None
## pyproject.toml和README.md
编写pyproject.toml文件和README.md文件
## 模块中使用的编码
模块中如果用到编码编码需保存在appbase模块的appcodes表和appcodes_kv两个表中
appcodes(编码表)有如下字段:
[
"id" # str 32 主键,可设置为数据表字段名称
"name" # 编码名称
"hierarchy_flg", # str 1, '0':单级编码“1”:多级编码
]
appcodes_kv编码键值表字段如下
[
"id", # str, 32,主键,唯一值
"parentid" # str, 32, 一级编码为appcodes表的id, 否则为上级编码键值记录的id
“k" # str 32, 编码值
“v” “ str 255,编码显示文本
]

60
kdb/table.md Normal file
View File

@ -0,0 +1,60 @@
# 数据库表定义规范
## 数据表定义规范
数据库表用一个json格式文件或数据来定义具体规范如下
{
"summary":[ # 仅一条记录
{
"name" # 表名
"title" # 表标题
"primary" # 主键=“id”, 所有表均以id为主键
"catelog" # 可选项entity relation dimession indication
}
],
"fields":[ # 字段
{
"name"
"title"
"type" #可选项有str char short long llong date time timestamp float double ddouble decimal text
"length"
"dec"
"nullable" # 可选项yes no
"default"
"comments" # 注释
}
]
"indexes":[
{
"name" # 每个索引一个idxname不能重复一个表中
"idxtype" # unique or index
"idxfields" # 字段名或字段名数组[f1,f1]
}
]
“codes":[ # 如果一个字段数据可以从其他表中获得,可以通过下面的模式定义选择输入逻
{
"field" # 字段的名字
"table" # 数据来源表
"valuefield" # 数据来源表值字段
"textfield" # 数据来源表显示字段
"cond" # 检索条件
}
]
}
说明:
id字段全部使用str 32类型
字典中的length如果type是str char float double ddouble decimal中的一个则必为>0的数字
字典中的dec: 如果type是float double ddouble decimal中的一个则必须是>0的数字
## 生成表定义要求
* 所有表定义文件均存放在models目录下
* 文件名:"xxxx.json", 其中"xxxx"为数据表名

149
kdb/variable.md Normal file
View File

@ -0,0 +1,149 @@
# 脚本中可直接使用的变量
脚本是wwwroot目录下的脚本文件包括.ui, .dspy, .xterm后缀的脚本
## request
web服务器的request
### request._run_ns
DictObject实例含所有可用的变量
### request['client_ip']
保存客户端实际的IP 支持最多一层代理多了就不能获得真实的客户ip
## params_kw
DictObjectdict子类, 支持a.b方式获取属性实例接收到客户端传来的数据如果有文件文件都保存在服务器指定的位置params_kw中属性保存的是其相对路径可用FileStorage().realPath(params_kw.myfile)来获得文件在服务器中的实际路径。
### get_user()
来自rbac模块协程函数获得当前登录用户如果用户没有登录返回None
### get_userorgid()
来自rbac模块协程函数获得当前登录用户的机构id如果用户没有登录返回None
### json
python的json模块
### ArgsConvert
### time
python的time模块
### curDateString
函数获得当前日期字符串格式为“yyyy-mm-dd“
### curTimeString
函数获得当前时间字符串格式为”HH:MM:SS“
### datetime
python的datetime模块
### random
python的random模块
### str2Date
函数将字符串格式的日期转换为datetime日期实例
### str2Datetime
函数,将"yyyy-mm-dd HH:MM:SS"字符串转换为datetime日期实例
### timestampstr
函数获得当前时间戳的字符串格式“yyyy-mm-dd HH:MM:SS.xxx"
### monthfirstday
函数,获得日期参数的第一天
### curDatetime
函数获得当前时间的datetime日期实例
### strdate_add
函数,在日期上增加多少天,月,年
### uuid
函数获得一个uuid
### DBPools
### DBFilter
### default_filterjson
### password_encode
函数,信息加密,返回加密后的字符串
### password_decode
函数,信息解密,返回解密后的字符串
### get_config_value
函数返回配置文件中的值
### DictObject
数据类支持a.b操作的字典
### async_sleep
异步睡眠
### quote
url编码
### realpath
函数获得webpath的文件系统路径
```
relp = realpath('/33/55/22/55/abc.doc')
### format_exc
函数,获得当前调用栈字符串
```
e=Exception('TEST exception')
exception(f'{e}{format_exc()}')
```
### stream_response
协程实现流式response给前端
```
async def gen(request):
cnt = 100000
i = 0
while i < cnt:
yield
### webpath
函数返回文件系统路径的webpath必须是配置文件中files目录下的文件
### partial
python的partial函数
### StreamHttpClient
appPublic模块的StreamHttpClient类
```
shc = StreamHttpClient()
x = await shc.get('https://www.baidu.com')
```
### tmpl_engine
支持异步的jinja2引擎, 例子:
```
tmpl='hello {{user}}'
d = {'user': 'John'}
s = tmpl_engine.renders(tmpl, d)
```
### downloadfile2url
协程从远端下载文件放到files目录下并构造url可供客户端下载
```
url = 'https://xxx.com/xxx.mp4'
newurl = await downloadfile2url(request, url)
```
### background_reco
函数,后台执行协程
```
async def dummy(*args, **kw):
while True:
async_sleep(1)
print('test')
background_reco(dummy)
```
### get_sor_context
异步上下午获得sor上下文用于操作数据库以下两组代码等价
```
async with sor.get_sor_context(env, dbname) as sor:
```
```
db = DBPools()
async with db.sqlorContext(dbname) as sor:
```
### json_response
函数json格式数据构造Response返回Response实例
```
d = {"a":"B"}
return json_response(d)
### Response
aiohttp的Response类
### web_rootpath
函数, 获得web应用的root文件系统路径