zlwu 6 месяцев назад
Родитель
Сommit
34dd37bfaa
100 измененных файлов с 430 добавлено и 209 удалено
  1. 102 39
      backend/app/api/admin.py
  2. 32 11
      backend/app/api/product_banner.py
  3. 32 11
      backend/app/api/product_detail_media.py
  4. 21 11
      backend/app/api/product_parameter_value.py
  5. 35 0
      backend/app/core/admin_request_json.py
  6. 7 4
      backend/app/core/api_crypto_middleware.py
  7. 7 5
      backend/app/core/request_payload.py
  8. 25 12
      backend/app/crud/product.py
  9. 1 1
      backend/app/crud/product_parameter.py
  10. 12 0
      backend/scripts/add_bas_product_list_indexes.sql
  11. 115 115
      graphify-out/GRAPH_REPORT.md
  12. 0 0
      graphify-out/cache/ast/033fdf926d87288017bce1d63ba0562948c15e46a791f4d020fc9f73e59d5709.json
  13. 0 0
      graphify-out/cache/ast/083aeed0599a6439baa3c274d57621e77e1bc9c60fd4efa7c8d8d1322229ca0b.json
  14. 0 0
      graphify-out/cache/ast/0cab5cd649f91092c0835d438f1e82eafa1864548bea03bb53866864f868e13f.json
  15. 0 0
      graphify-out/cache/ast/0cf3f5c3910d7e2f6ea372bfa2685141edc42ed0c9757f867c4248f94c4af0e4.json
  16. 0 0
      graphify-out/cache/ast/0d28ae9b538021272133dcfdde635c4fa828862f4bf0f6563763f8687f917189.json
  17. 1 0
      graphify-out/cache/ast/11deb2f6ab940a6fb2defa08eb4fdc9034257eac2851be704ad28fd5c839c2c0.json
  18. 1 0
      graphify-out/cache/ast/151ae550d254602e1a9e139bb264fa499a4a5b469b500e4aec29b0763ff8c059.json
  19. 1 0
      graphify-out/cache/ast/15dd8ebe0d650c86824af2f82e3a34fb74e2c19361b90df0c55774339e302778.json
  20. 0 0
      graphify-out/cache/ast/1796216ccf8dac8c15500aead6810897f1d9d3963c1b6c15e12cb343bc3cbae2.json
  21. 1 0
      graphify-out/cache/ast/1c090e9bebfcc5398aa005462eb2263178b11f3057a639654fc363dd38452c66.json
  22. 1 0
      graphify-out/cache/ast/1c593eeb3eec371c09a469f68f5fe1c43c114ec9e4e504aa703cc55c087a30cd.json
  23. 0 0
      graphify-out/cache/ast/1d13977105dc8d5d87bed1fa411afbd97460be09fb9a88113cb3fd84bf974762.json
  24. 0 0
      graphify-out/cache/ast/1de01c84c4d2eb6b87ecdfea13b31b866584c9a1288ed75e6658fd49a61a4169.json
  25. 0 0
      graphify-out/cache/ast/2028a4bb4efdb92f485471deb28e20f09c7e5c87ecb74c444a7d0033f944ca25.json
  26. 0 0
      graphify-out/cache/ast/2480140c5cdc3e9fc972fb87a210f46337389e123d68259de3ee6324a618f670.json
  27. 1 0
      graphify-out/cache/ast/27ea94e1c21637d0d34ede1f73fc1b619aac7fcac4b6e2aa84b4b92a55bce201.json
  28. 1 0
      graphify-out/cache/ast/288f6b2046e4129fd3b8d4d9e607c7d566d040be7a6298ef9d89e94c874d3d7e.json
  29. 0 0
      graphify-out/cache/ast/29cdd5d330c8ad809973fdb8a73d0b8beb3b2b16c4d5387ed45e42676c7f8294.json
  30. 0 0
      graphify-out/cache/ast/2a7ec7a6ed769114e9c4891a79ad56f96c5bed2c286ff978734a837c9b84c639.json
  31. 1 0
      graphify-out/cache/ast/2c226cd6c10994af2f93a081343a54b7ad11e1ed9ab34d7e3c8da867f43893e4.json
  32. 0 0
      graphify-out/cache/ast/2c3be9fb6eef07feade708520af7652c19f43f68b0288235a08b347859ed374e.json
  33. 0 0
      graphify-out/cache/ast/2cd076e33adccbee56880164fe5d1ec812eaf2a96edd3a73e42a1226df828cf8.json
  34. 0 0
      graphify-out/cache/ast/3086f23db6cd22394429779396d516a8eb9822ab42b99e0fc77eb3899fc4fe14.json
  35. 1 0
      graphify-out/cache/ast/33c9aa64e2c74bb91df85f4b1b2df8d1b4ca1b4c1fe65159ae8ade4ba7e7a11c.json
  36. 0 0
      graphify-out/cache/ast/381367caf22a5c0511fe3b6554eaa51aa1d5ea68ba2065a6d120dc3305613077.json
  37. 1 0
      graphify-out/cache/ast/381a37c7bf469d96edbd1244ccf5b9d0a70868ef26421e2dfe8ec69e9a1c0273.json
  38. 0 0
      graphify-out/cache/ast/3b7ba39de9f3b1d4c1a321556b6738e90fac306746fa913a31980a76baa36d29.json
  39. 0 0
      graphify-out/cache/ast/3c10bddab47cd3eab19e26088b30c16bf9366265463edc48c85cf290dad30716.json
  40. 0 0
      graphify-out/cache/ast/3e3bcaa336740aa5ba98b542d06402290166575a8c9cc8ecef71c1b7401e3715.json
  41. 1 0
      graphify-out/cache/ast/3fe3ec0c93c58893d23492254b4e131fd3f2100745af07ac94518771acbb1b43.json
  42. 1 0
      graphify-out/cache/ast/41403c17fc55664d907c5115d27f1e73616188a25d35677fe2edfc218fdb61c5.json
  43. 0 0
      graphify-out/cache/ast/424c07123d61cc00326f871011fd03b8e24becc4061f6d09fed6c7f286889a6b.json
  44. 1 0
      graphify-out/cache/ast/4297ae6fd0861837a1575cc4deba0dbaaaf54d6411d9a044607d62dccb29b89d.json
  45. 1 0
      graphify-out/cache/ast/482184b8bc7f99b1e839c1149677e8d50d72289afdb901144663ede637850ada.json
  46. 1 0
      graphify-out/cache/ast/48eed0adc2ada8a1b60391dbe68ad28775973469f0261b65306ce6e38a329edc.json
  47. 0 0
      graphify-out/cache/ast/4914bef0ffda07d7727b48838103125a3b76d4e2d6d32a3981df7bbf96811a77.json
  48. 0 0
      graphify-out/cache/ast/4a2f0324b349b4785c1581b3de79875aea98dcae9634b0616568c31738e10237.json
  49. 0 0
      graphify-out/cache/ast/4b8c397072e46e64a2b234c1da387edf03a7c62844a6314313b771a6c2c690d8.json
  50. 0 0
      graphify-out/cache/ast/4eefb920f602906f55603fa6e662f64ede7505cc15a5cc0c74e61cd92719fbff.json
  51. 0 0
      graphify-out/cache/ast/51545e8f8c5a3d4a0e8e2274dd87fdccec8eeeb176c1d4ad069b5a83bafbf692.json
  52. 1 0
      graphify-out/cache/ast/53d42ffb123417688b2160fa1f141623a3c25409693df853c16088a24ac2e032.json
  53. 1 0
      graphify-out/cache/ast/57f3e06ead5f05ffa6575a7a6803904ee266c7bfae79e2fec2432d359d15215e.json
  54. 1 0
      graphify-out/cache/ast/59bbbda722444f01b255352e3506741f69a6ece712b98d17c335f375320df1d6.json
  55. 0 0
      graphify-out/cache/ast/5cdca2518286b07013bbe3e1939de7caf7c7ae01253409068a441ba779bc5aac.json
  56. 0 0
      graphify-out/cache/ast/5cf1bda088836de1dee8c2ee1fa4ca079b8367a225b3f2b0c98721417108285d.json
  57. 1 0
      graphify-out/cache/ast/5dd1761a188f6a7ad57333d4eb8a1b77b70d008cafdc41262a24af8512b13dd0.json
  58. 0 0
      graphify-out/cache/ast/613180733590d1aa9c38862f42d9327d10070483d85fe7d0e17dfdd868c1ab77.json
  59. 1 0
      graphify-out/cache/ast/62ffaf3b0cba0accd7fe818587d9f27cab5edb1255b7533b77d64c6476748a78.json
  60. 0 0
      graphify-out/cache/ast/670e1253f384a2b6e4927b29b8afc4d08121658b797eb3cd476023ff2b95ab2f.json
  61. 1 0
      graphify-out/cache/ast/68961ae1da0d6d0192620e74e3362cc930fe0b2c6aa13dbcc8705fab05579311.json
  62. 1 0
      graphify-out/cache/ast/6a0364ad197dc1ecf1b06cb7b4bc1603836411540825ca18424a476c52953541.json
  63. 1 0
      graphify-out/cache/ast/6f5b7d255e649c53a2bc93ed7ef41a296aae246d5226d520ffec00b40fe3f169.json
  64. 0 0
      graphify-out/cache/ast/7211d0ad4dff0c5d62f0bc28c797b25fce9d2ea5232e2a442acdcd20aa8ca4b9.json
  65. 1 0
      graphify-out/cache/ast/7252776330420dd3411b6fabfb6a87d890ce91ef6f64548b8dbb5d4764d776db.json
  66. 0 0
      graphify-out/cache/ast/7330c7a04eff213bfb3388f39dc5a1cd8b6f42ddec96430893d60c6b99ce6c53.json
  67. 0 0
      graphify-out/cache/ast/74b75579e97e0f3b426ec7d4a1a406a15ab3310b45e85a4c1481d06d38bf48e0.json
  68. 1 0
      graphify-out/cache/ast/7563bc6803f55a408a37a710fd775318ec68af1f2400a0708b1959cb9cdaf02c.json
  69. 1 0
      graphify-out/cache/ast/7a7587dfe4007636860143aff5d11845bd2af2d999bd670e59090ecf1585b3bb.json
  70. 0 0
      graphify-out/cache/ast/7ae95b0a948286406744eeb53662fa9230c05f83c41dd4fd09cd3f51c4eddc29.json
  71. 1 0
      graphify-out/cache/ast/8144437e953a225fc7145ff49a6f870ce27b055d1c34283b13e22bdb76def0fd.json
  72. 1 0
      graphify-out/cache/ast/8295d0cd36708fe4d903ea8fb9975cefb51a80b5e5dc6bd3cb5f3310f0a2c4c9.json
  73. 1 0
      graphify-out/cache/ast/835b234195ee1ec13033bf0d81fac96891f1285aca90e23dff582694ecc6b3f8.json
  74. 0 0
      graphify-out/cache/ast/872ce9f0ef52cb5a66cc4442ca6906808403d3be99f332dd2a54e6f7b3b85a79.json
  75. 1 0
      graphify-out/cache/ast/8b935afbb3818ac91d066563437f6dbcf168db965565b1be8da823908cad7f7e.json
  76. 1 0
      graphify-out/cache/ast/8fecac71f5f595fa374e24d9745e127fdbb9e0d01952fa3c3bd0ad15e09466d6.json
  77. 0 0
      graphify-out/cache/ast/915da8c2a98052cc08ca6aee0f051e1d22545371846a8fe555bf396949a3297a.json
  78. 1 0
      graphify-out/cache/ast/95c7c9f6b4a245f77561c5fb860aacac365a2d1ee16caa279ae24e21806e74bb.json
  79. 1 0
      graphify-out/cache/ast/9796b30a84e614aa9bed210c5f70aa1f497540f58705e831aa993439100ec163.json
  80. 0 0
      graphify-out/cache/ast/97b56338a99786c3c0bef9a8671bc2be0aa52812f22ce8f29a92b5b61868f9c0.json
  81. 0 0
      graphify-out/cache/ast/9802e2537c0e13925406547284dcf766ef6c8e5efa3ef8022384a3adb9b65662.json
  82. 1 0
      graphify-out/cache/ast/9c1fc911ad53e4ffae2aa91961ee04ed56f1ef7ddbc206583cb1f753ae9281bc.json
  83. 1 0
      graphify-out/cache/ast/9d962aa9637e8a5935b44e2df66fcdba12663eb2932b79eea0129d0ad0cdae04.json
  84. 1 0
      graphify-out/cache/ast/9dbf0f57903ce35b355bd73a3b5a8bf143cd003e2f6976654c0765d5b14fd188.json
  85. 0 0
      graphify-out/cache/ast/9df2f1e70a9139e0229e45e1bc2ef6d35f55cea15f43856f0b7b130ad871b2c5.json
  86. 0 0
      graphify-out/cache/ast/9ea870d7c9faac46f749fa5e658c857a326491a4d4e3068360a268aa6fe1ef62.json
  87. 0 0
      graphify-out/cache/ast/a1f29c628493a3e9a73dd3df88234e3c28d52f28989ab208119321ce167b2042.json
  88. 0 0
      graphify-out/cache/ast/ac7d3bbb366d88a4844ef3ed4582217b3eadcdd5f464a5c12050d28f8281c513.json
  89. 1 0
      graphify-out/cache/ast/b390c77bd3a6e7f02539b80f294e9bfc65ce54ee6e978fa4bf9461fffbd2a204.json
  90. 1 0
      graphify-out/cache/ast/b3f70fad95150d9b7bb308bd6343c218228baca46feb6bf56f7c3ee4d1a13e4c.json
  91. 0 0
      graphify-out/cache/ast/b49e65a524a0634120f2145c9251ca327136f01c023263ccbb8b98cf5eb630c2.json
  92. 1 0
      graphify-out/cache/ast/b5ea9ec5fb1ab05e89af5b033d2b08c6facb2682b8c399d718d9fb8ad40f7bdc.json
  93. 1 0
      graphify-out/cache/ast/b6cc899ebdaea9d0609c91ddf859e52c5e437dfcea090a883049829484b05ffe.json
  94. 0 0
      graphify-out/cache/ast/bbdce7ec1a34b188ec5f44a9ca7defa8470fc43c2ae262236f3200b22226c034.json
  95. 0 0
      graphify-out/cache/ast/bd7d3198c76c66e84878ef70fcdbe4b747222a59e2a7cb6e55fb0989704f8b3b.json
  96. 0 0
      graphify-out/cache/ast/c006333a011c9e0be55d2de71f5945492aee3850f4a64a4dde09b6954020dc73.json
  97. 0 0
      graphify-out/cache/ast/c1f2ea4bc27e5eeaba1320f8c40f21d689820e18bed4313ae3c60833ac03005e.json
  98. 1 0
      graphify-out/cache/ast/c4044d8217ce145d8ef717698762404b35c114495c14094463436302048b5a9d.json
  99. 0 0
      graphify-out/cache/ast/c51d75bc316581a3b242f365f6363bfb9a1aee4d57256a62050813e6a3be327d.json
  100. 0 0
      graphify-out/cache/ast/c56db68b46d598bd6a29ebb3941ba6717ca61e2115c7cf8a92ddd3fad301ca77.json

+ 102 - 39
backend/app/api/admin.py

@@ -852,6 +852,59 @@ def _get_user_region_scope_ids(db: Session, user_id: str) -> list[str]:
     return uniq
 
 
+def _batch_scope_region_ids_by_user(db: Session, user_ids: list[str]) -> dict[str, set[str]]:
+    """批量读取用户区域授权 scope 中的 region_id(未删除),按 user_id 分组。"""
+    if not user_ids:
+        return {}
+    rows = (
+        db.query(SysUserRegionScope.user_id, SysUserRegionScope.region_id)
+        .filter(
+            SysUserRegionScope.user_id.in_(user_ids),
+            SysUserRegionScope.is_del == 0,
+        )
+        .all()
+    )
+    out: dict[str, set[str]] = {}
+    for uid, rid in rows:
+        if not uid or not rid:
+            continue
+        us = str(uid).strip()
+        rs = str(rid).strip()
+        if not us or not rs:
+            continue
+        out.setdefault(us, set()).add(rs)
+    return out
+
+
+def _user_display_region_ids(user: SysUser, scopes_by_user: dict[str, set[str]]) -> list[str]:
+    """列表/详情展示用:授权 scope 中的区域 + 用户主 region_id 并集,去重排序。"""
+    uid = str(user.id).strip()
+    ids = set(scopes_by_user.get(uid, set()))
+    if user.region_id:
+        rid = str(user.region_id).strip()
+        if rid:
+            ids.add(rid)
+    ids.discard("")
+    return sorted(ids)
+
+
+def _join_region_names(region_ids: list[str], id_to_name: dict[str, str]) -> Optional[str]:
+    names = [id_to_name[i] for i in region_ids if id_to_name.get(i)]
+    return ", ".join(names) if names else None
+
+
+def _region_names_csv(db: Session, region_ids: list[str]) -> Optional[str]:
+    if not region_ids:
+        return None
+    regs = (
+        db.query(SysRegion.id, SysRegion.name)
+        .filter(SysRegion.id.in_(region_ids), SysRegion.is_del == 0)
+        .all()
+    )
+    m = {r.id: r.name for r in regs}
+    return _join_region_names(region_ids, m)
+
+
 def _replace_user_region_scopes(
     db: Session,
     user_id: str,
@@ -1172,14 +1225,21 @@ def list_users(
         skip=(page - 1) * pageSize,
         limit=pageSize
     )
-    
-    region_map = {}
-    for user in users:
-        if user.region_id and user.region_id not in region_map:
-            region = region_crud.get(db, user.region_id)
-            if region:
-                region_map[user.region_id] = region.name
-    
+
+    uid_list = [u.id for u in users]
+    scopes_by_user = _batch_scope_region_ids_by_user(db, uid_list)
+    all_region_ids: set[str] = set()
+    for u in users:
+        all_region_ids.update(_user_display_region_ids(u, scopes_by_user))
+    id_to_name: dict[str, str] = {}
+    if all_region_ids:
+        regs = (
+            db.query(SysRegion.id, SysRegion.name)
+            .filter(SysRegion.id.in_(list(all_region_ids)), SysRegion.is_del == 0)
+            .all()
+        )
+        id_to_name = {r.id: r.name for r in regs}
+
     return success_response({
         "list": [{
             "id": u.id,
@@ -1189,8 +1249,8 @@ def list_users(
             "avatarUrl": (str(u.avatar_url).strip() if getattr(u, "avatar_url", None) else None) or None,
             "role": u.role,
             "regionId": u.region_id,
-            "regionName": region_map.get(u.region_id),
-            "regionIds": _get_user_region_scope_ids(db, u.id),
+            "regionName": _join_region_names(_user_display_region_ids(u, scopes_by_user), id_to_name),
+            "regionIds": sorted(scopes_by_user.get(str(u.id).strip(), set())),
             "isActive": u.is_active,
             "createAt": str(u.create_at) if u.create_at else None,
             "createName": getattr(u, "create_name", None)
@@ -1213,11 +1273,9 @@ def get_user(
     if not user:
         return error_response(404, "用户不存在")
     
-    region_name = None
-    if user.region_id:
-        region = region_crud.get(db, user.region_id)
-        region_name = region.name if region else None
-    
+    scopes_by_user = _batch_scope_region_ids_by_user(db, [user.id])
+    region_name = _region_names_csv(db, _user_display_region_ids(user, scopes_by_user))
+
     return success_response({
         "id": user.id,
         "username": user.username,
@@ -1296,15 +1354,13 @@ def update_user(
     if not user:
         return error_response(404, "用户不存在")
     
-    region_name = None
-    if user.region_id:
-        region = region_crud.get(db, user.region_id)
-        region_name = region.name if region else None
-    
     if isinstance(region_ids, list):
         _replace_user_region_scopes(db, user_id, region_ids, current_user.get("user_id"), current_user.get("real_name"))
         db.commit()
 
+    scopes_by_user = _batch_scope_region_ids_by_user(db, [user.id])
+    region_name = _region_names_csv(db, _user_display_region_ids(user, scopes_by_user))
+
     return success_response({
         "id": user.id,
         "username": user.username,
@@ -1548,35 +1604,42 @@ def list_products(
         keyword=keyword,
         is_active=isActive,
         skip=(page - 1) * pageSize,
-        limit=pageSize
+        limit=pageSize,
+        defer_detail_content=True,
     )
-    
-    # 获取分类和单位名称映射
-    category_map = {}
-    unit_map = {}
-    
-    for p in products:
-        if p.category_id and p.category_id not in category_map:
-            cat = category_crud.get(db, p.category_id)
-            if cat:
-                category_map[p.category_id] = cat.name
-        if p.unit_id and p.unit_id not in unit_map:
-            u = unit_crud.get(db, p.unit_id)
-            if u:
-                unit_map[p.unit_id] = u.name
-    
+
+    category_ids = {p.category_id for p in products if p.category_id}
+    unit_ids = {p.unit_id for p in products if p.unit_id}
+    category_map: dict[str, str] = {}
+    unit_map: dict[str, str] = {}
+    if category_ids:
+        rows = (
+            db.query(BasProductCategory.id, BasProductCategory.name)
+            .filter(BasProductCategory.id.in_(category_ids))
+            .all()
+        )
+        category_map = {str(r.id): r.name for r in rows}
+    if unit_ids:
+        rows = (
+            db.query(BasProductUnit.id, BasProductUnit.name)
+            .filter(BasProductUnit.id.in_(unit_ids))
+            .all()
+        )
+        unit_map = {str(r.id): r.name for r in rows}
+
     return success_response({
         "list": [{
             "id": p.id,
             "name": p.name,
             "skuCode": p.sku_code,
             "categoryId": p.category_id,
-            "categoryName": category_map.get(p.category_id),
+            "categoryName": category_map.get(str(p.category_id)) if p.category_id else None,
             "unitId": p.unit_id,
-            "unitName": unit_map.get(p.unit_id),
+            "unitName": unit_map.get(str(p.unit_id)) if p.unit_id else None,
             "measureValue": _numeric_to_float_optional(getattr(p, "measure_value", None)),
             "description": p.description,
-            "detailContent": p.detail_content,
+            # 列表不加载 detail_content(TEXT);详情见 GET /products/{id}
+            "detailContent": None,
             "imageUrl": p.image_url,
             "priceCoefficient": _numeric_to_float_optional(getattr(p, "price_coefficient", None)) or 0.0,
             "isActive": p.is_active,

+ 32 - 11
backend/app/api/product_banner.py

@@ -1,19 +1,18 @@
 """API - 商品轮播图管理"""
-from typing import Optional, List
 
-from fastapi import APIRouter, Depends, HTTPException, Query
+from fastapi import APIRouter, Depends, HTTPException, Request
+from pydantic import ValidationError
 from sqlalchemy.orm import Session
 
 from app.core.database import get_db
 from app.core.auth import get_current_user
+from app.core.admin_request_json import parse_admin_request_json
 from app.core.response import success_response, error_response
 from app.crud.product_banner import product_banner
 from app.schemas.product_banner import (
     ProductBannerCreate,
     ProductBannerUpdate,
     ProductBannerSortUpdate,
-    ProductBannerResponse,
-    ProductBannerListResponse
 )
 
 router = APIRouter(prefix="/web/admin/product-banners", tags=["商品轮播图管理"])
@@ -49,15 +48,26 @@ def list_banners(
 
 
 @router.post("/product/{product_id}", response_model=dict)
-def create_banners(
+async def create_banners(
     product_id: str,
-    items: List[ProductBannerCreate],
+    request: Request,
     db: Session = Depends(get_db),
     current_user: dict = Depends(require_admin)
 ):
-    """批量添加轮播图"""
+    """批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景)"""
+    items_raw, err = await parse_admin_request_json(
+        request, current_user, source="admin_product_banner"
+    )
+    if err:
+        return error_response(400, err)
+    if not isinstance(items_raw, list):
+        return error_response(400, "请求体应为轮播图条目数组")
+    try:
+        items = [ProductBannerCreate.model_validate(x) for x in items_raw]
+    except ValidationError as exc:
+        return error_response(400, str(exc))
+
     creator = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
-    
     items_data = [item.model_dump() for item in items]
     banners = product_banner.create_batch(db, product_id, items_data, creator)
     
@@ -75,13 +85,24 @@ def create_banners(
 
 
 @router.put("/product/{product_id}/sort", response_model=dict)
-def update_sort(
+async def update_sort(
     product_id: str,
-    sort_data: ProductBannerSortUpdate,
+    request: Request,
     db: Session = Depends(get_db),
     current_user: dict = Depends(require_admin)
 ):
-    """更新轮播图排序"""
+    """更新轮播图排序(兼容密文信封)"""
+    data, err = await parse_admin_request_json(
+        request, current_user, source="admin_product_banner_sort"
+    )
+    if err:
+        return error_response(400, err)
+    if not isinstance(data, dict):
+        return error_response(400, "请求体应为 JSON 对象且包含 items")
+    try:
+        sort_data = ProductBannerSortUpdate.model_validate(data)
+    except ValidationError as exc:
+        return error_response(400, str(exc))
     items_data = [item.model_dump() for item in sort_data.items]
     product_banner.update_sort(db, items_data)
     return success_response({"message": "排序更新成功"})

+ 32 - 11
backend/app/api/product_detail_media.py

@@ -1,19 +1,18 @@
 """API - 商品详情媒体管理"""
-from typing import Optional, List
 
-from fastapi import APIRouter, Depends, HTTPException, Query
+from fastapi import APIRouter, Depends, HTTPException, Request
+from pydantic import ValidationError
 from sqlalchemy.orm import Session
 
 from app.core.database import get_db
 from app.core.auth import get_current_user
+from app.core.admin_request_json import parse_admin_request_json
 from app.core.response import success_response, error_response
 from app.crud.product_detail_media import product_detail_media
 from app.schemas.product_detail_media import (
     ProductDetailMediaCreate,
     ProductDetailMediaUpdate,
     ProductDetailMediaSortUpdate,
-    ProductDetailMediaResponse,
-    ProductDetailMediaListResponse
 )
 
 router = APIRouter(prefix="/web/admin/product-detail-media", tags=["商品详情媒体管理"])
@@ -51,15 +50,26 @@ def list_detail_media(
 
 
 @router.post("/product/{product_id}", response_model=dict)
-def create_detail_media(
+async def create_detail_media(
     product_id: str,
-    items: List[ProductDetailMediaCreate],
+    request: Request,
     db: Session = Depends(get_db),
     current_user: dict = Depends(require_admin)
 ):
-    """批量添加详情媒体"""
+    """批量添加详情媒体(请求体为 JSON 数组;兼容密文信封)"""
+    items_raw, err = await parse_admin_request_json(
+        request, current_user, source="admin_product_detail_media"
+    )
+    if err:
+        return error_response(400, err)
+    if not isinstance(items_raw, list):
+        return error_response(400, "请求体应为详情媒体条目数组")
+    try:
+        items = [ProductDetailMediaCreate.model_validate(x) for x in items_raw]
+    except ValidationError as exc:
+        return error_response(400, str(exc))
+
     creator = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
-    
     items_data = [item.model_dump() for item in items]
     media_list = product_detail_media.create_batch(db, product_id, items_data, creator)
     
@@ -79,13 +89,24 @@ def create_detail_media(
 
 
 @router.put("/product/{product_id}/sort", response_model=dict)
-def update_sort(
+async def update_sort(
     product_id: str,
-    sort_data: ProductDetailMediaSortUpdate,
+    request: Request,
     db: Session = Depends(get_db),
     current_user: dict = Depends(require_admin)
 ):
-    """更新详情媒体排序"""
+    """更新详情媒体排序(兼容密文信封)"""
+    data, err = await parse_admin_request_json(
+        request, current_user, source="admin_product_detail_media_sort"
+    )
+    if err:
+        return error_response(400, err)
+    if not isinstance(data, dict):
+        return error_response(400, "请求体应为 JSON 对象且包含 items")
+    try:
+        sort_data = ProductDetailMediaSortUpdate.model_validate(data)
+    except ValidationError as exc:
+        return error_response(400, str(exc))
     items_data = [item.model_dump() for item in sort_data.items]
     product_detail_media.update_sort(db, items_data)
     return success_response({"message": "排序更新成功"})

+ 21 - 11
backend/app/api/product_parameter_value.py

@@ -1,18 +1,15 @@
 """API - 商品参数值管理"""
-from typing import List
 
-from fastapi import APIRouter, Depends, HTTPException
+from fastapi import APIRouter, Depends, HTTPException, Request
+from pydantic import ValidationError
 from sqlalchemy.orm import Session
 
 from app.core.database import get_db
 from app.core.auth import get_current_user
+from app.core.admin_request_json import parse_admin_request_json
 from app.core.response import success_response, error_response
 from app.crud.product_parameter_value import product_parameter_value
-from app.schemas.product_parameter_value import (
-    ProductParameterValueBatchSave,
-    ProductParameterValueResponse,
-    ProductParameterValueListResponse
-)
+from app.schemas.product_parameter_value import ProductParameterValueBatchSave
 
 router = APIRouter(prefix="/web/admin/product-parameter-values", tags=["商品参数值管理"])
 
@@ -40,15 +37,28 @@ def get_product_parameter_values(
 
 
 @router.post("/product/{product_id}", response_model=dict)
-def save_product_parameter_values(
+async def save_product_parameter_values(
     product_id: str,
-    save_data: ProductParameterValueBatchSave,
+    request: Request,
     db: Session = Depends(get_db),
     current_user: dict = Depends(require_admin)
 ):
-    """批量保存商品参数值"""
+    """批量保存商品参数值(兼容密文信封)"""
+    data, err = await parse_admin_request_json(
+        request, current_user, source="admin_product_parameter_values"
+    )
+    if err:
+        return error_response(400, err)
+    if not isinstance(data, dict):
+        return error_response(400, "请求体格式错误")
+    try:
+        save_data = ProductParameterValueBatchSave.model_validate(data)
+    except ValidationError as exc:
+        return error_response(400, str(exc))
+    if save_data.product_id != product_id:
+        return error_response(400, "请求体中的商品ID与路径不一致")
+
     creator = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
-    
     values_data = [item.model_dump() for item in save_data.values]
     product_parameter_value.batch_save(db, product_id, values_data, creator)
     

+ 35 - 0
backend/app/core/admin_request_json.py

@@ -0,0 +1,35 @@
+"""运营 Web(/web/admin)JSON 请求体解析。
+
+与 ApiCryptoMiddleware 配合:中间件未改写 body 时,在路由内解密 `{ enc, ts, nonce, edata }`
+并剥 `{ data: ... }`,避免 FastAPI 声明式 Body 在校验阶段只看到密文信封而 422。
+"""
+
+from __future__ import annotations
+
+from typing import Any, Optional, Tuple
+
+from fastapi import Request
+
+from app.core.request_payload import resolve_request_payload
+
+
+async def parse_admin_request_json(
+    request: Request,
+    current_user: dict,
+    *,
+    source: str,
+) -> Tuple[Any, Optional[str]]:
+    """
+    读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。
+
+    Returns:
+        (payload, None) 成功;
+        (_, err_msg) 密文解密失败(此时 payload 为原始 raw,调用方可忽略)。
+    """
+    raw: Any = await request.json()
+    if isinstance(raw, dict):
+        resolved = resolve_request_payload(raw, current_user, source=source)
+        if resolved == {} and int(raw.get("enc", 0) or 0) == 1:
+            return raw, "密文解密失败"
+        return resolved, None
+    return raw, None

+ 7 - 4
backend/app/core/api_crypto_middleware.py

@@ -165,15 +165,18 @@ class ApiCryptoMiddleware(BaseHTTPMiddleware):
 
         override_method = request.headers.get("x-http-method-override", "").upper()
         payload = plain.get("data", plain)
-        if not isinstance(payload, dict):
-            payload = {}
 
         if override_method == "GET":
             request.scope["method"] = "GET"
-            request.scope["query_string"] = urlencode(payload, doseq=True).encode("utf-8")
+            query_dict = payload if isinstance(payload, dict) else {}
+            request.scope["query_string"] = urlencode(query_dict, doseq=True).encode("utf-8")
             body_for_route = b""
         else:
-            body_for_route = json.dumps(payload, ensure_ascii=False).encode("utf-8")
+            # JSON body 可能是对象(多数 CRUD)或数组(如批量轮播图 POST)
+            if isinstance(payload, (dict, list)):
+                body_for_route = json.dumps(payload, ensure_ascii=False).encode("utf-8")
+            else:
+                body_for_route = json.dumps({}, ensure_ascii=False).encode("utf-8")
 
         async def receive():
             return {"type": "http.request", "body": body_for_route, "more_body": False}

+ 7 - 5
backend/app/core/request_payload.py

@@ -3,7 +3,7 @@
 from __future__ import annotations
 
 import logging
-from typing import Any
+from typing import Any, Union
 
 from app.core import api_crypto
 from app.core.config import settings
@@ -14,8 +14,8 @@ def resolve_request_payload(
     current_user: dict,
     *,
     source: str = "unknown",
-) -> dict:
-    """兼容明文与密文信封,返回业务层 payload。"""
+) -> Union[dict, list]:
+    """兼容明文与密文信封,返回业务层 payload(多为 dict;批量数组接口解密后为 list)。"""
     logger = logging.getLogger("sqxp")
     payload = request_body if isinstance(request_body, dict) else {}
     if int(payload.get("enc", 0) or 0) == 1 and isinstance(payload.get("edata"), str):
@@ -43,7 +43,9 @@ def resolve_request_payload(
         except Exception as exc:
             logger.warning("%s resolve_request_payload decrypt failed: err=%s", source, exc)
             return {}
-    if isinstance(payload.get("data"), dict):
-        payload = payload.get("data")
+    if isinstance(payload, dict):
+        inner = payload.get("data")
+        if isinstance(inner, (dict, list)):
+            return inner
     return payload if isinstance(payload, dict) else {}
 

+ 25 - 12
backend/app/crud/product.py

@@ -1,7 +1,7 @@
 """商品CRUD"""
 from typing import Optional, List
-from sqlalchemy.orm import Session
-from sqlalchemy import and_
+from sqlalchemy.orm import Session, defer
+from sqlalchemy import and_, func, or_
 
 from app.models.models import BasProduct, BasProductHis
 from app.crud.base import CRUDBase
@@ -35,22 +35,35 @@ class ProductCRUD(CRUDBase[BasProduct]):
         keyword: str = None,
         is_active: bool = None,
         skip: int = 0,
-        limit: int = 10
+        limit: int = 10,
+        *,
+        defer_detail_content: bool = False,
     ) -> tuple[List[BasProduct], int]:
         """多条件查询商品"""
-        query = db.query(BasProduct).filter(BasProduct.is_del == 0)
-        
+        filters = [BasProduct.is_del == 0]
         if category_id:
-            query = query.filter(BasProduct.category_id == category_id)
+            filters.append(BasProduct.category_id == category_id)
         if is_active is not None:
-            query = query.filter(BasProduct.is_active == is_active)
+            filters.append(BasProduct.is_active == is_active)
         if keyword:
-            query = query.filter(
-                (BasProduct.name.like(f"%{keyword}%")) |
-                (BasProduct.description.like(f"%{keyword}%"))
+            filters.append(
+                or_(
+                    BasProduct.name.like(f"%{keyword}%"),
+                    BasProduct.description.like(f"%{keyword}%"),
+                )
             )
-        
-        total = query.count()
+
+        total = (
+            db.query(func.count(BasProduct.id))
+            .filter(*filters)
+            .order_by(None)
+            .scalar()
+            or 0
+        )
+
+        query = db.query(BasProduct).filter(*filters)
+        if defer_detail_content:
+            query = query.options(defer(BasProduct.detail_content))
         products = query.order_by(BasProduct.create_at.desc()).offset(skip).limit(limit).all()
         return products, total
 

+ 1 - 1
backend/app/crud/product_parameter.py

@@ -1,7 +1,7 @@
 """CRUD - 商品参数模板"""
 from typing import List, Optional
 from sqlalchemy.orm import Session
-from sqlalchemy import and_
+from sqlalchemy import or_
 
 from app.crud.base import CRUDBase, HistoryCRUD
 from app.models.product_parameter import BasProductParameter, BasProductParameterHis

+ 12 - 0
backend/scripts/add_bas_product_list_indexes.sql

@@ -0,0 +1,12 @@
+-- 给 /api/web/admin/products 列表(GET,前端加密传输时为 POST + X-HTTP-Method-Override: GET)做索引补强
+-- 典型 SQL:WHERE is_del = 0 [AND category_id = ?] [AND (name LIKE ... OR description LIKE ...)] ORDER BY create_at DESC
+-- LIKE '%keyword%' 无法有效走 name/description 索引;无关键词时下面索引可帮助 COUNT + 分页排序。
+-- 执行前请先 SHOW INDEX FROM bas_product; 若索引已存在可跳过对应语句。
+
+-- 1) 全量列表 / 仅启用筛选:is_del + 排序字段
+ALTER TABLE bas_product
+  ADD INDEX idx_product_isdel_createat (is_del, create_at);
+
+-- 2) 按分类筛选 + 排序(与 1 并存;分类列表走此索引更省扫描)
+ALTER TABLE bas_product
+  ADD INDEX idx_product_isdel_category_createat (is_del, category_id, create_at);

+ 115 - 115
graphify-out/GRAPH_REPORT.md

@@ -1,12 +1,12 @@
-# Graph Report - sqxp_副本2  (2026-04-30)
+# Graph Report - sqxp_副本2  (2026-05-02)
 
 ## Corpus Check
-- 701 files · ~640,190 words
+- 702 files · ~642,948 words
 - Verdict: corpus is large enough that graph structure adds value.
 
 ## Summary
-- 4916 nodes · 25358 edges · 65 communities detected
-- Extraction: 20% EXTRACTED · 80% INFERRED · 0% AMBIGUOUS · INFERRED: 20196 edges (avg confidence: 0.55)
+- 4980 nodes · 26716 edges · 65 communities detected
+- Extraction: 19% EXTRACTED · 81% INFERRED · 0% AMBIGUOUS · INFERRED: 21528 edges (avg confidence: 0.54)
 - Token cost: 0 input · 0 output
 
 ## Community Hubs (Navigation)
@@ -30,7 +30,7 @@
 - [[_COMMUNITY_Community 17|Community 17]]
 - [[_COMMUNITY_Community 18|Community 18]]
 - [[_COMMUNITY_Community 19|Community 19]]
-- [[_COMMUNITY_Community 22|Community 22]]
+- [[_COMMUNITY_Community 20|Community 20]]
 - [[_COMMUNITY_Community 23|Community 23]]
 - [[_COMMUNITY_Community 24|Community 24]]
 - [[_COMMUNITY_Community 25|Community 25]]
@@ -46,47 +46,47 @@
 - [[_COMMUNITY_Community 38|Community 38]]
 - [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 40|Community 40]]
-- [[_COMMUNITY_Community 45|Community 45]]
-- [[_COMMUNITY_Community 47|Community 47]]
+- [[_COMMUNITY_Community 46|Community 46]]
 - [[_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 54|Community 54]]
+- [[_COMMUNITY_Community 53|Community 53]]
 - [[_COMMUNITY_Community 55|Community 55]]
-- [[_COMMUNITY_Community 59|Community 59]]
-- [[_COMMUNITY_Community 62|Community 62]]
+- [[_COMMUNITY_Community 56|Community 56]]
+- [[_COMMUNITY_Community 60|Community 60]]
 - [[_COMMUNITY_Community 63|Community 63]]
-- [[_COMMUNITY_Community 65|Community 65]]
+- [[_COMMUNITY_Community 64|Community 64]]
+- [[_COMMUNITY_Community 66|Community 66]]
 - [[_COMMUNITY_Community 78|Community 78]]
 - [[_COMMUNITY_Community 88|Community 88]]
 - [[_COMMUNITY_Community 90|Community 90]]
+- [[_COMMUNITY_Community 142|Community 142]]
 - [[_COMMUNITY_Community 143|Community 143]]
 - [[_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 415|Community 415]]
 - [[_COMMUNITY_Community 416|Community 416]]
 - [[_COMMUNITY_Community 417|Community 417]]
 - [[_COMMUNITY_Community 418|Community 418]]
 - [[_COMMUNITY_Community 419|Community 419]]
 - [[_COMMUNITY_Community 420|Community 420]]
 - [[_COMMUNITY_Community 421|Community 421]]
-- [[_COMMUNITY_Community 422|Community 422]]
 
 ## God Nodes (most connected - your core abstractions)
-1. `BizPriceListItem` - 753 edges
-2. `BizInquiry` - 682 edges
-3. `BizQuotation` - 680 edges
-4. `BizAttachment` - 670 edges
-5. `BasProduct` - 669 edges
-6. `SysUser` - 660 edges
-7. `BasProductCategory` - 659 edges
-8. `BasProductUnit` - 659 edges
-9. `BizDeliveryTask` - 652 edges
-10. `BizOrder` - 649 edges
+1. `BizPriceListItem` - 800 edges
+2. `BizInquiry` - 729 edges
+3. `BizQuotation` - 727 edges
+4. `BizAttachment` - 717 edges
+5. `BasProduct` - 716 edges
+6. `SysUser` - 707 edges
+7. `BasProductCategory` - 706 edges
+8. `BasProductUnit` - 706 edges
+9. `BizDeliveryTask` - 699 edges
+10. `BizOrder` - 696 edges
 
 ## Surprising Connections (you probably didn't know these)
 - `get_active()` --calls--> `query()`  [INFERRED]
@@ -97,99 +97,99 @@
   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
+- `showMenu()` --calls--> `nextTick()`  [INFERRED]
+  web-admin/src/views/widgets/context-menu/index.vue → sqxp-wx/wxcomponents/vant/common/utils.js
 
 ## Communities
 
 ### Community 0 - "Community 0"
 Cohesion: 0.01
-Nodes (815): 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() (+807 more)
+Nodes (826): 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() (+818 more)
 
 ### Community 1 - "Community 1"
 Cohesion: 0.07
-Nodes (665): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。 (+657 more)
+Nodes (710): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。 (+702 more)
 
 ### Community 2 - "Community 2"
 Cohesion: 0.01
-Nodes (196): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+188 more)
+Nodes (205): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+197 more)
 
 ### Community 3 - "Community 3"
 Cohesion: 0.01
-Nodes (157): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+149 more)
+Nodes (205): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+197 more)
 
 ### Community 4 - "Community 4"
 Cohesion: 0.01
-Nodes (247): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 兼容明文/密文请求,返回业务 payload。失败时返回 error_response。, BaseModel (+239 more)
+Nodes (158): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+150 more)
 
 ### Community 5 - "Community 5"
 Cohesion: 0.01
-Nodes (133): fetchActiveRegions(), fetchAssignableMenuCodes(), fetchCategoryList(), fetchCategoryParameters(), fetchCategoryTree(), fetchCreateUnit(), fetchNotDeletedRegions(), fetchProductBannerList() (+125 more)
+Nodes (274): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+266 more)
 
 ### Community 6 - "Community 6"
-Cohesion: 0.01
-Nodes (130): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), disableDevTools(), handleLock() (+122 more)
+Cohesion: 0.02
+Nodes (63): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+55 more)
 
 ### Community 7 - "Community 7"
-Cohesion: 0.02
-Nodes (64): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+56 more)
+Cohesion: 0.03
+Nodes (99): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+91 more)
 
 ### Community 8 - "Community 8"
-Cohesion: 0.02
-Nodes (64): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+56 more)
+Cohesion: 0.03
+Nodes (55): handleFormClose(), handleClose(), initForm(), resetFormModel(), _Blur(), created(), init(), inputContentStyle() (+47 more)
 
 ### Community 9 - "Community 9"
-Cohesion: 0.03
-Nodes (92): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+84 more)
+Cohesion: 0.05
+Nodes (77): 批量获取供应商取货地址,避免列表接口逐条查询。, 一级/二级分类用 / 连接(与订单详情一致)。, 价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。, (price_list_id, product_id, unit_id) -> BizPriceListItem, 供应商默认取货地址;无默认时取第一条 pickup 类型地址。, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供 (+69 more)
 
 ### Community 10 - "Community 10"
 Cohesion: 0.03
-Nodes (52): handleFormClose(), _Blur(), created(), init(), inputContentStyle(), inputMaxlength(), inputStyle(), obj2strStyle() (+44 more)
+Nodes (43): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+35 more)
 
 ### Community 11 - "Community 11"
 Cohesion: 0.05
-Nodes (77): 批量获取供应商取货地址,避免列表接口逐条查询。, 一级/二级分类用 / 连接(与订单详情一致)。, 价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。, (price_list_id, product_id, unit_id) -> BizPriceListItem, 供应商默认取货地址;无默认时取第一条 pickup 类型地址。, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供 (+69 more)
+Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
 
 ### Community 12 - "Community 12"
 Cohesion: 0.05
-Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
+Nodes (22): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+14 more)
 
 ### Community 13 - "Community 13"
+Cohesion: 0.09
+Nodes (27): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+19 more)
+
+### Community 14 - "Community 14"
 Cohesion: 0.18
 Nodes (21): destroyed(), getDirection(), getDom(), isPC(), mousedown(), mouseleave(), mousemove(), mouseup() (+13 more)
 
-### Community 14 - "Community 14"
+### Community 15 - "Community 15"
 Cohesion: 0.13
 Nodes (13): change(), clearVal(), created(), debounce(), emit(), formatItemName(), getCache(), handler() (+5 more)
 
-### Community 15 - "Community 15"
+### Community 16 - "Community 16"
 Cohesion: 0.12
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
-### Community 16 - "Community 16"
+### Community 17 - "Community 17"
 Cohesion: 0.17
 Nodes (12): BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation(), OrderLockedException (+4 more)
 
-### Community 17 - "Community 17"
+### Community 18 - "Community 18"
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
-### Community 18 - "Community 18"
+### Community 19 - "Community 19"
 Cohesion: 0.2
 Nodes (14): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+6 more)
 
-### Community 19 - "Community 19"
+### Community 20 - "Community 20"
 Cohesion: 0.28
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 
-### Community 22 - "Community 22"
+### Community 23 - "Community 23"
 Cohesion: 0.17
 Nodes (2): clean(), onInput()
 
-### Community 23 - "Community 23"
-Cohesion: 0.33
-Nodes (11): changeFlag(), countDown(), day(), handler(), hour(), minute(), second(), startData() (+3 more)
-
 ### Community 24 - "Community 24"
 Cohesion: 0.24
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
@@ -246,55 +246,55 @@ Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
 Cohesion: 0.38
 Nodes (3): _change(), close(), open()
 
-### Community 45 - "Community 45"
+### Community 46 - "Community 46"
 Cohesion: 0.33
 Nodes (1): BridgeError
 
-### Community 47 - "Community 47"
+### Community 48 - "Community 48"
 Cohesion: 0.4
 Nodes (2): created(), getTable()
 
-### Community 48 - "Community 48"
+### Community 49 - "Community 49"
 Cohesion: 0.47
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 
-### Community 49 - "Community 49"
+### Community 50 - "Community 50"
 Cohesion: 0.4
 Nodes (2): created(), getForm()
 
-### Community 50 - "Community 50"
+### Community 51 - "Community 51"
 Cohesion: 0.47
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 
-### Community 51 - "Community 51"
+### Community 52 - "Community 52"
 Cohesion: 0.4
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 
-### Community 52 - "Community 52"
+### Community 53 - "Community 53"
 Cohesion: 0.5
 Nodes (2): focusInput(), handleKeydown()
 
-### Community 54 - "Community 54"
+### Community 55 - "Community 55"
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 
-### Community 55 - "Community 55"
+### Community 56 - "Community 56"
 Cohesion: 0.5
 Nodes (1): collectIds()
 
-### Community 59 - "Community 59"
+### Community 60 - "Community 60"
 Cohesion: 0.83
 Nodes (3): classes(), isTrue(), onClick()
 
-### Community 62 - "Community 62"
+### Community 63 - "Community 63"
 Cohesion: 0.67
 Nodes (2): created(), getParent()
 
-### Community 63 - "Community 63"
+### Community 64 - "Community 64"
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
-### Community 65 - "Community 65"
+### Community 66 - "Community 66"
 Cohesion: 1.0
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 
@@ -310,62 +310,62 @@ Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 Cohesion: 0.67
 Nodes (1): return()
 
-### Community 143 - "Community 143"
+### Community 142 - "Community 142"
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
-### Community 144 - "Community 144"
+### Community 143 - "Community 143"
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
-### Community 145 - "Community 145"
+### Community 144 - "Community 144"
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
-### Community 146 - "Community 146"
+### Community 145 - "Community 145"
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
-### Community 147 - "Community 147"
+### Community 146 - "Community 146"
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
-### Community 148 - "Community 148"
+### Community 147 - "Community 147"
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
-### Community 416 - "Community 416"
+### Community 415 - "Community 415"
 Cohesion: 1.0
 Nodes (1): 按 region_id 管理 websocket 连接并广播。
 
-### Community 417 - "Community 417"
+### Community 416 - "Community 416"
 Cohesion: 1.0
 Nodes (1): 鉴权并接入房间。返回 current_user dict。
 
-### Community 418 - "Community 418"
+### Community 417 - "Community 417"
 Cohesion: 1.0
 Nodes (1): 向 region 房间广播消息。返回尝试发送的连接数。
 
-### Community 419 - "Community 419"
+### Community 418 - "Community 418"
 Cohesion: 1.0
 Nodes (1): 小程序端 WebSocket 入口:/ws/mini
 
-### Community 420 - "Community 420"
+### Community 419 - "Community 419"
 Cohesion: 1.0
 Nodes (1): 在 FastAPI app 上注册 websocket 路由。
 
-### Community 421 - "Community 421"
+### Community 420 - "Community 420"
 Cohesion: 1.0
 Nodes (1): Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。
 
-### Community 422 - "Community 422"
+### Community 421 - "Community 421"
 Cohesion: 1.0
 Nodes (1): 从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。
 
 ## Knowledge Gaps
-- **81 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 非空时表示失败。` (+76 more)
+- **83 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 非空时表示失败。` (+78 more)
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 22`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+- **Thin community `Community 23`** (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 30`** (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.
@@ -377,21 +377,21 @@ Nodes (1): 从请求体解析用户名、密码。     返回 (username, passwor
   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`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 45`** (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 47`** (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 49`** (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 52`** (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 55`** (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 62`** (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 63`** (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 65`** (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 78`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
@@ -399,47 +399,47 @@ Nodes (1): 从请求体解析用户名、密码。     返回 (username, passwor
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 - **Thin community `Community 90`** (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 143`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 142`** (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             ord`
+- **Thin community `Community 143`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 145`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 144`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 146`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 145`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 147`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 146`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 148`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 147`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 416`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
+- **Thin community `Community 415`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 417`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
+- **Thin community `Community 416`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 418`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
+- **Thin community `Community 417`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 419`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
+- **Thin community `Community 418`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 420`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
+- **Thin community `Community 419`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 421`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
+- **Thin community `Community 420`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 422`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
+- **Thin community `Community 421`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
   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 5`, `Community 8`, `Community 10`, `Community 12`?**
-  _High betweenness centrality (0.135) - this node is a cross-community bridge._
-- **Why does `query()` connect `Community 0` to `Community 14`, `Community 31`?**
-  _High betweenness centrality (0.049) - this node is a cross-community bridge._
-- **Why does `number()` connect `Community 5` to `Community 0`, `Community 2`, `Community 6`, `Community 8`, `Community 10`?**
+- **Why does `push()` connect `Community 2` to `Community 0`, `Community 3`, `Community 4`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 12`?**
+  _High betweenness centrality (0.102) - this node is a cross-community bridge._
+- **Why does `number()` connect `Community 3` to `Community 0`, `Community 2`, `Community 8`, `Community 10`, `Community 12`, `Community 13`?**
+  _High betweenness centrality (0.044) - this node is a cross-community bridge._
+- **Why does `BizPriceListItem` connect `Community 1` to `Community 0`, `Community 9`, `Community 7`?**
   _High betweenness centrality (0.041) - this node is a cross-community bridge._
-- **Are the 751 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
-  _`BizPriceListItem` has 751 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 680 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**
-  _`BizInquiry` has 680 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 678 inferred relationships involving `BizQuotation` (e.g. with `QuotationCRUD` and `QuotationItemCRUD`) actually correct?**
-  _`BizQuotation` has 678 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 668 inferred relationships involving `BizAttachment` (e.g. with `AttachmentCRUD` and `价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。`) actually correct?**
-  _`BizAttachment` has 668 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 798 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
+  _`BizPriceListItem` has 798 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 727 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**
+  _`BizInquiry` has 727 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 725 inferred relationships involving `BizQuotation` (e.g. with `QuotationCRUD` and `QuotationItemCRUD`) actually correct?**
+  _`BizQuotation` has 725 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 715 inferred relationships involving `BizAttachment` (e.g. with `AttachmentCRUD` and `价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。`) actually correct?**
+  _`BizAttachment` has 715 INFERRED edges - model-reasoned connections that need verification._

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


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


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


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


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


+ 1 - 0
graphify-out/cache/ast/11deb2f6ab940a6fb2defa08eb4fdc9034257eac2851be704ad28fd5c839c2c0.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_statistics_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/statistics/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/151ae550d254602e1a9e139bb264fa499a4a5b469b500e4aec29b0763ff8c059.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/15dd8ebe0d650c86824af2f82e3a34fb74e2c19361b90df0c55774339e302778.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_components_settlement_settlementsheetdetailpanel_vue", "label": "SettlementSheetDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/components/settlement/SettlementSheetDetailPanel.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/1c090e9bebfcc5398aa005462eb2263178b11f3057a639654fc363dd38452c66.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/1c593eeb3eec371c09a469f68f5fe1c43c114ec9e4e504aa703cc55c087a30cd.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


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


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


+ 1 - 0
graphify-out/cache/ast/27ea94e1c21637d0d34ede1f73fc1b619aac7fcac4b6e2aa84b4b92a55bce201.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/detail.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/288f6b2046e4129fd3b8d4d9e607c7d566d040be7a6298ef9d89e94c874d3d7e.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


+ 1 - 0
graphify-out/cache/ast/2c226cd6c10994af2f93a081343a54b7ad11e1ed9ab34d7e3c8da867f43893e4.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_parameter_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L1"}, {"id": "parameter_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L767"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_parameter_index_vue", "target": "parameter_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L767", "weight": 1.0}], "raw_calls": [{"caller_nid": "parameter_index_if", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L768"}]}

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


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


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


+ 1 - 0
graphify-out/cache/ast/33c9aa64e2c74bb91df85f4b1b2df8d1b4ca1b4c1fe65159ae8ade4ba7e7a11c.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_examples_tables_tree_vue", "label": "tree.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/examples/tables/tree.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/381a37c7bf469d96edbd1244ccf5b9d0a70868ef26421e2dfe8ec69e9a1c0273.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_price_list_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/price-list/index.vue", "source_location": "L1"}, {"id": "price_list_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/price-list/index.vue", "source_location": "L236"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_price_list_index_vue", "target": "price_list_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/price-list/index.vue", "source_location": "L236", "weight": 1.0}], "raw_calls": [{"caller_nid": "price_list_index_if", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/price-list/index.vue", "source_location": "L237"}]}

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


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


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


+ 1 - 0
graphify-out/cache/ast/3fe3ec0c93c58893d23492254b4e131fd3f2100745af07ac94518771acbb1b43.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_delivery_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/delivery/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/41403c17fc55664d907c5115d27f1e73616188a25d35677fe2edfc218fdb61c5.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_dashboard_console_modules_new_user_vue", "label": "new-user.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/dashboard/console/modules/new-user.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/4297ae6fd0861837a1575cc4deba0dbaaaf54d6411d9a044607d62dccb29b89d.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_widgets_drag_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/widgets/drag/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/482184b8bc7f99b1e839c1149677e8d50d72289afdb901144663ede637850ada.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_examples_tables_basic_vue", "label": "basic.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/examples/tables/basic.vue", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_examples_tables_basic_vue", "target": "users_wuzilong_work_00code_sqxp_2_web_admin_src_api_system_manage", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/examples/tables/basic.vue", "source_location": "L23", "weight": 1.0}], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/48eed0adc2ada8a1b60391dbe68ad28775973469f0261b65306ce6e38a329edc.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


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


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


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


+ 1 - 0
graphify-out/cache/ast/53d42ffb123417688b2160fa1f141623a3c25409693df853c16088a24ac2e032.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}, {"id": "user_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L586"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "target": "user_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L586", "weight": 1.0}, {"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "target": "user_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L915", "weight": 1.0}], "raw_calls": [{"caller_nid": "user_index_if", "callee": "isArray", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L587"}, {"caller_nid": "user_index_if", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L916"}]}

+ 1 - 0
graphify-out/cache/ast/57f3e06ead5f05ffa6575a7a6803904ee266c7bfae79e2fec2432d359d15215e.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/59bbbda722444f01b255352e3506741f69a6ece712b98d17c335f375320df1d6.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


+ 1 - 0
graphify-out/cache/ast/5dd1761a188f6a7ad57333d4eb8a1b77b70d008cafdc41262a24af8512b13dd0.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_review_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/review/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/62ffaf3b0cba0accd7fe818587d9f27cab5edb1255b7533b77d64c6476748a78.json

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

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


+ 1 - 0
graphify-out/cache/ast/68961ae1da0d6d0192620e74e3362cc930fe0b2c6aa13dbcc8705fab05579311.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_invoice_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/invoice/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/6a0364ad197dc1ecf1b06cb7b4bc1603836411540825ca18424a476c52953541.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_role_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/role/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/6f5b7d255e649c53a2bc93ed7ef41a296aae246d5226d520ffec00b40fe3f169.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_adjust_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order-adjust/index.vue", "source_location": "L1"}, {"id": "order_adjust_index_catch", "label": "catch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order-adjust/index.vue", "source_location": "L492"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_adjust_index_vue", "target": "order_adjust_index_catch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order-adjust/index.vue", "source_location": "L492", "weight": 1.0}], "raw_calls": [{"caller_nid": "order_adjust_index_catch", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order-adjust/index.vue", "source_location": "L493"}]}

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


+ 1 - 0
graphify-out/cache/ast/7252776330420dd3411b6fabfb6a87d890ce91ef6f64548b8dbb5d4764d776db.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


+ 1 - 0
graphify-out/cache/ast/7563bc6803f55a408a37a710fd775318ec68af1f2400a0708b1959cb9cdaf02c.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_price_list_version_vue", "label": "version.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/price-list/version.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/8144437e953a225fc7145ff49a6f870ce27b055d1c34283b13e22bdb76def0fd.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/8295d0cd36708fe4d903ea8fb9975cefb51a80b5e5dc6bd3cb5f3310f0a2c4c9.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_supplier_settlement_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/supplier-settlement/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/835b234195ee1ec13033bf0d81fac96891f1285aca90e23dff582694ecc6b3f8.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_widgets_drag_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/widgets/drag/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/8b935afbb3818ac91d066563437f6dbcf168db965565b1be8da823908cad7f7e.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/8fecac71f5f595fa374e24d9745e127fdbb9e0d01952fa3c3bd0ad15e09466d6.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


+ 1 - 0
graphify-out/cache/ast/95c7c9f6b4a245f77561c5fb860aacac365a2d1ee16caa279ae24e21806e74bb.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/9796b30a84e614aa9bed210c5f70aa1f497540f58705e831aa993439100ec163.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


+ 1 - 0
graphify-out/cache/ast/9c1fc911ad53e4ffae2aa91961ee04ed56f1ef7ddbc206583cb1f753ae9281bc.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_role_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/role/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/9d962aa9637e8a5935b44e2df66fcdba12663eb2932b79eea0129d0ad0cdae04.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_settlement_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/settlement/index.vue", "source_location": "L1"}, {"id": "settlement_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/settlement/index.vue", "source_location": "L224"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_settlement_index_vue", "target": "settlement_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/settlement/index.vue", "source_location": "L224", "weight": 1.0}], "raw_calls": [{"caller_nid": "settlement_index_if", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/settlement/index.vue", "source_location": "L225"}]}

+ 1 - 0
graphify-out/cache/ast/9dbf0f57903ce35b355bd73a3b5a8bf143cd003e2f6976654c0765d5b14fd188.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


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


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


+ 1 - 0
graphify-out/cache/ast/b390c77bd3a6e7f02539b80f294e9bfc65ce54ee6e978fa4bf9461fffbd2a204.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_delivery_demo_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/delivery-demo/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/b3f70fad95150d9b7bb308bd6343c218228baca46feb6bf56f7c3ee4d1a13e4c.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_parameter_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L1"}, {"id": "parameter_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L767"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_parameter_index_vue", "target": "parameter_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L767", "weight": 1.0}], "raw_calls": [{"caller_nid": "parameter_index_if", "callee": "error", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/parameter/index.vue", "source_location": "L768"}]}

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


+ 1 - 0
graphify-out/cache/ast/b5ea9ec5fb1ab05e89af5b033d2b08c6facb2682b8c399d718d9fb8ad40f7bdc.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/b6cc899ebdaea9d0609c91ddf859e52c5e437dfcea090a883049829484b05ffe.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_refund_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/refund/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


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


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


+ 1 - 0
graphify-out/cache/ast/c4044d8217ce145d8ef717698762404b35c114495c14094463436302048b5a9d.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_user_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/user/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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


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


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