account_resource/README.md

78 lines
7.1 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.

# account_resource — 账号资源模块
## 模块定位
账号类资源的**规格 / 权益配额 / 定价映射**模块,宿主为 pipeline-app也可装 sage。参照 llmage 资源模块范式:
```
llmage: llm(主表) → llm_api_map(llmid×apiname→ppid) → llmusage(计费六件套)
account_resource: acctres_spec → acctres_pricing_map(spec×charge_mode→ppid) → acctres_usage
```
账号按「计费方式」挂价:`charge_mode` = trial试用/ month月付/ year年付`RESOURCE_TYPE = 'account_spec'`product_resource.resource_type 用这个值)。业务口径:账号产品免费管并发(试用/月租/年租三档权益工作空间数、赠送容量、并发任务数、GPU、成员数上限
宿主集成:
```python
from account_resource.init import load_account_resource
load_account_resource()
```
## 表清单models/*.json共 3 张)
| 表 | 用途 | 关键字段 |
|---|---|---|
| `acctres_spec` | 账号规格(资源主表) | `id`(pk32), `spec_code`(唯一64), `spec_name`, `spec_type`(trial/standard), `workspace_max`(可建工作空间数), `workspace_gb`(赠送容量GB), `concurrent_task`(并发任务数), `gpu_allowed`('0'/'1'), `member_max`, `privilege_json`(其他权益JSON), `sort_order`, `status`(active) |
| `acctres_pricing_map` | 规格×计费方式 → 定价项目 | `spec_id`, `charge_mode`(唯一键 spec_id+charge_mode), `charge_unit`(次/月/年), `valid_days`(7/30/365), `ppid`(→pricing_program), `is_default`('1'=缺省方式), `status` |
| `acctres_usage` | 账号资源用量(计费落账凭证) | `spec_id`, `charge_mode`, `subscription_id`(订购ID), `userid`/`userorgid`, `use_date`/`use_time`, `period_start/end`, `quantity`, `usages`(JSON), `transno`, `amount`+`amount_currency`+`amount_base`, `cost`+`cost_currency`+`cost_base`, `discount_rate`, `accounting_status`(pending→accounted), `org_id` |
种子数据 `init/data.json`3 条规格ACC-TRIAL-7D / ACC-MONTHLY / ACC-YEARLY+ 3 条定价映射trial/month/year各 is_default='1')。**种子 ppid 为空串**——建好 pricing_program 后必须回填,否则计费金额记 0。
## 对外 APIload_account_resource() 注册到 ServerEnv
```python
# 规格 CRUD
env.acctres_spec_list(ns) # ns: {status, spec_type}
env.acctres_spec_create(data) / acctres_spec_update(data) / acctres_spec_delete(data)
env.get_account_spec(spec_id_or_code) # id 或 spec_code 均可
env.get_account_privileges(spec_id_or_code) # → {workspace_max, workspace_gb, concurrent_task,
# gpu_allowed(bool), member_max, **privilege_json}
# 定价映射 CRUD
env.acctres_pricing_map_list(ns) # ns: {spec_id}LEFT JOIN 带出 spec_code/spec_name
env.acctres_pricing_map_create/update/delete(data)
env.get_account_ppid(spec_id_or_code, charge_mode=None) # charge_mode空→取is_default='1'
env.account_charging(spec_id_or_code, charge_mode, userid, userorgid,
subscription_id='', quantity=1, usages=None,
period_start=None, period_end=None, discount_rate=None)
# 解析ppid → env.buffered_charging(ppid, usages) → 原价×discount_rate
# → 写 acctres_usage(accounting_status='pending'),返回 usage dict
# 记账对接
env.get_accounting_acctres_usages(limit=200) # pending 用量,供 accounting 出账扫描
env.mark_acctres_accounted(usage_id, status='accounted')
```
另有 `resolve_ppid(resource_ref_id, ctx)`ctx: {'charge_mode': ...})作为**统一定价解析器**注册:优先 `env.register_resource_pricing_resolver('account_spec', resolve_ppid)`;产品层未加载时兜底暂存到 `env._resource_pricing_resolvers`
## 页面/CRUDjson/*.json
`json/acctres_spec.json``acctres_pricing_map.json``acctres_usage.json` 是 xls2ui 格式的 Bricks CRUD 表定义editable 指向 `./add_*.dspy``./update_*.dspy``./delete_*.dspy`——**这些 dspy 端点不在本仓库**,由宿主 wwwroot 提供)。`scripts/load_path.py` 注册 RBAC 路径(全部 logined`/account_resource``/account_resource/{acctres_spec,acctres_pricing_map,acctres_usage}/index.ui`。宿主 `load_path.sh` 自动扫描 `pkgs/*/scripts/load_path.py`
## 与产品/计费链路的集成关系
- **product_management**`load_account_resource()` 末尾把 `env.product_interface` 设为本模块 `product_interface.py` 的接口字典module_name / get_product_display / check_product_availability / check_product_consumable / execute_product_service(_stream) / calculate_product_cost / load_product_category_product`resource_ref_id = acctres_spec.id`(产品表存这个)。
- `load_product_category_product(parent_category_id)`:导出「账号服务」类别 + 每规格一条 product_type='account' 产品,供产品导入。
- `execute_product_service(...)`request_data 必须带 `charge_mode`,内部调 `account_charging` 落 usage返回 `entitlement`duration_days: trial=7/month=30/year=365 + 各项配额),供产品层写 product_subscription。
- `calculate_product_cost(resource_ref_id, usage_data, user_org_id=None)`**只算原价**buffered_charging 求和discount 恒 1.0);客户/分销商折扣由产品层 product_management.core 按 product_id 精确计算commit f2972db 特意去掉了资源层折扣)。
- **accounting计费落账链**`account_charging` 写 acctres_usage(pending) → 产品层 `*_resource_accounting(usage_record)` 按 spec_id→product_id → product_accounting_generic/calculate_product_cost → biz_order/biz_orderdetail → consume_accounting 复式记账acc_balance/acc_detail/bill_detail`mark_acctres_accounted` 置 accounted。前置条件客户已开户openCustomerAccounts且余额充足否则 AccountIdNone/AccountOverDraw。详见 product-billing-accounting-chain 技能。
- **定价引擎pricing**:铁律——出账用 `env.buffered_charging(ppid, data)` **两参**、返回价格对象列表,金额 `sum(getattr(p,'amount',0) for p in prices)`;用三参 `pricing_program_charging` 会报缺参且金额静默记 0commit 3d5fb2c 已修)。
## 部署注意事项
1. **库名**:一律 `env.get_module_dbname('account_resource')`pipeline-app 返回 'pipeline'),禁写死 'sage'/'pipeline'。
2. **建表**models/*.json 交宿主建表流程accounting 相关表幂等不 DROPproduct_management 在 TABLE_MODULES 会 DROP 重建——生产库勿跑完整 create_tables.py。
3. **种子导入**init/data.json 由宿主 import_init 导入;导完**必须回填 ppid**(先建 pricing_program + pricing_program_timing.pricing_data
4. **RBAC**:部署后在宿主根目录执行 `py3/bin/python pkgs/account_resource/scripts/load_path.py`
5. **依赖**sqlor、bricks_for_pythonpyproject运行时依赖宿主提供 ahserver/appPublic/pricing/product_management。
6. i18n/{zh,en,jp,ko}/msg.txt 已备好词条(账号规格/计费方式/试用/月租/年租等)。
7. `init.py` 尾部的 `from .product_interface import load_product_category_product` 是刻意放在所有函数定义之后的延迟 import避免与 product_interface 循环导入),改代码时勿上移。