Преглед изворни кода

fix(mode_workflow/api): 将classified_cases接口path默认改为精确匹配

添加subtree查询参数以允许显式开启子树匹配,修复原接口默认取子树导致的帖数与树徽标不一致、substanceMatch不匹配的问题,同时更新相关文档与接口响应。
刘文武 пре 4 недеља
родитељ
комит
c1b3840e05

+ 12 - 6
examples/mode_workflow/db.py

@@ -1260,16 +1260,22 @@ def replace_step_classification(case_id, version, records):
         conn.close()
 
 
-def fetch_classified_cases(dimension, path=None, name=None):
-    """维度联动取帖:返回某维度下命中「该节点(及其整棵子树)」的 distinct case_id 集合。
-      - path:给定分类全路径,取该节点 **及其子树**(matched_path = path 或 LIKE 'path/%')。
+def fetch_classified_cases(dimension, path=None, name=None, subtree=False):
+    """维度联动取帖:返回某维度下命中分类的 distinct case_id 集合。
+      - path:给定分类全路径。**默认精确匹配本节点**(matched_path = path),与 cat-api 树徽标
+        (节点 knowledge_count)同口径,保证取到的帖工序解构 substanceMatch 就是该节点名;
+        subtree=True 时改取 **该节点及整棵子树**(matched_path = path 或 LIKE 'path/%')。
       - name:给定分类名精确匹配(matched_name = name)。
-    path 与 name 至少给一个;都给则取并集口径以 path 为主(name 作补充 OR)。
+    path 与 name 至少给一个;都给则取并集。
     与 mode_process.steps[].substanceMatch 同源(同一次归类双写),不依赖 cat-api 树。"""
     conds, params = [], [dimension]
     if path:
-        conds.append("(matched_path = %s OR matched_path LIKE %s)")
-        params += [path, path.rstrip("/") + "/%"]
+        if subtree:
+            conds.append("(matched_path = %s OR matched_path LIKE %s)")
+            params += [path, path.rstrip("/") + "/%"]
+        else:
+            conds.append("matched_path = %s")
+            params.append(path)
     if name:
         conds.append("matched_name = %s")
         params.append(name)

+ 6 - 4
examples/mode_workflow/docs/step_classification_3-I_设计.md

@@ -84,11 +84,13 @@ db.replace_step_classification(case_id, version, recs)   # DELETE+INSERT 原子
 `db.py` 新增:
 
 ```text
-fetch_classified_cases(dimension, path=None, name=None) -> set[case_id]
-    SELECT DISTINCT case_id FROM step_classification
-    WHERE dimension=%s AND ( matched_path = %s OR matched_path LIKE CONCAT(%s,'/%') )
-    -- 给定 path 取「该节点及其整棵子树」的帖;或给定 name 精确取。
+fetch_classified_cases(dimension, path=None, name=None, subtree=False) -> set[case_id]
+    -- 默认(subtree=False):精确本节点  WHERE dimension=%s AND matched_path = %s
+    --   → 帖数 = cat-api 树徽标(节点 knowledge_count)同口径,substanceMatch = 该节点名
+    -- subtree=True:本节点及整棵子树  matched_path = %s OR matched_path LIKE CONCAT(%s,'/%')
+    -- name:精确分类名  matched_name = %s
 ```
+> 2026-06-28:`path` 默认由「子树」改为「精确本节点」——修维度联动取帖与树徽标/substanceMatch 不一致(后代节点帖混入)。子树改为 `subtree=1` 显式开启。
 
 `server.py` 新增路由:
 

+ 3 - 2
examples/mode_workflow/server.py

@@ -682,10 +682,11 @@ class Handler(BaseHTTPRequestHandler):
                 dim = qs.get("dimension", "实质")
                 path = qs.get("path") or None
                 name = qs.get("name") or None
+                subtree = qs.get("subtree") in ("1", "true", "yes")  # 默认精确本节点;=1 取子树
                 if not path and not name:
                     return self._err("需提供 path 或 name", 400)
-                cases = db.fetch_classified_cases(dim, path=path, name=name)
-                self._json({"dimension": dim, "path": path, "name": name,
+                cases = db.fetch_classified_cases(dim, path=path, name=name, subtree=subtree)
+                self._json({"dimension": dim, "path": path, "name": name, "subtree": subtree,
                             "cases": sorted(cases), "count": len(cases)})
             elif u.path == "/api/post":
                 # 单帖详情(正文/配图/评估全量):列表已瘦身,详情按需取;带 ETag/304

+ 12 - 4
examples/mode_workflow/工序接口文档.md

@@ -7,6 +7,11 @@
 
 ## 变更记录(Changelog)
 
+### 2026-06-28 · `/api/classified_cases` 的 `path` 改为「默认精确本节点」(修维度联动取帖不匹配)
+
+> 现象:点分类节点(如「空间环境」,树徽标 24)取到的帖工序解构 `substanceMatch` 对不上——因为旧实现 `path` 取了**整棵子树**(46,含后代节点「内陆水体」等),后代帖的 `substanceMatch` 是后代名。
+> 修复:`path` **默认改为精确匹配本节点**(帖数 = 树徽标数,`substanceMatch` = 点击节点)。需要连子分类一起出时传 `subtree=1`。**前端无需改动**(原本只传 `dimension+path`,默认即精确)。响应新增 `subtree` 字段。
+
 ### 2026-06-27 · 归类口径修复 + 工序解构拆表(对 aigc-web **无破坏性**)
 
 > 本轮后端改动**不改变任何现有接口的请求/响应形状**(已 347/347 逐字节校验)。aigc-web 现有调用全部继续可用;仅 1 项**建议采用**的新接口。
@@ -252,17 +257,19 @@ GET /api/all_posts?mode=process&case_list=gzh_…,gzh_…
 **响应**:`{"prompt": "<prompts/procedure_extract_system.md 全文>"}`
 
 ### GET /api/classified_cases?dimension=实质&path=/表象/视觉/空间/空间环境
-**维度联动取帖**:返回某维度下命中「该分类节点**及其整棵子树**」的去重 `case_id`。
+**维度联动取帖**:返回某维度下命中分类的去重 `case_id`。
 数据源是 `step_classification`(与帖里显示的 `substanceMatch` 同源),**替代**先前「按外部分类树节点 `knowledge_ids` 取帖」的口径,从根上消除「树多归属 ≠ 显示单归属」的不一致。
 
 **Query 参数**
 | 参数 | 必填 | 缺省 | 说明 |
 |---|---|---|---|
 | `dimension` | 否 | `实质` | 维度:`实质` / `形式` / `类型` / `作用` / `动作` |
-| `path` | 二选一 | — | 分类**全路径**,取该节点及其子树(`matched_path = path` 或 `LIKE 'path/%'`) |
+| `path` | 二选一 | — | 分类**全路径**。**默认精确匹配本节点**(`matched_path = path`)——与 cat-api 树徽标(节点 `knowledge_count`)**同口径同数量**,取到的帖工序解构 `substanceMatch` 即该节点名。 |
+| `subtree` | 否 | `0` | `1`/`true` 时 `path` 改取**该节点及整棵子树**(含子分类,`matched_path = path` 或 `LIKE 'path/%'`)。点父节点想连子分类一起出时用。 |
 | `name` | 二选一 | — | 分类名精确匹配(`matched_name = name`) |
 
 > `path` 与 `name` 至少给一个,都不给返回 `400 {"error":"需提供 path 或 name"}`。两者都给取并集。
+> ⚠️ 取帖请用 **`path`(默认精确)**,使「帖数 = 树徽标数」「帖的 `substanceMatch` = 点击的节点」。需要把子分类也算进来时才传 `subtree=1`(此时帖数会大于徽标,后代帖的 `substanceMatch` 是后代节点名)。
 
 **响应**
 ```jsonc
@@ -270,8 +277,9 @@ GET /api/all_posts?mode=process&case_list=gzh_…,gzh_…
   "dimension": "实质",
   "path": "/表象/视觉/空间/空间环境",
   "name": null,
-  "cases": ["gzh_...", "xhs_..."],   // 命中该节点(含子树)的去重 case_id
-  "count": 46
+  "subtree": false,
+  "cases": ["gzh_...", "xhs_..."],   // 默认=精确命中该节点的去重 case_id(与树徽标同数)
+  "count": 24
 }
 ```
 > 典型用法:① 调本接口拿 `cases` → ② `GET /api/all_posts?case_list=<cases>` 取帖列表(见 §2)。