82 Commits

Author SHA1 Message Date
9389ae1237 fix(storefront): 产线年付展示价=月单价×months(10)——此前月/年显示同价
get_customer_price_display 只取定价单位价未乘 months 倍数,产线年付卡片
显示与月付相同价格(100 元/月 也显示 ¥100)。年付 months=10 → 总价=月价×10,
单位标签切'元/年'。存储/模型 months=1 不受影响。
2026-09-06 17:21:23 +08:00
1f399d6b00 feat(storefront): 显示价=定价×客户折扣率(三类统一)+产线订阅购买链路+修既有控件bug
1) get_customer_price_display 新增(core.py + __init__.py + env 注册)
   统一三类产品对外报价口径:显示价 = 资源真实定价 × 客户折扣率
   - 产线 pipeline_pricing_map / 存储 storres_pricing_map / 模型 llm_model.ppid
     各自解析 ppid → get_pricing_display 取单位价 → × 折扣
   - 折扣率解析走 discount 模块 get_min_product_discount(专属优先/'*'兜底/1.0)
     >1 加价(存储1.5)、<1 让利(百炼0.9);折扣≠1 时返回 original_text 原价
   - 未登录(user_org_id 空)传 '' 只命中 '*' 通用折扣 → 访客可见报价

2) purchase_realtime 加产线分支(product_type=='pipeline')
   订阅制:无 usage 表,直接算价→余额预检→product_accounting_generic 实时记账→开订阅权益
   charge_mode month/year,entitlement.duration_days 30/365

3) 展示页 index.ui:接 get_customer_price_display;产线卡显示月/年价+原价划线+订阅态文案;
   按钮按订阅状态分流(已订阅→进入产线 / 未订阅→立即购买);product_code 兼容
   opportunity_general/bidding_general/sdlc_general 与旧 PIPE-* 两套映射

4) 修两个既有 bug(购买页原本就坏):
   - InputNumber 控件 bricks 未注册 → widgetBuild 返回 null 静默不渲染,改 UiInt
   - 支付脚本 getWidgetById 缺第二参数(查找起点) → from_widget undefined 抛 TypeError
     点击支付无反应,补 bricks.app.root;UiType 取值 .value → resultValue()
   - UiType 系控件同时传 name(表单/getValue)+id(set_id 覆盖 DOM id 供 getWidgetById)

本地 mock 渲染测试 6 项全过:三类价格/订阅态分流/规划中占位/未登录访客可见价无购买按钮
2026-09-06 16:55:49 +08:00
275806c1cc fix(product): 账号/存储产品展示缺失——建树KeyError+幽灵列+IN占位符三处修复
根因链(2026-09-06):
1. core.get_category_tree 单遍建树隐含「父节点排序在子前」假设:
   子类别 sort_order=0 < 顶级 10/20,ORDER BY 后子先处理,
   parent['children'].append 时父未初始化 → KeyError → storefront 500
2. storefront get_products.dspy SELECT p.icon——product 表无 icon 列 → 1054
3. 同文件 IN 占位符 f'${key}$' 生成 $cid_0$(缺花括号)sqlor 不识别

配套:init/data.json resource_module 字典补 account_resource/storage_resource
(类别编辑下拉可选),product_type 字典对应项由测试库同步补充。

数据:测试库已建账号资源/存储资源顶级类别并执行导入
(账号3产品:试用7天/月租/年租;存储1产品:工作空间标准存储)。
2026-09-06 14:03:44 +08:00
8f3bd268ad feat(product): 产线模型产品化接入——product_accounting_generic暴露到env(独立记账进程调用)+resource_module种子加pipeline_llm 2026-09-04 19:32:42 +08:00
f2e828c7b6 feat(storefront): 产线产品展示型卡片——进产线入口/规划中占位,不走购买扣款 2026-08-31 08:38:32 +08:00
ff501c13b5 fix(purchase): user_org_id获取崩溃——DictObject的hasattr恒True导致await None()
- dspy侧显式取get_userorgid()传入(与purchase_confirm同款)
- core侧hasattr判断改callable检查,兜底_get_current_org_id
2026-08-28 17:09:33 +08:00
c32fa00c62 fix(storefront): purchase_confirm 支付fetch改绝对路径——.dspy非Jinja2渲染,{{entire_url()}}原样进浏览器导致支付按钮无响应 2026-08-28 17:00:16 +08:00
9e411bc9cd fix(storefront): get_products_by_category IN子句sqlor占位符被f-string吞掉花括号
f-string里${key}$被求值成$cid_0$(缺花括号),占位符原样进SQL报
Unknown column '$cid_0$'。${{org_id}}$/${{status}}$同理。
改为字符串拼接+双花括号转义,确保输出${cid_0}$格式。
2026-08-28 16:55:40 +08:00
d2162db306 fix(storefront): get_products_by_category包装函数补透传org_id(模板显式指定机构0) 2026-08-28 16:51:28 +08:00
fd794616b3 feat(storefront): 商城改 TabPanel+产品卡片布局; 补齐购买链路权限注册
- 展示层重写: 分类=Tab(服务端渲染), 卡片含介绍+真实定价+立即购买按钮,
  点击弹购买确认弹窗(purchase_confirm→purchase_realtime 实时记账链路不变)
- 定价文案取资源模块 get_product_display_info(含折扣前的资源真实价)
- load_path 补注册 purchase_confirm/purchase_realtime/my_subscriptions
  (此前缺失, 客户点购买会 401)
2026-08-28 16:34:16 +08:00
ymq
167d06cd93 feat(purchase): 实时购买(选A购买即记账)+续费延长+到期门禁+我的权益
核心链路:
- purchase_realtime:算价→余额预检→计费写用量→实时复式记账→开权益,失败回滚
- _grant_subscription 续费修复:未到期从 end_date 顺延不丢剩余天数,已到期重算
- check_subscription_valid 到期门禁:挂到 execute_product,到期/未购买阻断使用
- get_my_subscriptions 我的权益列表(到期状态)
- UI:purchase_confirm 确认页(余额+容量/月数输入)、purchase_realtime 支付、
  my_subscriptions 权益列表+续费、usermenu.ui 挂菜单
2026-08-27 10:40:54 +08:00
ymq
29f1c1fc92 feat(accounting): 计费落账通用化,账号/存储接入出账
- 抽 product_accounting_generic(product_id, usage_data, ...):算金额+分销链+consume_accounting,llm/账号/存储通用
- product_accounting(llmusage) 变薄封装:转换 llmusage→通用参数→generic+finalize+标记
- 新增 account_resource_accounting / storage_resource_accounting:acctres/storres usage → generic → 标记 accounted
- _resource_ref_to_product_id: spec.id → product.id(按 product_type 区分 account/workspace_storage)
2026-08-26 17:20:23 +08:00
ymq
622dec8495 feat(entitlement): execute_product 接权益生效
- 资源模块 execute_product_service 返回 entitlement(账号:有效期+工作空间配额; 存储:容量GB)
- 产品层 execute_product 调 _grant_subscription 写 product_subscription(订阅+配额,幂等)
2026-08-26 17:17:21 +08:00
ymq
d423f4ffd2 feat(supplier): 供应商成本补平台内供应商供货价
- 优先 product_supplier_mapping.supply_price(统一供货价,平台内/外部)
- 无供货价则外部供应商走合同折扣 supply_contract_items
- 无映射回退 product.providerid
2026-08-26 17:12:47 +08:00
ymq
1cf2f62d74 fix(product_accounting): 折扣对齐正确模型,消除双重折扣+换正确模块
- 客户折扣: calculate_product_cost 的 amount 已含客户折扣,product_accounting 不再重复乘
- 供应商成本: 用 cost_result['cost'](含 supplychain 供应商折扣,基于原价),不用 discount 模块的 get_min_supplier_discount
- 分销折扣: 用 supplychain calculate_sale_amounts 的 distribution_amount,不用 get_min_distributor_discount
- 供应商成本基于原价 original_amount,不再基于含客户折扣的售价
2026-08-26 17:11:28 +08:00
ymq
7852583739 fix(discount): 折扣模型纠正——三类折扣各归其位,不取 min
- 客户折扣(discount 模块 get_min_product_discount) → amount 售价
- 供应商折扣(supplychain 模块 calculate_sale_amounts) → cost 平台进货成本
- 分销商折扣不进本函数(分销场景单独用 calculate_sale_amounts 的 distribution_amount)
- 供应商 id 来源:product.providerid,空则查 product_supplier_mapping 首选供应商

之前"取 min 三类折扣"是错的:用户折扣不能采用供应商折扣,两者独立。
2026-08-26 16:52:44 +08:00
ymq
4ed6d15a59 feat(discount): calculate_product_cost 接产品级折扣
折扣精确到 product_id(discount_detail.productid),三类折扣取最优惠(min):
- 客户折扣 get_min_product_discount(pid, resellerid, customerid)
- 分销商折扣 get_min_distributor_discount(pid, resellerid, distributorid)
- 供应商折扣 get_min_supplier_discount(pid, resellerid, supplierid)
resellerid=product.org_id, supplierid=product.providerid,
distributorid 从 discount_customer_bind 按 customerid 查。
2026-08-26 16:04:38 +08:00
947b83ae65 feat(balance): pass llmid to finalize_balance for cold-start max_cost 2026-08-04 18:36:10 +08:00
a04b99c6c3 fix(init): register env.sync_llm_product so llmage publish hook can call it 2026-08-04 13:00:09 +08:00
9f37ec4717 fix(accounting): auto-sync published llm to product on publish; skip PAY leg for owner self-use 2026-08-04 12:55:35 +08:00
86057eaa3b feat: integrate Redis balance finalize_balance after consume_accounting 2026-07-31 14:57:25 +08:00
327f77dac1 fix: product_query_price.dspy改为any角色(无需登录) 2026-07-24 16:10:32 +08:00
91c579cff7 fix: product_query_price改用llm_query_price计价 2026-07-24 16:08:15 +08:00
2c5320bb4a feat: product_query_price.dspy替代platformbiz版本 2026-07-24 15:49:09 +08:00
93e12c2bad bugfix 2026-07-15 13:58:58 +08:00
b2bca68160 bugfix 2026-07-15 13:46:56 +08:00
66db401fcb fix: PAY* leg resellerid=ownerid not providerid, stop treating provider as distributor 2026-07-14 16:18:59 +08:00
8e2c701e23 fix: get_product.dspy清除ns.org_id防止覆盖product_org_auth子查询; JSON修复editable嵌套 2026-07-13 16:39:26 +08:00
9ea13564ef fix: 导入接受id参数(fallback),错误消息显示category_id 2026-07-13 16:21:24 +08:00
876e4581b7 fix: dspy f-string不能含反斜杠,改用%格式化 2026-07-13 16:15:23 +08:00
f8717ca9a8 debug: import_category_products加参数日志 2026-07-13 16:12:32 +08:00
cfa9b321c4 fix: 导入去除冗余org_id过滤 + 添加providerid回填dspy
- import_category_products: 移除org_id条件(树控件已做过滤)
- backfill_providerid: 从llm.providerid回填product.providerid
2026-07-13 15:36:31 +08:00
3096190f6d feat: product模型添加providerid字段 + 导入改为增量模式
- product.json: 添加providerid字段(供应商ID)
- core.py: import_categories_and_products改为增量模式
  已有产品跳过不再更新, 仅创建新资源对应的产品
  创建产品时自动设置providerid为资源模块返回的值
2026-07-13 14:53:53 +08:00
5f277949ae feat: product_org_auth model; product_list queries authorized products via product_org_auth 2026-07-10 15:38:29 +08:00
4f5a81e646 feat: multi-currency in product_accounting — real-time exchange + currency on accounting items 2026-07-07 16:25:23 +08:00
89847538f7 feat: product_accounting + backend_accounting — product-based accounting with distribution chain
- product_accounting: llm_id→product_id, calculate_product_cost, distribution chain discounts, consume_accounting
- backend_accounting: accounting loop migrated from llmage
- Both registered in load_product_management()
2026-07-07 12:27:40 +08:00
3132a837a6 feat: add llm_id_to_product_id — LLM model ID to product ID mapping
Converts llmage.llm.id → product.id via resource_ref_id + product_type filter.
2026-07-06 14:23:59 +08:00
ed32a8556c fix: CRUD endpoint return format - remove json.dumps, use widgettype format 2026-07-03 14:50:49 +08:00
7b69bb4eb2 chore: remove .swp file, add to gitignore 2026-06-29 16:05:26 +08:00
d33072ab21 debug: product_category_create.dspy 加追踪日志定位 WHERE None 错误
添加4处 debug:
- START: 记录完整 params_kw
- fields组装后: 记录 keys + id
- sor.C 前后: 定位崩溃在 INSERT 还是后续 SELECT
- EXCEPTION: 记录完整堆栈
2026-06-29 16:05:18 +08:00
5d77e6e20a fix: product_category create/update/delete去除json.dumps和import; create补充resource_module 2026-06-29 14:40:09 +08:00
7bdcb8cf5f bugfix 2026-06-26 14:36:25 +08:00
74d4b67906 bugfix 2026-06-26 14:06:05 +08:00
e998e2a4b2 bugfix 2026-06-26 14:00:55 +08:00
339fccf6d2 fix: 产品类别创建使用fields过滤确保列数匹配 2026-06-25 15:35:35 +08:00
84f1453856 fix: 修复产品类别创建时INSERT列数不匹配问题 2026-06-25 15:34:22 +08:00
Hermes Agent
47f7e8e5c1 fix: 树控件创建节点后返回user_data,修复新增子项不显示 2026-06-24 22:13:38 +08:00
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
Hermes Agent
6dae1676ef feat: 新增商家产品商城页面(storefront)
- 左侧可收缩产品分类树
- 右侧产品列表(卡片式)和产品详情
- 未登录可浏览,登录后显示购买按钮
- 页面和API注册到load_path.py
2026-06-23 18:19:42 +08:00
Hermes Agent
265081c43f fix: 产品导入去重 — 批次内product_code去重 + DB层按product_code+org_id兜底查重
- 批次内seen_codes去重: 同一导入批次中相同product_code只处理第一条
- DB查重增加fallback: resource_ref_id查不到时再按product_code+org_id查
- update时同步写入resource_ref_id
2026-06-23 18:07:30 +08:00