feat: add resource module interface dispatcher to ProductManager
- _get_product_interface: resolve product -> resource_module -> interface dict - get_product_display_info: delegate to resource module pricing display - check_product_availability: delegate to resource module availability check - check_product_consumable: delegate to resource module pre-consumption check - execute_product / execute_product_stream: delegate to resource module service execution - calculate_product_cost: delegate to resource module cost calculation - Register all dispatchers on ServerEnv in load_product_management()
This commit is contained in:
parent
c78be834d0
commit
811de2a863
@ -75,6 +75,42 @@ async def import_categories_and_products(resource_module, org_id, parent_categor
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_product_display_info(product_id=None, product_code=None):
|
||||||
|
"""Get product pricing display info via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.get_product_display_info(product_id, product_code)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_product_availability(product_id=None, product_code=None, user_org_id=None):
|
||||||
|
"""Check product availability via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.check_product_availability(product_id, product_code, user_org_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_product_consumable(product_id=None, product_code=None, user_id=None, user_org_id=None):
|
||||||
|
"""Pre-consumption check via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.check_product_consumable(product_id, product_code, user_id, user_org_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def execute_product(product_id=None, product_code=None, request_data=None, user_id=None, user_org_id=None):
|
||||||
|
"""Execute product service via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.execute_product(product_id, product_code, request_data, user_id, user_org_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def execute_product_stream(product_id=None, product_code=None, request_data=None, user_id=None, user_org_id=None):
|
||||||
|
"""Execute product service (streaming) via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.execute_product_stream(product_id, product_code, request_data, user_id, user_org_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def calculate_product_cost(product_id=None, product_code=None, usage_data=None, user_org_id=None):
|
||||||
|
"""Calculate product usage cost via resource module interface."""
|
||||||
|
manager = get_manager()
|
||||||
|
return await manager.calculate_product_cost(product_id, product_code, usage_data, user_org_id)
|
||||||
|
|
||||||
|
|
||||||
def load_product_management():
|
def load_product_management():
|
||||||
"""Register all functions with ServerEnv so they can be called from .ui/.dspy files."""
|
"""Register all functions with ServerEnv so they can be called from .ui/.dspy files."""
|
||||||
env = ServerEnv()
|
env = ServerEnv()
|
||||||
@ -87,4 +123,11 @@ def load_product_management():
|
|||||||
env.get_operator_config = get_operator_config
|
env.get_operator_config = get_operator_config
|
||||||
env.set_operator_config = set_operator_config
|
env.set_operator_config = set_operator_config
|
||||||
env.import_categories_and_products = import_categories_and_products
|
env.import_categories_and_products = import_categories_and_products
|
||||||
|
# Resource module interface dispatchers
|
||||||
|
env.get_product_display_info = get_product_display_info
|
||||||
|
env.check_product_availability = check_product_availability
|
||||||
|
env.check_product_consumable = check_product_consumable
|
||||||
|
env.execute_product = execute_product
|
||||||
|
env.execute_product_stream = execute_product_stream
|
||||||
|
env.calculate_product_cost = calculate_product_cost
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -1122,3 +1122,182 @@ class ProductManager:
|
|||||||
return {'success': False, 'error': f'资源模块 "{resource_module}" 未返回导入数据'}
|
return {'success': False, 'error': f'资源模块 "{resource_module}" 未返回导入数据'}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# ─── Resource Module Interface Dispatcher ───
|
||||||
|
|
||||||
|
async def _get_product_interface(self, product_id=None, product_code=None):
|
||||||
|
"""Resolve product to its resource module's interface dict.
|
||||||
|
|
||||||
|
Returns: (interface_dict, product_record, error_message)
|
||||||
|
"""
|
||||||
|
dbname = self._get_dbname()
|
||||||
|
conditions = ["status = '1'"]
|
||||||
|
params = {}
|
||||||
|
if product_id:
|
||||||
|
conditions.append("id = ${pid}$")
|
||||||
|
params['pid'] = product_id
|
||||||
|
elif product_code:
|
||||||
|
conditions.append("product_code = ${code}$")
|
||||||
|
params['code'] = product_code
|
||||||
|
else:
|
||||||
|
return None, None, '需要指定 product_id 或 product_code'
|
||||||
|
|
||||||
|
where = " AND ".join(conditions)
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
rows = await sor.sqlExe(
|
||||||
|
f"SELECT * FROM product WHERE {where}", params)
|
||||||
|
if not rows:
|
||||||
|
return None, None, '产品不存在或已下线'
|
||||||
|
product = dict(rows[0])
|
||||||
|
|
||||||
|
# Get resource_module from category
|
||||||
|
cat_id = product.get('category_id')
|
||||||
|
if cat_id:
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
cat_rows = await sor.sqlExe(
|
||||||
|
"SELECT resource_module FROM product_category WHERE id = ${cid}$",
|
||||||
|
{'cid': cat_id})
|
||||||
|
if cat_rows and cat_rows[0].get('resource_module'):
|
||||||
|
resource_module = cat_rows[0]['resource_module']
|
||||||
|
else:
|
||||||
|
return None, product, '产品类别未绑定资源模块'
|
||||||
|
else:
|
||||||
|
return None, product, '产品无类别信息'
|
||||||
|
|
||||||
|
# Get interface from ServerEnv or importlib
|
||||||
|
env = ServerEnv()
|
||||||
|
iface = getattr(env, 'product_interface', None)
|
||||||
|
if iface and iface.get('module_name') == resource_module:
|
||||||
|
return iface, product, None
|
||||||
|
|
||||||
|
# Fallback: dynamic import
|
||||||
|
import importlib
|
||||||
|
try:
|
||||||
|
mod = importlib.import_module(f'{resource_module}.init')
|
||||||
|
# Trigger load if not loaded yet
|
||||||
|
load_fn_name = f'load_{resource_module}'
|
||||||
|
load_fn = getattr(mod, load_fn_name, None)
|
||||||
|
if load_fn:
|
||||||
|
load_fn()
|
||||||
|
iface = getattr(env, 'product_interface', None)
|
||||||
|
if iface and iface.get('module_name') == resource_module:
|
||||||
|
return iface, product, None
|
||||||
|
except Exception as e:
|
||||||
|
return None, product, f'加载资源模块 {resource_module} 失败: {e}'
|
||||||
|
|
||||||
|
return None, product, f'资源模块 {resource_module} 未注册 product_interface'
|
||||||
|
|
||||||
|
async def get_product_display_info(self, product_id=None, product_code=None):
|
||||||
|
"""获取产品定价展示信息(通过资源模块接口)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
return {'success': False, 'message': err}
|
||||||
|
|
||||||
|
fn = iface.get('get_product_display')
|
||||||
|
if not fn:
|
||||||
|
return {'success': False, 'message': '资源模块未实现 get_product_display'}
|
||||||
|
|
||||||
|
return await fn(product['product_code'])
|
||||||
|
|
||||||
|
async def check_product_availability(self, product_id=None, product_code=None,
|
||||||
|
user_org_id=None):
|
||||||
|
"""检查产品可用性(通过资源模块接口)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
return {'available': False, 'reason': err}
|
||||||
|
|
||||||
|
fn = iface.get('check_product_availability')
|
||||||
|
if not fn:
|
||||||
|
return {'available': False, 'reason': '资源模块未实现 check_product_availability'}
|
||||||
|
|
||||||
|
return await fn(product['product_code'], user_org_id)
|
||||||
|
|
||||||
|
async def check_product_consumable(self, product_id=None, product_code=None,
|
||||||
|
user_id=None, user_org_id=None):
|
||||||
|
"""消费前综合预检(通过资源模块接口)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
return {'consumable': False, 'reason': err,
|
||||||
|
'min_balance': 0, 'pricing_available': False}
|
||||||
|
|
||||||
|
fn = iface.get('check_product_consumable')
|
||||||
|
if not fn:
|
||||||
|
return {'consumable': False, 'reason': '资源模块未实现 check_product_consumable',
|
||||||
|
'min_balance': 0, 'pricing_available': False}
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
env = ServerEnv()
|
||||||
|
user_id = await env.get_user()
|
||||||
|
if not user_org_id:
|
||||||
|
user_org_id = self._get_current_org_id()
|
||||||
|
|
||||||
|
return await fn(product['product_code'], user_id, user_org_id)
|
||||||
|
|
||||||
|
async def execute_product(self, product_id=None, product_code=None,
|
||||||
|
request_data=None, user_id=None, user_org_id=None):
|
||||||
|
"""执行产品服务(通过资源模块接口)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
return {'success': False, 'message': err, 'status': 'FAILED'}
|
||||||
|
|
||||||
|
fn = iface.get('execute_product_service')
|
||||||
|
if not fn:
|
||||||
|
return {'success': False, 'message': '资源模块未实现 execute_product_service',
|
||||||
|
'status': 'FAILED'}
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
env = ServerEnv()
|
||||||
|
user_id = await env.get_user()
|
||||||
|
if not user_org_id:
|
||||||
|
user_org_id = self._get_current_org_id()
|
||||||
|
|
||||||
|
return await fn(product['product_code'], user_id, user_org_id,
|
||||||
|
request_data or {})
|
||||||
|
|
||||||
|
async def execute_product_stream(self, product_id=None, product_code=None,
|
||||||
|
request_data=None, user_id=None,
|
||||||
|
user_org_id=None):
|
||||||
|
"""执行产品服务-流式(通过资源模块接口,返回异步生成器)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
async def _err_gen():
|
||||||
|
yield {'chunk': None, 'usage_data': None, 'done': True, 'error': err}
|
||||||
|
return _err_gen()
|
||||||
|
|
||||||
|
fn = iface.get('execute_product_service_stream')
|
||||||
|
if not fn:
|
||||||
|
async def _noimpl_gen():
|
||||||
|
yield {'chunk': None, 'usage_data': None, 'done': True,
|
||||||
|
'error': '资源模块未实现 execute_product_service_stream'}
|
||||||
|
return _noimpl_gen()
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
env = ServerEnv()
|
||||||
|
user_id = await env.get_user()
|
||||||
|
if not user_org_id:
|
||||||
|
user_org_id = self._get_current_org_id()
|
||||||
|
|
||||||
|
return fn(product['product_code'], user_id, user_org_id,
|
||||||
|
request_data or {})
|
||||||
|
|
||||||
|
async def calculate_product_cost(self, product_id=None, product_code=None,
|
||||||
|
usage_data=None, user_org_id=None):
|
||||||
|
"""计算消费费用(通过资源模块接口)。"""
|
||||||
|
iface, product, err = await self._get_product_interface(
|
||||||
|
product_id, product_code)
|
||||||
|
if err:
|
||||||
|
return {'success': False, 'message': err}
|
||||||
|
|
||||||
|
fn = iface.get('calculate_product_cost')
|
||||||
|
if not fn:
|
||||||
|
return {'success': False, 'message': '资源模块未实现 calculate_product_cost'}
|
||||||
|
|
||||||
|
if not user_org_id:
|
||||||
|
user_org_id = self._get_current_org_id()
|
||||||
|
|
||||||
|
return await fn(product['product_code'], usage_data or {}, user_org_id)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user