refactor: 删除 product_type_config(运营商产品类型配置)功能

- 删除 models/product_type_config.json
- 删除 json/product_type_config_list.json (CRUD定义)
- 删除 wwwroot/product_type_config_manage.ui 和 product_type_config_list/ 目录
- 删除 wwwroot/api/product_type_config_{create,update,delete}.dspy
- core.py: 移除 get_operator_config/set_operator_config 方法
- core.py: get_product_detail 不再查询 operator_config
- __init__.py: 移除 wrapper 函数和 env 注册
- product_detail.dspy: 移除 operator_config 查询逻辑
- scripts/load_path.py: 移除所有 product_type_config 权限路径
- menu.ui: 移除"运营商配置"菜单项
- index.ui: 移除"运营商配置"卡片,更新标题描述
- README.md: 移除相关文档
This commit is contained in:
Hermes Agent 2026-06-24 10:34:48 +08:00
parent 6dae1676ef
commit 1438de9d58
18 changed files with 9 additions and 1083 deletions

View File

@ -17,7 +17,6 @@ product_management 是产品生命周期的中枢,负责:
|------|------|
| `product_category` | 产品类别树parent_id 层级关系resource_module 标记来源模块) |
| `product` | 产品注册表resource_ref_id 关联资源模块内部ID |
| `product_type_config` | 产品类型配置(运营者级别的 JSON 配置) |
| `product_resource` | 产品-资源绑定(一个产品可绑定多个资源,含配额和溢出产品) |
| `product_resource_supplier` | 资源绑定的供应商路由(优先级+权重) |
| `product_subscription` | 产品订阅(月度/按量,含配额跟踪和溢出计费) |
@ -118,12 +117,10 @@ def load_product_management():
# 基础 CRUD
env.get_product_brief = get_product_brief
env.get_product_detail = get_product_detail
env.get_category_tree = get_category_tree
env.get_products_by_category = get_products_by_category
env.purchase_product = purchase_product
env.use_product = use_product
env.get_operator_config = get_operator_config
env.set_operator_config = set_operator_config
env.get_category_tree = get_category_tree
env.get_products_by_category = get_products_by_category
env.import_categories_and_products = import_categories_and_products
# 资源模块接口调度
env.get_product_display_info = get_product_display_info
@ -255,7 +252,6 @@ product_management/
│ ├── product_resource.json
│ ├── product_resource_supplier.json
│ ├── product_subscription.json
│ ├── product_type_config.json
│ └── product_usage_log.json
├── product_management/
│ ├── __init__.py # ServerEnv 注册 + 入口函数

View File

@ -1,41 +0,0 @@
{
"tblname": "product_type_config",
"alias": "product_type_config_list",
"title": "运营商产品类型配置",
"params": {
"sortby": ["created_at desc"],
"logined_userorgid": "org_id",
"logined_userid": "operator_id",
"data_filter": {
"AND": [
{"field": "config_name", "op": "LIKE", "var": "config_name"}
]
},
"filter_labels": {
"config_name": "配置名称"
},
"browserfields": {
"exclouded": [],
"alters": {
"category_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/category_options.dspy')}}",
"datamethod": "GET"
},
"enabled_flg": {
"uitype": "code",
"data": [
{"value": "1", "text": "启用"},
{"value": "0", "text": "禁用"}
]
}
}
},
"editexclouded": ["operator_id", "created_by", "created_at", "updated_at"],
"editable": {
"new_data_url": "{{entire_url('../api/product_type_config_create.dspy')}}",
"update_data_url": "{{entire_url('../api/product_type_config_update.dspy')}}",
"delete_data_url": "{{entire_url('../api/product_type_config_delete.dspy')}}"
}
}
}

View File

@ -1,122 +0,0 @@
{
"summary": [
{
"name": "product_type_config",
"title": "运营商产品类型配置",
"primary": [
"id"
],
"catelog": "relation"
}
],
"fields": [
{
"name": "id",
"title": "主键ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "operator_id",
"title": "运营商用户ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "org_id",
"title": "所属机构ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "category_id",
"title": "产品类别ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "config_name",
"title": "配置名称",
"type": "str",
"length": 255,
"nullable": "no"
},
{
"name": "config_json",
"title": "配置内容",
"type": "text"
},
{
"name": "enabled_flg",
"title": "是否启用",
"type": "char",
"length": 1,
"default": "1"
},
{
"name": "created_by",
"title": "创建人",
"type": "str",
"length": 32
},
{
"name": "created_at",
"title": "创建时间",
"type": "timestamp",
"nullable": "no"
},
{
"name": "updated_at",
"title": "更新时间",
"type": "timestamp",
"nullable": "no"
}
],
"indexes": [
{
"name": "idx_ptc_org_opr",
"idxtype": "index",
"idxfields": [
"org_id",
"operator_id"
]
},
{
"name": "idx_ptc_org_cat",
"idxtype": "unique",
"idxfields": [
"org_id",
"operator_id",
"category_id",
"config_name"
]
},
{
"name": "idx_ptc_enabled",
"idxtype": "index",
"idxfields": [
"enabled_flg"
]
}
],
"codes": [
{
"field": "category_id",
"table": "product_category",
"valuefield": "id",
"textfield": "name",
"cond": "has_product='1' AND status='1'"
},
{
"field": "enabled_flg",
"table": "appcodes_kv",
"valuefield": "k",
"textfield": "v",
"cond": "parentid='enabled_flg'"
}
]
}

View File

@ -55,17 +55,6 @@ async def get_products_by_category(category_id, status='1'):
return await manager.get_products_by_category(category_id, status)
async def get_operator_config(category_id, user_id=None):
"""Get operator configuration for a category."""
manager = get_manager()
return await manager.get_operator_config(category_id, user_id)
async def set_operator_config(category_id, config_name, config_json, user_id=None, org_id=None):
"""Create or update operator configuration for a category."""
manager = get_manager()
return await manager.set_operator_config(category_id, config_name, config_json, user_id, org_id)
async def import_categories_and_products(resource_module, org_id, parent_category_id, user_id):
"""Import sub-categories and products from a resource module."""
@ -120,8 +109,7 @@ def load_product_management():
env.use_product = use_product
env.get_category_tree = get_category_tree
env.get_products_by_category = get_products_by_category
env.get_operator_config = get_operator_config
env.set_operator_config = set_operator_config
env.import_categories_and_products = import_categories_and_products
# Resource module interface dispatchers
env.get_product_display_info = get_product_display_info

View File

@ -177,7 +177,7 @@ class ProductManager:
async def get_product_detail(self, product_id=None, product_code=None, org_id=None, user_id=None):
"""Get product detail for current org (reseller).
Returns product_info + category_info + operator_config + extra_parsed.
Returns product_info + category_info + extra_parsed.
"""
if not org_id:
org_id = self._get_current_org_id()
@ -225,29 +225,6 @@ class ProductManager:
extra_parsed = {'_raw': extra_str}
product_info['extra_parsed'] = extra_parsed
# Get operator config for this category
config_sql = """SELECT * FROM product_type_config
WHERE category_id = ${category_id}$
AND org_id = ${org_id}$
AND enabled_flg = '1'
AND (operator_id = ${user_id}$ OR operator_id = '0')
ORDER BY created_at DESC LIMIT 1"""
config_rows = await sor.sqlExe(config_sql, {
'category_id': product_info['category_id'],
'org_id': org_id,
'user_id': user_id
})
operator_config = {}
if config_rows:
operator_config = dict(config_rows[0])
config_json = operator_config.get('config_json', '')
if config_json:
try:
operator_config['config_parsed'] = json.loads(config_json)
except:
operator_config['config_parsed'] = {}
return {
'success': True,
'data': {
@ -255,8 +232,7 @@ class ProductManager:
'category_info': {
'name': product_info.get('category_name'),
'description': product_info.get('category_description')
},
'operator_config': operator_config
}
}
}
@ -387,110 +363,6 @@ class ProductManager:
'message': 'Product use successful'
}
async def get_operator_config(self, category_id, org_id=None, user_id=None):
"""Get operator configuration for a category within current org."""
if not org_id:
org_id = self._get_current_org_id()
if not user_id:
env = ServerEnv()
try:
user_id = await env.get_user()
except:
user_id = 'anonymous'
dbname = self._get_dbname()
async with DBPools().sqlorContext(dbname) as sor:
sql = """SELECT * FROM product_type_config
WHERE category_id = ${category_id}$
AND org_id = ${org_id}$
AND enabled_flg = '1'
AND (operator_id = ${user_id}$ OR operator_id = '0')
ORDER BY created_at DESC"""
rows = await sor.sqlExe(sql, {
'category_id': category_id,
'org_id': org_id,
'user_id': user_id
})
configs = []
for r in (rows or []):
r = dict(r)
config_json = r.get('config_json', '')
if config_json:
try:
r['config_parsed'] = json.loads(config_json)
except:
r['config_parsed'] = {}
configs.append(r)
return {'success': True, 'configs': configs}
async def set_operator_config(self, category_id, config_name, config_json, org_id=None, user_id=None):
"""Create or update operator configuration within current org."""
if not org_id:
org_id = self._get_current_org_id()
if not user_id:
env = ServerEnv()
try:
user_id = await env.get_user()
except:
return {'success': False, 'message': 'User not authenticated'}
if not config_name:
return {'success': False, 'message': 'Missing config_name'}
dbname = self._get_dbname()
now = time.strftime('%Y-%m-%d %H:%M:%S')
try:
json.loads(config_json)
except:
return {'success': False, 'message': 'Invalid config_json format'}
async with DBPools().sqlorContext(dbname) as sor:
# Verify category belongs to org
cat_check = await sor.sqlExe(
"SELECT id FROM product_category WHERE id = ${category_id}$ AND org_id = ${org_id}$",
{'category_id': category_id, 'org_id': org_id}
)
if not cat_check:
return {'success': False, 'message': 'Category not found or no access'}
existing = await sor.sqlExe(
"""SELECT id FROM product_type_config
WHERE category_id = ${category_id}$
AND org_id = ${org_id}$
AND operator_id = ${user_id}$
AND config_name = ${config_name}$""",
{'category_id': category_id, 'org_id': org_id,
'user_id': user_id, 'config_name': config_name}
)
if existing:
config_id = existing[0]['id']
await sor.U('product_type_config', {
'id': config_id,
'config_json': config_json,
'updated_at': now
})
return {'success': True, 'id': config_id, 'message': 'Config updated'}
else:
config_id = getID()
await sor.C('product_type_config', {
'id': config_id,
'operator_id': user_id,
'org_id': org_id,
'category_id': category_id,
'config_name': config_name,
'config_json': config_json,
'enabled_flg': '1',
'created_by': user_id,
'created_at': now,
'updated_at': now
})
return {'success': True, 'id': config_id, 'message': 'Config created'}
# ─── Resource Binding ───
async def bind_resource(self, product_id, resource_type, resource_ref_id,

View File

@ -57,15 +57,12 @@ PATHS_LOGINED = [
# Feature pages
f"/{MOD}/category_manage.ui",
f"/{MOD}/product_manage.ui",
f"/{MOD}/product_type_config_manage.ui",
# CRUD alias directories (directory path + index.ui path)
f"/{MOD}/product_category_tree",
f"/{MOD}/product_category_tree/index.ui",
f"/{MOD}/product_list",
f"/{MOD}/product_list/index.ui",
f"/{MOD}/product_type_config_list",
f"/{MOD}/product_type_config_list/index.ui",
# API endpoints (.dspy)
f"/{MOD}/api/category_options.dspy",
@ -79,9 +76,6 @@ PATHS_LOGINED = [
f"/{MOD}/api/product_detail.dspy",
f"/{MOD}/api/product_purchase.dspy",
f"/{MOD}/api/product_use.dspy",
f"/{MOD}/api/product_type_config_create.dspy",
f"/{MOD}/api/product_type_config_update.dspy",
f"/{MOD}/api/product_type_config_delete.dspy",
# CRUD auto-generated .dspy
f"/{MOD}/product_category_tree/get_product_category.dspy",
@ -92,10 +86,6 @@ PATHS_LOGINED = [
f"/{MOD}/product_list/add_product.dspy",
f"/{MOD}/product_list/update_product.dspy",
f"/{MOD}/product_list/delete_product.dspy",
f"/{MOD}/product_type_config_list/get_product_type_config.dspy",
f"/{MOD}/product_type_config_list/add_product_type_config.dspy",
f"/{MOD}/product_type_config_list/update_product_type_config.dspy",
f"/{MOD}/product_type_config_list/delete_product_type_config.dspy",
# Resource binding
f"/{MOD}/product_resource_list",

View File

@ -6,11 +6,10 @@
product_code: 产品编码 (可选)
org_id: 机构ID (可选,不传则用当前用户机构)
返回:
{success, data: {product_info, category_info, operator_config, extra_parsed}}
{success, data: {product_info, category_info, extra_parsed}}
说明:
product_info 包含产品全部信息
extra_parsed 是 extra_json 解析后的结构化数据
operator_config 是当前运营商对该产品类型的配置
"""
import json
@ -63,36 +62,12 @@ try:
extra_parsed = {'_raw': extra_str}
product_info['extra_parsed'] = extra_parsed
# Get operator config for this product_type
config_sql = """SELECT * FROM product_type_config
WHERE category_id = ${category_id}$
AND enabled_flg = '1'
AND org_id = ${org_id}$
AND (operator_id = ${user_id}$ OR operator_id = '0')
ORDER BY created_at DESC LIMIT 1"""
config_rows = await sor.sqlExe(config_sql, {
'category_id': product_info['category_id'],
'org_id': org_id,
'user_id': user_id
})
operator_config = {}
if config_rows:
operator_config = dict(config_rows[0])
config_json = operator_config.get('config_json', '')
if config_json:
try:
operator_config['config_parsed'] = json.loads(config_json)
except:
operator_config['config_parsed'] = {}
result['data'] = {
'product_info': product_info,
'category_info': {
'name': product_info.get('category_name'),
'description': product_info.get('category_description')
},
'operator_config': operator_config
}
}
result['success'] = True

View File

@ -1,26 +0,0 @@
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
try:
user_id = await get_user()
org_id = (await get_userorgid()) or '0'
dbname = get_module_dbname('product_management')
data = dict(params_kw)
data['id'] = getID()
data['operator_id'] = user_id
data['org_id'] = org_id
data['created_by'] = user_id
data['created_at'] = timestampstr()
data['updated_at'] = timestampstr()
if 'enabled_flg' not in data:
data['enabled_flg'] = '1'
async with DBPools().sqlorContext(dbname) as sor:
await sor.C('product_type_config', data)
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置创建成功', 'type': 'success'}}
except Exception as e:
result['options'] = {'title': 'Error', 'message': '创建失败: ' + str(e), 'type': 'error'}
return json.dumps(result, ensure_ascii=False)

View File

@ -1,31 +0,0 @@
#!/usr/bin/env python3
import json
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
try:
user_id = await get_user()
org_id = (await get_userorgid()) or '0'
dbname = get_module_dbname('product_management')
data = dict(params_kw)
record_id = data.get('id')
if not record_id:
raise ValueError('Missing id')
async with DBPools().sqlorContext(dbname) as sor:
check = await sor.sqlExe(
"SELECT id FROM product_type_config WHERE id = ${id}$ AND org_id = ${org_id}$",
{'id': record_id, 'org_id': org_id}
)
if not check:
raise ValueError('无权删除该记录')
await sor.D('product_type_config', {'id': record_id, 'org_id': org_id})
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置删除成功', 'type': 'success'}}
except Exception as e:
result['options'] = {'title': 'Error', 'message': '删除失败: ' + str(e), 'type': 'error'}
return json.dumps(result, ensure_ascii=False)

View File

@ -1,32 +0,0 @@
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
try:
user_id = await get_user()
org_id = (await get_userorgid()) or '0'
dbname = get_module_dbname('product_management')
data = dict(params_kw)
record_id = data.pop('id', None)
if not record_id:
raise ValueError('Missing id')
async with DBPools().sqlorContext(dbname) as sor:
check = await sor.sqlExe(
"SELECT id FROM product_type_config WHERE id = ${id}$ AND org_id = ${org_id}$",
{'id': record_id, 'org_id': org_id}
)
if not check:
raise ValueError('无权操作该记录')
data['updated_at'] = timestampstr()
data['id'] = record_id
async with DBPools().sqlorContext(dbname) as sor:
await sor.U('product_type_config', data)
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置更新成功', 'type': 'success'}}
except Exception as e:
result['options'] = {'title': 'Error', 'message': '更新失败: ' + str(e), 'type': 'error'}
return json.dumps(result, ensure_ascii=False)

View File

@ -28,7 +28,7 @@
{
"widgettype": "Text",
"options": {
"text": "产品类别、产品信息与运营商配置管理",
"text": "产品类别与产品信息管理",
"fontSize": "14px"
}
}
@ -113,42 +113,6 @@
}
}
]
},
{
"widgettype": "VBox",
"options": {
"padding": "16px 24px",
"borderRadius": "8px",
"cursor": "pointer"
},
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "urlwidget",
"target": "app.product_content",
"options": {
"url": "{{entire_url('product_type_config_list')}}"
},
"mode": "replace"
}
],
"subwidgets": [
{
"widgettype": "Title5",
"options": {
"text": "运营商配置",
"fontWeight": "600"
}
},
{
"widgettype": "Text",
"options": {
"text": "配置运营商产品类型参数",
"fontSize": "12px"
}
}
]
}
]
},

View File

@ -11,8 +11,7 @@
{"name": "resource", "label": "产品资源绑定", "url": "{{entire_url('/product_management/product_resource_list')}}"},
{"name": "supplier", "label": "资源供应商关联", "url": "{{entire_url('/product_management/product_resource_supplier_list')}}"},
{"name": "subscription", "label": "客户订购管理", "url": "{{entire_url('/product_management/product_subscription_list')}}"},
{"name": "usage", "label": "产品消费记录", "url": "{{entire_url('/product_management/product_usage_log_list')}}"},
{"name": "config", "label": "运营商配置", "url": "{{entire_url('/product_management/product_type_config_list')}}"}
{"name": "usage", "label": "产品消费记录", "url": "{{entire_url('/product_management/product_usage_log_list')}}"}
{% endif %}
]
}

View File

@ -1,67 +0,0 @@
ns = params_kw.copy()
for k,v in ns.items():
if v == 'NaN' or v == 'null':
ns[k] = None
id = params_kw.id
if not id or len(id) > 32:
id = uuid()
ns['id'] = id
userid = await get_user()
if not userid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['operator_id'] = userid
userorgid = await get_userorgid()
if not userorgid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['org_id'] = userorgid
ns['created_at'] = timestampstr()
ns['updated_at'] = timestampstr()
db = DBPools()
dbname = get_module_dbname('product_management')
async with db.sqlorContext(dbname) as sor:
r = await sor.C('product_type_config', ns.copy())
return {
"widgettype":"Message",
"options":{
"cwidth":16,
"cheight":9,
"title":"Add Success",
"timeout":3,
"message":"ok"
}
}
return {
"widgettype":"Error",
"options":{
"title":"Add Error",
"cwidth":16,
"cheight":9,
"timeout":3,
"message":"failed"
}
}

View File

@ -1,61 +0,0 @@
ns = {
'id':params_kw['id'],
}
userid = await get_user()
if not userid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['operator_id'] = userid
userorgid = await get_userorgid()
if not userorgid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['org_id'] = userorgid
db = DBPools()
dbname = get_module_dbname('product_management')
async with db.sqlorContext(dbname) as sor:
r = await sor.D('product_type_config', ns)
debug('delete success');
return {
"widgettype":"Message",
"options":{
"title":"Delete Success",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"ok"
}
}
debug('Delete failed');
return {
"widgettype":"Error",
"options":{
"title":"Delete Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"failed"
}
}

View File

@ -1,148 +0,0 @@
ns = params_kw.copy()
userid = await get_user()
if not userid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['operator_id'] = userid
ns['userid'] = userid
userorgid = await get_userorgid()
if not userorgid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['org_id'] = userorgid
ns['userorgid'] = userorgid
debug(f'get_product_type_config.dspy:{ns=}')
if not ns.get('page'):
ns['page'] = 1
if not ns.get('sort'):
ns['sort'] = ["created_at desc"]
sql = '''select a.*, b.category_id_text, c.enabled_flg_text
from (select * from product_type_config where 1=1 [[filterstr]]) a left join (select id as category_id,
name as category_id_text from product_category where has_product='1' AND status='1') b on a.category_id = b.category_id left join (select k as enabled_flg,
v as enabled_flg_text from appcodes_kv where id='enabled_flg') c on a.enabled_flg = c.enabled_flg'''
filterjson = params_kw.get('data_filter')
fields_str=r'''[
{
"name": "id",
"title": "主键ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "operator_id",
"title": "运营商用户ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "org_id",
"title": "所属机构ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "category_id",
"title": "产品类别ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "config_name",
"title": "配置名称",
"type": "str",
"length": 255,
"nullable": "no"
},
{
"name": "config_json",
"title": "配置内容",
"type": "text"
},
{
"name": "enabled_flg",
"title": "是否启用",
"type": "char",
"length": 1,
"default": "1"
},
{
"name": "created_by",
"title": "创建人",
"type": "str",
"length": 32
},
{
"name": "created_at",
"title": "创建时间",
"type": "datetime",
"nullable": "no"
},
{
"name": "updated_at",
"title": "更新时间",
"type": "datetime",
"nullable": "no"
}
]'''
ori_fields = json.loads(fields_str)
if not filterjson:
fields = [ f['name'] for f in ori_fields ]
filterjson = default_filterjson(fields, ns)
filterdic = ns.copy()
filterdic['filterstr'] = ''
filterdic['userorgid'] = '${userorgid}$'
filterdic['userid'] = '${userid}$'
if filterjson:
dbf = DBFilter(filterjson)
conds = dbf.gen(ns)
if conds:
ns.update(dbf.consts)
conds = f' and {conds}'
filterdic['filterstr'] = conds
ac = ArgsConvert('[[', ']]')
vars = ac.findAllVariables(sql)
NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' }
filterdic.update(NameSpace)
sql = ac.convert(sql, filterdic)
debug(f'{sql=}')
db = DBPools()
dbname = get_module_dbname('product_management')
async with db.sqlorContext(dbname) as sor:
r = await sor.sqlPaging(sql, ns)
return r
return {
"total":0,
"rows":[]
}

View File

@ -1,227 +0,0 @@
{
"widgettype": "VBox",
"options": {
"css": "filler", "height": "100%",
"width": "100%"
},
"subwidgets": [
{
"id":"product_type_config_tbl",
"widgettype":"Tabular",
"options":{
"width":"100%",
"height":"100%",
"title":"运营商产品类型配置",
"css":"card",
"editable":{
"new_data_url":"{{entire_url('add_product_type_config.dspy')}}",
"delete_data_url":"{{entire_url('delete_product_type_config.dspy')}}",
"update_data_url":"{{entire_url('update_product_type_config.dspy')}}"
},
"data_url":"{{entire_url('./get_product_type_config.dspy')}}",
"data_method":"GET",
"data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}},
"row_options":{
"browserfields": {
"exclouded": [],
"alters": {
"category_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/category_options.dspy')}}",
"datamethod": "GET"
},
"enabled_flg": {
"uitype": "code",
"data": [
{
"value": "1",
"text": "启用"
},
{
"value": "0",
"text": "禁用"
}
]
}
}
},
"editexclouded":[
"operator_id",
"created_by",
"created_at",
"updated_at"
],
"fields":[
{
"name": "id",
"title": "主键ID",
"type": "str",
"length": 32,
"nullable": "no",
"cwidth": 18,
"uitype": "str",
"datatype": "str",
"label": "主键ID"
},
{
"name": "operator_id",
"title": "运营商用户ID",
"type": "str",
"length": 32,
"nullable": "no",
"cwidth": 18,
"uitype": "str",
"datatype": "str",
"label": "运营商用户ID"
},
{
"name": "org_id",
"title": "所属机构ID",
"type": "str",
"length": 32,
"nullable": "no",
"cwidth": 18,
"uitype": "str",
"datatype": "str",
"label": "所属机构ID"
},
{
"name": "category_id",
"title": "产品类别ID",
"type": "str",
"length": 32,
"nullable": "no",
"label": "产品类别ID",
"uitype": "code",
"valueField": "category_id",
"textField": "category_id_text",
"params": {
"dbname": "{{get_module_dbname('product_management')}}",
"table": "product_category",
"tblvalue": "id",
"tbltext": "name",
"valueField": "category_id",
"textField": "category_id_text",
"cond": "has_product='1' AND status='1'"
},
"dataurl": "{{entire_url('../api/category_options.dspy')}}",
"datamethod": "GET"
},
{
"name": "config_name",
"title": "配置名称",
"type": "str",
"length": 255,
"nullable": "no",
"cwidth": 18,
"uitype": "str",
"datatype": "str",
"label": "配置名称"
},
{
"name": "config_json",
"title": "配置内容",
"type": "text",
"length": 0,
"uitype": "text",
"datatype": "text",
"label": "配置内容"
},
{
"name": "enabled_flg",
"title": "是否启用",
"type": "char",
"length": 1,
"default": "1",
"label": "是否启用",
"uitype": "code",
"valueField": "enabled_flg",
"textField": "enabled_flg_text",
"params": {
"dbname": "{{get_module_dbname('product_management')}}",
"table": "appcodes_kv",
"tblvalue": "k",
"tbltext": "v",
"valueField": "enabled_flg",
"textField": "enabled_flg_text",
"cond": "id='enabled_flg'"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}",
"data": [
{
"value": "1",
"text": "启用"
},
{
"value": "0",
"text": "禁用"
}
]
},
{
"name": "created_by",
"title": "创建人",
"type": "str",
"length": 32,
"cwidth": 18,
"uitype": "str",
"datatype": "str",
"label": "创建人"
},
{
"name": "created_at",
"title": "创建时间",
"type": "datetime",
"nullable": "no",
"length": 0,
"uitype": "str",
"datatype": "datetime",
"label": "创建时间"
},
{
"name": "updated_at",
"title": "更新时间",
"type": "datetime",
"nullable": "no",
"length": 0,
"uitype": "str",
"datatype": "datetime",
"label": "更新时间"
}
]
},
"page_rows":160,
"cache_limit":5
}
,"binds":[]
}
]
}

View File

@ -1,87 +0,0 @@
ns = params_kw.copy()
for k,v in ns.items():
if v == 'NaN' or v == 'null':
ns[k] = None
userid = await get_user()
if not userid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['operator_id'] = userid
userorgid = await get_userorgid()
if not userorgid:
return {
"widgettype":"Error",
"options":{
"title":"Authorization Error",
"timeout":3,
"cwidth":16,
"cheight":9,
"message":"Please login"
}
}
ns['org_id'] = userorgid
ns['updated_at'] = timestampstr()
db = DBPools()
dbname = get_module_dbname('product_management')
async with db.sqlorContext(dbname) as sor:
ns1 = {
"org_id": userorgid,
"operator_id": userid,
"id": params_kw.id
}
recs = await sor.R('product_type_config', ns1)
if len(recs) < 1:
return {
"widgettype":"Error",
"options":{
"title":"Update Error",
"cwidth":16,
"cheight":9,
"timeout":3,
"message":"Record no exist or with wrong ownership"
}
}
r = await sor.U('product_type_config', ns)
debug('update success');
return {
"widgettype":"Message",
"options":{
"title":"Update Success",
"cwidth":16,
"cheight":9,
"timeout":3,
"message":"ok"
}
}
return {
"widgettype":"Error",
"options":{
"title":"Update Error",
"cwidth":16,
"cheight":9,
"timeout":3,
"message":"failed"
}
}

View File

@ -1,16 +0,0 @@
{
"widgettype": "VBox",
"options": {"width": "100%", "height": "100%", "padding": "16px"},
"subwidgets": [
{
"widgettype": "Text",
"options": {"text": "运营商产品类型配置", "fontSize": "20px", "fontWeight": "bold", "marginBottom": "16px"}
},
{
"widgettype": "Html",
"options": {
"html": "<iframe src=\"{{entire_url('/product_management/product_type_config_list')}}\" style=\"width:100%;height:calc(100vh - 200px);border:none;\"></iframe>"
}
}
]
}