product_management/README.md
Hermes Agent 1438de9d58 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: 移除相关文档
2026-06-24 10:34:48 +08:00

279 lines
11 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.

# product_management
产品管理模块 —— 统一管理产品目录、类别树、资源绑定、订阅、消费引擎及资源模块对接。
## 模块定位
product_management 是产品生命周期的中枢,负责:
1. 管理产品类别树和产品注册表
2. 从资源模块(如 llmage导入类别和产品
3. 通过标准接口调度资源模块完成展示、预检、执行、计费
4. 管理资源绑定、供应商路由、订阅与配额
5. 记录消费日志并生成统计
## 数据模型
| 表名 | 说明 |
|------|------|
| `product_category` | 产品类别树parent_id 层级关系resource_module 标记来源模块) |
| `product` | 产品注册表resource_ref_id 关联资源模块内部ID |
| `product_resource` | 产品-资源绑定(一个产品可绑定多个资源,含配额和溢出产品) |
| `product_resource_supplier` | 资源绑定的供应商路由(优先级+权重) |
| `product_subscription` | 产品订阅(月度/按量,含配额跟踪和溢出计费) |
| `product_usage_log` | 消费日志(成本、售价、供应商、计费模式) |
### 关键字段
**product_category**
- `resource_module` — 资源模块名称(如 `llmage`),标识该类别下的产品来自哪个模块
- `has_product` — 是否可挂产品(`1`=是,`0`=仅分类节点)
- `product_type` — 产品类型标识(传递给产品的 product_type
**product**
- `resource_ref_id` — 资源模块内部 ID如 llmage 中 `llm.id`),用于直接调用资源模块接口,无需维护 product_code 对照表
- `product_code` — 产品编码(业务可读标识)
- `product_type` — 产品类型(如 `llm_model`, `llm_monthly`, `compute`
## 架构设计
### 1. 资源模块解耦导入
资源模块(如 llmage和产品模块product_management职责分离
```
资源模块 产品模块
───────── ─────────
load_product_category_product() import_categories_and_products()
│ 只读自己的数据 │ 调用资源模块获取标准化数据
│ 返回标准化格式 │ 负责所有写入和去重逻辑
└──────────────────┐ │
▼ │
{categories, products}
│ │
└───────────►│
│ 类别: name+parent_id+org_id 去重 → 已存在则跳过
│ 产品: resource_ref_id+org_id 去重 → 已存在则更新
```
**资源模块返回格式:**
```python
{
'success': True,
'categories': [
{
'source_id': '资源模块内部类别ID',
'name': '类别名称',
'description': '描述',
'product_type': 'llm_model',
'product_type_title': '大模型API',
'sort_order': 0,
},
],
'products': [
{
'source_category_id': '对应的 source_id',
'resource_ref_id': '资源模块内部ID如 llm.id',
'product_code': '产品编码',
'product_name': '产品名称',
'product_type': 'llm_model',
'brief_intro': '简介',
'sort_order': 0,
},
],
}
```
**去重规则:**
- 类别:按 `name + parent_id + org_id` 判断,已存在则跳过
- 产品:按 `resource_ref_id + org_id` 判断,已存在则更新(名称、简介等),不存在则创建
### 2. 资源模块接口调度
产品模块通过 `product_interface` 字典调度资源模块,所有接口参数统一使用 `resource_ref_id`
```
调用方UI/API
│ env.get_product_display_info(product_id='xxx')
product_management (调度层)
│ 1. 查 product 表 → 获取 resource_ref_id
│ 2. 查 product_category 表 → 获取 resource_module
│ 3. 从 ServerEnv.product_interface 获取接口实现
│ 4. 调用接口函数,传入 resource_ref_id
资源模块(如 llmage/product_interface.py
│ 直接用 resource_ref_id 操作自己的数据
└─ 返回结果
```
### 3. ServerEnv 注册
模块初始化时将所有方法注册到 ServerEnv
```python
def load_product_management():
env = ServerEnv()
# 基础 CRUD
env.get_product_brief = get_product_brief
env.get_product_detail = get_product_detail
env.purchase_product = purchase_product
env.use_product = use_product
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
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
```
## 资源模块标准接口规范
任何资源模块(如 llmage、supplychain要对接产品模块需实现以下 7 个接口函数并在 `load` 时注册到 `ServerEnv.product_interface`
### 接口函数列表
| # | 函数名 | 参数 | 返回值 | 用途 |
|---|--------|------|--------|------|
| 1 | `get_product_display` | `(resource_ref_id)` | `{success, pricing_text, pricing_detail, extra_info}` | 展示定价信息 |
| 2 | `check_product_availability` | `(resource_ref_id, user_org_id)` | `{available, reason}` | 检查可用性 |
| 3 | `check_product_consumable` | `(resource_ref_id, user_id, user_org_id)` | `{consumable, reason, min_balance, pricing_available}` | 消费前综合预检 |
| 4 | `execute_product_service` | `(resource_ref_id, user_id, user_org_id, request_data)` | `{success, result, usage_data, task_id, status}` | 执行服务(非流式) |
| 5 | `execute_product_service_stream` | `(resource_ref_id, user_id, user_org_id, request_data)` | `AsyncGenerator[{chunk, usage_data, done}]` | 执行服务(流式) |
| 6 | `calculate_product_cost` | `(resource_ref_id, usage_data, user_org_id)` | `{success, amount, cost, discount, pricing_program_id}` | 计算费用 |
| 7 | `load_product_category_product` | `(parent_category_id)` | `{success, categories, products}` | 导入数据(标准化格式) |
### 注册方式
资源模块在其 `__init__.py``init.py``load_*` 函数中注册:
```python
def load_llmage():
env = ServerEnv()
# ... 其他初始化 ...
from .product_interface import (
get_product_display, check_product_availability,
check_product_consumable, execute_product_service,
execute_product_service_stream, calculate_product_cost,
)
env.product_interface = {
'module_name': 'llmage',
'get_product_display': get_product_display,
'check_product_availability': check_product_availability,
'check_product_consumable': check_product_consumable,
'execute_product_service': execute_product_service,
'execute_product_service_stream': execute_product_service_stream,
'calculate_product_cost': calculate_product_cost,
}
```
### resource_ref_id 映射
- `resource_ref_id` 存储在 `product.resource_ref_id` 字段
- 对于 llmage`resource_ref_id = llm.id`
- 产品模块调度时自动从 product 表提取该字段,资源模块直接使用
- 资源模块无需知道 product_code产品模块无需知道资源模块内部结构
## 核心业务流程
### 产品导入
```
UI 操作 → import_category_products.dspy
→ env.import_categories_and_products('llmage', org_id, parent_cat_id, user_id)
→ importlib.import_module('llmage.init')
→ llmage.load_product_category_product(parent_cat_id) # 返回标准化数据
→ 产品模块执行去重写入
→ 返回统计: 新增N类别, 跳过M类别, 新增X产品, 更新Y产品
```
### 消费流程
```
1. 展示阶段
env.get_product_display_info(product_id)
→ 查 product → 查 category → 获取 resource_module
→ 调用 iface.get_product_display(resource_ref_id)
→ 返回定价文本 + 详情
2. 预检阶段
env.check_product_consumable(product_id, user_id, user_org_id)
→ 调用 iface.check_product_consumable(resource_ref_id, user_id, user_org_id)
→ 检查: 模型状态 + ppid定价 + 余额 ≥ min_balance
→ 返回: consumable + reason
3. 执行阶段
env.execute_product(product_id, request_data, user_id, user_org_id)
→ 调用 iface.execute_product_service(resource_ref_id, user_id, user_org_id, request_data)
→ 根据 llm.stream 选择 sync/async/stream 路径
→ 返回: result + usage_data + task_id
4. 计费阶段
env.calculate_product_cost(product_id, usage_data, user_org_id)
→ 调用 iface.calculate_product_cost(resource_ref_id, usage_data, user_org_id)
→ 调用 pricing 模块计算 + 客户折扣
→ 返回: amount + cost + discount
```
### 消费引擎 (product_use)
核心消费路由逻辑,支持订阅制和按量制:
```
product_use(product_id, user_id, user_org_id, used_amount, used_unit)
├─ 月度产品 (llm_monthly):
│ ├─ 查有效订阅 → 配额内扣减 (billing_mode='1')
│ └─ 超出配额 → 溢出计费 (overflow_rate × 超出量)
├─ 按量产品:
│ └─ sell_price = used_amount × product.price
├─ 供应商路由:
│ └─ product_resource → product_resource_supplier (按 priority+weight)
├─ 成本计算:
│ └─ supplychain.supplier_resource_price → unit_cost × used_amount
└─ 写入 product_usage_log
```
## 目录结构
```
product_management/
├── models/ # 数据模型定义 (JSON)
│ ├── product.json
│ ├── product_category.json
│ ├── product_resource.json
│ ├── product_resource_supplier.json
│ ├── product_subscription.json
│ └── product_usage_log.json
├── product_management/
│ ├── __init__.py # ServerEnv 注册 + 入口函数
│ └── core.py # ProductManager 全部业务逻辑
├── init/
│ └── data.yaml # 初始化数据 (appcodes 编码等)
├── json/ # CRUD 定义 (由 xls2crud 生成)
├── wwwroot/ # 前端页面 (由 xls2ui 生成)
├── README.md
└── setup.py
```
## 新增资源模块对接指南
以新模块 `supplychain` 为例:
1. **实现接口函数** — 在 `supplychain/product_interface.py` 中实现上述 7 个函数
2. **注册到 ServerEnv** — 在 `supplychain/init.py``load_supplychain()` 中注册 `product_interface` 字典
3. **导入函数** — 实现 `load_product_category_product(parent_category_id)` 返回标准化格式
4. **配置 appcodes** — 在 `init/data.yaml``resource_module` 下添加 `supplychain` 键值对
5. **创建类别** — 在 UI 中创建产品类别,`resource_module` 选择 `supplychain`
6. **触发导入** — 点击"产品导入"按钮,自动调用导入流程
所有参数使用 `resource_ref_id`supplychain 的内部 ID产品模块自动处理映射。