fix: CRUD JSON合规格式(tblname+params) + CRUD create/update/delete dspy + load_path更新
- 5个CRUD JSON从自定义格式重写为规范格式(tblname/alias/params) - 13个新CRUD dspy文件(create/update/delete + noop) - product_resource_delete含级联删除product_resource_supplier - product_subscription CRUD含完整校验 - product_usage_log只读(noop dspy) - load_path.py注册所有新增API和CRUD路径
This commit is contained in:
parent
e8860401bc
commit
0e79ddc939
@ -1,7 +1,33 @@
|
||||
{
|
||||
"product_resource": {
|
||||
"params": {
|
||||
"product_id": {"type": "str"}
|
||||
}
|
||||
"tblname": "product_resource",
|
||||
"alias": "product_resource_list",
|
||||
"title": "产品资源绑定管理",
|
||||
"params": {
|
||||
"sortby": ["priority asc", "created_at desc"],
|
||||
"data_filter": {
|
||||
"fields": [
|
||||
{"field": "product_id", "title": "产品", "uitype": "code"},
|
||||
{"field": "resource_type", "title": "资源类型", "uitype": "code"},
|
||||
{"field": "status", "title": "状态", "uitype": "code"}
|
||||
]
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": [],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/product_resource_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/product_resource_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/product_resource_delete.dspy')}}"
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "product_resource_id",
|
||||
"title": "供应商关联",
|
||||
"url": "{{entire_url('../product_resource_supplier_list')}}",
|
||||
"subtable": "product_resource_supplier"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
{
|
||||
"product_resource_supplier": {
|
||||
"params": {
|
||||
"product_resource_id": {"type": "str"}
|
||||
"tblname": "product_resource_supplier",
|
||||
"alias": "product_resource_supplier_list",
|
||||
"title": "资源供应商关联管理",
|
||||
"params": {
|
||||
"sortby": ["priority asc", "weight desc"],
|
||||
"data_filter": {
|
||||
"fields": [
|
||||
{"field": "product_resource_id", "title": "资源绑定", "uitype": "code"},
|
||||
{"field": "status", "title": "状态", "uitype": "code"}
|
||||
]
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": [],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": ["id", "created_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/product_resource_supplier_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/product_resource_supplier_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/product_resource_supplier_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,25 @@
|
||||
{
|
||||
"product_subscription": {
|
||||
"params": {
|
||||
"product_id": {"type": "str"},
|
||||
"user_id": {"type": "str"},
|
||||
"user_org_id": {"type": "str"},
|
||||
"status": {"type": "str"}
|
||||
"tblname": "product_subscription",
|
||||
"alias": "product_subscription_list",
|
||||
"title": "客户订购管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"data_filter": {
|
||||
"fields": [
|
||||
{"field": "product_id", "title": "产品", "uitype": "code"},
|
||||
{"field": "status", "title": "状态", "uitype": "code"},
|
||||
{"field": "subscription_type", "title": "订购类型", "uitype": "code"}
|
||||
]
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": [],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": ["id", "quota_used", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/product_subscription_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/product_subscription_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/product_subscription_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,30 @@
|
||||
{
|
||||
"product_usage_log": {
|
||||
"params": {
|
||||
"product_id": {"type": "str"},
|
||||
"subscription_id": {"type": "str"},
|
||||
"user_id": {"type": "str"},
|
||||
"supplier_org_id": {"type": "str"},
|
||||
"billing_mode": {"type": "str"},
|
||||
"start_date": {"type": "str"},
|
||||
"end_date": {"type": "str"}
|
||||
"tblname": "product_usage_log",
|
||||
"alias": "product_usage_log_list",
|
||||
"title": "产品消费记录",
|
||||
"params": {
|
||||
"sortby": ["use_time desc"],
|
||||
"data_filter": {
|
||||
"fields": [
|
||||
{"field": "product_id", "title": "产品", "uitype": "code"},
|
||||
{"field": "supplier_org_id", "title": "供应商", "uitype": "code"},
|
||||
{"field": "resource_type", "title": "资源类型", "uitype": "code"},
|
||||
{"field": "billing_mode", "title": "计费模式", "uitype": "code"}
|
||||
]
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": ["source_ref_table", "source_ref_id", "product_resource_id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": ["id", "product_id", "subscription_id", "user_id", "user_org_id",
|
||||
"product_resource_id", "supplier_org_id", "resource_type",
|
||||
"resource_ref_id", "used_amount", "used_unit", "unit_cost",
|
||||
"total_cost", "sell_price", "billing_mode", "source_ref_table",
|
||||
"source_ref_id", "use_time", "created_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/product_usage_log_noop.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/product_usage_log_noop.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/product_usage_log_noop.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +112,12 @@ PATHS_LOGINED = [
|
||||
f"/{MOD}/api/resource_supplier_remove.dspy",
|
||||
f"/{MOD}/api/resource_supplier_priority.dspy",
|
||||
f"/{MOD}/api/resource_overflow_set.dspy",
|
||||
f"/{MOD}/api/product_resource_create.dspy",
|
||||
f"/{MOD}/api/product_resource_update.dspy",
|
||||
f"/{MOD}/api/product_resource_delete.dspy",
|
||||
f"/{MOD}/api/product_resource_supplier_create.dspy",
|
||||
f"/{MOD}/api/product_resource_supplier_update.dspy",
|
||||
f"/{MOD}/api/product_resource_supplier_delete.dspy",
|
||||
|
||||
# Subscriptions
|
||||
f"/{MOD}/product_subscription_list",
|
||||
@ -125,6 +131,10 @@ PATHS_LOGINED = [
|
||||
f"/{MOD}/api/subscription_detail.dspy",
|
||||
f"/{MOD}/api/subscription_cancel.dspy",
|
||||
f"/{MOD}/api/quota_check.dspy",
|
||||
f"/{MOD}/api/product_subscription_create.dspy",
|
||||
f"/{MOD}/api/product_subscription_update.dspy",
|
||||
f"/{MOD}/api/product_subscription_delete.dspy",
|
||||
f"/{MOD}/api/product_usage_log_noop.dspy",
|
||||
|
||||
# Usage logs
|
||||
f"/{MOD}/product_usage_log_list",
|
||||
@ -133,6 +143,21 @@ PATHS_LOGINED = [
|
||||
f"/{MOD}/api/product_use_api.dspy",
|
||||
f"/{MOD}/api/usage_logs.dspy",
|
||||
f"/{MOD}/api/usage_stats.dspy",
|
||||
|
||||
# CRUD auto-generated .dspy (new tables)
|
||||
f"/{MOD}/product_resource_list/get_product_resource.dspy",
|
||||
f"/{MOD}/product_resource_list/add_product_resource.dspy",
|
||||
f"/{MOD}/product_resource_list/update_product_resource.dspy",
|
||||
f"/{MOD}/product_resource_list/delete_product_resource.dspy",
|
||||
f"/{MOD}/product_resource_supplier_list/get_product_resource_supplier.dspy",
|
||||
f"/{MOD}/product_resource_supplier_list/add_product_resource_supplier.dspy",
|
||||
f"/{MOD}/product_resource_supplier_list/update_product_resource_supplier.dspy",
|
||||
f"/{MOD}/product_resource_supplier_list/delete_product_resource_supplier.dspy",
|
||||
f"/{MOD}/product_subscription_list/get_product_subscription.dspy",
|
||||
f"/{MOD}/product_subscription_list/add_product_subscription.dspy",
|
||||
f"/{MOD}/product_subscription_list/update_product_subscription.dspy",
|
||||
f"/{MOD}/product_subscription_list/delete_product_subscription.dspy",
|
||||
f"/{MOD}/product_usage_log_list/get_product_usage_log.dspy",
|
||||
]
|
||||
|
||||
# ============================================================
|
||||
|
||||
30
wwwroot/api/product_resource_create.dspy
Normal file
30
wwwroot/api/product_resource_create.dspy
Normal file
@ -0,0 +1,30 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
data['id'] = getID()
|
||||
data['created_at'] = timestampstr()
|
||||
data['updated_at'] = timestampstr()
|
||||
if 'status' not in data:
|
||||
data['status'] = '1'
|
||||
if 'priority' not in data:
|
||||
data['priority'] = '1'
|
||||
if 'quota' not in data:
|
||||
data['quota'] = '0'
|
||||
|
||||
if not data.get('product_id'):
|
||||
raise ValueError('产品ID不能为空')
|
||||
if not data.get('resource_type'):
|
||||
raise ValueError('资源类型不能为空')
|
||||
if not data.get('resource_ref_id'):
|
||||
raise ValueError('资源引用ID不能为空')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.C('product_resource', 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)
|
||||
21
wwwroot/api/product_resource_delete.dspy
Normal file
21
wwwroot/api/product_resource_delete.dspy
Normal file
@ -0,0 +1,21 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
record_id = params_kw.get('id')
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# Cascade delete suppliers
|
||||
await sor.sqlExe(
|
||||
"DELETE FROM product_resource_supplier WHERE product_resource_id = ${id}$",
|
||||
{'id': record_id}
|
||||
)
|
||||
await sor.D('product_resource', {'id': record_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)
|
||||
27
wwwroot/api/product_resource_supplier_create.dspy
Normal file
27
wwwroot/api/product_resource_supplier_create.dspy
Normal file
@ -0,0 +1,27 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
data['id'] = getID()
|
||||
data['created_at'] = timestampstr()
|
||||
if 'status' not in data:
|
||||
data['status'] = '1'
|
||||
if 'priority' not in data:
|
||||
data['priority'] = '1'
|
||||
if 'weight' not in data:
|
||||
data['weight'] = '100'
|
||||
|
||||
if not data.get('product_resource_id'):
|
||||
raise ValueError('资源绑定ID不能为空')
|
||||
if not data.get('supplier_org_id'):
|
||||
raise ValueError('供应商ID不能为空')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.C('product_resource_supplier', 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)
|
||||
16
wwwroot/api/product_resource_supplier_delete.dspy
Normal file
16
wwwroot/api/product_resource_supplier_delete.dspy
Normal file
@ -0,0 +1,16 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
record_id = params_kw.get('id')
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.D('product_resource_supplier', {'id': record_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)
|
||||
17
wwwroot/api/product_resource_supplier_update.dspy
Normal file
17
wwwroot/api/product_resource_supplier_update.dspy
Normal file
@ -0,0 +1,17 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
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:
|
||||
await sor.U('product_resource_supplier', data, {'id': record_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)
|
||||
18
wwwroot/api/product_resource_update.dspy
Normal file
18
wwwroot/api/product_resource_update.dspy
Normal file
@ -0,0 +1,18 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
record_id = data.pop('id', None)
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
data['updated_at'] = timestampstr()
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_resource', data, {'id': record_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)
|
||||
44
wwwroot/api/product_subscription_create.dspy
Normal file
44
wwwroot/api/product_subscription_create.dspy
Normal file
@ -0,0 +1,44 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
data['id'] = getID()
|
||||
data['created_at'] = timestampstr()
|
||||
data['updated_at'] = timestampstr()
|
||||
if 'status' not in data:
|
||||
data['status'] = '1'
|
||||
if 'quota_total' not in data:
|
||||
data['quota_total'] = '0'
|
||||
if 'quota_used' not in data:
|
||||
data['quota_used'] = '0'
|
||||
if 'overflow_rate' not in data:
|
||||
data['overflow_rate'] = '0'
|
||||
if 'purchase_price' not in data:
|
||||
data['purchase_price'] = '0'
|
||||
if 'purchase_currency' not in data:
|
||||
data['purchase_currency'] = 'CNY'
|
||||
if 'overflow_mode' not in data:
|
||||
data['overflow_mode'] = '1'
|
||||
|
||||
if not data.get('product_id'):
|
||||
raise ValueError('产品ID不能为空')
|
||||
if not data.get('user_id'):
|
||||
raise ValueError('用户ID不能为空')
|
||||
if not data.get('user_org_id'):
|
||||
raise ValueError('用户机构ID不能为空')
|
||||
if not data.get('subscription_type'):
|
||||
raise ValueError('订购类型不能为空')
|
||||
if not data.get('start_date'):
|
||||
raise ValueError('生效日期不能为空')
|
||||
if not data.get('end_date'):
|
||||
raise ValueError('到期日期不能为空')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.C('product_subscription', 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)
|
||||
16
wwwroot/api/product_subscription_delete.dspy
Normal file
16
wwwroot/api/product_subscription_delete.dspy
Normal file
@ -0,0 +1,16 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
record_id = params_kw.get('id')
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.D('product_subscription', {'id': record_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)
|
||||
18
wwwroot/api/product_subscription_update.dspy
Normal file
18
wwwroot/api/product_subscription_update.dspy
Normal file
@ -0,0 +1,18 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
record_id = data.pop('id', None)
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
data['updated_at'] = timestampstr()
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_subscription', data, {'id': record_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)
|
||||
2
wwwroot/api/product_usage_log_noop.dspy
Normal file
2
wwwroot/api/product_usage_log_noop.dspy
Normal file
@ -0,0 +1,2 @@
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Info', 'message': '消费记录为系统自动生成,不支持手动操作', 'type': 'info'}}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
Loading…
x
Reference in New Issue
Block a user