aicode/kdb/rule.md
2025-12-11 21:59:36 +08:00

26 KiB
Raw Blame History

模块目录结构

模块名称为“mymodule", 以下为mymodule模块的目录文件结构

mymodule
    
    +--mymodule         # 存放模块的主要逻辑py代码
        
        +--init.py        # 模块初始化脚本需要定义一个load_mymodule()函数此函数将在ui和dspy文件中用到的模块变量通过ServerEnv实例传过去
        
        +--__init__.py    #python模块所需
        |
    |     +--其他源码.py	# 模块中需要的其他py源码文件
        
    +--wwwroot          # 目录,存放以.ui和.dspy结束的文件ui文件支持jinja2模板前端控件文件内容为json格式的控件描述文本 dspy是ahserver支持的受限python脚本可以按照需要设置下级目录
    
    +--models           # 存放数据表文件的目录
    
    +--json             # 存放CRUD描述文件的目录
    
    +--pyproject.toml   # pip打包文件, 其中项目名字与mymodule相同
    
    +--README.md       # 模块自说明文件
    
    +--init             # 模块初始化目录
        
        +--data.xlsx        # 初始化数据一个sheet一个表一行一个记录第一行为字>段名
        
        +--script.py        # 初始化脚本

要求

  • 模块中的主要逻辑用py源码实现放在mymodule目录下

  • 客户端业务功能放在wwwroot目录下以.ui和.dspy后缀结束用.ui文件遵守jinja2规范, 而.dspy文件是一个受控的脚本

  • .dspy中不允许import模块

  • 假设模块名为”mymodule“

mymodule目录

“mymodule”为模块名称 模块目录下存放以py结束的代码文件其中init.py文件为必须 在此目录下的代码中可以通过ahserver.serverenv模块的ServerEnv来引用其他模块的放进来的变量

init.py的主要内容

from ahserver.serverenv import ServerEnv
from appPublic.worker import awaitify
from .x import a, b, c  # 从同目录下的源码x.py中import ab, c是协程
from .y import x, y     # 从同目录下源码y.py中import 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”本身

模块的其他代码都需要放在这个目录中而模块中所有需要在uidspy脚本中用到的变量均需要通过init.py函数的load_mymodule()函数传递

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

各个模块通过load_xxxx()放到ServerEnv()中的变量

XXXX是模块名称

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,编码显示文本 ]

数据库表定义规范

模块中需要定义数据表要遵守一下规范

数据库表用一个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的数字

列表形式的数据表crud定义

当一张数据表不是树形结构的就是表形结构的crud

表形机构表的crud的json说明 { "tblname" # 表名 "alias" # 别名当需多个crud界面时可用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" # 外键表名称 }
],
}
}

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: id = env.uuid() # 事务中如果代码或sql失败全部滚回正常结束自动提交 await sor.C('user', {'id':id, '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')

前端设计要求

使用bricks框架前台设计完成界面的每个控件的设计 每个控件设计产出内容: 1 界面源码文件名,以".ui“结束 2 控件名称 3 构造参数 4 事件处理 5 无法用现有控件实现的功能请用bricks的控件扩展方法为bricks扩展控件文件保存在wwwroot目录下

bricks框架简介

目录

  • bricks目标
  • bricks概念
  • bricks开发方法
  • bricks运行

bricks目标

  • 无前端代码或极少代码
  • 降低前端开发技术难度
  • 数据驱动
  • 常用控件包装
  • 纯json开发

bricks概念

  • 控件与控件继承
  • 事件以及事件处理
  • 控件嵌套和页面组装

控件与控件继承

bricks采用控件这一概念来描述web GUI的显示部件每个控件均映射到一个html 的标签类型的一个javascript类。每个控件均可以实例化并可在页面显示。 控件分为:基本控件,容器控件。控件有内置方法,也能触发事件。

  • 基本控件

基本控件是一个原子控件,不能有子控件。

  • 容器控件

容器控件可以有子控件bricks通过在容器控件添加子控件以及在子容器控件中 在添加子子控件的方式来构造复杂的web页面。

bricks已实现的控件请参看控件清单

控件扩展

如果现有的控件没法满足系统要求bricks支持控件扩展控件扩展需遵守

  • 控件class继承自某一个控件的class

  • 按照需求实现控件逻辑

  • 在需要的地方用this.dispatch触发此控件的事件

假设需要扩展一个名字叫ExtContainer的控件

bricks.ExtContainer = class extends bricks.VBox {
	constructor(opts){
		super(opts);
		/* 新控件的创建代码 */
	}
	......
	/* 对象的其他方法在需要的时候在某个方法中使用this.dispatch('new_event', data)方法引发事件 */
}
bricks.register('ExtContainer', bricks.ExtContainer); /* 注册新控件 */

新控件的使用example.ui

{
	"widgettype":"ExtContainer",
	"options":{
		....
	},
	"subwidgets":[
		...
	],
	"binds":[
		{
			"wid":"self",
			"event":"new_event",
			"actiontype":"urlwidget",
			"target":"some_container",
			"options":{
				"url":"{{entire_url('./some_ui.ui')}}"
			}
		}
	]
}

事件以及事件处理

每个控件都能触发所映射dom元素的事件以及控件js类的成员函数以及祖先类的 成员函数中dispatch出的事件

所以bricks控件的事件来源于两类dom元素原生事件以及控件类中自定义的事件。 两类事件处理方式相同。

控件表达形式

在服务器的后台以json文件的形式表达控件每个ui文件定义一个控件 对于容器控件可以在ui文件中的subwidgets子属性中为此控件添加子控件

id属性

字符串属性定义控件的id让控件可以用getWidgetById找到如果不给定系统会自动生成一个id

options属性

字典属性,创建控件时的选项,每个控件可接受的选项请参看控件选项说明

binds属性

数组属性定义零到多个事件响应每个bind字典需要遵守事件要求

容器控件特有属性

subwidgets

数组属性,定义容器控件的子控件,每个元素定义一个子控件,子控件遵守控件的数据要素要求

应用开发开发

使用存放在服务器后台的.ui后缀的json格式文件来开发每个.ui文件定义一个控件 支持基本控件和容器空间。

关于如何书写ui文件请参考UI文件格式

调试

ui文件可以直接调试如在服务器根目录下的test目录下有一个hello.ui文件 就可以在浏览器中用urlhttps://sername/test/hello.ui调试

bricks控件

bricks内置许多的显示控件所有显示控件都继承自JsWidget容器控件Layout就继承自JsWidget其他的容器HBox VBox继承自Layout

基础控件

  • Form:输入表单控件 自动根据options中的fields数组下的每个field的uitype属性值构造表单输入项目前uitype属性支持的数据类型有 ** 'str' 对应的控件为: bricks.UiStr ** 'hide' 对应的控件为: bricks.UiHide ** 'tel' 对应的控件为: bricks.UiTel ** 'date' 对应的控件为: bricks.UiDate ** 'int' 对应的控件为: bricks.UiInt ** 'float' 对应的控件为: bricks.UiFloat ** 'check' 对应的控件为: bricks.UiCheck ** 'checkbox' 对应的控件为: bricks.UiCheckBox ** 'email' 对应的控件为: bricks.UiEmail ** 'file' 对应的控件为: bricks.UiFile ** 'image' 对应的控件为: bricks.UiImage ** 'code' 对应的控件为: bricks.UiCode ** 'text' 对应的控件为: bricks.UiText ** 'password' 对应的控件为: bricks.UiPassword ** 'audio' 对应的控件为: bricks.UiAudio ** 'video' 对应的控件为: bricks.UiVideo 上述控件都在输入定义中注册为输入项控件

  • Accordion bricks.Accordion 手风琴控件,支持多个标题,内容组成的控件,内容和展开和折叠

  • AudioPlayer bricks.AudioPlayer 音频播放控件

  • ChartBar bricks.ChartBar 将后台数据显示为条形图表

  • Button bricks.Button 按钮控件

  • Cols bricks.Cols 列式排列控件,可动态填满父控件的宽度

  • Conform bricks.Conform 确认控件,弹出窗口显示内容,并要求用户确认

  • Countdown bricks.Countdown 时间倒计时控件,显示从还剩下的时间

  • TimePassed bricks.TimePassed 时间消耗控件,显示从开始计时开始所消耗的时间

  • DataGrid bricks.DataGrid 数据表格控件

  • DataRow bricks.DataRow 数据行控件

  • DataViewer bricks.DataViewer 数据显示控件DynamicColumn控件的后代控件

  • DOCXviewer bricks.DOCXviewer docx文件显示控件

  • EXCELviewer bricks.EXCELviewer excel文件显示控件

  • PDFviewer bricks.PDFviewer pdf显示控件

  • DynamicAccordion bricks.DynamicAccordion 动态手风琴控件

  • IconBar bricks.IconBar 图标条控件

  • IconTextBar bricks.IconTextBar 图标文本条控件

  • FloatIconBar bricks.FloatIconBar 浮动图标条,平时显示一个标识图标,点击此标识图标后显示图标条

  • FloatIconTextBar bricks.FloatIconTextBar 浮动图标正文条,平时显示一个标识图标,点击此图标后显示图标正文条

  • Html bricks.Html HTML控件直接显示html内容

  • IconbarPage bricks.IconbarPage 图标条页控件,显示图标条,不同的图标点击后显示图标对应的内容

  • NewWindow bricks.NewWindow 新浏览器页签或窗口控件浏览器创建新的窗口或页签显示url的内容

  • Iframe bricks.Iframe Iframe控件用于显示外部网站内容

  • Image bricks.Image 图像控件

  • StatedIcon bricks.StatedIcon 多站台图标,点击后状态改变,支持多个状态,并发出状态改变事件

  • Icon bricks.Icon 图标控件支持多种图像格式url

  • BlankIcon bricks.BlankIcon 空白图标占位控件

  • ChartLine bricks.ChartLine

  • LlmIO bricks.LlmIO

  • LlmOut bricks.LlmOut

  • MarkdownViewer bricks.MarkdownViewer

  • MdWidget bricks.MdWidget

  • Menu bricks.Menu

  • Message bricks.Message

  • Error bricks.Error

  • MultipleStateImage bricks.MultipleStateImage 多状态图像控件

  • PeriodDays bricks.PeriodDays 日期期间控件,自动计算时间段的起始日期

  • ChartPie bricks.ChartPie 饼图控件基于echrts

  • ProgressBar bricks.ProgressBar 进度条控件

  • SysCamera bricks.SysCamera 照相控件,可拍摄照片

  • WidgetRecorder bricks.WidgetRecorder 控件视频录制控件,可录制浏览器播放的视频

  • SysAudioRecorder bricks.SysAudioRecorder 浏览器音频录制控件,用来录制音频

  • SysVideoRecorder bricks.SysVideoRecorder 浏览器视频录制控件,用来录制视频

  • Running bricks.Running 运行图标控件modal模式显示正在运行相关控件不可操作需要在完成 任务后dismiss它

  • Splitter bricks.Splitter 分割器控件,显示水平或垂直分割线

  • Svg bricks.Svg Svg图标控件

  • StatedSvg bricks.StatedSvg 带状态的svg图标控件不同状态显示不同的图标

  • MultipleStateIcon bricks.MultipleStateIcon 多状态图标控件

  • TabPanel bricks.TabPanel 页签控件

  • Tabular bricks.Tabular 数据列表形式的数据维护控件,支持数据的显示,增加,修改和删除

xls2ddl工具能根据数据表结构自动生成数据Tabular控件以及相关的数据维护dspy

  • Toolbar bricks.Toolbar 工具条控件
  • Tree bricks.Tree 树形控件
  • VadText bricks.VadText 自动捕获语音并将捕获的语音发送给服务器
  • VideoPlayer bricks.VideoPlayer

视频播放控件支持浏览器支持的视频格式外还支持m3u8流媒体和Dash流媒体, bricks已在3parties目录中包含了所依赖的hls和dash包

  • Video bricks.VideoPlayer 视频播放控件同VideoPlayer
  • WebSocket bricks.WebSocket

支持websocket

  • WebTTS bricks.WebTTS 为完成控件,浏览器内置文本转语音能力
  • WebASR bricks.WebASR 未完成控件,浏览器内部的语音识别能力
  • Tooltip bricks.Tooltip

Tooltip控件不直接创建而是在控件中添加“tip":"提示字符串“属性为控件添加Tooltip控件

文本控件

第一号标题

第二号标题

第三号标题

第四号标题

第五号标题

第六号标题

xterm.js在bricks中的实现

容器类控件

  • VScrollPanel bricks.VScrollPanel 垂直滚动容器,需要设置固定的高度或占满全部父容器高度

  • HScrollPanel bricks.HScrollPanel 水平滚动容器,需要设置固定的宽度或占满全部父容器宽度

  • Popup bricks.Popup 弹出容器,置于当前全部控件最上面

  • PopupWindow bricks.PopupWindow 弹出窗口,置于当前全部控件最上面

  • HBox bricks.HBox 水平扩展容器,全部子控件水平排放

  • VBox bricks.VBox 垂直扩展容器,全部子控件垂直排放

  • Filler bricks.Filler 占满父容器剩余控件如果父容器有多个Filler控件则平均分配剩余控件Filler容器下可添加子控件

  • DynamicColumn bricks.DynamicColumn 子控件需要设置固定宽度,动态从左到右,从上到下排列子控件

  • ResponsableBox bricks.ResponsableBox 自适应容器,当宽度大则水平排列子控件,而高度大时则水平排列子控件, 并能根据宽高变化自动改变。

  • Modal bricks.Modal modal容器 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 UiMessage(title='SMS', message=f'validate code send to {params_kw.cell_no}')


return UiError(title='Error', message=f'{params_kw.cell_no} generate validate code error')

ui文件例子

{
    "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('')"
        }
    ]
}