zlwu vor 6 Monaten
Ursprung
Commit
0d0fcc338e

+ 108 - 98
graphify-out/GRAPH_REPORT.md

@@ -1,12 +1,12 @@
-# Graph Report - sqxp_副本2  (2026-04-29)
+# Graph Report - sqxp_副本2  (2026-04-30)
 
 ## Corpus Check
-- 701 files · ~636,299 words
+- 701 files · ~636,516 words
 - Verdict: corpus is large enough that graph structure adds value.
 
 ## Summary
-- 4824 nodes · 23137 edges · 57 communities detected
-- Extraction: 22% EXTRACTED · 78% INFERRED · 0% AMBIGUOUS · INFERRED: 17993 edges (avg confidence: 0.55)
+- 4826 nodes · 23142 edges · 59 communities detected
+- Extraction: 22% EXTRACTED · 78% INFERRED · 0% AMBIGUOUS · INFERRED: 17994 edges (avg confidence: 0.55)
 - Token cost: 0 input · 0 output
 
 ## Community Hubs (Navigation)
@@ -32,41 +32,43 @@
 - [[_COMMUNITY_Community 19|Community 19]]
 - [[_COMMUNITY_Community 20|Community 20]]
 - [[_COMMUNITY_Community 21|Community 21]]
-- [[_COMMUNITY_Community 24|Community 24]]
+- [[_COMMUNITY_Community 22|Community 22]]
 - [[_COMMUNITY_Community 25|Community 25]]
 - [[_COMMUNITY_Community 26|Community 26]]
 - [[_COMMUNITY_Community 27|Community 27]]
-- [[_COMMUNITY_Community 29|Community 29]]
-- [[_COMMUNITY_Community 31|Community 31]]
+- [[_COMMUNITY_Community 28|Community 28]]
+- [[_COMMUNITY_Community 30|Community 30]]
 - [[_COMMUNITY_Community 32|Community 32]]
 - [[_COMMUNITY_Community 33|Community 33]]
 - [[_COMMUNITY_Community 34|Community 34]]
-- [[_COMMUNITY_Community 36|Community 36]]
+- [[_COMMUNITY_Community 35|Community 35]]
 - [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 38|Community 38]]
 - [[_COMMUNITY_Community 39|Community 39]]
-- [[_COMMUNITY_Community 44|Community 44]]
+- [[_COMMUNITY_Community 40|Community 40]]
+- [[_COMMUNITY_Community 41|Community 41]]
 - [[_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]]
 - [[_COMMUNITY_Community 53|Community 53]]
-- [[_COMMUNITY_Community 54|Community 54]]
-- [[_COMMUNITY_Community 58|Community 58]]
-- [[_COMMUNITY_Community 61|Community 61]]
-- [[_COMMUNITY_Community 62|Community 62]]
+- [[_COMMUNITY_Community 55|Community 55]]
+- [[_COMMUNITY_Community 56|Community 56]]
+- [[_COMMUNITY_Community 60|Community 60]]
+- [[_COMMUNITY_Community 63|Community 63]]
 - [[_COMMUNITY_Community 64|Community 64]]
-- [[_COMMUNITY_Community 77|Community 77]]
-- [[_COMMUNITY_Community 87|Community 87]]
+- [[_COMMUNITY_Community 66|Community 66]]
+- [[_COMMUNITY_Community 79|Community 79]]
 - [[_COMMUNITY_Community 89|Community 89]]
-- [[_COMMUNITY_Community 142|Community 142]]
-- [[_COMMUNITY_Community 143|Community 143]]
+- [[_COMMUNITY_Community 91|Community 91]]
 - [[_COMMUNITY_Community 144|Community 144]]
 - [[_COMMUNITY_Community 145|Community 145]]
 - [[_COMMUNITY_Community 146|Community 146]]
 - [[_COMMUNITY_Community 147|Community 147]]
+- [[_COMMUNITY_Community 148|Community 148]]
+- [[_COMMUNITY_Community 149|Community 149]]
 
 ## God Nodes (most connected - your core abstractions)
 1. `BizPriceListItem` - 672 edges
@@ -85,26 +87,26 @@
   backend/app/models/base.py → sqxp-wx/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue
 - `register()` --calls--> `validate()`  [INFERRED]
   web-admin/src/views/auth/register/index.vue → sqxp-wx/pages/client/address/edit.vue
+- `toTime()` --calls--> `getTime()`  [INFERRED]
+  web-admin/src/views/dashboard/console/index.vue → sqxp-wx/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
 - `number()` --calls--> `inputMaxlength()`  [INFERRED]
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
 - `number()` --calls--> `mounted()`  [INFERRED]
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-table/components/uni-tr/uni-tr.vue
-- `number()` --calls--> `onPickerChange()`  [INFERRED]
-  web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/pages/client/invoice/invoice.vue
 
 ## Communities
 
 ### Community 0 - "Community 0"
 Cohesion: 0.01
-Nodes (623): adjust_order(), _aggregate_client_settlement_history(), _aggregate_supplier_settlement_history(), _apply_region_minutes_to_data(), approve_quotation(), assign_deliverer_to_order(), assign_delivery_alias(), batch_create_settlements() (+615 more)
+Nodes (624): adjust_order(), _aggregate_client_settlement_history(), _aggregate_supplier_settlement_history(), _apply_region_minutes_to_data(), approve_quotation(), assign_deliverer_to_order(), assign_delivery_alias(), batch_create_settlements() (+616 more)
 
 ### Community 1 - "Community 1"
 Cohesion: 0.08
-Nodes (585): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 上传用户头像图片,返回可写入 avatarUrl 的地址 (+577 more)
+Nodes (596): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 上传用户头像图片,返回可写入 avatarUrl 的地址 (+588 more)
 
 ### Community 2 - "Community 2"
 Cohesion: 0.01
-Nodes (183): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+175 more)
+Nodes (187): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+179 more)
 
 ### Community 3 - "Community 3"
 Cohesion: 0.01
@@ -112,7 +114,7 @@ Nodes (146): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMen
 
 ### Community 4 - "Community 4"
 Cohesion: 0.01
-Nodes (160): fetchActiveRegions(), fetchAssignableMenuCodes(), fetchCategoryList(), fetchCategoryParameters(), fetchCategoryTree(), fetchCreateUnit(), fetchNotDeletedRegions(), fetchProductBannerList() (+152 more)
+Nodes (121): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+113 more)
 
 ### Community 5 - "Community 5"
 Cohesion: 0.01
@@ -120,23 +122,23 @@ Nodes (249): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价
 
 ### Community 6 - "Community 6"
 Cohesion: 0.02
-Nodes (190): # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, 记录处理过程(不结案,结案由配送完成/取消自动触发), 获取我抢到的任务列表      返回当前配送员已抢但尚未开始配送的任务, 获取我抢到的任务列表      返回当前配送员已抢但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务 (+182 more)
+Nodes (186): # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, 记录处理过程(不结案,结案由配送完成/取消自动触发), 获取我抢到的任务列表      返回当前配送员已抢但尚未开始配送的任务, 获取我抢到的任务列表      返回当前配送员已抢但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务, 取消抢单      配送员可以取消自己抢到但尚未开始配送的任务 (+178 more)
 
 ### Community 7 - "Community 7"
-Cohesion: 0.01
-Nodes (122): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), disableDevTools(), handleLock() (+114 more)
+Cohesion: 0.02
+Nodes (111): endDate(), selected(), startDate(), Calendar, changeFlag(), countDown(), day(), handler() (+103 more)
 
 ### Community 8 - "Community 8"
-Cohesion: 0.02
-Nodes (75): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+67 more)
+Cohesion: 0.01
+Nodes (91): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+83 more)
 
 ### Community 9 - "Community 9"
 Cohesion: 0.03
-Nodes (101): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+93 more)
+Nodes (102): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+94 more)
 
 ### Community 10 - "Community 10"
-Cohesion: 0.03
-Nodes (52): handleFormClose(), _Blur(), created(), init(), inputContentStyle(), inputMaxlength(), inputStyle(), obj2strStyle() (+44 more)
+Cohesion: 0.04
+Nodes (40): BaseHTTPMiddleware, ApiCryptoError, _b64url_decode(), _b64url_encode(), encrypt_json(), gen_nonce_hex(), is_ts_in_window(), ApiCryptoMiddleware (+32 more)
 
 ### Community 11 - "Community 11"
 Cohesion: 0.03
@@ -147,12 +149,12 @@ Cohesion: 0.05
 Nodes (72): 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 备货完成后通知配送员(微信订阅消息,失败不阻断主流程), 备货完成后通知配送员(微信订阅消息,失败不阻断主流程), 备货完成后通知配送员(微信订阅消息,失败不阻断主流程), 供应商开始备货:assigned -> supplier_stocking,并写入订单状态日志(配送进度) (+64 more)
 
 ### Community 13 - "Community 13"
-Cohesion: 0.05
-Nodes (14): if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig, getSystemStorage() (+6 more)
+Cohesion: 0.04
+Nodes (35): handleFormClose(), _Blur(), created(), init(), inputContentStyle(), inputMaxlength(), inputStyle(), obj2strStyle() (+27 more)
 
 ### Community 14 - "Community 14"
-Cohesion: 0.06
-Nodes (21): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+13 more)
+Cohesion: 0.05
+Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
 
 ### Community 15 - "Community 15"
 Cohesion: 0.11
@@ -171,213 +173,221 @@ Cohesion: 0.12
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
 ### Community 19 - "Community 19"
+Cohesion: 0.17
+Nodes (16): animation(), bindTiming(), close(), getDom(), getEl(), getSelectorQuery(), init(), leftOptions() (+8 more)
+
+### Community 20 - "Community 20"
 Cohesion: 0.18
 Nodes (16): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+8 more)
 
-### Community 20 - "Community 20"
+### Community 21 - "Community 21"
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
-### Community 21 - "Community 21"
+### Community 22 - "Community 22"
 Cohesion: 0.28
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 
-### Community 24 - "Community 24"
+### Community 25 - "Community 25"
 Cohesion: 0.17
 Nodes (2): clean(), onInput()
 
-### Community 25 - "Community 25"
+### Community 26 - "Community 26"
 Cohesion: 0.24
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
 
-### Community 26 - "Community 26"
+### Community 27 - "Community 27"
 Cohesion: 0.4
 Nodes (9): backup_table(), count_affected(), get_text_columns(), main(), make_backup_table_name(), parse_args(), quote_ident(), 生成备份表名,控制在 MySQL 64 字符限制内。 (+1 more)
 
-### Community 27 - "Community 27"
+### Community 28 - "Community 28"
 Cohesion: 0.24
 Nodes (3): _calcValue(), _getDecimalScale(), _onBlur()
 
-### Community 29 - "Community 29"
+### Community 30 - "Community 30"
 Cohesion: 0.27
 Nodes (4): dataValue(), emit(), onChange(), setOpen()
 
-### Community 31 - "Community 31"
+### Community 32 - "Community 32"
 Cohesion: 0.25
 Nodes (2): borderStyle(), value2px()
 
-### Community 32 - "Community 32"
+### Community 33 - "Community 33"
 Cohesion: 0.39
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 
-### Community 33 - "Community 33"
+### Community 34 - "Community 34"
 Cohesion: 0.29
 Nodes (2): handleTouchEnd(), setTransition()
 
-### Community 34 - "Community 34"
+### Community 35 - "Community 35"
 Cohesion: 0.36
 Nodes (1): TableHeightCalculator
 
-### Community 36 - "Community 36"
+### Community 37 - "Community 37"
 Cohesion: 0.36
 Nodes (1): RouteValidator
 
-### Community 37 - "Community 37"
+### Community 38 - "Community 38"
 Cohesion: 0.39
 Nodes (5): chooseAll(), chooseAndUploadFile(), chooseImage(), chooseVideo(), uploadFiles()
 
-### Community 38 - "Community 38"
+### Community 39 - "Community 39"
 Cohesion: 0.33
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 
-### Community 39 - "Community 39"
+### Community 40 - "Community 40"
+Cohesion: 0.38
+Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
+
+### Community 41 - "Community 41"
 Cohesion: 0.38
 Nodes (3): _change(), close(), open()
 
-### Community 44 - "Community 44"
+### Community 46 - "Community 46"
 Cohesion: 0.33
 Nodes (1): BridgeError
 
-### Community 46 - "Community 46"
+### Community 48 - "Community 48"
 Cohesion: 0.4
 Nodes (2): created(), getTable()
 
-### Community 47 - "Community 47"
+### Community 49 - "Community 49"
 Cohesion: 0.47
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 
-### Community 48 - "Community 48"
+### Community 50 - "Community 50"
 Cohesion: 0.4
 Nodes (2): created(), getForm()
 
-### Community 49 - "Community 49"
+### Community 51 - "Community 51"
 Cohesion: 0.47
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 
-### Community 50 - "Community 50"
+### Community 52 - "Community 52"
 Cohesion: 0.4
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 
-### Community 51 - "Community 51"
+### Community 53 - "Community 53"
 Cohesion: 0.5
 Nodes (2): focusInput(), handleKeydown()
 
-### Community 53 - "Community 53"
+### Community 55 - "Community 55"
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 
-### Community 54 - "Community 54"
+### Community 56 - "Community 56"
 Cohesion: 0.5
 Nodes (1): collectIds()
 
-### Community 58 - "Community 58"
+### Community 60 - "Community 60"
 Cohesion: 0.83
 Nodes (3): classes(), isTrue(), onClick()
 
-### Community 61 - "Community 61"
+### Community 63 - "Community 63"
 Cohesion: 0.67
 Nodes (2): created(), getParent()
 
-### Community 62 - "Community 62"
+### Community 64 - "Community 64"
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
-### Community 64 - "Community 64"
+### Community 66 - "Community 66"
 Cohesion: 1.0
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 
-### Community 77 - "Community 77"
+### Community 79 - "Community 79"
 Cohesion: 0.67
 Nodes (1): updateUseModal()
 
-### Community 87 - "Community 87"
+### Community 89 - "Community 89"
 Cohesion: 1.0
 Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 
-### Community 89 - "Community 89"
+### Community 91 - "Community 91"
 Cohesion: 0.67
 Nodes (1): return()
 
-### Community 142 - "Community 142"
+### Community 144 - "Community 144"
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
-### Community 143 - "Community 143"
+### Community 145 - "Community 145"
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
-### Community 144 - "Community 144"
+### Community 146 - "Community 146"
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
-### Community 145 - "Community 145"
+### Community 147 - "Community 147"
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
-### Community 146 - "Community 146"
+### Community 148 - "Community 148"
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
-### Community 147 - "Community 147"
+### Community 149 - "Community 149"
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
 ## Knowledge Gaps
 - **74 isolated node(s):** `关闭超时未支付的线上订单(超时分钟数来自系统设置 auto_cancel_minutes)。`, `初始化定时任务      Args:         get_db_session: 数据库会话获取函数(从 app.core.database 导入)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`, `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。` (+69 more)
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 24`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+- **Thin community `Community 25`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 31`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
+- **Thin community `Community 32`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 33`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
+- **Thin community `Community 34`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 34`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
+- **Thin community `Community 35`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 36`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
+- **Thin community `Community 37`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 38`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
+- **Thin community `Community 39`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 44`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
+- **Thin community `Community 46`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 46`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
+- **Thin community `Community 48`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 48`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
+- **Thin community `Community 50`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 51`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
+- **Thin community `Community 53`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 54`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
+- **Thin community `Community 56`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 61`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
+- **Thin community `Community 63`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 62`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
+- **Thin community `Community 64`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 64`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
+- **Thin community `Community 66`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 77`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
+- **Thin community `Community 79`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 87`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
+- **Thin community `Community 89`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 89`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
+- **Thin community `Community 91`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 142`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 144`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 143`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 145`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 144`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 146`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 145`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 147`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 146`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 148`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 147`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 149`** (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 `push()` connect `Community 2` to `Community 0`, `Community 3`, `Community 4`, `Community 10`, `Community 11`, `Community 13`, `Community 14`?**
+- **Why does `push()` connect `Community 4` to `Community 0`, `Community 2`, `Community 3`, `Community 7`, `Community 9`, `Community 11`, `Community 13`, `Community 14`?**
   _High betweenness centrality (0.133) - this node is a cross-community bridge._
-- **Why does `number()` connect `Community 4` to `Community 0`, `Community 2`, `Community 7`, `Community 10`, `Community 11`, `Community 14`?**
+- **Why does `number()` connect `Community 2` to `Community 0`, `Community 4`, `Community 7`, `Community 8`, `Community 11`, `Community 13`?**
   _High betweenness centrality (0.052) - this node is a cross-community bridge._
 - **Why does `query()` connect `Community 0` to `Community 17`, `Community 6`?**
   _High betweenness centrality (0.049) - this node is a cross-community bridge._

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
graphify-out/cache/ast/083ffd9b55549629f8865f4610d19aa715b3549c195186e000211b181894ffcf.json


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
graphify-out/cache/ast/43957cb492bc6d8c8579767cd5389727275b3ac1d467fc45985120777fe6a4b5.json


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
graphify-out/cache/ast/61a985164aae6bf4bbf08f933ee582143f9840a179e0b09652928c504d8c81db.json


+ 1 - 0
graphify-out/cache/ast/7289a27b34e03c675e75bd8844ab133ed59696205ad4ded83a6e328c551b0168.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_home_home_vue", "label": "home.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/home/home.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/7af8b262b2d3bc065e8b99b2a62e717d388bb3bc6b2145e7f8c097648a1494a0.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_home_home_vue", "label": "home.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/home/home.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
graphify-out/graph.html


Datei-Diff unterdrückt, da er zu groß ist
+ 112 - 112
graphify-out/graph.json


+ 57 - 27
sqxp-wx/pages/client/home/home.vue

@@ -239,6 +239,7 @@ const productsLoadingMore = ref(false)
 const nextProductPage = ref(1)
 const productHasMore = ref(true)
 const initialized = ref(false)
+let rootCategoryLoadingPromise = null
 
 // 商品详情覆盖弹窗
 const showProductOverlay = ref(false)
@@ -385,35 +386,58 @@ function onHomeBottomScrollToLower() {
 }
 
 async function loadAllRootCategories() {
-  rootCategoryList.value = []
   const token = uni.getStorageSync(TOKEN_KEY)
-  if (!token) return
-  let page = 1
-  try {
-    while (true) {
-      const res = await request({
-        url: '/mini/client/product-categories/first-level',
-        method: 'GET',
-        data: { page, pageSize: ROOT_CATEGORY_PAGE_SIZE }
-      })
-      const body = parseResponseJson(res)
-      if (body.code !== 200) break
-      const d = body.data || {}
-      const list = d.list || []
-      for (const c of list) {
-        rootCategoryList.value.push({
-          id: c.id,
-          name: c.name,
-          imageUrl: c.imageUrl || ''
+  if (!token) {
+    rootCategoryList.value = []
+    return
+  }
+  if (rootCategoryLoadingPromise) {
+    await rootCategoryLoadingPromise
+    return
+  }
+
+  rootCategoryLoadingPromise = (async () => {
+    const loaded = []
+    let page = 1
+    try {
+      while (true) {
+        const res = await request({
+          url: '/mini/client/product-categories/first-level',
+          method: 'GET',
+          data: { page, pageSize: ROOT_CATEGORY_PAGE_SIZE }
         })
+        const body = parseResponseJson(res)
+        if (body.code !== 200) break
+        const d = body.data || {}
+        const list = d.list || []
+        for (const c of list) {
+          loaded.push({
+            id: c.id,
+            name: c.name,
+            imageUrl: c.imageUrl || ''
+          })
+        }
+        const totalPages = Number(d.totalPages) || 0
+        if (page >= totalPages) break
+        page += 1
       }
-      const totalPages = Number(d.totalPages) || 0
-      if (page >= totalPages) break
-      page += 1
+      const deduped = []
+      const seen = new Set()
+      for (const c of loaded) {
+        const id = String(c.id || '').trim()
+        if (!id || seen.has(id)) continue
+        seen.add(id)
+        deduped.push(c)
+      }
+      rootCategoryList.value = deduped
+    } catch (error) {
+      console.error('加载一级分类失败:', error)
+    } finally {
+      rootCategoryLoadingPromise = null
     }
-  } catch (error) {
-    console.error('加载一级分类失败:', error)
-  }
+  })()
+
+  await rootCategoryLoadingPromise
 }
 
 /** 侧栏(二级/筛选项)按接口分页循环拉全,与一级分类 loadAllRootCategories 策略一致 */
@@ -572,6 +596,9 @@ onMounted(async () => {
   }
   isLoading.value = true
   try {
+    // 首次进入首页(尤其是“本地已有登录态直接进首页”)也要确保实时通道已建立。
+    void ensurePriceRealtimeConnected()
+    void checkActivePriceVersionOnce()
     await Promise.all([loadAllRootCategories(), cartStore.fetchCart(), reloadSidebar()])
     await reloadProducts()
     initialized.value = true
@@ -581,10 +608,11 @@ onMounted(async () => {
 })
 
 onShow(() => {
-  if (!initialized.value) return
+  // onShow 早于 mounted 完成时也需要先尝试建连,避免错过后台发布后的实时事件。
   if (!userStore.isLoggedIn || !userStore.isClient) return
   void ensurePriceRealtimeConnected()
   void checkActivePriceVersionOnce()
+  if (!initialized.value) return
   void Promise.all([loadAllRootCategories(), reloadSidebar(), cartStore.fetchCart()]).finally(() => {
     void reloadProducts()
   })
@@ -592,17 +620,19 @@ onShow(() => {
 
 const onPriceListUpdated = () => {
   // 价格清单切换后:刷新商品与侧栏(分类若与清单绑定,也会随之变化)
-  void Promise.all([reloadSidebar(), cartStore.fetchCart()]).finally(() => {
+  void Promise.all([loadAllRootCategories(), reloadSidebar(), cartStore.fetchCart()]).finally(() => {
     void reloadProducts()
   })
 }
 
 onMounted(() => {
   busOn('price_list_updated', onPriceListUpdated)
+  uni.$on && uni.$on('price_list_updated', onPriceListUpdated)
 })
 
 onUnmounted(() => {
   busOff('price_list_updated', onPriceListUpdated)
+  uni.$off && uni.$off('price_list_updated', onPriceListUpdated)
 })
 </script>
 

+ 67 - 24
sqxp-wx/utils/price-realtime.js

@@ -16,6 +16,10 @@ function setStoredVersion(version, priceListId) {
   if (priceListId) uni.setStorageSync(STORAGE_PRICE_LIST_ID_KEY, String(priceListId))
 }
 
+function getStoredPriceListId() {
+  return String(uni.getStorageSync(STORAGE_PRICE_LIST_ID_KEY) || '').trim()
+}
+
 function buildWsUrl(token) {
   const base = String(BASE_URL || '').trim()
   const origin = base.replace(/\/api\/?$/, '').replace(/\/$/, '')
@@ -28,6 +32,49 @@ let socketTask = null
 let reconnectTimer = null
 let reconnectDelayMs = 1000
 let isConnecting = false
+let listenersBound = false
+
+function bindSocketListeners() {
+  if (listenersBound) return
+
+  const onOpen = () => {
+    reconnectDelayMs = 1000
+    void checkActiveVersionAndEmitIfChanged()
+  }
+  const onMessage = (evt) => {
+    handleWsMessage(evt?.data)
+  }
+  const onClose = () => {
+    socketTask = null
+    scheduleReconnect()
+  }
+  const onError = () => {
+    try {
+      socketTask && socketTask.close && socketTask.close()
+    } catch (e) {
+      // ignore
+    }
+    socketTask = null
+    scheduleReconnect()
+  }
+
+  // App/小程序端:SocketTask 事件方法
+  if (socketTask && typeof socketTask.onOpen === 'function') {
+    socketTask.onOpen(onOpen)
+    socketTask.onMessage(onMessage)
+    socketTask.onClose(onClose)
+    socketTask.onError(onError)
+    listenersBound = true
+    return
+  }
+
+  // H5 端:uni 全局 socket 事件
+  if (typeof uni.onSocketOpen === 'function') uni.onSocketOpen(onOpen)
+  if (typeof uni.onSocketMessage === 'function') uni.onSocketMessage(onMessage)
+  if (typeof uni.onSocketClose === 'function') uni.onSocketClose(onClose)
+  if (typeof uni.onSocketError === 'function') uni.onSocketError(onError)
+  listenersBound = true
+}
 
 function clearReconnect() {
   if (reconnectTimer) clearTimeout(reconnectTimer)
@@ -73,11 +120,22 @@ function handleWsMessage(raw) {
   if (!msg || msg.event !== 'price_list_updated') return
   const data = msg.data || {}
   const newVersion = Number(data.priceVersion || 0) || 0
-  if (!newVersion) return
   const oldVersion = getStoredVersion()
-  if (newVersion <= oldVersion) return
-  setStoredVersion(newVersion, data.priceListId)
+  const oldPriceListId = getStoredPriceListId()
+  const newPriceListId = String(data.priceListId || '').trim()
+
+  // WebSocket 推送属于“已发生更新”的强信号:
+  // 即便版本号缺失/相同,也触发页面刷新,避免界面停留旧价。
+  const nextVersion = newVersion > 0 ? Math.max(newVersion, oldVersion) : oldVersion
+  setStoredVersion(nextVersion, newPriceListId || oldPriceListId)
+
+  // 若版本与清单都完全没变化,仍允许刷新一次,确保客户端与服务端最终一致。
   emit('price_list_updated', data)
+  try {
+    uni.$emit && uni.$emit('price_list_updated', data)
+  } catch (e) {
+    // ignore
+  }
 }
 
 export async function ensurePriceRealtimeConnected() {
@@ -91,26 +149,7 @@ export async function ensurePriceRealtimeConnected() {
     clearReconnect()
     const url = buildWsUrl(token)
     socketTask = uni.connectSocket({ url })
-    socketTask.onOpen(() => {
-      reconnectDelayMs = 1000
-      void checkActiveVersionAndEmitIfChanged()
-    })
-    socketTask.onMessage((evt) => {
-      handleWsMessage(evt?.data)
-    })
-    socketTask.onClose(() => {
-      socketTask = null
-      scheduleReconnect()
-    })
-    socketTask.onError(() => {
-      try {
-        socketTask && socketTask.close && socketTask.close()
-      } catch (e) {
-        // ignore
-      }
-      socketTask = null
-      scheduleReconnect()
-    })
+    bindSocketListeners()
   } finally {
     isConnecting = false
   }
@@ -121,7 +160,11 @@ export function stopPriceRealtime() {
   reconnectDelayMs = 1000
   if (socketTask) {
     try {
-      socketTask.close()
+      if (typeof socketTask.close === 'function') {
+        socketTask.close()
+      } else if (typeof uni.closeSocket === 'function') {
+        uni.closeSocket()
+      }
     } catch (e) {
       // ignore
     }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.