storage_resource — 存储资源模块

模块定位

工作空间容量GB/月)类资源的规格 / 容量采样 / 定价映射模块,宿主为 pipeline-app也可装 sage。参照 llmage 范式,但 ppid 挂法与账号资源不同:

account_resource: acctres_spec × charge_mode(月/年/试用)   → ppid
storage_resource: storres_spec × meter_mode(按量/包月/峰值) → ppid

存储比账号多一层「采样」:容量是连续占用量,必须周期采样再按 GB月 汇总;账号是一次性/周期性开通事件。两者都通过 resolve_ppid(resource_ref_id, ctx) 对外统一,产品层不必知道内部差异。RESOURCE_TYPE = 'workspace_storage'。业务定价口径 1 元/GB/月(示例定价 unit_prices: 2.8 见链路技能,实际以 pricing_program 配置为准)。

宿主集成:

from storage_resource.init import load_storage_resource
load_storage_resource()

表清单models/*.json共 4 张)

用途 关键字段
storres_spec 存储规格(资源主表) id(pk32), spec_code(唯一64), spec_name, storage_class(standard…), min_gb(起售), max_gb(单次上限), step_gb(购买步长), capacity_total(可售总量,-1=不限), capacity_used(已分配), backup_enabled('0'/'1'), attr_json, sort_order, status(active)
storres_pricing_map 规格×计量方式 → 定价项目 spec_id, meter_mode(唯一键 spec_id+meter_mode: ondemand/package/peak), meter_unit(GB月), settle_period(month), ppid(→pricing_program), is_default('1'=缺省), status
storres_sample 容量采样(存储特有) spec_id, subscription_id, target_type(计量对象类型), target_id, userid/userorgid, sample_date/sample_time, used_gb(实测), quota_gb(配额), over_gb(超额=used-quota,≥0), source(scan…)
storres_usage 存储资源用量(计费落账凭证) spec_id, meter_mode, subscription_id, userid/userorgid, use_date/use_time(出账), period_start/end, sample_count(采样点数), avg_gb/max_gb/billable_gb(计费容量), usages(JSON), transno, amount+amount_currency+amount_base, cost+cost_currency+cost_base, discount_rate, accounting_status(pending→accounted)

种子数据 init/data.json1 条规格WS-STORAGE-STDmin_gb=1capacity_total=-1 不限)+ 2 条定价映射ondemand is_default='1'、package种子 ppid 为空串——建好 pricing_program 后必须回填,否则计费金额记 0meter_mode='peak' 若业务要用峰值计费也必须补映射条目,否则记 0。

对外 APIload_storage_resource() 注册到 ServerEnv

# 规格 CRUD
env.storres_spec_list(ns)          # ns: {status, storage_class}
env.storres_spec_create/update/delete(data)
env.get_storage_spec(spec_id_or_code)
env.check_capacity_available(spec_id_or_code, need_gb)  # → (bool, msg)capacity_total<0=不限
# 定价映射 CRUD
env.storres_pricing_map_list(ns)   # ns: {spec_id}LEFT JOIN 带出 spec_code/spec_name
env.storres_pricing_map_create/update/delete(data)
env.get_storage_ppid(spec_id_or_code, meter_mode=None)  # meter_mode空→取is_default='1'
# 采样
env.record_storage_sample(spec_id, target_type, target_id, used_gb,
                          userid='', userorgid='', subscription_id='',
                          quota_gb=None, source='scan')   # 定时扫盘后调over_gb自动算
env.summarize_samples(subscription_id, period_start, period_end)  # → (采样点数, 均值GB, 峰值GB)
# 出账
env.storage_charging(spec_id_or_code, meter_mode, userid, userorgid,
                     subscription_id, period_start, period_end, discount_rate=None)
    # 汇总采样 → billable_gb = max_gb if meter_mode=='peak' else avg_gb
    # → 解析ppid → env.buffered_charging → 原价×discount_rate → 写 storres_usage(pending)
env.storage_direct_charging(spec_id_or_code, storage_gb, userid, userorgid,
                            valid_months=1, subscription_id='', discount_rate=None)
    # 一次性直购(购买即记账,选A模式): 不依赖采样; 按 package 定价(无则缺省);
    # 金额 = 单价(GB月) × storage_gb × valid_months; sample_count=0 标记直购; meter_mode='direct'
# 记账对接
env.get_accounting_storres_usages(limit=200)   # pending 用量,供 accounting 出账扫描
env.mark_storres_accounted(usage_id, status='accounted')

resolve_ppid(resource_ref_id, ctx)ctx: {'meter_mode': 'ondemand'|'package'|'peak'})作为统一定价解析器注册:优先 env.register_resource_pricing_resolver('workspace_storage', resolve_ppid),产品层未就绪时兜底暂存 env._resource_pricing_resolvers

页面/CRUDjson/*.json

4 个 xls2ui 格式 Bricks CRUD 定义:storres_specstorres_pricing_mapstorres_samplestorres_usageeditable 指向 ./add_*.dspy 等——这些 dspy 端点不在本仓库,由宿主 wwwroot 提供)。scripts/load_path.py 注册 RBAC 路径(全部 logined/storage_resource/storage_resource/{storres_spec,storres_pricing_map,storres_sample,storres_usage}/index.ui

与产品/计费链路的集成关系

  • product_managementenv.product_interface 指向本模块 product_interface.pymodule_name / get_product_display / check_product_availability / check_product_consumable / execute_product_service(_stream) / calculate_product_cost / load_product_category_productresource_ref_id = storres_spec.id
    • load_product_category_product:导出「存储服务」类别 + 每规格一条 product_type='workspace_storage' 产品。
    • execute_product_servicerequest_data 取 meter_mode(默认ondemand)/storage_gb(默认 min_gb),返回 entitlement容量配额实际配额分配后续接入。
    • calculate_product_cost(resource_ref_id, usage_data, user_org_id=None)只算原价discount 恒 1.0meter_mode='direct' 复用 package 定价valid_months>1 时按 N 倍计;客户/分销商折扣由产品层 product_management.core 精确计算。
  • accounting计费落账链storage_charging/storage_direct_charging 写 storres_usage(pending) → 产品层 storage_resource_accounting(usage_record) 按 spec_id→product_id → product_accounting_generic → biz_order/biz_orderdetail → consume_accounting 复式记账acc_balance/acc_detail/bill_detailmark_storres_accounted 置 accounted。前置客户已开户 + 余额充足,否则 AccountIdNone/AccountOverDraw。
  • 定价引擎pricing:铁律——用 env.buffered_charging(ppid, data) 两参、返回价格对象列表,金额 sum(getattr(p,'amount',0) for p in prices);用三参 pricing_program_charging 报缺参且静默记 0commit 3e6f448 已修)。存储定价 YAML 示例:pricings: [{price_factors: storage_gb, unit_prices: 2.8}]

部署注意事项

  1. 库名:一律 env.get_module_dbname('storage_resource')pipeline-app 返回 'pipeline'),禁写死。
  2. 建表models/*.json 交宿主建表流程;生产库勿跑完整 create_tables.pyproduct_management 会 DROP 重建丢数据)。
  3. 种子导入init/data.json 由 import_init 导入;导完回填 ppid(先建 pricing_program + pricing_program_timing.pricing_data
  4. 采样定时任务:需宿主配周期扫盘任务调 record_storage_sample(容量事实可来自 filemgr 的 file.filesize 汇总 / 工作空间实际占用),再按月 summarize_samplesstorage_charging
  5. RBAC:宿主根目录执行 py3/bin/python pkgs/storage_resource/scripts/load_path.py
  6. meter_mode 语义ondemand(按量,均值)/package(包月)/peak(峰值)三种都是设计值;summarize_samples 里 AVG/MAX 返回 Decimal必须 float() 否则 JSON 序列化炸(已在代码处理)。
  7. 依赖sqlor、bricks_for_python运行时依赖宿主提供 ahserver/appPublic/pricing/product_management。i18n/{zh,en,jp,ko}/msg.txt 已备词条(存储规格/容量采样/计量方式/计费容量/峰值容量等)。
  8. init.py 尾部延迟 import load_product_category_product(避免与 product_interface 循环导入),勿上移。
Description
No description provided
Readme 25 KiB