From 275806c1ccaf5becc8373c31002c811f198c7f20 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 6 Sep 2026 14:03:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(product):=20=E8=B4=A6=E5=8F=B7/=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E4=BA=A7=E5=93=81=E5=B1=95=E7=A4=BA=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=BB=BA=E6=A0=91KeyError+=E5=B9=BD=E7=81=B5?= =?UTF-8?q?=E5=88=97+IN=E5=8D=A0=E4=BD=8D=E7=AC=A6=E4=B8=89=E5=A4=84?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因链(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产品:工作空间标准存储)。 --- init/data.json | 4 +++- product_management/core.py | 8 +++++++- wwwroot/storefront/api/get_products.dspy | 8 +++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/init/data.json b/init/data.json index 5c08aef..1d0e567 100644 --- a/init/data.json +++ b/init/data.json @@ -126,7 +126,9 @@ {"id": "resource_module_llmage", "parentid": "resource_module", "k": "llmage", "v": "大模型管理"}, {"id": "resource_module_supplychain", "parentid": "resource_module", "k": "supplychain", "v": "供应链管理"}, - {"id": "resource_module_pipeline_llm", "parentid": "resource_module", "k": "pipeline_llm", "v": "产线模型治理"} + {"id": "resource_module_pipeline_llm", "parentid": "resource_module", "k": "pipeline_llm", "v": "产线模型治理"}, + {"id": "resource_module_account_resource", "parentid": "resource_module", "k": "account_resource", "v": "账号资源"}, + {"id": "resource_module_storage_resource", "parentid": "resource_module", "k": "storage_resource", "v": "存储资源"} ], "_note_product_category": "产品类别树由每个 reseller (org_id) 自行管理,不在 init/data.json 中预设全局数据。新机构注册时自动创建根类别。" } diff --git a/product_management/core.py b/product_management/core.py index 4da0e6b..007574f 100644 --- a/product_management/core.py +++ b/product_management/core.py @@ -47,12 +47,18 @@ class ProductManager: rows = rows or [] # Build tree + # 两遍建树(2026-09-06 根治 KeyError):先给全部节点初始化 children, + # 再挂父子关系。单遍写法隐含「父节点排序在子节点前」的假设—— + # ORDER BY sort_order ASC, name ASC 下子类别 sort_order 更小(如导入引擎 + # 给子类别 0、顶级类别 10/20)时,子先被处理,父还未初始化 children + # → parent['children'].append 抛 KeyError → storefront 整页 500。 nodes = [dict(r) for r in rows] + for n in nodes: + n['children'] = [] node_map = {n['id']: n for n in nodes} tree = [] for node in nodes: - node['children'] = [] parent_id = node.get('parent_id', '0') if parent_id == '0' or parent_id not in node_map: tree.append(node) diff --git a/wwwroot/storefront/api/get_products.dspy b/wwwroot/storefront/api/get_products.dspy index ff6320f..22de397 100644 --- a/wwwroot/storefront/api/get_products.dspy +++ b/wwwroot/storefront/api/get_products.dspy @@ -20,21 +20,23 @@ else: queue.append(c.id) # Build IN clause + # sqlor 占位符格式 ${name}$;f-string 里花括号需转义,否则 ${key} 会被求值 param_keys = [] params = {'org_id': org_id} for i, cid in enumerate(all_ids): key = f'cid_{i}' - param_keys.append(f'${key}$') + param_keys.append('${' + key + '}$') params[key] = cid placeholders = ','.join(param_keys) + # f-string 里 sqlor 占位符须写成 ${{name}}$,否则 {name} 被 Python 求值 sql = f"""SELECT p.id, p.product_name, p.product_code, p.brief_intro, - p.product_type, p.price, p.currency, p.icon, + p.product_type, p.price, p.currency, pc.name as category_name FROM product p LEFT JOIN product_category pc ON p.category_id = pc.id WHERE p.category_id IN ({placeholders}) - AND p.org_id = ${org_id}$ + AND p.org_id = ${{org_id}}$ AND p.status = '1' ORDER BY p.sort_order ASC, p.product_name ASC""" params['org_id'] = org_id