diff --git a/README.md b/README.md index 7bb699f..81b149f 100644 --- a/README.md +++ b/README.md @@ -108,21 +108,55 @@ accounting.record_order(order_id, total_amount, balance_deducted=result['remaining']) ``` -### dapi 模块(客户自助查询) +### 客户自助查询 API(voucher 模块提供) -```python -# 客户通过 API 查询自己的可用代金券 -# dapi 端点调用 voucher 引擎 -available = get_available_vouchers(sor, customer_id) +voucher 模块自身提供面向远端客户的查询 API,无需通过 dapi 转发。 + +**接口**: `GET /voucher/api/v1/available.dspy` +**认证**: Bearer Token(登录后用户) +**参数**: +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| product_type | string | 否 | 产品类型过滤(llm/image/video/audio) | +| product_name | string | 否 | 具体产品名过滤(如 gpt-4) | +| request_amount | float | 否 | 预期消费金额(用于规则预校验) | + +**返回**: +```json +{ + "status": "success", + "data": [ + { + "id": "xxx", + "code": "VCH-A1B2C3D4E5F6", + "face_value": 50.00, + "valid_from": "2026-01-01 00:00:00", + "valid_to": "2026-01-31 00:00:00", + "template_name": "新用户满减券" + } + ], + "total": 1 +} +``` + +**调用示例**: +```bash +# 查询所有可用券 +curl -H "Authorization: Bearer " \ + https://ai.atvoe.com/voucher/api/v1/available.dspy + +# 按产品类型过滤 +curl -H "Authorization: Bearer " \ + "https://ai.atvoe.com/voucher/api/v1/available.dspy?product_type=llm&request_amount=100" ``` ### 集成点总结 -| 调用方 | 场景 | 调用函数 | +| 调用方 | 场景 | 调用方式 | |--------|------|----------| -| llmage | 推理/生成时抵扣 | `apply_voucher()` | -| accounting | 月度账单结算 | `batch_apply_vouchers()` | -| dapi | 客户查询可用券 | `get_available_vouchers()` | +| llmage | 推理/生成时抵扣 | `apply_voucher()` 函数调用 | +| accounting | 月度账单结算 | `batch_apply_vouchers()` 函数调用 | +| 远端客户 | 自助查询可用券 | `GET /voucher/api/v1/available.dspy` HTTP API | | 任意模块 | 新增规则类型 | `@register_rule` + 模板管理界面 | --- @@ -146,8 +180,10 @@ voucher/ │ ├── rule/ # 规则 CRUD │ ├── instance/ # 实例 CRUD │ ├── usage/ # 流水 CRUD +│ ├── v1/ # 客户自助查询 API +│ │ └── available.dspy # 查询可用券(Bearer 认证) │ ├── apply_voucher.dspy # 使用代金券 -│ ├── get_available.dspy # 查询可用券 +│ ├── get_available.dspy # 内部查询可用券 │ └── rule_types.dspy # 获取规则类型列表 ├── models/ # 表定义 JSON ├── json/ # CRUD 定义 JSON diff --git a/scripts/load_path.py b/scripts/load_path.py index a2ec443..8cbd646 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -58,6 +58,7 @@ def main(): "/voucher/api/usage/voucher_usage_log_create.dspy", "/voucher/api/usage/voucher_usage_log_update.dspy", "/voucher/api/usage/voucher_usage_log_delete.dspy", + "/voucher/api/v1/available.dspy", "/voucher/api/apply_voucher.dspy", "/voucher/api/get_available.dspy", "/voucher/api/rule_types.dspy", diff --git a/voucher/init.py b/voucher/init.py index d342ece..6a4714f 100644 --- a/voucher/init.py +++ b/voucher/init.py @@ -171,16 +171,27 @@ async def apply_voucher_api(customer_id, order_id, voucher_ids, context): async def get_available_vouchers_api(customer_id, context=None): - """外部调用:查询可用代金券""" + """外部调用:查询可用代金券(含模板名称)""" from voucher.rules.engine import get_available_vouchers sor = DBPools().sqlorContext(_get_dbname()) vouchers = get_available_vouchers(sor, customer_id, context) + # 预加载模板名称 + template_names = {} + for v in vouchers: + tid = v.template_id if hasattr(v, 'template_id') else v.get('template_id', '') + if tid and tid not in template_names: + t = sor.R('voucher_template', {'id': tid}).first() + if t: + template_names[tid] = t.name if hasattr(t, 'name') else t.get('name', '') items = [] for v in vouchers: if hasattr(v, '__dict__'): - items.append(v.__dict__) + item = v.__dict__ else: - items.append(v) + item = dict(v) + tid = item.get('template_id', '') + item['template_name'] = template_names.get(tid, '') + items.append(item) return {'status': 'success', 'data': items} diff --git a/wwwroot/api/v1/available.dspy b/wwwroot/api/v1/available.dspy new file mode 100644 index 0000000..c567a4e --- /dev/null +++ b/wwwroot/api/v1/available.dspy @@ -0,0 +1,39 @@ +import json + +# 获取当前登录用户的组织ID作为客户ID +customer_id = await get_userorgid() + +# 构建查询上下文(可选过滤条件) +context = {} +if params_kw.get('product_type'): + context['product_type'] = params_kw.get('product_type') +if params_kw.get('product_name'): + context['product_name'] = params_kw.get('product_name') +if params_kw.get('request_amount'): + try: + context['request_amount'] = float(params_kw.get('request_amount')) + except (ValueError, TypeError): + pass + +# 查询可用代金券 +result = await get_available_vouchers_api(customer_id, context if context else None) + +# 格式化输出 +if result.get('status') == 'success': + vouchers = [] + for v in result.get('data', []): + vouchers.append({ + 'id': v.get('id'), + 'code': v.get('code'), + 'face_value': float(v.get('face_value', 0)), + 'valid_from': str(v.get('valid_from', '')), + 'valid_to': str(v.get('valid_to', '')), + 'template_name': v.get('template_name', ''), + }) + return json.dumps({ + 'status': 'success', + 'data': vouchers, + 'total': len(vouchers) + }) +else: + return json.dumps(result)