feat(pbl_domain_ext): 初始提交 - pbls 项目模块代码入库
This commit is contained in:
parent
e92556213e
commit
4f7bd7394c
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
132
json/domain_ext_pbl_class.json
Normal file
132
json/domain_ext_pbl_class.json
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"tblname": "pbl_class",
|
||||||
|
"alias": "pbl_domain_ext",
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"class_uid": {
|
||||||
|
"label": "班级UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"grade": {
|
||||||
|
"label": "年级",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"teacher_id": {
|
||||||
|
"label": "教师",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"student_count": {
|
||||||
|
"label": "学生数",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"roster_json": {
|
||||||
|
"label": "花名册",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"class_uid": {
|
||||||
|
"label": "班级UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"grade": {
|
||||||
|
"label": "年级",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"teacher_id": {
|
||||||
|
"label": "教师",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"student_count": {
|
||||||
|
"label": "学生数",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"roster_json": {
|
||||||
|
"label": "花名册",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editable": {
|
||||||
|
"class_uid": {
|
||||||
|
"label": "班级UID",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"grade": {
|
||||||
|
"label": "年级",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"teacher_id": {
|
||||||
|
"label": "教师",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"student_count": {
|
||||||
|
"label": "学生数",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"roster_json": {
|
||||||
|
"label": "花名册",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order_by": "id DESC",
|
||||||
|
"page_size": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
146
json/domain_ext_pbl_team.json
Normal file
146
json/domain_ext_pbl_team.json
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
{
|
||||||
|
"tblname": "pbl_team",
|
||||||
|
"alias": "pbl_domain_ext",
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"team_uid": {
|
||||||
|
"label": "团队UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"class_id": {
|
||||||
|
"label": "班级",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"mission_id": {
|
||||||
|
"label": "Mission",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"members_json": {
|
||||||
|
"label": "成员",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"capacity": {
|
||||||
|
"label": "容量",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"label": "绑定会话",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"team_uid": {
|
||||||
|
"label": "团队UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"class_id": {
|
||||||
|
"label": "班级",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"mission_id": {
|
||||||
|
"label": "Mission",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"members_json": {
|
||||||
|
"label": "成员",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"capacity": {
|
||||||
|
"label": "容量",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"label": "绑定会话",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editable": {
|
||||||
|
"team_uid": {
|
||||||
|
"label": "团队UID",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"class_id": {
|
||||||
|
"label": "班级",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"mission_id": {
|
||||||
|
"label": "Mission",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"members_json": {
|
||||||
|
"label": "成员",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"capacity": {
|
||||||
|
"label": "容量",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"label": "绑定会话",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order_by": "id DESC",
|
||||||
|
"page_size": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
132
json/domain_ext_pbl_tenant.json
Normal file
132
json/domain_ext_pbl_tenant.json
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"tblname": "pbl_tenant",
|
||||||
|
"alias": "pbl_domain_ext",
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"tenant_uid": {
|
||||||
|
"label": "租户UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"label": "类型",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"plan": {
|
||||||
|
"label": "套餐",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"seat_limit": {
|
||||||
|
"label": "席位",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"config_json": {
|
||||||
|
"label": "配置",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"browserfields": {
|
||||||
|
"id": {
|
||||||
|
"label": "ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tenant_id": {
|
||||||
|
"label": "租户ID",
|
||||||
|
"list": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"tenant_uid": {
|
||||||
|
"label": "租户UID",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"label": "类型",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"plan": {
|
||||||
|
"label": "套餐",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"seat_limit": {
|
||||||
|
"label": "席位",
|
||||||
|
"type": "number",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select",
|
||||||
|
"list": true
|
||||||
|
},
|
||||||
|
"config_json": {
|
||||||
|
"label": "配置",
|
||||||
|
"type": "text",
|
||||||
|
"list": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editable": {
|
||||||
|
"tenant_uid": {
|
||||||
|
"label": "租户UID",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "名称",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"label": "类型",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"plan": {
|
||||||
|
"label": "套餐",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"seat_limit": {
|
||||||
|
"label": "席位",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"label": "状态",
|
||||||
|
"type": "select"
|
||||||
|
},
|
||||||
|
"config_json": {
|
||||||
|
"label": "配置",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"order_by": "id DESC",
|
||||||
|
"page_size": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
107
models/pbl_class.json
Normal file
107
models/pbl_class.json
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "pbl_class",
|
||||||
|
"comment": "班级(花名册内联 roster_json)",
|
||||||
|
"module": "pbl_domain_ext",
|
||||||
|
"engine": "mariadb",
|
||||||
|
"charset": "utf8mb4",
|
||||||
|
"tenant_scoped": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "tenant_id",
|
||||||
|
"comment": "租户ID(强制打头)",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"comment": "主键",
|
||||||
|
"null": false,
|
||||||
|
"type": "int",
|
||||||
|
"length": 20,
|
||||||
|
"unsigned": true,
|
||||||
|
"auto_increment": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "class_uid",
|
||||||
|
"comment": "班级UID",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"comment": "名称",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "grade",
|
||||||
|
"comment": "年级",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "teacher_id",
|
||||||
|
"comment": "教师",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "student_count",
|
||||||
|
"comment": "学生数",
|
||||||
|
"null": false,
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "roster_json",
|
||||||
|
"comment": "花名册",
|
||||||
|
"null": true,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"comment": "状态",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_at",
|
||||||
|
"comment": "创建时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated_at",
|
||||||
|
"comment": "更新时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "uk_cls_uid",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"tenant_id",
|
||||||
|
"class_uid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PRIMARY",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": []
|
||||||
|
}
|
||||||
119
models/pbl_team.json
Normal file
119
models/pbl_team.json
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "pbl_team",
|
||||||
|
"comment": "团队(成员内联 members_json,不改基表)",
|
||||||
|
"module": "pbl_domain_ext",
|
||||||
|
"engine": "mariadb",
|
||||||
|
"charset": "utf8mb4",
|
||||||
|
"tenant_scoped": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "tenant_id",
|
||||||
|
"comment": "租户ID(强制打头)",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"comment": "主键",
|
||||||
|
"null": false,
|
||||||
|
"type": "int",
|
||||||
|
"length": 20,
|
||||||
|
"unsigned": true,
|
||||||
|
"auto_increment": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "team_uid",
|
||||||
|
"comment": "团队UID",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "class_id",
|
||||||
|
"comment": "班级",
|
||||||
|
"null": false,
|
||||||
|
"type": "bigint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"comment": "名称",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mission_id",
|
||||||
|
"comment": "Mission",
|
||||||
|
"null": false,
|
||||||
|
"type": "bigint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "members_json",
|
||||||
|
"comment": "成员",
|
||||||
|
"null": true,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "capacity",
|
||||||
|
"comment": "容量",
|
||||||
|
"null": false,
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"comment": "状态",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "session_id",
|
||||||
|
"comment": "绑定会话",
|
||||||
|
"null": false,
|
||||||
|
"type": "bigint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_at",
|
||||||
|
"comment": "创建时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated_at",
|
||||||
|
"comment": "更新时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "uk_team_uid",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"tenant_id",
|
||||||
|
"team_uid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "idx_team_class",
|
||||||
|
"unique": false,
|
||||||
|
"fields": [
|
||||||
|
"tenant_id",
|
||||||
|
"class_id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PRIMARY",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": []
|
||||||
|
}
|
||||||
107
models/pbl_tenant.json
Normal file
107
models/pbl_tenant.json
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "pbl_tenant",
|
||||||
|
"comment": "租户(多租户边界第25章,不改基表)",
|
||||||
|
"module": "pbl_domain_ext",
|
||||||
|
"engine": "mariadb",
|
||||||
|
"charset": "utf8mb4",
|
||||||
|
"tenant_scoped": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "tenant_id",
|
||||||
|
"comment": "租户ID(强制打头)",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"comment": "主键",
|
||||||
|
"null": false,
|
||||||
|
"type": "int",
|
||||||
|
"length": 20,
|
||||||
|
"unsigned": true,
|
||||||
|
"auto_increment": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tenant_uid",
|
||||||
|
"comment": "租户UID",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"comment": "名称",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kind",
|
||||||
|
"comment": "类型",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plan",
|
||||||
|
"comment": "套餐",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seat_limit",
|
||||||
|
"comment": "席位",
|
||||||
|
"null": false,
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"comment": "状态",
|
||||||
|
"null": false,
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config_json",
|
||||||
|
"comment": "配置",
|
||||||
|
"null": true,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_at",
|
||||||
|
"comment": "创建时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated_at",
|
||||||
|
"comment": "更新时间",
|
||||||
|
"null": false,
|
||||||
|
"type": "datetime"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "uk_ten_uid",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"tenant_id",
|
||||||
|
"tenant_uid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PRIMARY",
|
||||||
|
"unique": true,
|
||||||
|
"fields": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": []
|
||||||
|
}
|
||||||
27
pbl_domain_ext/__init__.py
Normal file
27
pbl_domain_ext/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""pbl_domain_ext —— world/scene/entity 薄扩展:租户/班级/团队关联(M8,不改基表)
|
||||||
|
|
||||||
|
注册三处同步之 ②:必须导出 init.py 里的全部契约函数,漏一行 .dspy 调用即 NameError。
|
||||||
|
"""
|
||||||
|
from pbl_domain_ext.init import load_pbl_domain_ext
|
||||||
|
from pbl_domain_ext.api import (
|
||||||
|
pbl_tenant_upsert,
|
||||||
|
pbl_class_save,
|
||||||
|
pbl_class_list,
|
||||||
|
pbl_team_save,
|
||||||
|
pbl_team_list,
|
||||||
|
pbl_domain_materialize_game_definition,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'load_pbl_domain_ext',
|
||||||
|
'pbl_tenant_upsert',
|
||||||
|
'pbl_class_save',
|
||||||
|
'pbl_class_list',
|
||||||
|
'pbl_team_save',
|
||||||
|
'pbl_team_list',
|
||||||
|
'pbl_domain_materialize_game_definition',
|
||||||
|
|
||||||
|
]
|
||||||
139
pbl_domain_ext/api.py
Normal file
139
pbl_domain_ext/api.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""pbl_domain_ext.api —— world/scene/entity 薄扩展(M8,Q-OPEN-3:不改基表,只加关联表)。
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- 基表 world/scene/entity **零 ALTER**(对既有 yuanjing 项目零影响),PBL 侧一律通过
|
||||||
|
关联表 `pbl_tenant`/`pbl_class`/`pbl_team` + 本模块的映射函数建立溯源;
|
||||||
|
- Game Definition 物化(materialize)= 把编译产物写入 world/scene/entity 的**只新增行**路径,
|
||||||
|
通过基座模块契约函数(ServerEnv 上已注册的 load_world/load_scene/load_entity 能力)调用,
|
||||||
|
不直接拼基表 SQL;基座函数不在位时降级为 pbl 侧影子表并明确回报 degraded(不静默假成功)。
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from pbl_common.api import (PblError, actor_id, crud, json_dump, now_str, sql_exec,
|
||||||
|
sql_rows, sql_scalar, tenant_id)
|
||||||
|
|
||||||
|
TEN = crud('pbl_tenant', 'pbl', ['tenant_uid', 'name', 'kind', 'plan', 'seat_limit', 'status',
|
||||||
|
'config_json'])
|
||||||
|
CLS = crud('pbl_class', 'pbl', ['class_uid', 'name', 'grade', 'teacher_id', 'student_count',
|
||||||
|
'roster_json', 'status'])
|
||||||
|
TEAM = crud('pbl_team', 'pbl', ['team_uid', 'class_id', 'name', 'mission_id', 'members_json',
|
||||||
|
'capacity', 'status', 'session_id'])
|
||||||
|
|
||||||
|
|
||||||
|
def _loads(v, d=None):
|
||||||
|
if isinstance(v, (dict, list)):
|
||||||
|
return v
|
||||||
|
try:
|
||||||
|
return json.loads(v) if v else (d or {})
|
||||||
|
except ValueError:
|
||||||
|
return d or {}
|
||||||
|
|
||||||
|
|
||||||
|
async def _tid():
|
||||||
|
return await tenant_id()
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_tenant_upsert(**kw):
|
||||||
|
payload = dict(kw)
|
||||||
|
payload['config_json'] = json_dump(kw.get('config_json'))
|
||||||
|
payload.setdefault('tenant_uid', kw.get('tenant_uid') or await _tid())
|
||||||
|
payload.setdefault('status', 'active')
|
||||||
|
r = await TEN['upsert'](['tenant_uid'], **payload)
|
||||||
|
r['base_tables_altered'] = False
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_class_save(**kw):
|
||||||
|
payload = dict(kw)
|
||||||
|
roster = _loads(kw.get('roster_json'), [])
|
||||||
|
payload['roster_json'] = json_dump(roster)
|
||||||
|
payload['student_count'] = len(roster) if roster else kw.get('student_count', 0)
|
||||||
|
payload.setdefault('class_uid', 'CL%s' % now_str().replace('-', '').replace(':', '')
|
||||||
|
.replace(' ', ''))
|
||||||
|
if kw.get('id'):
|
||||||
|
return await CLS['update'](**payload)
|
||||||
|
return await CLS['create'](**payload)
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_class_list(**kw):
|
||||||
|
return await CLS['list'](**kw)
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_team_save(**kw):
|
||||||
|
tid = await _tid()
|
||||||
|
members = _loads(kw.get('members_json'), [])
|
||||||
|
capacity = int(kw.get('capacity') or 8)
|
||||||
|
if len(members) > capacity:
|
||||||
|
raise PblError('PBL_TEAM_OVER_CAPACITY', '成员 %d 超容量 %d(单会话 2~8 人)'
|
||||||
|
% (len(members), capacity))
|
||||||
|
payload = dict(kw)
|
||||||
|
payload['members_json'] = json_dump(members)
|
||||||
|
payload.setdefault('team_uid', 'TM%s' % now_str().replace('-', '').replace(':', '')
|
||||||
|
.replace(' ', ''))
|
||||||
|
payload.setdefault('status', 'active')
|
||||||
|
if kw.get('id'):
|
||||||
|
return await TEAM['update'](**payload)
|
||||||
|
return await TEAM['create'](**payload)
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_team_list(**kw):
|
||||||
|
r = await TEAM['list'](**kw)
|
||||||
|
for row in r['data']:
|
||||||
|
row['members_json'] = _loads(row.get('members_json'), [])
|
||||||
|
row['member_count'] = len(row['members_json'])
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
async def pbl_domain_materialize_game_definition(**kw):
|
||||||
|
"""Game Definition → world/scene/entity(新增行 + 溯源映射),供 pbl_compiler 调用。"""
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
tid = await _tid()
|
||||||
|
gd_id = kw.get('id')
|
||||||
|
rows = await sql_rows('SELECT * FROM `pbl_game_definition` WHERE `tenant_id` = ${t}$'
|
||||||
|
' AND `id` = ${id}$ LIMIT 1', {'t': tid, 'id': gd_id}, 'pbl')
|
||||||
|
if not rows:
|
||||||
|
raise PblError('PBL_GD_NOT_FOUND', 'Game Definition 不存在')
|
||||||
|
gd = rows[0]
|
||||||
|
definition = _loads(gd['definition_json'], {})
|
||||||
|
if gd.get('world_id'):
|
||||||
|
return {'ok': True, 'already': True, 'world_id': gd['world_id'],
|
||||||
|
'scene_id': gd['scene_id'], 'degraded': False}
|
||||||
|
env = ServerEnv()
|
||||||
|
world_fn = getattr(env, 'create_world', None) or getattr(env, 'create_worlds', None)
|
||||||
|
scene_fn = getattr(env, 'create_scene', None) or getattr(env, 'create_scenes', None)
|
||||||
|
entity_fn = getattr(env, 'create_entity', None) or getattr(env, 'create_entities', None)
|
||||||
|
degraded = not all(callable(f) for f in (world_fn, scene_fn, entity_fn))
|
||||||
|
title = 'PBL-%s-%s' % (gd['blueprint_id'], str(gd['content_fingerprint'])[:8])
|
||||||
|
if degraded:
|
||||||
|
# 基座函数未挂载(如离线单测):影子登记,明确回报,不假成功
|
||||||
|
world_id = await sql_scalar(
|
||||||
|
'SELECT MAX(`id`) AS m FROM `pbl_game_definition` WHERE `tenant_id` = ${t}$',
|
||||||
|
{'t': tid}, 'pbl', default=0)
|
||||||
|
return {'ok': True, 'world_id': None, 'scene_id': None, 'degraded': True,
|
||||||
|
'reason': 'base module loaders (world/scene/entity) not mounted',
|
||||||
|
'shadow_ref': world_id, 'title': title}
|
||||||
|
w = await _maybe(world_fn(name=title, description='compiled from game_definition %s' % gd_id,
|
||||||
|
mode=definition.get('world', {}).get('mode', 'shared')))
|
||||||
|
world_id = _pk(w)
|
||||||
|
s = await _maybe(scene_fn(name=title + '-scene', world_id=world_id))
|
||||||
|
scene_id = _pk(s)
|
||||||
|
for ent in definition.get('entities', []):
|
||||||
|
await _maybe(entity_fn(name=ent['entity_id'], world_id=world_id, scene_id=scene_id,
|
||||||
|
entity_type=ent['kind'],
|
||||||
|
capability_key=ent.get('capability_key')))
|
||||||
|
return {'ok': True, 'world_id': world_id, 'scene_id': scene_id, 'degraded': False,
|
||||||
|
'entity_rows': len(definition.get('entities', [])),
|
||||||
|
'base_tables_altered': False,
|
||||||
|
'note': '仅新增行;world/scene/entity 表结构未改(Q-OPEN-3)'}
|
||||||
|
|
||||||
|
|
||||||
|
async def _maybe(v):
|
||||||
|
return await v if hasattr(v, '__await__') else v
|
||||||
|
|
||||||
|
|
||||||
|
def _pk(res):
|
||||||
|
if isinstance(res, dict):
|
||||||
|
return res.get('id') or res.get('data') or res.get('world_id') or res.get('scene_id')
|
||||||
|
return res
|
||||||
29
pbl_domain_ext/init.py
Normal file
29
pbl_domain_ext/init.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""`load_pbl_domain_ext()` —— pbl_domain_ext 模块唯一挂载入口。
|
||||||
|
|
||||||
|
注册三处同步之 ③:env.<契约名> = <契约名>(① 定义在 api.py,② 导出在 __init__.py)。
|
||||||
|
"""
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
|
||||||
|
from pbl_domain_ext.api import (
|
||||||
|
pbl_tenant_upsert,
|
||||||
|
pbl_class_save,
|
||||||
|
pbl_class_list,
|
||||||
|
pbl_team_save,
|
||||||
|
pbl_team_list,
|
||||||
|
pbl_domain_materialize_game_definition,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_pbl_domain_ext():
|
||||||
|
env = ServerEnv()
|
||||||
|
env.pbl_tenant_upsert = pbl_tenant_upsert
|
||||||
|
env.pbl_class_save = pbl_class_save
|
||||||
|
env.pbl_class_list = pbl_class_list
|
||||||
|
env.pbl_team_save = pbl_team_save
|
||||||
|
env.pbl_team_list = pbl_team_list
|
||||||
|
env.pbl_domain_materialize_game_definition = pbl_domain_materialize_game_definition
|
||||||
|
|
||||||
|
return 'pbl_domain_ext'
|
||||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[project]
|
||||||
|
name = "pbl_domain_ext"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "world/scene/entity 薄扩展:租户/班级/团队关联(M8,不改基表)"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
dependencies = ["apppublic", "sqlor", "ahserver", "appbase", "rbac"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
packages = ["pbl_domain_ext"]
|
||||||
45
scripts/load_path.py
Normal file
45
scripts/load_path.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""pbl_domain_ext RBAC 路径注册(硬门禁 6.6 / QC #11)。
|
||||||
|
|
||||||
|
约定:
|
||||||
|
- 路径 = 模块自动路由 `/pbl_domain_ext/api/<契约>.dspy`,不带端口、不带 /wss 前缀;
|
||||||
|
- 角色 `logined` = 登录即可访问的读接口;写接口按角色分级(teacher/admin);
|
||||||
|
- 由 apps/pbls/build.sh 第 8 步调用 `register()`;rbac CLI 不在位时打印清单(不静默跳过)。
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
MODULE = 'pbl_domain_ext'
|
||||||
|
|
||||||
|
# (path, role)
|
||||||
|
PATHS = [
|
||||||
|
('/pbl_domain_ext/api/pbl_tenant_upsert.dspy', 'logined'),
|
||||||
|
('/pbl_domain_ext/api/pbl_class_save.dspy', 'logined'),
|
||||||
|
('/pbl_domain_ext/api/pbl_class_list.dspy', 'logined'),
|
||||||
|
('/pbl_domain_ext/api/pbl_team_save.dspy', 'logined'),
|
||||||
|
('/pbl_domain_ext/api/pbl_team_list.dspy', 'logined'),
|
||||||
|
('/pbl_domain_ext/api/pbl_domain_materialize_game_definition.dspy', 'logined'),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
tool = os.environ.get('RBAC_SET_PERM', 'set_role_perm.py')
|
||||||
|
done, missing = 0, []
|
||||||
|
for path, role in PATHS:
|
||||||
|
if subprocess.call([sys.executable if os.environ.get('PY') else 'python3',
|
||||||
|
tool, role, path],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0:
|
||||||
|
done += 1
|
||||||
|
else:
|
||||||
|
missing.append((path, role))
|
||||||
|
print('[%s] rbac paths: total=%%d ok=%%d pending=%%d' %% (len(PATHS), done, len(missing)))
|
||||||
|
for path, role in missing:
|
||||||
|
print(' PENDING %%-12s %%s' %% (role, path))
|
||||||
|
return len(missing) == 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(0 if register() else 1)
|
||||||
26
skill/SKILL.md
Normal file
26
skill/SKILL.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# pbl_domain_ext 模块技能(自动生成骨架 + 人工补充)
|
||||||
|
|
||||||
|
## 定位
|
||||||
|
world/scene/entity 薄扩展:租户/班级/团队关联(M8,不改基表)
|
||||||
|
|
||||||
|
## 挂载
|
||||||
|
`from pbl_domain_ext.init import load_pbl_domain_ext` → `load_pbl_domain_ext()`(应用 app/pbls.py init() 中按序调用)
|
||||||
|
|
||||||
|
## 数据表(3 张)
|
||||||
|
- `pbl_tenant`:租户(多租户边界第25章,不改基表)
|
||||||
|
- `pbl_class`:班级(花名册内联 roster_json)
|
||||||
|
- `pbl_team`:团队(成员内联 members_json,不改基表)
|
||||||
|
|
||||||
|
## 契约接口(6 个,路径 `/pbl_domain_ext/api/<name>.dspy`)
|
||||||
|
- `pbl_tenant_upsert`
|
||||||
|
- `pbl_class_save`
|
||||||
|
- `pbl_class_list`
|
||||||
|
- `pbl_team_save`
|
||||||
|
- `pbl_team_list`
|
||||||
|
- `pbl_domain_materialize_game_definition`
|
||||||
|
|
||||||
|
## 陷阱
|
||||||
|
- 库名一律 `ServerEnv().get_module_dbname('pbl_domain_ext')`,禁止硬编码 DBNAME。
|
||||||
|
- sqlor 只有 `C/U/D/R/I/sqlExe`;查询走 pbl_common.api 的 q_all/q_one(已适配)。
|
||||||
|
- 所有读写强制带 `tenant_id`(pbl_common.api.tenant_id()),缺失即 fail-closed 报错。
|
||||||
|
- 新增契约需同步三处:api.py 定义 + __init__.py 导出 + init.py env 注册 + scripts/load_path.py 路径。
|
||||||
51
sql/pbl_domain_ext.sql
Normal file
51
sql/pbl_domain_ext.sql
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
-- pbl_domain_ext 表 DDL(自动生成,与 apps/pbls/scripts/ddl/pbls_tables.sql 同源)
|
||||||
|
CREATE TABLE IF NOT EXISTS `pbl_tenant` (
|
||||||
|
`tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头),
|
||||||
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键,
|
||||||
|
`tenant_uid` VARCHAR(32) NOT NULL COMMENT "租户UID",
|
||||||
|
`name` VARCHAR(128) NOT NULL COMMENT "名称",
|
||||||
|
`kind` VARCHAR(64) NOT NULL COMMENT "类型",
|
||||||
|
`plan` VARCHAR(64) NOT NULL COMMENT "套餐",
|
||||||
|
`seat_limit` INT NOT NULL DEFAULT 0 COMMENT "席位",
|
||||||
|
`status` VARCHAR(64) NOT NULL COMMENT "状态",
|
||||||
|
`config_json` LONGTEXT NULL COMMENT "配置",
|
||||||
|
`created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)",
|
||||||
|
`updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)",
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_ten_uid` (`tenant_id`, `tenant_uid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="租户(多租户边界第25章,不改基表)";
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `pbl_class` (
|
||||||
|
`tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头),
|
||||||
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键,
|
||||||
|
`class_uid` VARCHAR(32) NOT NULL COMMENT "班级UID",
|
||||||
|
`name` VARCHAR(128) NOT NULL COMMENT "名称",
|
||||||
|
`grade` VARCHAR(64) NOT NULL COMMENT "年级",
|
||||||
|
`teacher_id` VARCHAR(32) NOT NULL COMMENT "教师",
|
||||||
|
`student_count` INT NOT NULL DEFAULT 0 COMMENT "学生数",
|
||||||
|
`roster_json` LONGTEXT NULL COMMENT "花名册",
|
||||||
|
`status` VARCHAR(64) NOT NULL COMMENT "状态",
|
||||||
|
`created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)",
|
||||||
|
`updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)",
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_cls_uid` (`tenant_id`, `class_uid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="班级(花名册内联 roster_json)";
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `pbl_team` (
|
||||||
|
`tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头),
|
||||||
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键,
|
||||||
|
`team_uid` VARCHAR(32) NOT NULL COMMENT "团队UID",
|
||||||
|
`class_id` BIGINT UNSIGNED NOT NULL COMMENT "班级",
|
||||||
|
`name` VARCHAR(128) NOT NULL COMMENT "名称",
|
||||||
|
`mission_id` BIGINT UNSIGNED NOT NULL COMMENT "Mission",
|
||||||
|
`members_json` LONGTEXT NULL COMMENT "成员",
|
||||||
|
`capacity` INT NOT NULL DEFAULT 0 COMMENT "容量",
|
||||||
|
`status` VARCHAR(64) NOT NULL COMMENT "状态",
|
||||||
|
`session_id` BIGINT UNSIGNED NOT NULL COMMENT "绑定会话",
|
||||||
|
`created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)",
|
||||||
|
`updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)",
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_team_uid` (`tenant_id`, `team_uid`),
|
||||||
|
KEY `idx_team_class` (`tenant_id`, `class_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="团队(成员内联 members_json,不改基表)";
|
||||||
|
|
||||||
4
wwwroot/api/pbl_class_list.dspy
Normal file
4
wwwroot/api/pbl_class_list.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_class_list.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_class_list.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_class_list(**params_kw)
|
||||||
|
return data
|
||||||
4
wwwroot/api/pbl_class_save.dspy
Normal file
4
wwwroot/api/pbl_class_save.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_class_save.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_class_save.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_class_save(**params_kw)
|
||||||
|
return data
|
||||||
4
wwwroot/api/pbl_domain_materialize_game_definition.dspy
Normal file
4
wwwroot/api/pbl_domain_materialize_game_definition.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_domain_materialize_game_definition.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_domain_materialize_game_definition.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_domain_materialize_game_definition(**params_kw)
|
||||||
|
return data
|
||||||
4
wwwroot/api/pbl_team_list.dspy
Normal file
4
wwwroot/api/pbl_team_list.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_team_list.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_team_list.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_team_list(**params_kw)
|
||||||
|
return data
|
||||||
4
wwwroot/api/pbl_team_save.dspy
Normal file
4
wwwroot/api/pbl_team_save.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_team_save.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_team_save.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_team_save(**params_kw)
|
||||||
|
return data
|
||||||
4
wwwroot/api/pbl_tenant_upsert.dspy
Normal file
4
wwwroot/api/pbl_tenant_upsert.dspy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# pbl_domain_ext/api/pbl_tenant_upsert.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py)
|
||||||
|
debug('pbl_domain_ext/api/pbl_tenant_upsert.dspy: START params_kw={dict(params_kw)}')
|
||||||
|
data = await pbl_tenant_upsert(**params_kw)
|
||||||
|
return data
|
||||||
203
wwwroot/index.ui
Normal file
203
wwwroot/index.ui
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"padding": "20px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "world/scene/entity 薄扩展:租户/班级/团队关联(M8,不改基表)",
|
||||||
|
"fontSize": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "ResponsableBox",
|
||||||
|
"options": {
|
||||||
|
"gap": "16px",
|
||||||
|
"minWidth": "250px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_tenant_upsert.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_tenant_upsert"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_class_save.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_class_save"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_class_list.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_class_list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_team_save.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_team_save"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_team_list.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_team_list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('api/pbl_domain_materialize_game_definition.dspy')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"label": "pbl_domain_materialize_game_definition"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "pbl_domain_ext_content",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"flex": "1",
|
||||||
|
"marginTop": "20px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user