diff --git a/kdb/brief.md b/kdb/brief.md index 0faa7ae..a9c9dcd 100644 --- a/kdb/brief.md +++ b/kdb/brief.md @@ -98,7 +98,7 @@ bricks.register('ExtContainer', bricks.ExtContainer); /* 注册新控件 */ ##### subwidgets 数组属性,定义容器控件的子控件,每个元素定义一个子控件,子控件遵守控件的数据要素要求 -## 应用开发开发 +## 应用开发 使用存放在服务器后台的.ui后缀的json格式文件来开发,每个.ui文件定义一个控件, 支持基本控件和容器空间。 关于如何书写ui文件请参考[UI文件格式](descjson.md) diff --git a/kdb/crud.md b/kdb/crud.md new file mode 100644 index 0000000..858a46f --- /dev/null +++ b/kdb/crud.md @@ -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" # 外键表名称 + } + ], + } +} + diff --git a/kdb/module.md b/kdb/module.md new file mode 100644 index 0000000..b6f8b26 --- /dev/null +++ b/kdb/module.md @@ -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 # a,b, 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 +DictObject(dict子类, 支持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,编码显示文本 +] diff --git a/kdb/table.md b/kdb/table.md new file mode 100644 index 0000000..d1e3d1b --- /dev/null +++ b/kdb/table.md @@ -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"为数据表名 + diff --git a/kdb/variable.md b/kdb/variable.md new file mode 100644 index 0000000..ffb2b94 --- /dev/null +++ b/kdb/variable.md @@ -0,0 +1,149 @@ +# 脚本中可直接使用的变量 + +脚本是wwwroot目录下的脚本文件包括:.ui, .dspy, .xterm后缀的脚本 + +## request +web服务器的request, + +### request._run_ns + +DictObject实例,含所有可用的变量 + +### request['client_ip'] + +保存客户端实际的IP, 支持最多一层代理,多了就不能获得真实的客户ip + +## params_kw +DictObject(dict子类, 支持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文件系统路径 +