zlwu 5 месяцев назад
Родитель
Сommit
2f350e7d73

+ 1 - 1
backend/app.pid

@@ -1 +1 @@
-4602
+45363

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
backend/app/graphify-out/cache/ast/0b40a771c97ca69a22d504c3f5ad82cbaeb386a79e7192c015ea7cab1a154b91.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
backend/app/graphify-out/cache/ast/ac03efd773195a2229ef4d052628d141a37e938f1c597656b57a7688ce99f89a.json


Разница между файлами не показана из-за своего большого размера
+ 357 - 351
backend/graphify-out/.graphify_analysis.json


+ 77 - 86
backend/graphify-out/.graphify_extract.json

@@ -9301,91 +9301,91 @@
       "label": "_unicode_ci()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L26"
+      "source_location": "L25"
     },
     {
       "id": "crud_storage_entry_sms_notification_total_pages",
       "label": "total_pages()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L30"
+      "source_location": "L29"
     },
     {
       "id": "crud_storage_entry_sms_notification_fmt_dt",
       "label": "_fmt_dt()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L36"
+      "source_location": "L35"
     },
     {
       "id": "crud_storage_entry_sms_notification_storage_status_label",
       "label": "_storage_status_label()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L40"
+      "source_location": "L39"
     },
     {
       "id": "crud_storage_entry_sms_notification_row_to_dict",
       "label": "_row_to_dict()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L50"
+      "source_location": "L49"
     },
     {
       "id": "crud_storage_entry_sms_notification_base_query",
       "label": "_base_query()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L85"
+      "source_location": "L84"
     },
     {
       "id": "crud_storage_entry_sms_notification_ensure_notification",
       "label": "_ensure_notification()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L145"
+      "source_location": "L132"
     },
     {
       "id": "crud_storage_entry_sms_notification_resolve_notification",
       "label": "resolve_notification()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L181"
+      "source_location": "L168"
     },
     {
       "id": "crud_storage_entry_sms_notification_fetch_sample_type_name",
       "label": "_fetch_sample_type_name()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L219"
+      "source_location": "L206"
     },
     {
       "id": "crud_storage_entry_sms_notification_list_notifications",
       "label": "list_notifications()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L233"
+      "source_location": "L220"
     },
     {
       "id": "crud_storage_entry_sms_notification_toggle_notification_status",
       "label": "toggle_notification_status()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L349"
+      "source_location": "L342"
     },
     {
       "id": "crud_storage_entry_sms_notification_generate_sms_content",
       "label": "generate_sms_content()",
       "file_type": "code",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L385"
+      "source_location": "L378"
     },
     {
-      "id": "crud_storage_entry_sms_notification_rationale_189",
+      "id": "crud_storage_entry_sms_notification_rationale_176",
       "label": "Find notification by id; fallback to storage_review_id and ensure row exists.",
       "file_type": "rationale",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L189"
+      "source_location": "L176"
     },
     {
       "id": "crud_express_company_py",
@@ -12052,42 +12052,42 @@
       "label": "_ensure_notification()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L139"
+      "source_location": "L127"
     },
     {
       "id": "crud_sample_sms_notification_resolve_notification",
       "label": "resolve_notification()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L179"
+      "source_location": "L167"
     },
     {
       "id": "crud_sample_sms_notification_fetch_sample_type_name",
       "label": "_fetch_sample_type_name()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L229"
+      "source_location": "L217"
     },
     {
       "id": "crud_sample_sms_notification_list_notifications",
       "label": "list_notifications()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L243"
+      "source_location": "L231"
     },
     {
       "id": "crud_sample_sms_notification_toggle_notification_status",
       "label": "toggle_notification_status()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L349"
+      "source_location": "L339"
     },
     {
       "id": "crud_sample_sms_notification_generate_sms_content",
       "label": "generate_sms_content()",
       "file_type": "code",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L378"
+      "source_location": "L368"
     },
     {
       "id": "crud_sample_reception_py",
@@ -41881,7 +41881,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L26",
+      "source_location": "L25",
       "weight": 1.0
     },
     {
@@ -41890,7 +41890,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L30",
+      "source_location": "L29",
       "weight": 1.0
     },
     {
@@ -41899,7 +41899,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L36",
+      "source_location": "L35",
       "weight": 1.0
     },
     {
@@ -41908,7 +41908,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L40",
+      "source_location": "L39",
       "weight": 1.0
     },
     {
@@ -41917,7 +41917,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L50",
+      "source_location": "L49",
       "weight": 1.0
     },
     {
@@ -41926,7 +41926,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L85",
+      "source_location": "L84",
       "weight": 1.0
     },
     {
@@ -41935,7 +41935,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L145",
+      "source_location": "L132",
       "weight": 1.0
     },
     {
@@ -41944,7 +41944,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L181",
+      "source_location": "L168",
       "weight": 1.0
     },
     {
@@ -41953,7 +41953,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L219",
+      "source_location": "L206",
       "weight": 1.0
     },
     {
@@ -41962,7 +41962,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L233",
+      "source_location": "L220",
       "weight": 1.0
     },
     {
@@ -41971,7 +41971,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L349",
+      "source_location": "L342",
       "weight": 1.0
     },
     {
@@ -41980,7 +41980,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L385",
+      "source_location": "L378",
       "weight": 1.0
     },
     {
@@ -41989,7 +41989,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L108",
+      "source_location": "L96",
       "weight": 1.0
     },
     {
@@ -41998,7 +41998,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L216",
+      "source_location": "L203",
       "weight": 1.0
     },
     {
@@ -42007,7 +42007,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L247",
+      "source_location": "L234",
       "weight": 1.0
     },
     {
@@ -42016,7 +42016,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L315",
+      "source_location": "L308",
       "weight": 1.0
     },
     {
@@ -42025,7 +42025,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L324",
+      "source_location": "L317",
       "weight": 1.0
     },
     {
@@ -42034,7 +42034,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L329",
+      "source_location": "L322",
       "weight": 1.0
     },
     {
@@ -42043,7 +42043,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L341",
+      "source_location": "L334",
       "weight": 1.0
     },
     {
@@ -42052,7 +42052,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L358",
+      "source_location": "L351",
       "weight": 1.0
     },
     {
@@ -42061,7 +42061,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L393",
+      "source_location": "L386",
       "weight": 1.0
     },
     {
@@ -42070,16 +42070,16 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L440",
+      "source_location": "L433",
       "weight": 1.0
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "crud_storage_entry_sms_notification_resolve_notification",
       "relation": "rationale_for",
       "confidence": "EXTRACTED",
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L189",
+      "source_location": "L176",
       "weight": 1.0
     },
     {
@@ -49000,7 +49000,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L139",
+      "source_location": "L127",
       "weight": 1.0
     },
     {
@@ -49009,7 +49009,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L179",
+      "source_location": "L167",
       "weight": 1.0
     },
     {
@@ -49018,7 +49018,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L229",
+      "source_location": "L217",
       "weight": 1.0
     },
     {
@@ -49027,7 +49027,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L243",
+      "source_location": "L231",
       "weight": 1.0
     },
     {
@@ -49036,7 +49036,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L349",
+      "source_location": "L339",
       "weight": 1.0
     },
     {
@@ -49045,7 +49045,7 @@
       "relation": "contains",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L378",
+      "source_location": "L368",
       "weight": 1.0
     },
     {
@@ -49063,7 +49063,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L102",
+      "source_location": "L91",
       "weight": 1.0
     },
     {
@@ -49072,7 +49072,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L212",
+      "source_location": "L200",
       "weight": 1.0
     },
     {
@@ -49081,7 +49081,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L257",
+      "source_location": "L245",
       "weight": 1.0
     },
     {
@@ -49090,7 +49090,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L323",
+      "source_location": "L313",
       "weight": 1.0
     },
     {
@@ -49099,7 +49099,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L329",
+      "source_location": "L319",
       "weight": 1.0
     },
     {
@@ -49108,7 +49108,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L340",
+      "source_location": "L330",
       "weight": 1.0
     },
     {
@@ -49117,7 +49117,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L358",
+      "source_location": "L348",
       "weight": 1.0
     },
     {
@@ -49126,7 +49126,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L386",
+      "source_location": "L376",
       "weight": 1.0
     },
     {
@@ -49135,7 +49135,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L434",
+      "source_location": "L424",
       "weight": 1.0
     },
     {
@@ -49144,7 +49144,7 @@
       "relation": "calls",
       "confidence": "EXTRACTED",
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L435",
+      "source_location": "L425",
       "weight": 1.0
     },
     {
@@ -82754,7 +82754,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizcellsampletype",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82763,7 +82763,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizcollectionprotocol",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82772,7 +82772,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizprotocolpartycustomer",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82781,7 +82781,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizprotocolsample",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82790,16 +82790,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
-      "target": "models_models_bizsamplereception",
-      "relation": "uses",
-      "confidence": "INFERRED",
-      "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L12",
-      "weight": 0.8
-    },
-    {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizstorageentrysmsnotification",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82808,7 +82799,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_bizstoragereleasereview",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -82817,7 +82808,7 @@
       "weight": 0.8
     },
     {
-      "source": "crud_storage_entry_sms_notification_rationale_189",
+      "source": "crud_storage_entry_sms_notification_rationale_176",
       "target": "models_models_sysuser",
       "relation": "uses",
       "confidence": "INFERRED",
@@ -102303,7 +102294,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L165",
+      "source_location": "L152",
       "weight": 1.0
     },
     {
@@ -102313,7 +102304,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L166",
+      "source_location": "L153",
       "weight": 1.0
     },
     {
@@ -102323,7 +102314,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L285",
+      "source_location": "L272",
       "weight": 1.0
     },
     {
@@ -102333,7 +102324,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L287",
+      "source_location": "L274",
       "weight": 1.0
     },
     {
@@ -102343,7 +102334,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/storage_entry_sms_notification.py",
-      "source_location": "L319",
+      "source_location": "L312",
       "weight": 1.0
     },
     {
@@ -104803,7 +104794,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L163",
+      "source_location": "L151",
       "weight": 1.0
     },
     {
@@ -104813,7 +104804,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L164",
+      "source_location": "L152",
       "weight": 1.0
     },
     {
@@ -104823,7 +104814,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L288",
+      "source_location": "L276",
       "weight": 1.0
     },
     {
@@ -104833,7 +104824,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L290",
+      "source_location": "L278",
       "weight": 1.0
     },
     {
@@ -104843,7 +104834,7 @@
       "confidence": "INFERRED",
       "confidence_score": 0.8,
       "source_file": "crud/sample_sms_notification.py",
-      "source_location": "L327",
+      "source_location": "L317",
       "weight": 1.0
     },
     {

+ 80 - 65
backend/graphify-out/GRAPH_REPORT.md

@@ -1,12 +1,12 @@
-# Graph Report - app  (2026-05-23)
+# Graph Report - app  (2026-05-24)
 
 ## Corpus Check
-- 146 files · ~134,832 words
+- 146 files · ~134,783 words
 - Verdict: corpus is large enough that graph structure adds value.
 
 ## Summary
-- 2597 nodes · 10461 edges · 48 communities detected
-- Extraction: 41% EXTRACTED · 59% INFERRED · 0% AMBIGUOUS · INFERRED: 6177 edges (avg confidence: 0.63)
+- 2597 nodes · 10460 edges · 51 communities detected
+- Extraction: 41% EXTRACTED · 59% INFERRED · 0% AMBIGUOUS · INFERRED: 6176 edges (avg confidence: 0.63)
 - Token cost: 0 input · 0 output
 
 ## Community Hubs (Navigation)
@@ -52,12 +52,15 @@
 - [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 40|Community 40]]
 - [[_COMMUNITY_Community 41|Community 41]]
+- [[_COMMUNITY_Community 42|Community 42]]
+- [[_COMMUNITY_Community 43|Community 43]]
 - [[_COMMUNITY_Community 44|Community 44]]
-- [[_COMMUNITY_Community 45|Community 45]]
-- [[_COMMUNITY_Community 46|Community 46]]
 - [[_COMMUNITY_Community 47|Community 47]]
 - [[_COMMUNITY_Community 48|Community 48]]
 - [[_COMMUNITY_Community 49|Community 49]]
+- [[_COMMUNITY_Community 50|Community 50]]
+- [[_COMMUNITY_Community 51|Community 51]]
+- [[_COMMUNITY_Community 52|Community 52]]
 
 ## God Nodes (most connected - your core abstractions)
 1. `success_response()` - 409 edges
@@ -87,11 +90,11 @@
 
 ### Community 0 - "Community 0"
 Cohesion: 0.02
-Nodes (451): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+443 more)
+Nodes (434): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+426 more)
 
 ### Community 1 - "Community 1"
 Cohesion: 0.01
-Nodes (388): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+380 more)
+Nodes (399): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+391 more)
 
 ### Community 2 - "Community 2"
 Cohesion: 0.06
@@ -99,27 +102,27 @@ Nodes (226): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审
 
 ### Community 3 - "Community 3"
 Cohesion: 0.02
-Nodes (166): generate_uuid(), batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status() (+158 more)
+Nodes (161): generate_uuid(), batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status() (+153 more)
 
 ### Community 4 - "Community 4"
-Cohesion: 0.02
-Nodes (158): _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), delete(), get_multi_with_filters() (+150 more)
-
-### Community 5 - "Community 5"
 Cohesion: 0.06
 Nodes (164): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+156 more)
 
-### Community 6 - "Community 6"
+### Community 5 - "Community 5"
 Cohesion: 0.02
 Nodes (139): list_roles_for_select(), _avatar_url_field(), _build_user_info_payload(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 与 web-admin `userInfo` 字段约定一致。 (+131 more)
 
+### Community 6 - "Community 6"
+Cohesion: 0.03
+Nodes (135): create_collection_protocol(), delete_collection_protocol(), _ensure_customer_for_mother(), get_collection_protocol_detail(), _get_current_user_id(), _get_tenant_no(), list_collection_protocol_audits(), list_collection_protocols() (+127 more)
+
 ### Community 7 - "Community 7"
 Cohesion: 0.04
 Nodes (113): _format_attachment_file_size(), get_protocol_nos_used_in_protocols(), urls_to_attachment_list(), _accept_conclusion_label(), _attach_reception_detail(), complete_production_work_order(), create_production_work_order_from_reception(), _enrich_wo_items() (+105 more)
 
 ### Community 8 - "Community 8"
 Cohesion: 0.03
-Nodes (54): get_designer_data(), BaseStorage, convert_heic_to_jpeg(), get_storage(), LocalStorage, MinIOStorage, OSSStorage, process_image_content() (+46 more)
+Nodes (66): get_designer_data(), BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation() (+58 more)
 
 ### Community 9 - "Community 9"
 Cohesion: 0.07
@@ -142,29 +145,29 @@ Cohesion: 0.11
 Nodes (27): get_next_cell_product_code(), _build_facility_path_label(), build_split_product_codes(), create_cell_products_batch(), create_cell_products_by_quantity(), delete_cell_product(), _enrich_product_dicts(), get_cell_product() (+19 more)
 
 ### Community 14 - "Community 14"
-Cohesion: 0.2
-Nodes (23): _create_insurance_company(), _create_insurance_company_invoice(), _create_insurance_project_price(), _fmt_dt(), _generate_uuid(), InsuranceCompanyCreateBody, InsuranceCompanyInvoiceCreateBody, InsuranceCompanyInvoiceUpdateBody (+15 more)
+Cohesion: 0.18
+Nodes (17): generate_sms(), _get_current_user_id(), _get_tenant_no(), list_notifications(), _page_payload(), toggle_status(), ToggleStatusBody, _accept_conclusion_label() (+9 more)
 
 ### Community 15 - "Community 15"
-Cohesion: 0.17
-Nodes (12): BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation(), OrderLockedException (+4 more)
+Cohesion: 0.16
+Nodes (17): _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), _resolve_receiver_purpose(), revert_step_stock_changes(), _row_to_dict(), _user_nick_name_map(), _base_query() (+9 more)
 
 ### Community 16 - "Community 16"
 Cohesion: 0.18
-Nodes (17): generate_sms(), _get_current_user_id(), _get_tenant_no(), list_notifications(), _page_payload(), toggle_status(), ToggleStatusBody, _accept_conclusion_label() (+9 more)
-
-### Community 17 - "Community 17"
-Cohesion: 0.18
 Nodes (18): company_code_exists(), create_company(), get_company(), list_companies(), list_company_options(), _row_to_dict(), soft_delete_company(), update_company() (+10 more)
 
-### Community 18 - "Community 18"
+### Community 17 - "Community 17"
 Cohesion: 0.21
 Nodes (15): batch_issue_protocol_nos(), batch_revoke_protocol_nos(), create_protocol_no(), enrich_issued_user_names(), get_protocol_no(), list_protocol_nos(), _parse_issued_time(), protocol_no_exists() (+7 more)
 
-### Community 19 - "Community 19"
+### Community 18 - "Community 18"
 Cohesion: 0.21
 Nodes (13): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_names_by_region_ids(), _hospital_to_dict(), list_collection_regions(), list_hospitals() (+5 more)
 
+### Community 19 - "Community 19"
+Cohesion: 0.25
+Nodes (13): code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), list_materials(), _norm_status(), options_list() (+5 more)
+
 ### Community 20 - "Community 20"
 Cohesion: 0.26
 Nodes (12): batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), 样本编码池 CRUD(biz_sample_code), _resolve_issued_user_display_name() (+4 more)
@@ -174,143 +177,155 @@ Cohesion: 0.24
 Nodes (9): 微信消息通知服务  支持微信小程序模板消息推送  使用方式:     from app.core.wechat_message import Wech, 发送模板消息                  Args:             openid: 用户openid             templ, send_delivery_notice(), send_delivery_warning(), send_order_status(), send_payment_success(), send_refund_result(), send_urge_notification() (+1 more)
 
 ### Community 22 - "Community 22"
+Cohesion: 0.23
+Nodes (12): Config, ProductBase, ProductCategoryBase, ProductCategoryCreate, ProductCategoryResponse, ProductCreate, ProductResponse, ProductUnitBase (+4 more)
+
+### Community 23 - "Community 23"
 Cohesion: 0.27
 Nodes (11): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。 (+3 more)
 
-### Community 23 - "Community 23"
+### Community 24 - "Community 24"
 Cohesion: 0.29
 Nodes (9): _build_tree(), _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), list_menus_tree(), _row_to_dict(), soft_delete_menu() (+1 more)
 
-### Community 24 - "Community 24"
-Cohesion: 0.27
-Nodes (9): _ensure_customer_for_mother(), create_customer(), get_customer(), id_card_taken(), list_customers(), 客户 CRUD(biz_customer), _row_to_dict(), soft_delete_customer() (+1 more)
-
 ### Community 25 - "Community 25"
 Cohesion: 0.31
 Nodes (8): create_sop_document(), get_sop_document(), list_sop_documents(), SOP文档 CRUD(biz_sop_document), _row_to_dict(), soft_delete_sop_document(), sop_code_exists(), update_sop_document()
 
 ### Community 26 - "Community 26"
+Cohesion: 0.36
+Nodes (9): deduct_for_task(), _display_remark(), list_usages(), list_usages_by_task(), _parse_receiver_purpose_from_remark(), _resolve_receiver_purpose(), revert_task_material_usages(), _row_to_dict() (+1 more)
+
+### Community 27 - "Community 27"
 Cohesion: 0.31
 Nodes (8): create_express_company(), express_code_exists(), get_express_company(), list_express_companies(), 快递公司 CRUD(biz_express_company), _row_to_dict(), soft_delete_express_company(), update_express_company()
 
-### Community 27 - "Community 27"
+### Community 28 - "Community 28"
 Cohesion: 0.38
 Nodes (9): create_check_item(), get_check_item(), item_code_exists(), list_active_check_items(), list_check_items(), _order_query(), _row_to_dict(), soft_delete_check_item() (+1 more)
 
-### Community 28 - "Community 28"
+### Community 29 - "Community 29"
 Cohesion: 0.27
 Nodes (8): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), 字典数据 CRUD(sys_dict_data), _row_to_dict(), soft_delete_dict_data(), update_dict_data()
 
-### Community 29 - "Community 29"
+### Community 30 - "Community 30"
 Cohesion: 0.31
 Nodes (8): category_code_exists(), create_process_category(), get_process_category(), list_process_categories(), 工艺工序分类 CRUD(biz_process_category), _row_to_dict(), soft_delete_process_category(), update_process_category()
 
-### Community 30 - "Community 30"
+### Community 31 - "Community 31"
 Cohesion: 0.31
 Nodes (8): create_item_unit(), get_item_unit(), list_item_units(), 物品单位 CRUD(biz_item_unit), _row_to_dict(), soft_delete_item_unit(), unit_code_exists(), update_item_unit()
 
-### Community 31 - "Community 31"
+### Community 32 - "Community 32"
 Cohesion: 0.31
 Nodes (8): create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict(), soft_delete_cell_sample_type(), type_code_exists(), update_cell_sample_type()
 
-### Community 32 - "Community 32"
+### Community 33 - "Community 33"
+Cohesion: 0.31
+Nodes (8): create_output_type(), get_output_type(), list_output_types(), 工艺产出物类型 CRUD(biz_output_type), _row_to_dict(), soft_delete_output_type(), type_code_exists(), update_output_type()
+
+### Community 34 - "Community 34"
 Cohesion: 0.36
 Nodes (7): create_post(), get_post(), list_posts(), post_code_exists(), _row_to_dict(), soft_delete_post(), update_post()
 
-### Community 33 - "Community 33"
+### Community 35 - "Community 35"
 Cohesion: 0.36
 Nodes (7): create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), _row_to_dict(), soft_delete_dict_type(), update_dict_type()
 
-### Community 34 - "Community 34"
+### Community 36 - "Community 36"
+Cohesion: 0.36
+Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
+
+### Community 37 - "Community 37"
 Cohesion: 0.36
 Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
 
-### Community 35 - "Community 35"
+### Community 38 - "Community 38"
 Cohesion: 0.29
 Nodes (2): BaseModel, SoftDeleteModel
 
-### Community 36 - "Community 36"
+### Community 39 - "Community 39"
 Cohesion: 0.38
 Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。     约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断;     若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
 
-### Community 37 - "Community 37"
+### Community 40 - "Community 40"
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 
-### Community 38 - "Community 38"
+### Community 41 - "Community 41"
 Cohesion: 0.5
 Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
 
-### Community 39 - "Community 39"
+### Community 42 - "Community 42"
 Cohesion: 0.67
 Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)
 
-### Community 40 - "Community 40"
+### Community 43 - "Community 43"
 Cohesion: 1.0
 Nodes (1): 请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
 
-### Community 41 - "Community 41"
+### Community 44 - "Community 44"
 Cohesion: 1.0
 Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
 
-### Community 44 - "Community 44"
+### Community 47 - "Community 47"
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
-### Community 45 - "Community 45"
+### Community 48 - "Community 48"
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
-### Community 46 - "Community 46"
+### Community 49 - "Community 49"
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
-### Community 47 - "Community 47"
+### Community 50 - "Community 50"
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
-### Community 48 - "Community 48"
+### Community 51 - "Community 51"
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
-### Community 49 - "Community 49"
+### Community 52 - "Community 52"
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
 ## Knowledge Gaps
 - **90 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+85 more)
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 35`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
+- **Thin community `Community 38`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 38`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
+- **Thin community `Community 41`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 39`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)`
+- **Thin community `Community 42`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 40`** (2 nodes): `context.py`, `请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
+- **Thin community `Community 43`** (2 nodes): `context.py`, `请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 41`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
+- **Thin community `Community 44`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 44`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 47`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 45`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 48`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 46`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 49`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 47`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 50`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 48`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 51`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 49`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 52`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 
 ## Suggested Questions
 _Questions this graph is uniquely positioned to answer:_
 
-- **Why does `generate_uuid()` connect `Community 3` to `Community 0`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 13`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`?**
+- **Why does `generate_uuid()` connect `Community 3` to `Community 0`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 36`, `Community 37`?**
   _High betweenness centrality (0.103) - this node is a cross-community bridge._
-- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 9`, `Community 10`, `Community 14`, `Community 16`, `Community 18`, `Community 20`?**
+- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 9`, `Community 10`, `Community 14`, `Community 17`, `Community 20`?**
   _High betweenness centrality (0.071) - this node is a cross-community bridge._
-- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 4`, `Community 6`, `Community 39`, `Community 8`, `Community 13`, `Community 14`, `Community 16`?**
+- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 5`, `Community 6`, `Community 8`, `Community 42`, `Community 13`, `Community 14`?**
   _High betweenness centrality (0.070) - this node is a cross-community bridge._
 - **Are the 408 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
   _`success_response()` has 408 INFERRED edges - model-reasoned connections that need verification._

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
backend/graphify-out/graph.html


Разница между файлами не показана из-за своего большого размера
+ 112 - 112
backend/graphify-out/graph.json


+ 2 - 2
backend/graphify-out/manifest.json

@@ -86,7 +86,7 @@
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/reagent_stock_log.py": 1779515479.6599197,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/clean_room.py": 1778775238.2993922,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/__init__.py": 1778605691.3816392,
-  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_entry_sms_notification.py": 1779532930.08716,
+  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_entry_sms_notification.py": 1779545611.322996,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/express_company.py": 1779265659.3453207,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_lab_test_item.py": 1779265673.3888667,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/role.py": 1779265616.3701816,
@@ -106,7 +106,7 @@
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_service.py": 1779310971.8304002,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/customer.py": 1778733031.447253,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/workflow.py": 1779265993.677624,
-  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_sms_notification.py": 1779532932.769933,
+  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_sms_notification.py": 1779545309.6706858,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py": 1779532933.8137784,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/consumable_pack.py": 1779265715.416005,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material.py": 1779284415.64655,

+ 1 - 1
web-admin/graphify-out/GRAPH_REPORT.md

@@ -1,4 +1,4 @@
-# Graph Report - src  (2026-05-23)
+# Graph Report - src  (2026-05-24)
 
 ## Corpus Check
 - Large corpus: 595 files · ~325,194 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.

+ 75 - 105
web-admin/src/components/business/qc-task-view-dialog/index.vue

@@ -28,61 +28,48 @@
         </div>
       </div>
     </template>
-    <template #append>
-      <div
-        v-if="viewDetail?.taskType !== 'dsfjc'"
-        class="view-section qc-task-view-material-usages"
-      >
-        <h3>物料使用</h3>
-        <el-table
-          v-if="viewMaterialUsages.length"
-          :data="viewMaterialUsages"
-          stripe
-          class="list-table inner-table qc-task-view-material-table"
-        >
-          <el-table-column
-            type="index"
-            label="序号"
-            min-width="56"
-            align="center"
-            header-align="center"
-            :index="(index) => index + 1"
-          />
-          <el-table-column
-            prop="materialName"
-            label="物料名称"
-            min-width="160"
-            align="center"
-            header-align="center"
-          />
-          <el-table-column label="规格型号" min-width="120" align="center" header-align="center">
-            <template #default="{ row }">{{ row.specModel || '—' }}</template>
-          </el-table-column>
-          <el-table-column label="用量" min-width="100" align="center" header-align="center">
-            <template #default="{ row }">
-              {{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
-            </template>
-          </el-table-column>
-          <el-table-column label="单位" min-width="72" align="center" header-align="center">
-            <template #default="{ row }">{{ row.unit || '—' }}</template>
-          </el-table-column>
-          <el-table-column label="备注" min-width="140" align="center" header-align="center">
-            <template #default="{ row }">{{ row.remark || '—' }}</template>
-          </el-table-column>
-        </el-table>
-        <p v-else class="view-material-empty">暂无物料使用记录</p>
-      </div>
-      <div
-        v-if="viewDetail?.attachmentFiles?.length"
-        class="view-section qc-task-view-attachments"
-      >
-        <h3>{{ viewDetail?.taskType === 'dsfjc' ? '检测材料' : '检测附件' }}</h3>
-        <ProtocolAttachmentReadonly
-          :files="viewDetail.attachmentFiles"
-          @preview="handleAttachmentPreview"
-          @download="handleAttachmentDownload"
+    <template #material-usages>
+      <div v-if="viewMaterialUsages.length" class="view-subtable-wrap">
+        <el-table :data="viewMaterialUsages" stripe class="list-table view-subtable">
+        <el-table-column
+          type="index"
+          label="序号"
+          min-width="56"
+          align="center"
+          header-align="center"
+          :index="(index) => index + 1"
         />
+        <el-table-column
+          prop="materialName"
+          label="物料名称"
+          min-width="160"
+          align="center"
+          header-align="center"
+        />
+        <el-table-column label="规格型号" min-width="120" align="center" header-align="center">
+          <template #default="{ row }">{{ row.specModel || '—' }}</template>
+        </el-table-column>
+        <el-table-column label="用量" min-width="100" align="center" header-align="center">
+          <template #default="{ row }">
+            {{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="单位" min-width="72" align="center" header-align="center">
+          <template #default="{ row }">{{ row.unit || '—' }}</template>
+        </el-table-column>
+        <el-table-column label="备注" min-width="140" align="center" header-align="center">
+          <template #default="{ row }">{{ row.remark || '—' }}</template>
+        </el-table-column>
+        </el-table>
       </div>
+      <p v-else class="view-empty-hint">暂无物料使用记录</p>
+    </template>
+    <template #task-attachments>
+      <ProtocolAttachmentReadonly
+        :files="viewDetail?.attachmentFiles || []"
+        @preview="handleAttachmentPreview"
+        @download="handleAttachmentDownload"
+      />
     </template>
   </SystemViewDialog>
 </template>
@@ -262,8 +249,25 @@
     if (r.etaTime) {
       transportFields.push({ label: '预计到达', value: r.etaTime })
     }
-    const resultSection =
-      d.results && d.results.length > 0
+    const taskResultSummaryFields: ViewSection['fields'] = [
+      {
+        label: '任务状态',
+        value: taskStatusLabel(d.taskStatus),
+        tagType: d.taskStatus === 'ywc' ? 'success' : d.taskStatus === 'jxz' ? 'warning' : 'info'
+      },
+      {
+        label: '检测结论',
+        value: d.conclusionLabel || qcTaskConclusionLabel(d.conclusion || '') || '—',
+        tagType: d.conclusion === 'tg' ? 'success' : d.conclusion === 'btg' ? 'danger' : 'info'
+      },
+      { label: '备注', value: d.remark || '—' }
+    ]
+    const resultSection: ViewSection[] = [
+      {
+        title: '质检结论',
+        fields: taskResultSummaryFields
+      },
+      ...(d.results && d.results.length > 0
         ? [
             {
               title: '检测参数',
@@ -273,7 +277,19 @@
               }))
             }
           ]
-        : []
+        : [{ title: '检测参数', fields: [] }])
+    ]
+    const tailSections: ViewSection[] = []
+    if (d.taskType !== 'dsfjc') {
+      tailSections.push({ title: '物料使用', fields: [], slot: 'material-usages' })
+    }
+    if (d.attachmentFiles?.length) {
+      tailSections.push({
+        title: d.taskType === 'dsfjc' ? '检测材料' : '检测附件',
+        fields: [],
+        slot: 'task-attachments'
+      })
+    }
     return [
       {
         title: '任务信息',
@@ -290,19 +306,8 @@
           { label: '检测项目', value: d.itemName },
           { label: '项目编码', value: d.itemCode || '—' },
           { label: '第三方公司', value: d.companyName || '—' },
-          {
-            label: '任务状态',
-            value: taskStatusLabel(d.taskStatus),
-            tagType: d.taskStatus === 'ywc' ? 'success' : d.taskStatus === 'jxz' ? 'warning' : 'info'
-          },
-          {
-            label: '检测结论',
-            value: d.conclusionLabel || qcTaskConclusionLabel(d.conclusion || '') || '—',
-            tagType: d.conclusion === 'tg' ? 'success' : d.conclusion === 'btg' ? 'danger' : 'info'
-          },
           { label: '完成时间', value: d.completedTime || '—' },
-          { label: '创建时间', value: d.createTime || '—' },
-          { label: '备注', value: d.remark || '—', span: true }
+          { label: '创建时间', value: d.createTime || '—' }
         ]
       },
       {
@@ -343,7 +348,8 @@
           { label: '备注', value: r.remark || '—', span: true }
         ]
       },
-      ...resultSection
+      ...resultSection,
+      ...tailSections
     ]
   })
 
@@ -395,40 +401,4 @@
     }
   }
 
-  .qc-task-view-material-usages,
-  .qc-task-view-attachments {
-    padding: 12px 18px;
-    border-bottom: none;
-
-    h3 {
-      margin: 0 0 10px;
-      padding-left: 10px;
-      font-size: 13px;
-      font-weight: 600;
-      color: #1e293b;
-      border-left: 3px solid #10b981;
-      line-height: 1;
-    }
-  }
-
-  .qc-task-view-material-table {
-    width: 100%;
-
-    :deep(.el-table__header .cell),
-    :deep(.el-table__body .cell) {
-      font-size: 12px;
-      line-height: 1.4;
-    }
-
-    :deep(.el-table__header th.el-table__cell),
-    :deep(.el-table__body td.el-table__cell) {
-      padding: 6px 0;
-    }
-  }
-
-  .view-material-empty {
-    margin: 0;
-    font-size: 12px;
-    color: #64748b;
-  }
 </style>

+ 57 - 0
web-admin/src/components/business/system-view-dialog/index.vue

@@ -407,5 +407,62 @@
       border-top: 1px solid #d9e8e2;
       background: #f8fafc;
     }
+
+    /* 区块标题:深色字 + 主色竖条(与 OpenSpec ui-conventions 一致,避免被全局 h3 染色) */
+    .view-section > h3 {
+      color: #1e293b;
+      font-weight: 600;
+      letter-spacing: normal;
+      text-transform: none;
+    }
+
+    /* 查看弹窗内嵌子表(对齐 fresh-mint §9.7 + OpenSpec 嵌套列表 14px) */
+    .view-section-slot .view-subtable-wrap {
+      border: 1px solid #e2e8f0;
+      border-radius: 8px;
+      overflow: hidden;
+    }
+
+    .view-section-slot .view-subtable-wrap .el-table.view-subtable {
+      --el-table-header-bg-color: #f8fafc;
+      --el-table-header-text-color: #64748b;
+      --el-table-border-color: #e2e8f0;
+      width: 100%;
+
+      .el-table__header th.el-table__cell,
+      .el-table__body td.el-table__cell {
+        font-size: 14px;
+        padding: 8px 0;
+      }
+
+      .el-table__header th.el-table__cell .cell {
+        font-weight: 500;
+        color: #64748b;
+      }
+
+      .el-table__body td.el-table__cell .cell {
+        color: #1e293b;
+      }
+    }
+
+    .view-section-slot .view-empty-hint {
+      margin: 0;
+      font-size: 13px;
+      color: #64748b;
+      line-height: 1.5;
+    }
+  }
+
+  .system-view-dialog--compact {
+    .view-section-slot .view-subtable-wrap .el-table.view-subtable {
+      .el-table__header th.el-table__cell,
+      .el-table__body td.el-table__cell {
+        font-size: 13px;
+      }
+    }
+
+    .view-section-slot .view-empty-hint {
+      font-size: 12px;
+    }
   }
 </style>

+ 36 - 9
web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue

@@ -11,7 +11,12 @@
     @update:model-value="emit('update:modelValue', $event)"
     @closed="handleClosed"
   >
-    <div class="protocol-view-body" :class="{ 'is-detail-loading': detailLoading && !bundle }">
+    <div
+      class="protocol-view-body"
+      :class="{ 'is-detail-loading': detailLoading && !bundle }"
+      v-loading="detailLoading && !bundle"
+      element-loading-text="加载中…"
+    >
       <div class="detail-tabs">
         <div
           v-for="tab in formTabs"
@@ -25,7 +30,7 @@
       </div>
 
       <template v-if="bundle">
-        <div v-loading="detailLoading && !bundle">
+        <div>
           <!-- KPI 摘要条 -->
           <div class="kpi-strip">
             <div class="kpi-card">
@@ -891,19 +896,32 @@
                               </div>
                             </template>
                             <template v-else-if="step.key === 'receipt-finance'">
-                              <!-- 财务确认 -->
+                              <!-- 财务确认:jfqrz 为待财务确认,updateTime 为提交财务时间非确认时间 -->
                               <div class="flow-detail-card__kv">
                                 <span class="flow-detail-card__label">确认状态</span>
-                                <span class="flow-detail-card__value"
-                                  ><el-tag type="success" size="small">已确认</el-tag></span
-                                >
+                                <span class="flow-detail-card__value">
+                                  <el-tag
+                                    v-if="isReceiptFinanceConfirmed(detail)"
+                                    type="success"
+                                    size="small"
+                                    >已确认</el-tag
+                                  >
+                                  <el-tag v-else type="warning" size="small">待确认</el-tag>
+                                </span>
                               </div>
-                              <div v-if="detail.updateTime" class="flow-detail-card__kv">
+                              <div
+                                v-if="isReceiptFinanceConfirmed(detail) && detail.updateTime"
+                                class="flow-detail-card__kv"
+                              >
                                 <span class="flow-detail-card__label">确认时间</span>
                                 <span class="flow-detail-card__value">{{ detail.updateTime }}</span>
                               </div>
                               <div
-                                v-if="detail.financeRemark && String(detail.financeRemark).trim()"
+                                v-if="
+                                  isReceiptFinanceConfirmed(detail) &&
+                                  detail.financeRemark &&
+                                  String(detail.financeRemark).trim()
+                                "
                                 class="flow-detail-card__kv"
                               >
                                 <span class="flow-detail-card__label">财务备注</span>
@@ -912,7 +930,10 @@
                                 }}</span>
                               </div>
                               <div
-                                v-if="financeConfirmAttachmentFiles(detail).length"
+                                v-if="
+                                  isReceiptFinanceConfirmed(detail) &&
+                                  financeConfirmAttachmentFiles(detail).length
+                                "
                                 class="flow-detail-card__kv"
                               >
                                 <span class="flow-detail-card__label">财务附件</span>
@@ -1437,6 +1458,12 @@
     return 'green'
   }
 
+  /** 缴费明细是否已完成财务收款确认(jfqrz 仅已提交待财务处理) */
+  function isReceiptFinanceConfirmed(detail: Record<string, any>) {
+    const s = String(detail.payStatus || '')
+    return s !== 'jfqrz' && s !== 'dfj'
+  }
+
   interface MiniFlowStep {
     key: string
     label: string

+ 53 - 80
web-admin/src/views/quality-inspection/qc-task/index.vue

@@ -313,61 +313,48 @@
             </div>
           </div>
         </template>
-        <template #append>
-          <div
-            v-if="viewDetail?.taskType !== 'dsfjc'"
-            class="view-section qc-task-view-material-usages"
-          >
-            <h3>物料使用</h3>
-            <el-table
-              v-if="viewMaterialUsages.length"
-              :data="viewMaterialUsages"
-              stripe
-              class="list-table inner-table qc-task-view-material-table"
-            >
-              <el-table-column
-                type="index"
-                label="序号"
-                min-width="56"
-                align="center"
-                header-align="center"
-                :index="(index) => index + 1"
-              />
-              <el-table-column
-                prop="materialName"
-                label="物料名称"
-                min-width="160"
-                align="center"
-                header-align="center"
-              />
-              <el-table-column label="规格型号" min-width="120" align="center" header-align="center">
-                <template #default="{ row }">{{ row.specModel || '—' }}</template>
-              </el-table-column>
-              <el-table-column label="用量" min-width="100" align="center" header-align="center">
-                <template #default="{ row }">
-                  {{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
-                </template>
-              </el-table-column>
-              <el-table-column label="单位" min-width="72" align="center" header-align="center">
-                <template #default="{ row }">{{ row.unit || '—' }}</template>
-              </el-table-column>
-              <el-table-column label="备注" min-width="140" align="center" header-align="center">
-                <template #default="{ row }">{{ row.remark || '—' }}</template>
-              </el-table-column>
-            </el-table>
-            <p v-else class="view-material-empty">暂无物料使用记录</p>
-          </div>
-          <div
-            v-if="viewDetail?.attachmentFiles?.length"
-            class="view-section qc-task-view-attachments"
-          >
-            <h3>{{ viewDetail?.taskType === 'dsfjc' ? '检测材料' : '检测附件' }}</h3>
-            <ProtocolAttachmentReadonly
-              :files="viewDetail.attachmentFiles"
-              @preview="handleViewAttachmentPreview"
-              @download="handleViewAttachmentDownload"
+        <template #material-usages>
+          <div v-if="viewMaterialUsages.length" class="view-subtable-wrap">
+            <el-table :data="viewMaterialUsages" stripe class="list-table view-subtable">
+            <el-table-column
+              type="index"
+              label="序号"
+              min-width="56"
+              align="center"
+              header-align="center"
+              :index="(index) => index + 1"
             />
+            <el-table-column
+              prop="materialName"
+              label="物料名称"
+              min-width="160"
+              align="center"
+              header-align="center"
+            />
+            <el-table-column label="规格型号" min-width="120" align="center" header-align="center">
+              <template #default="{ row }">{{ row.specModel || '—' }}</template>
+            </el-table-column>
+            <el-table-column label="用量" min-width="100" align="center" header-align="center">
+              <template #default="{ row }">
+                {{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="单位" min-width="72" align="center" header-align="center">
+              <template #default="{ row }">{{ row.unit || '—' }}</template>
+            </el-table-column>
+            <el-table-column label="备注" min-width="140" align="center" header-align="center">
+              <template #default="{ row }">{{ row.remark || '—' }}</template>
+            </el-table-column>
+            </el-table>
           </div>
+          <p v-else class="view-empty-hint">暂无物料使用记录</p>
+        </template>
+        <template #task-attachments>
+          <ProtocolAttachmentReadonly
+            :files="viewDetail?.attachmentFiles || []"
+            @preview="handleViewAttachmentPreview"
+            @download="handleViewAttachmentDownload"
+          />
         </template>
       </SystemViewDialog>
     </section>
@@ -718,6 +705,17 @@
             }
           ]
         : []
+    const tailSections: ViewSection[] = []
+    if (d.taskType !== 'dsfjc') {
+      tailSections.push({ title: '物料使用', fields: [], slot: 'material-usages' })
+    }
+    if (d.attachmentFiles?.length) {
+      tailSections.push({
+        title: d.taskType === 'dsfjc' ? '检测材料' : '检测附件',
+        fields: [],
+        slot: 'task-attachments'
+      })
+    }
     return [
       {
         title: '任务信息',
@@ -789,7 +787,8 @@
           { label: '备注', value: r.remark || '—', span: true }
         ]
       },
-      ...resultSection
+      ...resultSection,
+      ...tailSections
     ]
   })
 
@@ -1086,32 +1085,6 @@
     }
   }
 
-  .qc-task-view-material-usages,
-  .qc-task-view-attachments {
-    padding: 18px 22px;
-    border-bottom: none;
-
-    h3 {
-      margin: 0 0 14px;
-      padding-left: 10px;
-      font-size: 14px;
-      font-weight: 600;
-      color: #1e293b;
-      border-left: 3px solid #10b981;
-      line-height: 1;
-    }
-  }
-
-  .qc-task-view-material-table {
-    width: 100%;
-  }
-
-  .view-material-empty {
-    margin: 0;
-    font-size: 13px;
-    color: #64748b;
-  }
-
   .view-reception-attachments {
     margin-top: 4px;
 

+ 9 - 2
web-admin/src/views/research-donation/list/DonationViewDialog.vue

@@ -11,7 +11,12 @@
     @update:model-value="emit('update:modelValue', $event)"
     @closed="handleClosed"
   >
-    <div class="donation-view-body" :class="{ 'is-detail-loading': loading && !bundle }">
+    <div
+      class="donation-view-body"
+      :class="{ 'is-detail-loading': loading && !bundle }"
+      v-loading="loading && !bundle"
+      element-loading-text="加载中…"
+    >
       <div class="detail-tabs">
         <div
           v-for="tab in formTabs"
@@ -25,7 +30,7 @@
       </div>
 
       <template v-if="bundle">
-        <div v-loading="loading && !bundle">
+        <div>
         <!-- KPI 摘要条 -->
         <div class="kpi-strip">
           <div class="kpi-card">
@@ -964,6 +969,8 @@
     bundle.value = null
     auditRecords.value = []
     activeTab.value = 'basic'
+    loading.value = false
+    auditLoading.value = false
   }
 
   watch(

Некоторые файлы не были показаны из-за большого количества измененных файлов