voucher/README.md
yumoqing 2b9942f356 feat: 新增客户自助查询 API (v1/available)
- wwwroot/api/v1/available.dspy: Bearer 认证,支持 product_type/product_name/request_amount 过滤
- init.py: get_available_vouchers_api 返回含 template_name 的完整数据
- README: 完善 API 文档,移除 dapi 依赖说明
- load_path.py: 注册 v1 端点权限
2026-05-29 00:51:27 +08:00

270 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# voucher — 代金券模块
独立代金券管理模块,支持**可配置规则引擎**,一次性使用,不找零。
## 核心架构
```
模板(类别) → 定义规则集
实例(代金券)→ 属于某个模板,继承规则
使用流水 → 记录每次消费抵扣
```
### 表结构
| 表 | 说明 |
|---|---|
| voucher_template | 代金券模板(面值、有效期、发行量、状态) |
| voucher_rule | 规则定义(模板关联,可增删启用禁用) |
| voucher_instance | 券实例一次性使用状态unused/used/expired |
| voucher_usage_log | 使用流水(订单、抵扣金额、产品信息) |
---
## 可配置规则引擎
规则通过 `@register_rule` 装饰器注册到 `RULE_REGISTRY`,新增规则只需写 validator 函数,无需修改引擎代码。
### 内置规则类型
| rule_type | 说明 | rule_config 示例 |
|-----------|------|------------------|
| `min_amount` | 最低消费门槛 | `{"min_value": 100}` |
| `max_amount` | 最高消费限制 | `{"max_value": 1000}` |
| `applicable_product_type` | 限定产品类型llm/image/video/audio | `{"product_types": ["llm", "image"]}` |
| `applicable_product` | 限定特定产品(具体模型名) | `{"products": ["gpt-4", "claude-3"]}` |
| `exclude_product` | 排除特定产品 | `{"products": ["gpt-4"]}` |
| `max_usage_count` | 最大使用次数 | `{"max_count": 1}` |
| `valid_period` | 有效期检查 | `{}` (由实例 valid_from/valid_to 处理) |
| `user_level` | 用户等级限制 | `{"min_level": 2}` |
### 规则执行流程
1. 查询券实例状态unused + 未过期)
2. 加载模板关联的所有启用规则(按 sort_order 排序)
3. 依次执行 validator任一失败即拒绝
4. 全部通过 → 计算抵扣金额 = min(面值, 消费金额)
5. 记录流水 + 标记券为已使用(一次性作废)
### 新增规则步骤
```python
# rules/validators.py
@register_rule('new_rule_type')
def check_new_rule(config, context):
# config: 从 rule_config JSON 解析
# context: 包含 request_amount, product_type, product_name, user_level 等
if not some_condition:
return False, "不满足条件"
return True, None
```
然后在模板管理界面添加 `rule_type: "new_rule_type"` 的规则记录即可。
---
## 与其他模块交互
### llmage 模块(消费时使用代金券)
```python
# llmage 的计费逻辑中调用 voucher 引擎
from voucher.rules.engine import apply_voucher, get_available_vouchers
# 查询客户可用代金券
vouchers = get_available_vouchers(sor, customer_id, context={
'product_type': 'llm', # 从 llm.catelog 获取
'product_name': 'gpt-4', # 具体模型名
'request_amount': amount,
'user_level': customer.level
})
# 使用代金券抵扣
ok, deducted, err = apply_voucher(sor, instance_id, customer_id, order_id, context={
'request_amount': amount,
'product_type': 'llm',
'product_name': 'gpt-4'
})
# 批量使用多张券
from voucher.rules.engine import batch_apply_vouchers
result = batch_apply_vouchers(sor, customer_id, order_id, voucher_ids, context)
# result: {total_deducted: 150.0, remaining: 50.0, details: [...]}
# remaining > 0 时从余额扣减
```
### accounting 模块(余额扣减联动)
```python
# accounting 消费流程中先尝试代金券,剩余从余额扣
total_amount = calculate_consumption(customer_id, period)
# Step 1: 尝试代金券抵扣
result = batch_apply_vouchers(sor, customer_id, order_id, voucher_ids, context)
# Step 2: 剩余金额从余额扣减
if result['remaining'] > 0:
accounting.deduct_balance(customer_id, result['remaining'], order_id)
# Step 3: 记录完整账单
accounting.record_order(order_id, total_amount,
voucher_deducted=result['total_deducted'],
balance_deducted=result['remaining'])
```
### 客户自助查询 APIvoucher 模块提供)
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 <token>" \
https://ai.atvoe.com/voucher/api/v1/available.dspy
# 按产品类型过滤
curl -H "Authorization: Bearer <token>" \
"https://ai.atvoe.com/voucher/api/v1/available.dspy?product_type=llm&request_amount=100"
```
### 集成点总结
| 调用方 | 场景 | 调用方式 |
|--------|------|----------|
| llmage | 推理/生成时抵扣 | `apply_voucher()` 函数调用 |
| accounting | 月度账单结算 | `batch_apply_vouchers()` 函数调用 |
| 远端客户 | 自助查询可用券 | `GET /voucher/api/v1/available.dspy` HTTP API |
| 任意模块 | 新增规则类型 | `@register_rule` + 模板管理界面 |
---
## 目录结构
```
voucher/
├── voucher/ # Python 包
│ ├── __init__.py
│ └── init.py # 模块初始化 + ServerEnv 注册
├── rules/ # 规则引擎(纯 Python
│ ├── registry.py # @register_rule 装饰器
│ ├── validators.py # 内置规则校验器
│ └── engine.py # 校验/使用/批量使用
├── wwwroot/ # 前端
│ ├── index.ui # 入口页(卡片导航)
│ ├── menu.json # 菜单定义
│ └── api/ # API 端点
│ ├── template/ # 模板 CRUD
│ ├── rule/ # 规则 CRUD
│ ├── instance/ # 实例 CRUD
│ ├── usage/ # 流水 CRUD
│ ├── v1/ # 客户自助查询 API
│ │ └── available.dspy # 查询可用券Bearer 认证)
│ ├── apply_voucher.dspy # 使用代金券
│ ├── get_available.dspy # 内部查询可用券
│ └── rule_types.dspy # 获取规则类型列表
├── models/ # 表定义 JSON
├── json/ # CRUD 定义 JSON
├── sql/
│ └── tables.sql # 建表 + 编码初始化
├── scripts/
│ └── load_path.py # RBAC 权限注册
├── pyproject.toml
└── README.md
```
---
## 部署步骤
### 1. 代码部署
```bash
cd ~/repos/voucher && git pull
cd ~/repos/sage/pkgs && ln -sf ~/repos/voucher voucher
cd voucher && ~/repos/sage/py3/bin/pip install -e .
```
### 2. 数据库建表
```bash
mysql -u root -p sage < ~/repos/voucher/sql/tables.sql
```
### 3. Sage 集成
```python
# sage/app/sage.py
from voucher.init import load_voucher
# ... in init():
load_voucher()
```
```bash
# sage/build.sh 安装循环
for m in ... voucher
```
### 4. 菜单入口
```json
// sage/wwwroot/global_menu.ui items 数组
,{
"name": "voucher",
"label": "代金券",
"icon": "fa fa-ticket",
"url": "{{entire_url('/voucher/index.ui')}}",
"target": "app.sage_main_content"
}
```
### 5. RBAC 权限
```bash
cd ~/repos/sage && ./py3/bin/python ~/repos/voucher/scripts/load_path.py
```
### 6. 重启
```bash
cd ~/repos/sage && ./stop.sh && ./start.sh
```
---
## 使用示例
### 创建模板 + 配置规则
1. 模板管理 → 新增 → 名称"新用户满减券",面值 50有效期 30 天
2. 点击模板 → 规则配置 → 添加规则:
- 规则类型: `min_amount`,配置: `{"min_value": 100}`
- 规则类型: `applicable_product_type`,配置: `{"product_types": ["llm"]}`
### 发放代金券
1. 券实例管理 → 新增 → 选择模板,填写客户 ID
2. 系统自动生成券码,设置有效期
### 消费时抵扣
```python
# llmage 推理完成后
context = {'request_amount': 120.0, 'product_type': 'llm', 'product_name': 'gpt-4'}
result = batch_apply_vouchers(sor, customer_id, order_id, [vid1, vid2], context)
# → 抵扣 50满100可用剩余 70 从余额扣
```