first commit
This commit is contained in:
commit
2a6e483d50
246
kdb/architech.md
Normal file
246
kdb/architech.md
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
# 模块开发规范
|
||||||
|
一下假设开发的模块名称为mymodule
|
||||||
|
|
||||||
|
模块使用ahserver后台和bricks开发h5规范前端web应用,模块需要遵守以下约素:
|
||||||
|
|
||||||
|
* 模块目录必须符合下面的模块目录结构要求
|
||||||
|
|
||||||
|
* 函数export,定义模块可被dspy脚本和ui前端界面或其他模块调用函数
|
||||||
|
|
||||||
|
## 列表形式的数据表crud定义
|
||||||
|
|
||||||
|
当一张数据表不是树形结构的就是表形结构的crud
|
||||||
|
|
||||||
|
表形机构表的crud的json说明
|
||||||
|
{
|
||||||
|
"tblname" # 表名
|
||||||
|
"alias" # 别名,当需多个crud界面时,可用alias来生成份不同的功能
|
||||||
|
"title" # 标题,如果不给定,使用数据表定义中的表标题
|
||||||
|
"params":{ # crud 的参数
|
||||||
|
"sortby" # 指定排序字段,可以多个字段,用["a desc", "b"]>形式给出按照多个字段排序,"desc“表示倒排,不给定就是正排
|
||||||
|
"logined_userorgid" # 可选,如果表中有机构编码id,并且需要按照机构过滤
|
||||||
|
"logined_userid" # 可选,如果表中有用户id,并且需要登录用户过滤
|
||||||
|
"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“表示倒排,不给定就是正排
|
||||||
|
"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" # 外键表名称
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## 数据库表定义规范
|
||||||
|
模块中需要定义数据表要遵守一下规范
|
||||||
|
|
||||||
|
数据库表用一个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的数字
|
||||||
|
|
||||||
|
|
||||||
|
## 模块目录结构
|
||||||
|
```
|
||||||
|
mymodule
|
||||||
|
|
|
||||||
|
+--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.xlsx # 初始化数据,一个sheet一个表,一行一个记录,第一行为字>段名
|
||||||
|
|
|
||||||
|
+--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”本身
|
||||||
|
|
||||||
|
## 模块设计要求
|
||||||
|
|
||||||
|
* 模块中的主要逻辑用py源码实现,放在mymodule目录下
|
||||||
|
|
||||||
|
* 客户端业务功能放在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,编码显示文本
|
||||||
|
]
|
||||||
|
|
||||||
|
python 模块设计要完成
|
||||||
|
1)定义模块所需的数据库表,每个数据表使用二级标题为"models/<表名>.json", 内容为原始格式的json文档
|
||||||
|
2)初始化数据, 二级标题为"init/data.json", 内容为原始格式初始化数据;
|
||||||
|
3)mymodule下的init.py源码,二级标题为“mymodule/init.py",内容为原始格式的代码
|
||||||
|
4)mymodule下的其他代码,二级标题为“mymodule/x.py”,内容为原始格式的用中文编写伪代码,描述业务处理逻辑,说明使用的接口以及提供的接口
|
||||||
|
5)二级标题“init/script.py“,提供中文编写的初始化业务处理逻辑伪代码
|
||||||
|
6) 二级标题pyproject.toml和README.md, 内容原始格式的完整内容
|
||||||
|
7) 前端界面设计,每个界面一个设计文档,文档中使用bricks的控件组合描述界面,如果需要的控件bricks中没有,按照bricks控件扩展规范编写新控件代码,二级标题为“wwwroot/myui.ui" 内容为ui界面内容
|
||||||
|
8)bricks扩展控件,二级标题为"wwwroot/ext.js", 和 “wwwroot/ext.css", 内容分别为原始格式的js和css
|
||||||
31
kdb/dspy
Normal file
31
kdb/dspy
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""
|
||||||
|
dspy脚本可用的变量
|
||||||
|
ahserver/globalEnv.py程序中推送到ServerEnv中的变量
|
||||||
|
各个模块init_mymodule函数推送的变量
|
||||||
|
request
|
||||||
|
"""
|
||||||
|
info(f'{params_kw=}, {request.url=}, {request.path=}')
|
||||||
|
def vcode():
|
||||||
|
codes = [ str(random.randint(0, 10)) for i in range(6) ]
|
||||||
|
return ''.join(codes)
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
|
||||||
|
async with db.sqlorContext('sage') as sor:
|
||||||
|
ns = {
|
||||||
|
'id':params_kw.codeid,
|
||||||
|
'code':vcode()
|
||||||
|
}
|
||||||
|
r = await sor.R('validatecode', {'id':params_kw.codeid})
|
||||||
|
if len(r) == 0:
|
||||||
|
await sor.C('validatecode', ns.copy())
|
||||||
|
else:
|
||||||
|
ns = r[0]
|
||||||
|
|
||||||
|
ns = DictObject(**ns)
|
||||||
|
x = await sms_engine.send_validate_code(params_kw.cell_no, ns.code)
|
||||||
|
info(f'{params_kw.cell_no}, {ns.code=}, send_validatecode() return {x}')
|
||||||
|
return PopMessage(title='SMS', message=f'validate code send to {params_kw.cell_no}')
|
||||||
|
|
||||||
|
|
||||||
|
return PopError(title='Error', message=f'{params_kw.cell_no} generate validate code error')
|
||||||
32
kdb/sqlor.py
Normal file
32
kdb/sqlor.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from sqlor.dbpools import DBPools
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
# 假设当前模块名称为"mymodule"
|
||||||
|
|
||||||
|
async def subcoro(sor, pid):
|
||||||
|
sql = "select * from appcoodes_kw where parentid=${pid}$"
|
||||||
|
r = await sor.sqlExe(sql, {'pid': pid})
|
||||||
|
return r
|
||||||
|
|
||||||
|
“”“
|
||||||
|
sor.R()
|
||||||
|
sor.sqlExe() 的select语句
|
||||||
|
如果ns中有‘page'属性,返回数据格式如下
|
||||||
|
{
|
||||||
|
"total" # 查询结果总记录数
|
||||||
|
"rows": page指定的页数据, 缺省每页返回80条记录,pagerows属性可设置每页记录数
|
||||||
|
}
|
||||||
|
否则返回全部记录的数组
|
||||||
|
"""
|
||||||
|
async def sqlor_op():
|
||||||
|
db = DBPools()
|
||||||
|
env = ServerEnv()
|
||||||
|
dbname = env.get_module_dbname()
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
# 事务中,如果代码或sql失败全部滚回,正常结束自动提交
|
||||||
|
await sor.C('user', {'id':'yuewfiuwe', 'username':'john'})
|
||||||
|
# 添加数据表“user”数据
|
||||||
|
await sor.D('user', {'id': 'yuewfiuwe'})
|
||||||
|
# await sor.U('user', {'id', 'email':'test@abc.com'})
|
||||||
|
# await sor.R('user', {'id': 'yuewfiuwe'})
|
||||||
|
return subcoro(sor, 'test_data')
|
||||||
|
|
||||||
75
kdb/ui
Normal file
75
kdb/ui
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"widgettype":"VBox",
|
||||||
|
"options":{
|
||||||
|
"width":"100%",
|
||||||
|
"height":"100%"
|
||||||
|
},
|
||||||
|
"subwidgets":[
|
||||||
|
{
|
||||||
|
"widgettype":"Filler",
|
||||||
|
"options":{},
|
||||||
|
"subwidgets":[
|
||||||
|
{
|
||||||
|
"id":"dialog",
|
||||||
|
"widgettype":"LlmDialog",
|
||||||
|
"options":{
|
||||||
|
"models":[{
|
||||||
|
"url":"{{entire_url('llm/doubao.llm')}}",
|
||||||
|
"mapi":"chat",
|
||||||
|
"icon":"{{entire_url('imgs/doubao.png')}}",
|
||||||
|
"model":"ep-20240614051803-shld5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype":"HBox",
|
||||||
|
"options":{
|
||||||
|
"cheight":5
|
||||||
|
},
|
||||||
|
"subwidgets":[
|
||||||
|
{
|
||||||
|
"id":"prompt",
|
||||||
|
"widgettype":"UiAudioText",
|
||||||
|
"options":{
|
||||||
|
"css":"filler",
|
||||||
|
"upload_url":"https://sage.opencomputing.cn/stt/generate",
|
||||||
|
"name":"prompt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype":"IconBar",
|
||||||
|
"id":"prompt_bar",
|
||||||
|
"options":{
|
||||||
|
"tools":[
|
||||||
|
{
|
||||||
|
"name":"submit",
|
||||||
|
"icon":"{{entire_url('imgs/submit.png')}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"binds":[
|
||||||
|
{
|
||||||
|
"wid":"prompt_bar",
|
||||||
|
"event":"submit",
|
||||||
|
"actiontype":"script",
|
||||||
|
"target":"dialog",
|
||||||
|
"script":"console.log(params); this.set_prompt(params.prompt);",
|
||||||
|
"datawidget":"prompt",
|
||||||
|
"datamethod":"getValue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wid":"prompt_bar",
|
||||||
|
"event":"submit",
|
||||||
|
"actiontype":"script",
|
||||||
|
"target":"prompt",
|
||||||
|
"script":"this.setValue('')"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user