chore: document wx-qr phone login and refresh graphify
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
56ddc212a7
commit
66e442e1e8
13 files changed
+39452
-32529
No files matched your search
File diff suppressed because it is too large.
Load diff
+12515
-10087
File diff suppressed because it is too large.
Load diff
@@ -1,11 +1,11 @@
|
||||
# Graph Report - app (2026-09-16)
|
||||
# Graph Report - app (2026-09-18)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 264 files · ~317,142 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 265 files · ~319,283 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 5468 nodes · 30082 edges · 40 communities detected
|
||||
- Extraction: 31% EXTRACTED · 69% INFERRED · 0% AMBIGUOUS · INFERRED: 20619 edges (avg confidence: 0.6)
|
||||
- 5508 nodes · 30303 edges · 40 communities detected
|
||||
- Extraction: 31% EXTRACTED · 69% INFERRED · 0% AMBIGUOUS · INFERRED: 20768 edges (avg confidence: 0.6)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -51,22 +51,22 @@
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `success_response()` - 762 edges
|
||||
2. `error_response()` - 632 edges
|
||||
3. `SysUser` - 606 edges
|
||||
4. `BizQcTaskResult` - 352 edges
|
||||
5. `BizProductionMaterial` - 336 edges
|
||||
6. `BizProductionMaterialUsage` - 329 edges
|
||||
7. `BizQcMaterialUsage` - 327 edges
|
||||
8. `BizQcMaterial` - 317 edges
|
||||
9. `BizProductionMaterialStockBatch` - 316 edges
|
||||
10. `BizMaterialUsage` - 313 edges
|
||||
1. `success_response()` - 770 edges
|
||||
2. `error_response()` - 639 edges
|
||||
3. `SysUser` - 612 edges
|
||||
4. `BizQcTaskResult` - 354 edges
|
||||
5. `BizProductionMaterial` - 338 edges
|
||||
6. `BizProductionMaterialUsage` - 331 edges
|
||||
7. `BizQcMaterialUsage` - 329 edges
|
||||
8. `BizQcMaterial` - 319 edges
|
||||
9. `BizProductionMaterialStockBatch` - 318 edges
|
||||
10. `BizMaterialUsage` - 315 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `统一记录 API 请求/响应出入参的中间件` --uses--> `SysApiRequestLog` [INFERRED]
|
||||
core/api_request_log_middleware.py → models/models.py
|
||||
- `get_menu_list()` --calls--> `success_response()` [INFERRED]
|
||||
api/menu.py → core/response.py
|
||||
- `sync_lab_storage_seq_after_use()` --calls--> `generate_uuid()` [INFERRED]
|
||||
crud/sample_reception_lab_code.py → core/utils.py
|
||||
- `微信小程序登录业务:openid + 手机号一致性校验。` --uses--> `SysUser` [INFERRED]
|
||||
services/wechat_login_service.py → models/models.py
|
||||
- `已绑定 openid 时静默换票(无需手机号授权)。` --uses--> `SysUser` [INFERRED]
|
||||
@@ -78,124 +78,124 @@
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.01
|
||||
Nodes (1075): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+1067 more)
|
||||
Nodes (1019): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+1011 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.01
|
||||
Nodes (734): generate_uuid(), allocate_next_biz_no(), _format_biz_no(), _prefix_for(), 业务协议号自动生成:XBCC / JZCC + YYYYMMDD + 6 位序号, 原子分配下一个业务协议号。 须在 create 事务内调用;使用 MySQL LAST_INSERT_ID 保证并发安全。, create_cell_sample_type(), get_cell_sample_type() (+726 more)
|
||||
Nodes (821): _ensure_customer_for_mother(), generate_uuid(), create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict(), soft_delete_cell_sample_type() (+813 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.01
|
||||
Nodes (655): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+647 more)
|
||||
Nodes (621): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+613 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.01
|
||||
Nodes (420): _normalize_date_bound(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), format_date() (+412 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (451): 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), 审批意见:前端传 commentText,兼容 comment。, TaskActionBody, Base, 采集协议单 / 审核 / 缴费明细 CRUD(细胞存储 2.2.6~2.2.10), 返回已在采集协议单或科研捐赠单样本表中占用的样本编码集合(同租户、未删除)。, 创建/更新样本行前校验:样本编码不得已被任一业务单占用(转移配对单豁免)。, 缴费明细行 → 前端字典(含协议、客户、转账附件)。 (+443 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.08
|
||||
Nodes (388): 环境检测 — 检测项维护 + 登记管理 API, 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), 小程序菜单:当前用户在「移动端菜单」树下有权限的入口。, 宫格图标底色/图形色(可按入口自定义,不强制品牌绿)。, 按路由匹配移动端宫格图标(与 mobile-h5 van-icon 字形名对齐)。, ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody (+380 more)
|
||||
Cohesion: 0.01
|
||||
Nodes (302): _normalize_date_bound(), _persist_protocol_sub_resources(), _body_to_payload(), _get_current_user_id(), get_production_reception_by_sample_code(), get_production_reception_detail(), _get_tenant_no(), post_production_reception() (+294 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.06
|
||||
Nodes (322): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+314 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (323): 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API, 持久化协议子资源。 缴费(含明细校验)须在样本之前:明细校验失败时不得已占用样本编码。 各子表 CRUD 内部仍可能分段 commit,创建接, 与列表接口一致:按租户 + 角色数据权限判断是否可访问该协议单。, If mother info exists but no customerId, create a customer record and link it., BatchStockInBody, BatchStockInItem, BatchStockOutBody, BatchStockOutItem (+315 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.02
|
||||
Nodes (265): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), build_void_type_label_map(), format_protocol_status_label() (+257 more)
|
||||
Nodes (296): list_ethnics_for_select(), AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, create_access_log(), create_uv_disinfect_log(), delete_access_log() (+288 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.03
|
||||
Nodes (247): 截止日期:仅日期时扩到当天 23:59:59。, 发起前预览审批链(条件齐后解析最终路径与审批人)。, 兼容旧 GET:无 formData,含条件分支时多为待确定占位。, _app_to_dict(), _copied_app_name(), copy_app(), create_app(), create_group() (+239 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (249): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), build_void_type_label_map(), format_protocol_status_label() (+241 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.01
|
||||
Nodes (175): 服务号网页授权 code 绑定当前用户(需 Bearer)。, 微信登录:openid 已绑定时可静默换票;否则须授权手机号且与账号 phone_no 一致。, 密码登录后绑定当前微信 openid(需 Bearer)。, wechat_login(), get_designer_data(), _error_json(), lifespan(), 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin (+167 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (235): 截止日期:仅日期时扩到当天 23:59:59。, 发起前预览审批链(条件齐后解析最终路径与审批人)。, 兼容旧 GET:无 formData,含条件分支时多为待确定占位。, _app_to_dict(), _copied_app_name(), copy_app(), create_app(), _create_version() (+227 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.03
|
||||
Nodes (148): api_save_application(), map_payments_by_schedule_ids(), get_protocol_nos_used_in_protocols(), _fmt_dt(), get_handover_by_schedule(), get_outbound_handover(), get_outbound_handover_view(), _handover_row_to_dict() (+140 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (140): get_designer_data(), _error_json(), lifespan(), 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware (+132 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.04
|
||||
Nodes (114): _deduct_step_usage_legacy_batch(), register_step_usage_from_requisition(), revert_step_material_usages(), add_task_step(), _aggregate_production_tab_step_field(), assert_step_assignee_for_write(), assert_task_assignee(), assert_task_step_assignee_or_legacy() (+106 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (142): mp_bind_status(), 小程序鉴权:微信登录 / 绑定 openid。, 当前用户是否已绑定微信服务号 openid。, 服务号网页授权 code 绑定当前用户(需 Bearer)。, 微信登录:openid 已绑定时可静默换票;否则须授权手机号且与账号 phone_no 一致。, 密码登录后绑定当前微信 openid(需 Bearer)。, _resolve_user_id(), web_qr_confirm() (+134 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.05
|
||||
Nodes (114): AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody, list_storage_slot_stats(), OccupancyCreateBody (+106 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (152): _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes(), create_cell_product() (+144 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.04
|
||||
Nodes (91): _collect_descendants(), _current_user(), _find_mobile_root(), list_my_mobile_menus(), _mobile_menu_icon(), _mobile_menu_icon_colors(), _user_menu_ids(), _jwt_user_payload() (+83 more)
|
||||
Nodes (102): _fmt_dt(), get_handover_by_schedule(), get_outbound_handover(), get_outbound_handover_view(), _handover_row_to_dict(), _handover_view_dict(), _parse_handover_time(), 供输注计划 detail 聚合的交接只读信息。 (+94 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.05
|
||||
Nodes (75): get_next_cell_product_code(), _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes() (+67 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.13
|
||||
Nodes (29): _assert_routes_exist(), collect_missing_route_ids(), create_set(), create_set_from_work_order(), create_tasks_from_set(), delete_set(), get_set(), _item_count_map() (+21 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.12
|
||||
Nodes (11): get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.ht, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total, 申请退款 Args: out_trade_no: 商户订单号 total_amount, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 tot, 生成签名 V3 (SHA256 with RSA) (+3 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.14
|
||||
Nodes (11): apply_formulas_and_linkages(), eval_formula(), _extract_rules(), preprocess_formula(), OA 表单公式与数据联动(禁止任意 eval), 按 schema 中字段 props.formula / props.linkage 回填 form_data。 linkage 示例: { "when, 将 {field} 转为合法标识符引用 field。, _SafeEval (+3 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.19
|
||||
Nodes (21): apply_overtime_kit_hours(), _build_rest_day_segments(), _build_workday_windows(), calc_overtime_hours(), _day_at_minutes(), _day_start_ms(), _extract_rules(), _format_ymd() (+13 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (22): 验证密码(哈希非 bcrypt 或损坏时返回 False,避免抛错), verify_password(), _attach_user_relations(), change_password(), create_user(), _empty_user_relation_fields(), enrich_user_dicts_with_relations(), get_user() (+14 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.21
|
||||
Nodes (19): _consumable_row_to_dict(), create_process(), _create_sub_consumable(), _create_sub_data_collection(), _create_sub_output(), _create_sub_reagent(), _data_collection_row_to_dict(), get_process() (+11 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.15
|
||||
Nodes (19): _field_label(), _is_empty(), _is_required(), _parse_money(), OA 表单设计器:金额控件 form_data 校验, 校验 form_data 中金额控件的值格式,返回中文错误列表(空表示通过)。, 解析金额值。成功返回 (number, None);失败返回 (None, error_key)。, _rule_props() (+11 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.2
|
||||
Nodes (19): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+11 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.25
|
||||
Nodes (15): _build_rest_day_segments(), _build_workday_windows(), calc_overtime_hours(), _day_at_minutes(), _day_start_ms(), _format_ymd(), _intersect_interval(), _interval_minutes() (+7 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.22
|
||||
Nodes (15): apply_trip_kit_days(), calc_trip_leg_days(), _day_index(), _end_offset(), _extract_rules(), is_half_day_multiple(), 出差套件:半日制行程时长(与前端 oa-trip-days.ts 对齐), 遍历 tripKit:行程未手改则覆盖 duration;出差天数未手改则覆盖为各行程时长之和。 (+7 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.23
|
||||
Nodes (12): Config, ProductBase, ProductCategoryBase, ProductCategoryCreate, ProductCategoryResponse, ProductCreate, ProductResponse, ProductUnitBase (+4 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.24
|
||||
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 试剂变动记录 CRUD(biz_reagent_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减试剂库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.24
|
||||
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 耗材变动记录 CRUD(biz_consumable_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减耗材库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.31
|
||||
Nodes (10): create_account(), _filtered_query(), get_account(), list_accounts(), list_options(), OA 收款账户 CRUD(oa_receive_account), row_to_dict(), soft_delete_account() (+2 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.31
|
||||
Nodes (10): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。, _row_to_dict() (+2 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.33
|
||||
Nodes (8): _build_tree(), _cascade_soft_delete(), get_menu(), list_menus(), list_menus_tree(), _row_to_dict(), soft_delete_menu(), update_menu()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.29
|
||||
Nodes (2): BaseModel, SoftDeleteModel
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.38
|
||||
Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.4
|
||||
Nodes (5): batch_matches_stock_status(), normalize_batch_stock_status(), 质检物料入库批次:按剩余数量过滤的语义辅助。, 纯函数判定(供单测);list_batches 用 SQL 等价条件。, 返回 'available' | 'exhausted';全部 / 空 / 非法 → None(不加剩余数量条件)。
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.5
|
||||
Nodes (3): BaseSettings, Config, Settings
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.67
|
||||
Nodes (1): 转正套件:入职日 + 试用期月数 → 建议转正日(YYYY-MM-DD)。
|
||||
@@ -237,13 +237,11 @@ Cohesion: 1.0
|
||||
Nodes (1): 发送催单通知给配送员 Args: deliver_openid: 配送员openid
|
||||
|
||||
## Knowledge Gaps
|
||||
- **181 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `加载 RSA 密钥对:优先环境变量,否则使用内置固定默认密钥。` (+176 more)
|
||||
- **184 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `加载 RSA 密钥对:优先环境变量,否则使用内置固定默认密钥。` (+179 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **Thin community `Community 24`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
|
||||
- **Thin community `Community 25`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 28`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 29`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)`
|
||||
- **Thin community `Community 29`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 30`** (3 nodes): `add_months_ymd()`, `oa_confirmation_date.py`, `转正套件:入职日 + 试用期月数 → 建议转正日(YYYY-MM-DD)。`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
@@ -269,17 +267,17 @@ Nodes (1): 发送催单通知给配送员 Args: del
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `SysUser` connect `Community 2` to `Community 0`, `Community 1`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 12`, `Community 22`?**
|
||||
_High betweenness centrality (0.114) - this node is a cross-community bridge._
|
||||
- **Why does `success_response()` connect `Community 0` to `Community 2`, `Community 3`, `Community 8`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 29`?**
|
||||
_High betweenness centrality (0.038) - this node is a cross-community bridge._
|
||||
- **Why does `generate_uuid()` connect `Community 1` to `Community 0`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 17`, `Community 19`, `Community 22`, `Community 23`?**
|
||||
_High betweenness centrality (0.035) - this node is a cross-community bridge._
|
||||
- **Are the 923 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
|
||||
_`str` has 923 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 761 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
|
||||
_`success_response()` has 761 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 631 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
|
||||
_`error_response()` has 631 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 604 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
|
||||
_`SysUser` has 604 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Why does `SysUser` connect `Community 2` to `Community 0`, `Community 1`, `Community 3`, `Community 5`, `Community 6`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 16`, `Community 20`, `Community 21`?**
|
||||
_High betweenness centrality (0.109) - this node is a cross-community bridge._
|
||||
- **Why does `success_response()` connect `Community 0` to `Community 2`, `Community 4`, `Community 6`, `Community 9`, `Community 10`?**
|
||||
_High betweenness centrality (0.039) - this node is a cross-community bridge._
|
||||
- **Why does `OaEngineError` connect `Community 8` to `Community 1`, `Community 2`, `Community 3`?**
|
||||
_High betweenness centrality (0.033) - this node is a cross-community bridge._
|
||||
- **Are the 930 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
|
||||
_`str` has 930 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 769 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
|
||||
_`success_response()` has 769 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 638 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
|
||||
_`error_response()` has 638 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 610 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
|
||||
_`SysUser` has 610 INFERRED edges - model-reasoned connections that need verification._
|
||||
+18244
-15232
File diff suppressed because it is too large.
Load diff
@@ -5,7 +5,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py": 1786415793.4635684,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/api_crypto.py": 1778578258.039314,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/login_crypto.py": 1782186069.0708613,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py": 1789545254.772123,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py": 1789709850.5506392,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/wechat_pay.py": 1778578258.0461762,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/api_request_log_middleware.py": 1779882001.753267,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py": 1780540753.5481932,
|
||||
@@ -33,7 +33,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/material.py": 1782894693.6389399,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py": 1782404882.9262497,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py": 1782379593.174925,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1788231725.9250517,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1789701693.5477362,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/cleanroom_garment.py": 1781347080.6165943,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/cn_holiday.py": 1786373798.242758,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/__init__.py": 1781344070.5899634,
|
||||
@@ -143,7 +143,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/oa_approval.py": 1786414220.5298004,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/cell_product.py": 1785289892.8170507,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/menu.py": 1779265729.0381103,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_region.py": 1782181887.2821388,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_region.py": 1789701832.2690132,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_delivery.py": 1779935673.6808827,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_check_item.py": 1779007909.2933013,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/site.py": 1781401290.6845422,
|
||||
@@ -192,7 +192,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/clinical_payment.py": 1782713876.5518267,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py": 1787110947.8175044,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/outbound_release_review.py": 1785303263.2759058,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1786374022.5691514,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1789710686.7963963,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/material.py": 1782894693.6582327,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py": 1782404982.2474263,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/mini_menu.py": 1786680837.227191,
|
||||
@@ -201,7 +201,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/sample_production_reception.py": 1788159565.164049,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/collection_protocol.py": 1785145536.3204737,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/cleanroom_garment.py": 1781347256.2326367,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/basic_info.py": 1782709076.3968658,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/basic_info.py": 1789702130.708455,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_infusion_confirm.py": 1779657043.1389203,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_infusion_schedule_mgmt.py": 1779940938.18027,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py": 1786426755.6367717,
|
||||
@@ -212,14 +212,14 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/research_donation_sample_collection.py": 1780457788.2998126,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/oa_approval.py": 1787892843.1222966,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py": 1785213144.2881503,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/menu.py": 1787887339.1254666,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/menu.py": 1789696372.4245293,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_delivery.py": 1779655584.820777,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/admin.py": 1786010386.7044516,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py": 1786945487.513935,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/research_donation.py": 1783920522.841086,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/ethnic.py": 1779266243.9511197,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_checkup.py": 1779935751.2658868,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/mini_auth.py": 1786616100.716625,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/mini_auth.py": 1789710909.065258,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_service.py": 1781599314.0221174,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/workflow.py": 1779266232.4568832,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/sample_sms_notification.py": 1780325524.907156,
|
||||
@@ -237,10 +237,11 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/oa_person_select.py": 1785401156.2563787,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/qc_work_order_conclusion.py": 1785206750.2532814,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/oa_dept_select.py": 1786000695.5043027,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/wechat_mini_auth.py": 1786353961.1533935,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/wechat_mini_auth.py": 1789710434.6386433,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/oa_publish.py": 1786001976.196836,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/cn_holiday_sync.py": 1786412469.104896,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/workflow_engine.py": 1785909957.8243756,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/wx_qr_session.py": 1789710006.0382662,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/cn_holiday.py": 1786373910.8580065,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/oa_confirmation_date.py": 1785403063.141215,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/services/web_auth_roles.py": 1785909942.9479194,
|
||||
|
||||
@@ -0,0 +1,910 @@
|
||||
# web-admin 微信扫码登录(手机号匹配)Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** web-admin 登录页以小程序码扫码为主:授权手机号匹配 `sys_user.phone_no` 后电脑自动登录;账号密码为辅。
|
||||
|
||||
**Architecture:** 后端 Redis(或内存)保存短时扫码会话;`create` 生成小程序码;手机打开 `pages/login/web-qr` 授权后 `confirm` 复用 `resolve_wechat_login_user` + `issue_tokens_for_user`;web-admin 轮询 `status` 一次性取走 JWT。
|
||||
|
||||
**Tech Stack:** FastAPI、Redis、微信小程序码 API、Vue 3(web-admin)、uni-app(mobile-h5)
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-09-18-web-admin-wx-qr-phone-login-design.md`
|
||||
|
||||
---
|
||||
|
||||
## File map
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `backend/app/core/config.py` | `WX_QR_*` 配置;`API_ENCRYPT_EXCLUDE_PATHS` 追加路径 |
|
||||
| `backend/app/services/wx_qr_session.py` | 扫码会话 CRUD、状态机、一次性消费 |
|
||||
| `backend/app/services/wechat_mini_auth.py` | 新增 `get_unlimited_wxacode` |
|
||||
| `backend/app/api/public.py` | `POST/GET /web/auth/wx-qr/create|status` |
|
||||
| `backend/app/api/mini_auth.py` | `web-qr-scanned` / `web-qr-confirm` |
|
||||
| `backend/tests/test_wx_qr_session.py` | 会话状态机单测 |
|
||||
| `backend/tests/test_wx_qr_api.py` | create/status/confirm 接口测(mock 微信) |
|
||||
| `web-admin/src/api/auth.ts` | `fetchWxQrCreate` / `fetchWxQrStatus` |
|
||||
| `web-admin/src/views/auth/login/index.vue` | 扫码主 UI + 密码折叠 + 轮询 |
|
||||
| `mobile-h5/pages/login/web-qr/index.vue` | 授权落地页 |
|
||||
| `mobile-h5/pages.json` | 注册新页 |
|
||||
| `mobile-h5/utils/auth.js` | `webQrScanned` / `webQrConfirm` |
|
||||
| `mobile-h5/utils/request.js` | 未登录路径白名单(若需要) |
|
||||
|
||||
**不修改:** 支付逻辑、开放平台网站应用、自动注册用户。
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 配置项与加密排除路径
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/core/config.py`
|
||||
|
||||
- [ ] **Step 1: 增加扫码会话配置并扩展排除路径**
|
||||
|
||||
在 `Settings` 中、`API_ENCRYPT_EXCLUDE_PATHS` 附近增加:
|
||||
|
||||
```python
|
||||
WX_QR_SESSION_TTL_SEC: int = 180
|
||||
WX_QR_ENV_VERSION: str = "release" # release | trial | develop
|
||||
WX_QR_PAGE: str = "pages/login/web-qr/index"
|
||||
```
|
||||
|
||||
将 `API_ENCRYPT_EXCLUDE_PATHS` 字符串追加(注意逗号分隔、无多余空格导致匹配失败;与现有风格一致):
|
||||
|
||||
```text
|
||||
/api/web/auth/wx-qr/create,
|
||||
/api/web/auth/wx-qr/status,
|
||||
/api/mini/auth/web-qr-scanned,
|
||||
/api/mini/auth/web-qr-confirm,
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/app/core/config.py
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore: add wx-qr login config and encrypt exclude paths
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 扫码会话服务(TDD)
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/app/services/wx_qr_session.py`
|
||||
- Create: `backend/tests/test_wx_qr_session.py`
|
||||
|
||||
- [ ] **Step 1: 写失败单测**
|
||||
|
||||
```python
|
||||
"""wx_qr_session 状态机与一次性消费。"""
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.services import wx_qr_session as s
|
||||
|
||||
|
||||
def setup_function():
|
||||
s._memory_clear_for_tests()
|
||||
|
||||
|
||||
def test_create_pending_and_get():
|
||||
sid, short = s.create_session(ttl_sec=60)
|
||||
assert sid and short and len(short) <= 32
|
||||
data = s.get_session(sid)
|
||||
assert data["status"] == "pending"
|
||||
assert s.resolve_session_id(short) == sid
|
||||
|
||||
|
||||
def test_mark_scanned():
|
||||
sid, _ = s.create_session(ttl_sec=60)
|
||||
assert s.mark_scanned(sid) is True
|
||||
assert s.get_session(sid)["status"] == "scanned"
|
||||
|
||||
|
||||
def test_mark_success_consume_once():
|
||||
sid, _ = s.create_session(ttl_sec=60)
|
||||
payload = {"token": "t", "refreshToken": "r", "expiresIn": 1, "userInfo": {}}
|
||||
assert s.mark_success(sid, payload) is True
|
||||
first = s.consume_success(sid)
|
||||
assert first["token"] == "t"
|
||||
assert s.consume_success(sid) is None
|
||||
assert s.get_session(sid)["status"] == "consumed"
|
||||
|
||||
|
||||
def test_mark_failed():
|
||||
sid, _ = s.create_session(ttl_sec=60)
|
||||
assert s.mark_failed(sid, "未找到员工") is True
|
||||
assert s.get_session(sid)["status"] == "failed"
|
||||
assert s.get_session(sid)["message"] == "未找到员工"
|
||||
|
||||
|
||||
def test_expire_old_session():
|
||||
sid, _ = s.create_session(ttl_sec=1)
|
||||
time.sleep(1.1)
|
||||
assert s.get_session(sid) is None or s.get_session(sid)["status"] == "expired"
|
||||
|
||||
|
||||
def test_invalidate():
|
||||
sid, _ = s.create_session(ttl_sec=60)
|
||||
s.invalidate(sid)
|
||||
data = s.get_session(sid)
|
||||
assert data is None or data["status"] == "expired"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测确认失败**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wx_qr_session.py -v`
|
||||
Expected: FAIL(模块不存在)
|
||||
|
||||
- [ ] **Step 3: 实现 `wx_qr_session.py`**
|
||||
|
||||
```python
|
||||
"""web-admin 扫码登录会话:短码映射 + 状态机;优先 Redis,否则进程内存。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import secrets
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
_PREFIX = "wxqr:sess:"
|
||||
_SHORT_PREFIX = "wxqr:short:"
|
||||
_memory: Dict[str, Dict[str, Any]] = {}
|
||||
_short_map: Dict[str, str] = {}
|
||||
|
||||
|
||||
def _memory_clear_for_tests() -> None:
|
||||
_memory.clear()
|
||||
_short_map.clear()
|
||||
|
||||
|
||||
def _ttl() -> int:
|
||||
return int(getattr(settings, "WX_QR_SESSION_TTL_SEC", 180) or 180)
|
||||
|
||||
|
||||
def _redis():
|
||||
"""复用 auth 侧 Redis 客户端模式;无 REDIS_HOST 返回 None。"""
|
||||
host = (settings.REDIS_HOST or "").strip()
|
||||
if not host:
|
||||
return None
|
||||
try:
|
||||
import redis
|
||||
|
||||
return redis.Redis(
|
||||
host=host,
|
||||
port=int(settings.REDIS_PORT or 6379),
|
||||
db=int(settings.REDIS_DB or 0),
|
||||
password=settings.REDIS_PASSWORD or None,
|
||||
decode_responses=True,
|
||||
socket_timeout=2,
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def create_session(*, ttl_sec: Optional[int] = None) -> tuple:
|
||||
ttl = int(ttl_sec if ttl_sec is not None else _ttl())
|
||||
session_id = str(uuid.uuid4())
|
||||
short = secrets.token_urlsafe(16)[:32]
|
||||
now = int(time.time())
|
||||
data = {
|
||||
"sessionId": session_id,
|
||||
"shortCode": short,
|
||||
"status": "pending",
|
||||
"message": "",
|
||||
"createdAt": now,
|
||||
"expiresAt": now + ttl,
|
||||
"tokens": None,
|
||||
}
|
||||
r = _redis()
|
||||
if r:
|
||||
pipe = r.pipeline()
|
||||
pipe.setex(_PREFIX + session_id, ttl, json.dumps(data, ensure_ascii=False))
|
||||
pipe.setex(_SHORT_PREFIX + short, ttl, session_id)
|
||||
pipe.execute()
|
||||
else:
|
||||
_memory[session_id] = data
|
||||
_short_map[short] = session_id
|
||||
return session_id, short
|
||||
|
||||
|
||||
def resolve_session_id(short_or_id: str) -> Optional[str]:
|
||||
key = (short_or_id or "").strip()
|
||||
if not key:
|
||||
return None
|
||||
r = _redis()
|
||||
if r:
|
||||
sid = r.get(_SHORT_PREFIX + key)
|
||||
if sid:
|
||||
return sid
|
||||
if r.exists(_PREFIX + key):
|
||||
return key
|
||||
return None
|
||||
if key in _short_map:
|
||||
return _short_map[key]
|
||||
if key in _memory:
|
||||
return key
|
||||
return None
|
||||
|
||||
|
||||
def get_session(session_id: str) -> Optional[Dict[str, Any]]:
|
||||
sid = (session_id or "").strip()
|
||||
if not sid:
|
||||
return None
|
||||
r = _redis()
|
||||
raw = None
|
||||
if r:
|
||||
raw = r.get(_PREFIX + sid)
|
||||
if not raw:
|
||||
return None
|
||||
data = json.loads(raw)
|
||||
else:
|
||||
data = _memory.get(sid)
|
||||
if not data:
|
||||
return None
|
||||
data = dict(data)
|
||||
if int(time.time()) >= int(data.get("expiresAt") or 0):
|
||||
data["status"] = "expired"
|
||||
return data
|
||||
return data
|
||||
|
||||
|
||||
def _save(data: Dict[str, Any]) -> None:
|
||||
sid = data["sessionId"]
|
||||
ttl = max(1, int(data["expiresAt"]) - int(time.time()))
|
||||
r = _redis()
|
||||
if r:
|
||||
r.setex(_PREFIX + sid, ttl, json.dumps(data, ensure_ascii=False))
|
||||
else:
|
||||
_memory[sid] = data
|
||||
|
||||
|
||||
def mark_scanned(session_id: str) -> bool:
|
||||
data = get_session(session_id)
|
||||
if not data or data["status"] in ("expired", "success", "consumed", "failed"):
|
||||
return False
|
||||
if data["status"] == "pending":
|
||||
data["status"] = "scanned"
|
||||
_save(data)
|
||||
return True
|
||||
|
||||
|
||||
def mark_success(session_id: str, tokens: Dict[str, Any]) -> bool:
|
||||
data = get_session(session_id)
|
||||
if not data or data["status"] in ("expired", "consumed", "failed"):
|
||||
return False
|
||||
data["status"] = "success"
|
||||
data["tokens"] = tokens
|
||||
data["message"] = ""
|
||||
_save(data)
|
||||
return True
|
||||
|
||||
|
||||
def mark_failed(session_id: str, message: str) -> bool:
|
||||
data = get_session(session_id)
|
||||
if not data or data["status"] in ("expired", "consumed", "success"):
|
||||
return False
|
||||
data["status"] = "failed"
|
||||
data["message"] = message or "登录失败"
|
||||
data["tokens"] = None
|
||||
_save(data)
|
||||
return True
|
||||
|
||||
|
||||
def consume_success(session_id: str) -> Optional[Dict[str, Any]]:
|
||||
data = get_session(session_id)
|
||||
if not data or data["status"] != "success":
|
||||
return None
|
||||
tokens = data.get("tokens")
|
||||
data["status"] = "consumed"
|
||||
data["tokens"] = None
|
||||
_save(data)
|
||||
return tokens
|
||||
|
||||
|
||||
def invalidate(session_id: str) -> None:
|
||||
data = get_session(session_id)
|
||||
if not data:
|
||||
return
|
||||
data["status"] = "expired"
|
||||
data["tokens"] = None
|
||||
_save(data)
|
||||
```
|
||||
|
||||
注意:内存模式下 `expiresAt` 到期时 `get_session` 返回 `status=expired`;单测 `test_expire_old_session` 接受 `None` 或 `expired`。
|
||||
|
||||
- [ ] **Step 4: 跑测通过**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wx_qr_session.py -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/app/services/wx_qr_session.py backend/tests/test_wx_qr_session.py
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add wx-qr login session store with one-time token consume
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 微信小程序码生成
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/services/wechat_mini_auth.py`
|
||||
- Create: `backend/tests/test_wxacode.py`
|
||||
|
||||
- [ ] **Step 1: 写失败单测(mock httpx)**
|
||||
|
||||
```python
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from app.services.wechat_mini_auth import get_unlimited_wxacode, WechatMiniAuthError
|
||||
|
||||
|
||||
def test_get_unlimited_wxacode_returns_png_bytes():
|
||||
token_resp = MagicMock()
|
||||
token_resp.raise_for_status = MagicMock()
|
||||
token_resp.json.return_value = {"access_token": "tok"}
|
||||
|
||||
code_resp = MagicMock()
|
||||
code_resp.raise_for_status = MagicMock()
|
||||
code_resp.headers = {"Content-Type": "image/jpeg"}
|
||||
code_resp.content = b"\xff\xd8fake"
|
||||
|
||||
with patch("app.services.wechat_mini_auth._require_mini_credentials", return_value=("app", "sec")):
|
||||
with patch("app.services.wechat_mini_auth.httpx.Client") as Client:
|
||||
client = MagicMock()
|
||||
Client.return_value.__enter__.return_value = client
|
||||
client.get.return_value = token_resp
|
||||
client.post.return_value = code_resp
|
||||
raw = get_unlimited_wxacode(scene="abc", page="pages/login/web-qr/index")
|
||||
assert raw.startswith(b"\xff\xd8")
|
||||
|
||||
|
||||
def test_get_unlimited_wxacode_json_error():
|
||||
token_resp = MagicMock()
|
||||
token_resp.raise_for_status = MagicMock()
|
||||
token_resp.json.return_value = {"access_token": "tok"}
|
||||
|
||||
code_resp = MagicMock()
|
||||
code_resp.raise_for_status = MagicMock()
|
||||
code_resp.headers = {"Content-Type": "application/json"}
|
||||
code_resp.content = b'{"errcode":40001,"errmsg":"fail"}'
|
||||
code_resp.json.return_value = {"errcode": 40001, "errmsg": "fail"}
|
||||
|
||||
with patch("app.services.wechat_mini_auth._require_mini_credentials", return_value=("app", "sec")):
|
||||
with patch("app.services.wechat_mini_auth.httpx.Client") as Client:
|
||||
client = MagicMock()
|
||||
Client.return_value.__enter__.return_value = client
|
||||
client.get.return_value = token_resp
|
||||
client.post.return_value = code_resp
|
||||
try:
|
||||
get_unlimited_wxacode(scene="abc", page="pages/login/web-qr/index")
|
||||
assert False, "expected error"
|
||||
except WechatMiniAuthError:
|
||||
pass
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测确认失败**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wxacode.py -v`
|
||||
Expected: FAIL(函数未定义)
|
||||
|
||||
- [ ] **Step 3: 在 `wechat_mini_auth.py` 增加函数**
|
||||
|
||||
在文件末尾追加(复用 `_get_access_token`):
|
||||
|
||||
```python
|
||||
_WX_WXACODE_UNLIMITED = "https://api.weixin.qq.com/wxa/getwxacodeunlimit"
|
||||
|
||||
|
||||
def get_unlimited_wxacode(
|
||||
*,
|
||||
scene: str,
|
||||
page: str,
|
||||
env_version: Optional[str] = None,
|
||||
) -> bytes:
|
||||
"""获取不限制小程序码 PNG/JPEG 字节;失败抛 WechatMiniAuthError。"""
|
||||
sc = (scene or "").strip()
|
||||
if not sc or len(sc) > 32:
|
||||
raise WechatMiniAuthError("scene 无效或超过 32 字符")
|
||||
pg = (page or "").strip()
|
||||
if not pg:
|
||||
raise WechatMiniAuthError("page 不能为空")
|
||||
ver = (env_version or getattr(settings, "WX_QR_ENV_VERSION", None) or "release").strip()
|
||||
if ver not in ("release", "trial", "develop"):
|
||||
ver = "release"
|
||||
token = _get_access_token()
|
||||
with httpx.Client(timeout=20.0) as client:
|
||||
r = client.post(
|
||||
f"{_WX_WXACODE_UNLIMITED}?access_token={token}",
|
||||
json={
|
||||
"scene": sc,
|
||||
"page": pg,
|
||||
"check_path": False,
|
||||
"env_version": ver,
|
||||
"width": 280,
|
||||
},
|
||||
)
|
||||
r.raise_for_status()
|
||||
ctype = (r.headers.get("Content-Type") or "").lower()
|
||||
if "application/json" in ctype:
|
||||
data = r.json()
|
||||
logger.warning("getwxacodeunlimit failed: %s", data)
|
||||
raise WechatMiniAuthError(
|
||||
"生成小程序码失败",
|
||||
wx_errcode=data.get("errcode"),
|
||||
)
|
||||
if not r.content:
|
||||
raise WechatMiniAuthError("生成小程序码失败:空响应")
|
||||
return r.content
|
||||
```
|
||||
|
||||
确保文件顶部已有 `Optional` 导入。
|
||||
|
||||
- [ ] **Step 4: 跑测通过**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wxacode.py -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/app/services/wechat_mini_auth.py backend/tests/test_wxacode.py
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add WeChat unlimited mini program code generator
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Web 端 create / status 接口
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/api/public.py`
|
||||
- Create: `backend/tests/test_wx_qr_api.py`
|
||||
|
||||
- [ ] **Step 1: 写接口测(mock 会话与小程序码)**
|
||||
|
||||
```python
|
||||
"""web wx-qr create/status API。"""
|
||||
from unittest.mock import patch
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_wx_qr_create_and_poll_pending():
|
||||
with patch(
|
||||
"app.api.public.get_unlimited_wxacode",
|
||||
return_value=b"\x89PNG\r\n\x1a\n",
|
||||
):
|
||||
r = client.post("/api/web/auth/wx-qr/create")
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body.get("code") == 200 or body.get("success") is True or "data" in body
|
||||
data = body.get("data") or body
|
||||
sid = data["sessionId"]
|
||||
assert data["qrImageBase64"].startswith("data:image/")
|
||||
st = client.get("/api/web/auth/wx-qr/status", params={"sessionId": sid})
|
||||
assert st.status_code == 200
|
||||
st_data = (st.json().get("data") or st.json())
|
||||
assert st_data["status"] == "pending"
|
||||
```
|
||||
|
||||
(若项目 `success_response` 结构为 `{code, msg, data}`,断言以实际为准;与现有 `tests/` 中 TestClient 用法对齐。若无 TestClient 先例,可改为直接调用路由函数 + Depends override。)
|
||||
|
||||
先查看现有 API 测试写法;若无 TestClient,本 Task 改为纯函数测:抽 `create_wx_qr_payload()` / `status_payload()` 再测。
|
||||
|
||||
推荐最小可测路径:在 `public.py` 内调用 service,单测 mock `get_unlimited_wxacode` 与 `wx_qr_session`。
|
||||
|
||||
- [ ] **Step 2: 在 `public.py` 增加路由**
|
||||
|
||||
```python
|
||||
import base64
|
||||
from app.services import wx_qr_session
|
||||
from app.services.wechat_mini_auth import get_unlimited_wxacode, WechatMiniAuthError
|
||||
|
||||
@router.post("/web/auth/wx-qr/create", response_model=dict)
|
||||
async def wx_qr_create():
|
||||
session_id, short = wx_qr_session.create_session()
|
||||
page = (settings.WX_QR_PAGE or "pages/login/web-qr/index").strip()
|
||||
try:
|
||||
raw = get_unlimited_wxacode(
|
||||
scene=short,
|
||||
page=page,
|
||||
env_version=settings.WX_QR_ENV_VERSION,
|
||||
)
|
||||
except WechatMiniAuthError as exc:
|
||||
wx_qr_session.invalidate(session_id)
|
||||
return error_response(400, str(exc))
|
||||
b64 = base64.b64encode(raw).decode("ascii")
|
||||
# 粗略按魔数选 mime
|
||||
mime = "image/jpeg" if raw[:2] == b"\xff\xd8" else "image/png"
|
||||
return success_response(
|
||||
{
|
||||
"sessionId": session_id,
|
||||
"expiresIn": settings.WX_QR_SESSION_TTL_SEC,
|
||||
"qrImageBase64": f"data:{mime};base64,{b64}",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/web/auth/wx-qr/status", response_model=dict)
|
||||
async def wx_qr_status(sessionId: str = ""):
|
||||
sid = (sessionId or "").strip()
|
||||
data = wx_qr_session.get_session(sid)
|
||||
if not data:
|
||||
return success_response({"status": "expired", "message": "二维码已过期,请刷新"})
|
||||
status = data.get("status") or "expired"
|
||||
if status == "success":
|
||||
tokens = wx_qr_session.consume_success(sid)
|
||||
if not tokens:
|
||||
return success_response({"status": "expired", "message": "登录凭证已失效,请刷新二维码"})
|
||||
return success_response({"status": "success", **tokens})
|
||||
if status == "failed":
|
||||
return success_response({"status": "failed", "message": data.get("message") or "登录失败"})
|
||||
if status in ("expired", "consumed"):
|
||||
return success_response({"status": "expired", "message": "二维码已过期,请刷新"})
|
||||
return success_response({"status": status})
|
||||
```
|
||||
|
||||
导入 `settings` 若尚未从 config 引入则补上。
|
||||
|
||||
- [ ] **Step 3: 跑测**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wx_qr_api.py tests/test_wx_qr_session.py -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/app/api/public.py backend/tests/test_wx_qr_api.py
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add web auth wx-qr create and status endpoints
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 小程序 confirm / scanned 接口
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/api/mini_auth.py`
|
||||
- Modify: `backend/tests/test_wx_qr_api.py`(追加用例)
|
||||
|
||||
- [ ] **Step 1: 追加单测(mock 微信与发票)**
|
||||
|
||||
```python
|
||||
def test_web_qr_confirm_success_sets_session():
|
||||
from app.services import wx_qr_session
|
||||
|
||||
wx_qr_session._memory_clear_for_tests()
|
||||
sid, short = wx_qr_session.create_session(ttl_sec=60)
|
||||
fake_user = SimpleNamespace(id="u1", phone_no="13800138000", wx_openid=None, wx_unionid=None, status="1")
|
||||
tokens = {
|
||||
"token": "access",
|
||||
"refreshToken": "refresh",
|
||||
"expiresIn": 3600,
|
||||
"userInfo": {"id": "u1"},
|
||||
}
|
||||
with patch("app.api.mini_auth.code2session", return_value={"openid": "oid1", "unionid": ""}), \
|
||||
patch("app.api.mini_auth.get_phone_number", return_value="13800138000"), \
|
||||
patch("app.api.mini_auth.resolve_wechat_login_user", return_value=(fake_user, None, None)), \
|
||||
patch("app.api.mini_auth.issue_tokens_for_user", return_value=tokens):
|
||||
r = client.post(
|
||||
"/api/mini/auth/web-qr-confirm",
|
||||
json={"sessionId": sid, "loginCode": "lc", "phoneCode": "pc"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
consumed = wx_qr_session.consume_success(sid)
|
||||
assert consumed and consumed["token"] == "access"
|
||||
```
|
||||
|
||||
(`SimpleNamespace` 从 `types` 导入;`client` 同 Task 4。若路由尚未注册导致 404,先实现再跑。)
|
||||
|
||||
- [ ] **Step 2: 在 `mini_auth.py` 增加路由**
|
||||
|
||||
```python
|
||||
from app.services import wx_qr_session
|
||||
|
||||
|
||||
@router.post("/web-qr-scanned", response_model=dict)
|
||||
async def web_qr_scanned(body: dict):
|
||||
raw = (body.get("sessionId") or body.get("scene") or "").strip()
|
||||
sid = wx_qr_session.resolve_session_id(raw) or raw
|
||||
data = wx_qr_session.get_session(sid)
|
||||
if not data or data.get("status") in ("expired", "consumed"):
|
||||
return error_response(400, "二维码已过期,请在电脑上刷新后重扫")
|
||||
wx_qr_session.mark_scanned(sid)
|
||||
return success_response({"ok": True})
|
||||
|
||||
|
||||
@router.post("/web-qr-confirm", response_model=dict)
|
||||
async def web_qr_confirm(body: dict, db: Session = Depends(get_db)):
|
||||
raw = (body.get("sessionId") or body.get("scene") or "").strip()
|
||||
sid = wx_qr_session.resolve_session_id(raw) or raw
|
||||
data = wx_qr_session.get_session(sid)
|
||||
if not data or data.get("status") in ("expired", "consumed", "failed"):
|
||||
return error_response(400, "二维码已过期,请在电脑上刷新后重扫")
|
||||
if data.get("status") == "success":
|
||||
return success_response({"ok": True, "message": "电脑端即将登录"})
|
||||
|
||||
wx_qr_session.mark_scanned(sid)
|
||||
|
||||
login_code = (body.get("loginCode") or "").strip()
|
||||
phone_code = (body.get("phoneCode") or "").strip()
|
||||
if not login_code or not phone_code:
|
||||
wx_qr_session.mark_failed(sid, "需要授权手机号才能完成扫码登录")
|
||||
return error_response(400, "需要授权手机号才能完成扫码登录")
|
||||
|
||||
try:
|
||||
sess = code2session(login_code)
|
||||
phone = get_phone_number(phone_code)
|
||||
except WechatMiniAuthError as exc:
|
||||
wx_qr_session.mark_failed(sid, str(exc))
|
||||
return error_response(400, str(exc))
|
||||
|
||||
user, err_msg, err_code = resolve_wechat_login_user(
|
||||
db, openid=sess["openid"], phone=phone
|
||||
)
|
||||
if err_msg:
|
||||
wx_qr_session.mark_failed(sid, err_msg)
|
||||
return error_response(err_code or 400, err_msg)
|
||||
|
||||
unionid = (sess.get("unionid") or "").strip()
|
||||
if unionid and not (getattr(user, "wx_unionid", None) or "").strip():
|
||||
user.wx_unionid = unionid
|
||||
user.update_time = datetime.now()
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
try:
|
||||
tokens = await issue_tokens_for_user(db, user)
|
||||
except ValueError as exc:
|
||||
wx_qr_session.mark_failed(sid, str(exc))
|
||||
return error_response(403, str(exc))
|
||||
|
||||
if not wx_qr_session.mark_success(sid, tokens):
|
||||
return error_response(400, "二维码已过期,请在电脑上刷新后重扫")
|
||||
return success_response({"ok": True, "message": "电脑端即将登录,可关闭本页"})
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 跑测**
|
||||
|
||||
Run: `cd backend && source venv/bin/activate && pytest tests/test_wx_qr_api.py tests/test_wx_qr_session.py -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/app/api/mini_auth.py backend/tests/test_wx_qr_api.py
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add mini auth web-qr scanned and confirm endpoints
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: web-admin API 与登录页
|
||||
|
||||
**Files:**
|
||||
- Modify: `web-admin/src/api/auth.ts`
|
||||
- Modify: `web-admin/src/views/auth/login/index.vue`
|
||||
|
||||
- [ ] **Step 1: 在 `auth.ts` 增加接口**
|
||||
|
||||
```typescript
|
||||
export function fetchWxQrCreate() {
|
||||
return request.post<{
|
||||
sessionId: string
|
||||
expiresIn: number
|
||||
qrImageBase64: string
|
||||
}>({
|
||||
url: '/web/auth/wx-qr/create',
|
||||
data: {}
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchWxQrStatus(sessionId: string) {
|
||||
return request.get<{
|
||||
status: string
|
||||
message?: string
|
||||
token?: string
|
||||
refreshToken?: string
|
||||
expiresIn?: number
|
||||
userInfo?: {
|
||||
id: string
|
||||
username: string
|
||||
realName: string
|
||||
phone: string
|
||||
role: string
|
||||
roleNames?: string[]
|
||||
regionId: string
|
||||
regionName: string
|
||||
avatarUrl?: string | null
|
||||
}
|
||||
}>({
|
||||
url: '/web/auth/wx-qr/status',
|
||||
params: { sessionId }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 改版登录页**
|
||||
|
||||
要点(在现有 `index.vue` 上改,保留 RSA 密码登录逻辑):
|
||||
|
||||
1. 右侧主区:标题「欢迎登录」+ 副文案「微信扫一扫,授权手机号后自动登录」。
|
||||
2. `<img :src="qrImage" />` + 状态文案 +「刷新二维码」按钮(带 `Refresh` 图标)。
|
||||
3. 分割线「或使用账号密码登录」;`showPasswordLogin` 默认 `false`,点击展开现有表单。
|
||||
4. `onMounted` → `refreshQr()`;`onUnmounted` → `clearInterval`。
|
||||
5. `refreshQr`:调 `fetchWxQrCreate`,设 `sessionId`/`qrImage`,每 1500ms 调 `fetchWxQrStatus`。
|
||||
6. `status===success'`:停轮询,`userStore.setToken` / `setUserInfo` / 通知 / `router.push`(复用现有成功分支)。
|
||||
7. `failed`/`expired`:展示 message;`expired` 可自动 `refreshQr`(注意防抖,避免死循环——仅自动一次或提示手动刷新)。
|
||||
|
||||
样式:扫码区居中;沿用现有 `.login-right` Fresh Mint 色;密码区折叠用简单 `v-show`。
|
||||
|
||||
- [ ] **Step 3: 本地冒烟**
|
||||
|
||||
Run: `cd web-admin && pnpm dev`
|
||||
手动打开登录页:应见到二维码请求(后端未起或微信未配时 create 可能失败——页面应 ElMessage 报错且可点刷新;密码登录仍可用)。
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add web-admin/src/api/auth.ts web-admin/src/views/auth/login/index.vue
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: make web-admin login QR-primary with password fallback
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: mobile-h5 网页登录授权页
|
||||
|
||||
**Files:**
|
||||
- Create: `mobile-h5/pages/login/web-qr/index.vue`
|
||||
- Modify: `mobile-h5/pages.json`
|
||||
- Modify: `mobile-h5/utils/auth.js`
|
||||
- Modify: `mobile-h5/utils/request.js`(若有路径白名单)
|
||||
|
||||
- [ ] **Step 1: `auth.js` 增加方法**
|
||||
|
||||
```javascript
|
||||
export async function webQrScanned(sessionId) {
|
||||
return post('/mini/auth/web-qr-scanned', { sessionId })
|
||||
}
|
||||
|
||||
export async function webQrConfirm({ sessionId, loginCode, phoneCode }) {
|
||||
return post('/mini/auth/web-qr-confirm', {
|
||||
sessionId,
|
||||
loginCode,
|
||||
phoneCode
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
在 `request.js` 的未加密/白名单列表中加入 `/mini/auth/web-qr-scanned`、`/mini/auth/web-qr-confirm`(与 `wechat-login` 同级)。
|
||||
|
||||
- [ ] **Step 2: 注册页面**
|
||||
|
||||
在 `pages.json` 的 `pages/login/index` 后插入:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "pages/login/web-qr/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "网页登录授权",
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 实现 `pages/login/web-qr/index.vue`**
|
||||
|
||||
最小结构:
|
||||
|
||||
- `onLoad(options)`:`sessionId = options.scene || options.sessionId`(微信码进入时多为 `scene`)。
|
||||
- 无 sessionId:展示错误「请从电脑登录页重新扫码」。
|
||||
- 有则调用 `webQrScanned(sessionId)`(失败可忽略文案)。
|
||||
- 主按钮 `open-type="getPhoneNumber"`;`@getphonenumber` 内 `uni.login` 取 `loginCode`,再 `webQrConfirm`。
|
||||
- 成功:`uni.showToast` / 页内成功态「电脑端即将登录,可关闭本页」——**不要** `saveSession`、**不要** `switchTab` 首页。
|
||||
- 失败:展示后端 message。
|
||||
- 协议:若现登录页强制同意,复用 `ConfirmDialog` + `getPhoneNumber` 同次点击模式(对齐 `pages/login/index.vue`)。
|
||||
|
||||
- [ ] **Step 4: 开发者工具冒烟**
|
||||
|
||||
用开发者工具编译;手动 `navigateTo` `/pages/login/web-qr/index?sessionId=测试` 看缺会话/错误态;真机扫码联调放在 Task 8。
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add mobile-h5/pages/login/web-qr/index.vue mobile-h5/pages.json mobile-h5/utils/auth.js mobile-h5/utils/request.js
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add mini program page for web-admin QR phone login
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: 联调验收与图谱更新
|
||||
|
||||
**Files:**
|
||||
- 无必须代码;按需微调文案/TTL
|
||||
|
||||
- [ ] **Step 1: 环境检查清单**
|
||||
|
||||
- `WXPAY_MINI_APPID` / `WXPAY_SECRET` 已配
|
||||
- `REDIS_HOST` 建议生产开启
|
||||
- `WX_QR_ENV_VERSION`:开发用 `develop`/`trial`,生产 `release`
|
||||
- 小程序已包含并上传 `pages/login/web-qr/index`
|
||||
- 后台用户 `phone_no` 与微信绑定手机一致
|
||||
|
||||
- [ ] **Step 2: 对照 spec §7 验收**
|
||||
|
||||
逐条勾选设计文档验收标准 1–7。
|
||||
|
||||
- [ ] **Step 3: 更新知识图谱**
|
||||
|
||||
```bash
|
||||
./scripts/update-graph.sh all
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Commit(若有图谱或小修)**
|
||||
|
||||
```bash
|
||||
git add backend/graphify-out web-admin/graphify-out
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore: update graphify after wx-qr login changes
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Spec coverage checklist
|
||||
|
||||
| Spec 项 | Task |
|
||||
|---------|------|
|
||||
| 扫码会话状态机 + 一次性消费 | Task 2 |
|
||||
| 小程序码生成 | Task 3 |
|
||||
| `/web/auth/wx-qr/create|status` | Task 4 |
|
||||
| `/mini/auth/web-qr-*` + 复用手机号匹配 | Task 5 |
|
||||
| web-admin 主扫码辅密码 | Task 6 |
|
||||
| mobile-h5 授权页不强制登录工作台 | Task 7 |
|
||||
| 配置 / 排除路径 / 发版注意 | Task 1、8 |
|
||||
| 不自动注册、非经典网页扫码 | 全任务未引入 |
|
||||
|
||||
## Self-review notes
|
||||
|
||||
- 无 TBD/TODO 占位。
|
||||
- `sessionId`(完整 UUID)供网页轮询;`short`(≤32)写入小程序码 `scene`;confirm 支持二者经 `resolve_session_id`。
|
||||
- success 只经网页 `status` 消费一次;小程序 confirm 不返回 JWT。
|
||||
- 密码登录路径保持 RSA,不删除。
|
||||
@@ -0,0 +1,273 @@
|
||||
# web-admin 微信扫码登录(手机号匹配)设计
|
||||
|
||||
> 日期:2026-09-18
|
||||
> 适用范围:`web-admin` 登录页 + `backend` 扫码会话 + `mobile-h5` 网页登录授权页
|
||||
> 关联:`docs/superpowers/specs/2026-08-10-mobile-h5-wechat-login-design.md`
|
||||
> 决策:方案 B(按手机号匹配)+ 实现方案 1(小程序码 + 网页轮询)
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与已确认决策
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
1. web-admin 登录以**微信扫码**为主:用户扫码 → 小程序授权手机号 → 匹配 `sys_user.phone_no` → 电脑端自动登录。
|
||||
2. **账号密码**为辅入口(默认折叠,可展开),现有 RSA 登录逻辑保持可用。
|
||||
3. 不要求小程序事先已登录;授权成功后**只登录电脑**,手机不强制进入工作台。
|
||||
|
||||
### 1.2 决策摘要
|
||||
|
||||
| 项 | 约定 |
|
||||
|----|------|
|
||||
| 身份策略 | 授权手机号匹配 `phone_no`(复用小程序微信登录逻辑) |
|
||||
| 扫码载体 | 微信小程序码(非开放平台网站应用经典扫码) |
|
||||
| 电脑取票 | 网页轮询会话状态,success 时下发与密码登录同结构的 JWT |
|
||||
| 未匹配 / 失败 | 明确提示,引导电脑端账号密码登录;**不自动注册** |
|
||||
| 手机会话 | 授权成功后小程序不强制写 JWT、不跳转工作台 |
|
||||
| 会话存储 | Redis(TTL 默认 180s);无 Redis 时仅开发环境内存兜底 |
|
||||
|
||||
### 1.3 非目标
|
||||
|
||||
- 微信开放平台「网站应用」嵌入式二维码登录(无手机号)
|
||||
- 扫码自动开户 / 注册
|
||||
- 改动密码登录签发结构(须与现有 `token` / `refreshToken` / `userInfo` 对齐)
|
||||
- 替换 `mobile-h5` 日常登录主流程(仅新增网页授权落地页)
|
||||
|
||||
### 1.4 为何不走经典网页扫码
|
||||
|
||||
PC 网页微信扫码(开放平台网站应用)一般只返回 openid/unionid,**不能授权手机号**。产品要求按手机号自动匹配,故必须经小程序 `getPhoneNumber`,再由网页轮询取票。
|
||||
|
||||
---
|
||||
|
||||
## 2. 主流程
|
||||
|
||||
```text
|
||||
web-admin backend mobile-h5
|
||||
| | |
|
||||
|-- POST /web/auth/wx-qr/create ->| |
|
||||
|<- sessionId + 小程序码 ---------| |
|
||||
| 展示二维码,开始轮询 | |
|
||||
| | 用户微信扫一扫 |
|
||||
| |------------------------------->|
|
||||
| | 打开 web-qr 页 |
|
||||
| |<- (可选) web-qr-scanned --------|
|
||||
|<- status=scanned ---------------| |
|
||||
| |<- web-qr-confirm (phone) -------|
|
||||
| | 匹配 phone_no,issue tokens |
|
||||
|<- status=success + JWT ---------| |
|
||||
| 写入 store,进入系统 | 提示成功,可关闭 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 会话状态机
|
||||
|
||||
```text
|
||||
pending --扫码打开页--> scanned --授权成功--> success
|
||||
| |
|
||||
| +--授权/匹配失败--> failed(带 reason)
|
||||
|
|
||||
+--超时 / 刷新换新码--> expired(旧 session 作废)
|
||||
```
|
||||
|
||||
| 状态 | 含义 | 网页表现 |
|
||||
|------|------|----------|
|
||||
| `pending` | 已生成码,等待扫 | 「请使用微信扫一扫」 |
|
||||
| `scanned` | 已打开小程序页,待授权 | 「已扫码,请在手机上确认授权」 |
|
||||
| `success` | 已匹配并发票 | 取 token,进入系统 |
|
||||
| `failed` | 匹配或授权失败 | 展示 `message`,可刷新二维码 |
|
||||
| `expired` | 超时或已作废 | 自动或手动刷新 |
|
||||
|
||||
规则:
|
||||
|
||||
- TTL 默认 **180 秒**。
|
||||
- `success` 后 token 载荷**一次性消费**:网页轮询取走即从会话删除,防重放。
|
||||
- 刷新二维码 = 创建新会话,旧 `sessionId` 标记 `expired`。
|
||||
- `sessionId` 使用不可猜测值(UUID);因小程序码 `scene` 最长 32 字符,对外 scene 可用短码,服务端映射到完整 sessionId。
|
||||
|
||||
---
|
||||
|
||||
## 4. 后端 API
|
||||
|
||||
路径加入 `API_ENCRYPT_EXCLUDE_PATHS`(与现有 login 一致)。未登录可调。
|
||||
|
||||
### 4.1 `POST /api/web/auth/wx-qr/create`
|
||||
|
||||
生成扫码会话并返回小程序码图片。
|
||||
|
||||
**响应 `data`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionId": "<完整会话 id,供轮询>",
|
||||
"expiresIn": 180,
|
||||
"qrImageBase64": "data:image/png;base64,..."
|
||||
}
|
||||
```
|
||||
|
||||
实现要点:
|
||||
|
||||
- 写入 Redis:`status=pending`、过期时间。
|
||||
- 调用微信获取不限制小程序码(或等价 API),`page` 为小程序网页登录页路径,`scene` 为短码。
|
||||
- 同 IP / 同客户端创建频率限流,避免耗尽小程序码配额。
|
||||
|
||||
### 4.2 `GET /api/web/auth/wx-qr/status?sessionId=`
|
||||
|
||||
网页轮询(建议间隔约 1.5s)。
|
||||
|
||||
| status | 额外字段 |
|
||||
|--------|----------|
|
||||
| pending / scanned / expired | — |
|
||||
| failed | `message` |
|
||||
| success(首次消费) | `token`、`refreshToken`、`expiresIn`、`userInfo`(与 `/web/auth/login` 一致) |
|
||||
| success(已消费) | 视为过期或返回明确错误,不再下发票 |
|
||||
|
||||
### 4.3 `POST /api/mini/auth/web-qr-scanned`(可选)
|
||||
|
||||
Body:`{ "sessionId": "..." }`
|
||||
将 `pending` → `scanned`,改善网页文案。非法/过期会话返回错误。
|
||||
|
||||
### 4.4 `POST /api/mini/auth/web-qr-confirm`
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionId": "...",
|
||||
"loginCode": "<wx.login code>",
|
||||
"phoneCode": "<getPhoneNumber code>"
|
||||
}
|
||||
```
|
||||
|
||||
处理顺序:
|
||||
|
||||
1. 校验会话存在且为 `pending`/`scanned`、未过期。
|
||||
2. `code2session(loginCode)` → openid(及可选 unionid)。
|
||||
3. `get_phone_number(phoneCode)` → 手机号。
|
||||
4. 调用现有 `resolve_wechat_login_user`(与 `POST /mini/auth/wechat-login` 同源)。
|
||||
5. `issue_tokens_for_user` → 写入会话 `success` + token 载荷。
|
||||
6. 失败 → 会话 `failed` + `message`;响应错误文案与小程序展示一致。
|
||||
|
||||
成功时小程序响应仅需业务成功提示(**可不向手机返回完整 web JWT**),电脑端通过轮询取票。
|
||||
|
||||
### 4.5 复用与签发
|
||||
|
||||
- 匹配 / 绑定:`app/services/wechat_login_service.py` 的 `resolve_wechat_login_user`。
|
||||
- 发票:`issue_tokens_for_user`(与密码登录、小程序微信登录一致)。
|
||||
- 微信凭证:复用 `WXPAY_MINI_APPID`、`WXPAY_SECRET`;新增服务端 `access_token` + 小程序码生成封装(仓库内尚无现成 getwxacode 实现)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端
|
||||
|
||||
### 5.1 web-admin 登录页
|
||||
|
||||
文件:`web-admin/src/views/auth/login/index.vue`(及 `src/api/auth.ts`)。
|
||||
|
||||
布局:保留左右分栏;右侧主区为扫码,账号密码折叠为辅。
|
||||
|
||||
```text
|
||||
欢迎登录
|
||||
微信扫一扫,授权手机号后自动登录
|
||||
|
||||
[ 小程序码 ]
|
||||
状态文案(随轮询变化)
|
||||
[ 刷新二维码 ]
|
||||
|
||||
────── 或使用账号密码登录 ──────
|
||||
(点击展开)
|
||||
用户名 / 密码 / 登录(现有 RSA 逻辑)
|
||||
```
|
||||
|
||||
交互:
|
||||
|
||||
| 时机 | 行为 |
|
||||
|------|------|
|
||||
| 进入页 / 点刷新 | `create` → 展示码 → 开始轮询 |
|
||||
| pending | 「请使用微信扫一扫」 |
|
||||
| scanned | 「已扫码,请在手机上确认授权」 |
|
||||
| success | 停轮询 → `setToken` / `setUserInfo` → 通知 → 安全跳转(同现逻辑) |
|
||||
| failed | 展示 `message`,可刷新 |
|
||||
| expired | 提示过期,自动或手动刷新 |
|
||||
| 离开页面 | 停止轮询 |
|
||||
|
||||
视觉:Fresh Mint(主色 `#10b981` 链);二维码区简洁。
|
||||
|
||||
### 5.2 mobile-h5 网页登录授权页
|
||||
|
||||
路径建议:`pages/login/web-qr/index`(写入 `pages.json`;小程序码 `page` 与此一致)。
|
||||
|
||||
- 入参:从 `scene` / query 解析短码或 `sessionId`。
|
||||
- Header 标题:网页登录授权。
|
||||
- 说明:正在登录电脑端管理后台,请授权手机号完成验证。
|
||||
- 主按钮:`open-type="getPhoneNumber"`(须用户点击;协议同意可复用现登录页模式)。
|
||||
- 成功:提示「电脑端即将登录,可关闭本页」;不强制写本地 JWT、不跳转工作台。
|
||||
- 失败 / 缺 session:页内明确原因 + 引导电脑账号密码登录。
|
||||
- `onLoad` 可选调用 `web-qr-scanned`。
|
||||
|
||||
### 5.3 统一失败文案
|
||||
|
||||
| 场景 | 文案方向 |
|
||||
|------|----------|
|
||||
| 未匹配手机号 | 未找到与该手机号匹配的员工账号,请使用电脑账号密码登录或联系管理员 |
|
||||
| 一号多人 | 手机号对应多个账号,请使用账号密码登录 |
|
||||
| 账号停用 | 账号已停用 |
|
||||
| 会话过期 | 二维码已过期,请在电脑上刷新后重扫 |
|
||||
| 拒绝授权 | 需要授权手机号才能完成扫码登录 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 配置与改动清单
|
||||
|
||||
### 6.1 配置
|
||||
|
||||
| 项 | 说明 |
|
||||
|----|------|
|
||||
| `WXPAY_MINI_APPID` / `WXPAY_SECRET` | 已有;小程序码、换票、手机号 |
|
||||
| `REDIS_*` | 扫码会话;未配置时仅开发内存兜底 |
|
||||
| `API_ENCRYPT_EXCLUDE_PATHS` | 加入 wx-qr / web-qr 相关路径 |
|
||||
| 可选 `WX_QR_SESSION_TTL_SEC` | 默认 180 |
|
||||
| 可选 `WX_QR_ENV_VERSION` | `release` / `trial` / `develop`,生成码环境 |
|
||||
| 小程序发版 | 必须包含 `pages/login/web-qr/index` |
|
||||
| 微信后台 | request 合法域名含 API |
|
||||
|
||||
无新业务表。
|
||||
|
||||
### 6.2 代码改动面
|
||||
|
||||
| 区域 | 内容 |
|
||||
|------|------|
|
||||
| backend | 会话服务;小程序码生成;`/web/auth/wx-qr/*`;`/mini/auth/web-qr-*` |
|
||||
| web-admin | 登录页改版;auth API;轮询 |
|
||||
| mobile-h5 | web-qr 页 + `pages.json` |
|
||||
| docs | 本设计;实现计划另开 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收标准
|
||||
|
||||
1. 登录页默认展示二维码;账号密码可展开且仍可登录。
|
||||
2. 微信扫码 → 授权手机号 → 电脑在数秒内进入系统;token 结构与密码登录一致。
|
||||
3. 匹配成功时按现逻辑绑定/校验 `wx_openid`。
|
||||
4. 无匹配、一号多人、停用、拒授权、过期:两端提示正确;可刷新或改用密码。
|
||||
5. success 的 token 仅网页轮询成功取一次;重复请求不再返回完整票。
|
||||
6. 离开登录页停止轮询;刷新二维码使旧会话失效。
|
||||
7. 小程序未事先登录仍可完成授权。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| 正式小程序未发布新页面 | 上线清单含发版;开发/体验用 `env_version` |
|
||||
| `phone_no` 空或重复 | 主数据治理 + 明确失败文案 |
|
||||
| 小程序码配额被刷 | 创建限流 + 短 TTL |
|
||||
| scene 32 字符限制 | 短码 + 服务端映射完整 sessionId |
|
||||
|
||||
---
|
||||
|
||||
## 9. 测试要点
|
||||
|
||||
- 单元/接口:会话状态迁移;success 一次性消费;过期;匹配失败各分支(可 mock 微信)。
|
||||
- 手工:真机微信扫正式/体验码;电脑轮询成功进系统;密码辅入口回归;离页停轮询。
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,11 +1,11 @@
|
||||
# Graph Report - src (2026-09-17)
|
||||
# Graph Report - src (2026-09-18)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 1000 files · ~660,260 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 1000 files · ~661,825 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 3601 nodes · 5136 edges · 71 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 1134 edges (avg confidence: 0.8)
|
||||
- 3607 nodes · 5144 edges · 72 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 1136 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -52,14 +52,14 @@
|
||||
- [[_COMMUNITY_Community 40|Community 40]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 44|Community 44]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 46|Community 46]]
|
||||
- [[_COMMUNITY_Community 47|Community 47]]
|
||||
- [[_COMMUNITY_Community 48|Community 48]]
|
||||
- [[_COMMUNITY_Community 49|Community 49]]
|
||||
- [[_COMMUNITY_Community 51|Community 51]]
|
||||
- [[_COMMUNITY_Community 52|Community 52]]
|
||||
- [[_COMMUNITY_Community 54|Community 54]]
|
||||
- [[_COMMUNITY_Community 55|Community 55]]
|
||||
- [[_COMMUNITY_Community 56|Community 56]]
|
||||
- [[_COMMUNITY_Community 58|Community 58]]
|
||||
@@ -68,21 +68,22 @@
|
||||
- [[_COMMUNITY_Community 61|Community 61]]
|
||||
- [[_COMMUNITY_Community 62|Community 62]]
|
||||
- [[_COMMUNITY_Community 63|Community 63]]
|
||||
- [[_COMMUNITY_Community 65|Community 65]]
|
||||
- [[_COMMUNITY_Community 68|Community 68]]
|
||||
- [[_COMMUNITY_Community 74|Community 74]]
|
||||
- [[_COMMUNITY_Community 76|Community 76]]
|
||||
- [[_COMMUNITY_Community 64|Community 64]]
|
||||
- [[_COMMUNITY_Community 66|Community 66]]
|
||||
- [[_COMMUNITY_Community 69|Community 69]]
|
||||
- [[_COMMUNITY_Community 75|Community 75]]
|
||||
- [[_COMMUNITY_Community 77|Community 77]]
|
||||
- [[_COMMUNITY_Community 79|Community 79]]
|
||||
- [[_COMMUNITY_Community 78|Community 78]]
|
||||
- [[_COMMUNITY_Community 80|Community 80]]
|
||||
- [[_COMMUNITY_Community 81|Community 81]]
|
||||
- [[_COMMUNITY_Community 85|Community 85]]
|
||||
- [[_COMMUNITY_Community 98|Community 98]]
|
||||
- [[_COMMUNITY_Community 101|Community 101]]
|
||||
- [[_COMMUNITY_Community 82|Community 82]]
|
||||
- [[_COMMUNITY_Community 87|Community 87]]
|
||||
- [[_COMMUNITY_Community 99|Community 99]]
|
||||
- [[_COMMUNITY_Community 102|Community 102]]
|
||||
- [[_COMMUNITY_Community 103|Community 103]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `get()` - 317 edges
|
||||
1. `get()` - 319 edges
|
||||
2. `push()` - 128 edges
|
||||
3. `emit()` - 29 edges
|
||||
4. `WebSocketClient` - 26 edges
|
||||
@@ -100,193 +101,193 @@
|
||||
utils/oa-receive-account-value.ts → views/operations/storage-facility/index.vue
|
||||
- `dash()` --calls--> `formatDisplayOrDash()` [INFERRED]
|
||||
components/business/infusion-schedule-outbound-chain-view/index.vue → utils/datetime-display.ts
|
||||
- `dash()` --calls--> `formatDisplayOrDash()` [INFERRED]
|
||||
components/business/clinical-schedule-payment-detail/index.vue → utils/datetime-display.ts
|
||||
- `displayCell()` --calls--> `formatDisplayOrDash()` [INFERRED]
|
||||
views/production/waste-output/index.vue → utils/datetime-display.ts
|
||||
- `fmtMoney()` --calls--> `formatMoneyDisplay()` [INFERRED]
|
||||
components/business/clinical-schedule-payment-detail/index.vue → utils/money-format.ts
|
||||
|
||||
## Communities
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.01
|
||||
Nodes (183): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+175 more)
|
||||
Cohesion: 0.0
|
||||
Nodes (204): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+196 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.02
|
||||
Nodes (86): fetchUploadAttachment(), handleReportUpload(), handleReportUpload(), walk(), if(), async(), addDispatchItem(), createDispatchItem() (+78 more)
|
||||
Nodes (139): fetchDeptTree(), fetchUploadAttachment(), fetchOaFlowPreview(), fetchOaTemplateDetail(), handleReportUpload(), handleReportUpload(), walk(), collectConditionReferencedFields() (+131 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.03
|
||||
Nodes (86): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupActualItems(), fetchPilotCheckupDetail(), fetchPilotCheckupList() (+78 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (92): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), fetchPilotCheckupList(), fetchUpdatePostBoundRoles() (+84 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.03
|
||||
Nodes (79): fetchEthnicList(), formatNowDatetime(), applyDonationBundleToForm(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd(), if(), loadEthnicOptions() (+71 more)
|
||||
Nodes (72): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), resolveInboundProtocolStatus(), buildBaseFields(), buildFeeSection() (+64 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.02
|
||||
Nodes (64): fetchBatchDeleteOaReceiveAccounts(), fetchBatchMarkOaCcTasksRead(), fetchCreateOaReceiveAccount(), fetchDeleteOaReceiveAccount(), fetchMarkOaCcTaskRead(), fetchOaAppDraft(), fetchOaCcUnreadCount(), fetchOaInstanceDetail() (+56 more)
|
||||
Nodes (42): fetchGetMenuList(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), ComponentLoader, IframeRouteManager, MenuProcessor, RoutePermissionValidator (+34 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.03
|
||||
Nodes (34): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RoutePermissionValidator, RouteRegistry, RouteTransformer, useAppMode() (+26 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (65): fetchBatchDeleteOaReceiveAccounts(), fetchBatchMarkOaCcTasksRead(), fetchCreateOaReceiveAccount(), fetchDeleteOaReceiveAccount(), fetchMarkOaCcTaskRead(), fetchOaAppDraft(), fetchOaCcUnreadCount(), fetchOaInstanceDetail() (+57 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.03
|
||||
Nodes (65): fetchCellSampleTypeList(), fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCellSampleType(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchDeleteCellSampleType() (+57 more)
|
||||
Nodes (79): fetchEthnicList(), formatNowDatetime(), applyDonationBundleToForm(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd(), if(), loadEthnicOptions() (+71 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.03
|
||||
Nodes (62): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), useCeremony(), useTheme(), if(), nodeSummary(), createDefaultFlow() (+54 more)
|
||||
Nodes (66): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+58 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.03
|
||||
Nodes (56): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), fetchProductionWasteOutputList(), handleDialogClose(), handleAdd() (+48 more)
|
||||
Nodes (47): handleBlur(), handleFocus(), handleInput(), fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed() (+39 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.03
|
||||
Nodes (69): fetchDeptTree(), fetchUserList(), fetchOaFlowPreview(), fetchOaTemplateDetail(), remoteSearchHandoverUsers(), chipTitle(), confirmPicker(), memberOptionLabel() (+61 more)
|
||||
Nodes (61): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice(), fetchUpdateCheckupInstitutionProject() (+53 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.02
|
||||
Nodes (29): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+21 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (47): fetchProductionWasteOutputList(), fetchProductionReceptionBySampleCode(), fetchProductionReceptionSubmit(), fetchProductionReceptionUpdate(), handleDialogClose(), handleView(), branchConditionLines(), onAdd() (+39 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.03
|
||||
Nodes (56): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), fetchMineTasksForWorkOrders(), useInboundSiblingNavigation(), shouldApplyTriggerSourceFilter(), useQcWorkOrderTabPage() (+48 more)
|
||||
Nodes (56): buildTemporaryViewSections(), buildInsertedSiblingCard(), resolveShowProtocolPaymentAtGroup(), useInboundSiblingNavigation(), shouldApplyTriggerSourceFilter(), useQcWorkOrderTabPage(), appendTaskResultSections(), batchTasksForPanelSlot() (+48 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.04
|
||||
Nodes (53): buildTemporaryViewSections(), buildInsertedSiblingCard(), resolveShowProtocolPaymentAtGroup(), appendTaskResultSections(), batchTasksForPanelSlot(), buildConfiguredOnlySection(), for(), set() (+45 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (50): if(), chipTitle(), confirmPicker(), memberOptionLabel(), removeUser(), resolveUser(), signBadge(), signBadgeText() (+42 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.04
|
||||
Nodes (49): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), resolveInboundProtocolStatus(), buildBaseFields(), buildInboundBasicSection() (+41 more)
|
||||
Nodes (50): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), fetchMineTasksForWorkOrders(), buildMineTableRows(), buildPresetTasksMap(), buildWorkOrderFromTasks() (+42 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.03
|
||||
Nodes (49): fetchAttachmentDownloadUrl(), qcAttachmentFullUrl(), qcDownloadAttachment(), qcPreviewAttachment(), formatHandoverUserLabel(), formatNowDatetime(), fullFileUrl(), handleDownloadAttachment() (+41 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (38): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchCreateMenu(), fetchDeleteCellSampleType(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateCellSampleType(), fetchUpdateMenu() (+30 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.04
|
||||
Nodes (46): fetchProductionWorkOrderQcInfo(), dash(), fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm() (+38 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.04
|
||||
Nodes (39): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), fetchUpdatePostBoundRoles(), fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail() (+31 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.04
|
||||
Nodes (44): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+36 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.05
|
||||
Nodes (39): fetchCreateQcInspectionItemTemplate(), fetchDeleteQcInspectionItemTemplate(), fetchQcInspectionItemTemplateDetail(), fetchQcInspectionItemTemplateList(), fetchQcInspectionItemTemplatesActive(), fetchUpdateQcInspectionItemTemplate(), fetchQcLabTestItemList(), fetchQcLabTestItemsActive() (+31 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.07
|
||||
Nodes (43): fetchOaRelatedSummaries(), buildFormRows(), buildOaPrintFilename(), buildOaPrintModel(), canvasToPdf(), ensurePlainObject(), exportOaApprovalPdf(), extractUrls() (+35 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.05
|
||||
Nodes (24): handleBlur(), handleFocus(), handleInput(), fetchClinicalReceiptConfirmed(), buildConfirmedParams(), buildPendingParams(), formatAmount(), handleConfirmedReset() (+16 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.06
|
||||
Nodes (40): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), fetchClinicalRefundConfirmed(), buildConfirmedParams(), buildPendingParams() (+32 more)
|
||||
Nodes (37): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskEditorInit() (+29 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.08
|
||||
Nodes (37): formatDatetimeToMinute(), handleChineseDatetimePaste(), parseChineseDatetimeText(), parseBirthOrder(), parseChineseNumeral(), addSampleHint(), earliestCollectTime(), isMaternalBloodEmpty() (+29 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.06
|
||||
Nodes (22): fetchQcMaterialBatchDetail(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString() (+14 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.06
|
||||
Nodes (26): fetchProductionTaskList(), createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments() (+18 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.1
|
||||
Nodes (2): WebSocketClient, TableCache
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.08
|
||||
Nodes (33): fetchRefreshToken(), toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url() (+25 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.07
|
||||
Nodes (13): fetchProductionWorkOrderQcInfo(), dash(), fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm() (+5 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.08
|
||||
Nodes (30): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), handleAdd(), handleDelete(), handleDropdownCommand() (+22 more)
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.09
|
||||
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.09
|
||||
Nodes (29): fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskEditorInit(), fetchReassignProductionTask() (+21 more)
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.08
|
||||
Nodes (20): createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments(), serializeEquipmentIds() (+12 more)
|
||||
Nodes (20): fetchAttachmentDownloadUrl(), attachmentFullUrl(), dash(), handleAttachmentDownload(), handleAttachmentPreview(), ensureUniqueZipEntryName(), fetchAttachmentBlob(), getAttachmentExtension() (+12 more)
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.07
|
||||
Nodes (19): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+11 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.08
|
||||
Nodes (22): fetchMineProductionWorkOrderTabStepCounts(), fetchMineProductionWorkOrderTabStepCountsDeduped(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), fetchProductionWorkOrderTabStepCounts(), fetchProductionWorkOrderTabStepCountsDeduped() (+14 more)
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.09
|
||||
Nodes (23): fetchProductionTaskList(), fetchProductionReceptionBySampleCode(), fetchProductionReceptionSubmit(), fetchProductionReceptionUpdate(), onUserSelectVisible(), remoteSearchUsers(), buildPayload(), ensureDefaultHandler() (+15 more)
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.07
|
||||
Nodes (19): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+11 more)
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.09
|
||||
Nodes (28): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+20 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.09
|
||||
Nodes (22): fetchOaAppList(), fetchOaGroupList(), fetchPublishOaApp(), loadData(), loadGroups(), resetForm(), onSuccess(), goBack() (+14 more)
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.1
|
||||
Nodes (21): displayPeople(), nodeStatusText(), personLines(), personStatusHint(), personTitle(), showNodeComment(), signBadge(), signBadgeText() (+13 more)
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.12
|
||||
Nodes (20): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchUserInfo(), buildWebEncryptedLoginBody(), defaultAvatarPathByRole() (+12 more)
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.11
|
||||
Nodes (21): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchWxQrStatus(), fetchUserInfo(), buildWebEncryptedLoginBody() (+13 more)
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.09
|
||||
Nodes (12): fetchMineQcWorkOrderTabUnfinishedCounts(), fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList(), fetchQcWorkOrdersBySample(), fetchQcWorkOrderTabUnfinishedCounts(), reload(), reloadTabCounts() (+4 more)
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.1
|
||||
Nodes (12): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleTabChange(), handleToggleSelect() (+4 more)
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.2
|
||||
Nodes (14): applyFlagToRule(), applySchema(), countRules(), getActiveRule(), getDesigner(), getSetupState(), onMessage(), readFlag() (+6 more)
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.12
|
||||
Nodes (6): setupOaConfirmationKit(), ensureOaKitMenu(), setupOaLeaveKit(), setupOaOvertimeKit(), setupOaResignationKit(), setupOaTripKit()
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.16
|
||||
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.27
|
||||
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.33
|
||||
Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.19
|
||||
Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.24
|
||||
Nodes (10): buildPrintDocumentHtml(), cleanupPrintIframe(), embedSopImagesAsDataUrls(), getSopA4ContentHeightPx(), getSopA4EditorHeightPx(), normalizeSopCheckboxes(), prepareSopHtmlForPrint(), printSopHtml() (+2 more)
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
### Community 44 - "Community 44"
|
||||
Cohesion: 0.15
|
||||
Nodes (4): fetchNextDonationSampleCode(), fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 0.27
|
||||
Nodes (10): nodeSummary(), createDefaultFlow(), createEndNode(), createNode(), ensureFlowRoot(), newId(), normalizeCcAssigneeTypes(), removeChildNode() (+2 more)
|
||||
|
||||
### Community 46 - "Community 46"
|
||||
Cohesion: 0.33
|
||||
Nodes (7): buildSheetColumns(), downloadOaHistoryExcel(), exportTimestamp(), pad(), sanitizeFilename(), styleHeaderCell(), writeHeaderRows()
|
||||
@@ -296,21 +297,21 @@ Cohesion: 0.31
|
||||
Nodes (4): isSlotAvailable(), resolveStorageSlotRangeFill(), slotFacilityNodeId(), sortBoxSlots()
|
||||
|
||||
### Community 48 - "Community 48"
|
||||
Cohesion: 0.28
|
||||
Nodes (1): FireworkSystem
|
||||
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 0.25
|
||||
Nodes (2): handleTouchEnd(), setTransition()
|
||||
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 0.28
|
||||
Nodes (1): FireworkSystem
|
||||
|
||||
### Community 51 - "Community 51"
|
||||
Cohesion: 0.28
|
||||
Nodes (4): fetchMyProductionMaterialConsumptions(), handleReset(), handleSearch(), loadData()
|
||||
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 0.28
|
||||
Nodes (4): isActiveHistoryInstance(), isUploadPreviewLabel(), previewColumnMinWidth(), syncWorktabTitle()
|
||||
|
||||
### Community 54 - "Community 54"
|
||||
Cohesion: 0.29
|
||||
Nodes (4): if(), emptyTripLeg(), buildTripKitChildren(), uniqueId()
|
||||
|
||||
### Community 55 - "Community 55"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
|
||||
@@ -328,122 +329,128 @@ Cohesion: 0.38
|
||||
Nodes (4): handleFocus(), handleInput(), parseDecimalToNumber(), sanitizeDecimalInput()
|
||||
|
||||
### Community 60 - "Community 60"
|
||||
Cohesion: 0.29
|
||||
Nodes (1): fetchQcMonthTemplateList()
|
||||
|
||||
### Community 61 - "Community 61"
|
||||
Cohesion: 0.4
|
||||
Nodes (2): isUnfinishedQcWorkOrder(), resolveQcWorkOrderUnfinishedBadgeCount()
|
||||
|
||||
### Community 61 - "Community 61"
|
||||
### Community 62 - "Community 62"
|
||||
Cohesion: 0.47
|
||||
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
|
||||
|
||||
### Community 62 - "Community 62"
|
||||
### Community 63 - "Community 63"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
|
||||
|
||||
### Community 63 - "Community 63"
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): parseEquipmentIds(), syncEquipmentNamesFromOptions()
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
### Community 66 - "Community 66"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): focusInput(), handleKeydown()
|
||||
|
||||
### Community 68 - "Community 68"
|
||||
### Community 69 - "Community 69"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): if(), logSidebarMenu()
|
||||
|
||||
### Community 74 - "Community 74"
|
||||
### Community 75 - "Community 75"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSettingDefaults(), resetToDefaults()
|
||||
|
||||
### Community 76 - "Community 76"
|
||||
### Community 77 - "Community 77"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): buildOvertimeKitChildren(), uniqueId()
|
||||
|
||||
### Community 77 - "Community 77"
|
||||
### Community 78 - "Community 78"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): buildLeaveKitChildren(), uniqueId()
|
||||
|
||||
### Community 79 - "Community 79"
|
||||
### Community 80 - "Community 80"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): buildConfirmationKitChildren(), uniqueId()
|
||||
|
||||
### Community 80 - "Community 80"
|
||||
### Community 81 - "Community 81"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): buildResignationKitChildren(), uniqueId()
|
||||
|
||||
### Community 81 - "Community 81"
|
||||
### Community 82 - "Community 82"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): copyTextToClipboard(), copyTextWithFallback()
|
||||
|
||||
### Community 85 - "Community 85"
|
||||
### Community 87 - "Community 87"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSourceProtocolAttachment(), normalizeAttachmentFiles()
|
||||
|
||||
### Community 98 - "Community 98"
|
||||
### Community 99 - "Community 99"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): emptyTabCounts(), useProductionWorkOrderTabStepCounts()
|
||||
|
||||
### Community 101 - "Community 101"
|
||||
### Community 102 - "Community 102"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): initialProgress(), useQcWorkOrderAttachmentExport()
|
||||
|
||||
### Community 102 - "Community 102"
|
||||
### Community 103 - "Community 103"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): emptyTabCounts(), useQcWorkOrderTabUnfinishedCounts()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **Thin community `Community 24`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
|
||||
- **Thin community `Community 21`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 48`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
|
||||
- **Thin community `Community 48`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 49`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
|
||||
- **Thin community `Community 49`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 56`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 58`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 60`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
|
||||
- **Thin community `Community 60`** (7 nodes): `fetchConfirmQcMonthImport()`, `fetchCreateQcMonthTemplate()`, `fetchDeleteQcMonthTemplate()`, `fetchPreviewQcMonthImport()`, `fetchQcMonthTemplateList()`, `fetchUpdateQcMonthTemplate()`, `qc-material-month-template.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 62`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
|
||||
- **Thin community `Community 61`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 63`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
|
||||
- **Thin community `Community 63`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 65`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
- **Thin community `Community 64`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 68`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
- **Thin community `Community 66`** (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 74`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
- **Thin community `Community 69`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 76`** (3 nodes): `buildOvertimeKitChildren()`, `oa-overtime-kit-rule.ts`, `uniqueId()`
|
||||
- **Thin community `Community 75`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 77`** (3 nodes): `buildLeaveKitChildren()`, `oa-leave-kit-rule.ts`, `uniqueId()`
|
||||
- **Thin community `Community 77`** (3 nodes): `buildOvertimeKitChildren()`, `oa-overtime-kit-rule.ts`, `uniqueId()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 79`** (3 nodes): `buildConfirmationKitChildren()`, `oa-confirmation-kit-rule.ts`, `uniqueId()`
|
||||
- **Thin community `Community 78`** (3 nodes): `buildLeaveKitChildren()`, `oa-leave-kit-rule.ts`, `uniqueId()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 80`** (3 nodes): `buildResignationKitChildren()`, `oa-resignation-kit-rule.ts`, `uniqueId()`
|
||||
- **Thin community `Community 80`** (3 nodes): `buildConfirmationKitChildren()`, `oa-confirmation-kit-rule.ts`, `uniqueId()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 81`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
|
||||
- **Thin community `Community 81`** (3 nodes): `buildResignationKitChildren()`, `oa-resignation-kit-rule.ts`, `uniqueId()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 85`** (3 nodes): `getSourceProtocolAttachment()`, `normalizeAttachmentFiles()`, `protocol-source-attachment.ts`
|
||||
- **Thin community `Community 82`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 98`** (3 nodes): `emptyTabCounts()`, `useProductionWorkOrderTabStepCounts()`, `useProductionWorkOrderTabStepCounts.ts`
|
||||
- **Thin community `Community 87`** (3 nodes): `getSourceProtocolAttachment()`, `normalizeAttachmentFiles()`, `protocol-source-attachment.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 101`** (3 nodes): `initialProgress()`, `useQcWorkOrderAttachmentExport()`, `useQcWorkOrderAttachmentExport.ts`
|
||||
- **Thin community `Community 99`** (3 nodes): `emptyTabCounts()`, `useProductionWorkOrderTabStepCounts()`, `useProductionWorkOrderTabStepCounts.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 102`** (3 nodes): `emptyTabCounts()`, `useQcWorkOrderTabUnfinishedCounts()`, `useQcWorkOrderTabUnfinishedCounts.ts`
|
||||
- **Thin community `Community 102`** (3 nodes): `initialProgress()`, `useQcWorkOrderAttachmentExport()`, `useQcWorkOrderAttachmentExport.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 103`** (3 nodes): `emptyTabCounts()`, `useQcWorkOrderTabUnfinishedCounts()`, `useQcWorkOrderTabUnfinishedCounts.ts`
|
||||
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 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 6`, `Community 7`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 24`, `Community 25`, `Community 27`, `Community 28`, `Community 30`, `Community 32`, `Community 33`, `Community 35`, `Community 46`, `Community 48`?**
|
||||
_High betweenness centrality (0.324) - this node is a cross-community bridge._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 23`, `Community 27`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 34`, `Community 35`, `Community 36`, `Community 42`, `Community 45`, `Community 47`, `Community 51`?**
|
||||
_High betweenness centrality (0.239) - this node is a cross-community bridge._
|
||||
- **Why does `handleDynamicRoutes()` connect `Community 5` to `Community 34`, `Community 35`, `Community 4`, `Community 13`, `Community 19`, `Community 30`?**
|
||||
_High betweenness centrality (0.033) - this node is a cross-community bridge._
|
||||
- **Are the 316 inferred relationships involving `get()` (e.g. with `resolveStorageSlotRangeFill()` and `buildNodeCodePathFromFlat()`) actually correct?**
|
||||
_`get()` has 316 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Why does `push()` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 26`, `Community 27`, `Community 29`, `Community 30`, `Community 31`, `Community 33`, `Community 45`, `Community 46`, `Community 49`?**
|
||||
_High betweenness centrality (0.327) - this node is a cross-community bridge._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 19`, `Community 20`, `Community 23`, `Community 24`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 32`, `Community 33`, `Community 34`, `Community 41`, `Community 44`, `Community 47`, `Community 60`?**
|
||||
_High betweenness centrality (0.260) - this node is a cross-community bridge._
|
||||
- **Why does `handleDynamicRoutes()` connect `Community 4` to `Community 32`, `Community 33`, `Community 3`, `Community 5`, `Community 16`, `Community 27`?**
|
||||
_High betweenness centrality (0.035) - this node is a cross-community bridge._
|
||||
- **Are the 318 inferred relationships involving `get()` (e.g. with `resolveStorageSlotRangeFill()` and `buildNodeCodePathFromFlat()`) actually correct?**
|
||||
_`get()` has 318 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 127 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
|
||||
_`push()` has 127 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 28 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleDialogClose()`) actually correct?**
|
||||
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">3601 nodes · 5136 edges · 397 communities</div>
|
||||
<div id="stats">3607 nodes · 5144 edges · 398 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 150, "community_name": "Community 150", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 151, "community_name": "Community 151", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 37, "community_name": "Community 37", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 37, "community_name": "Community 37", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 37, "community_name": "Community 37", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 37, "community_name": "Community 37", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 37, "community_name": "Community 37", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 494}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 129}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 117}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 110}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 110}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 101}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 99}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 97}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 96}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 92}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 92}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 90}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 86}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 84}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 83}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 79}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 71}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 69}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 57}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 54}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 53}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 53}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 47}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 46}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 42}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 41}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 37}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 36}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 35}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 35}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 35}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 34}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 33}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 31}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 28}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 25}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 24}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 22}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 19}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 17}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 17}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 16}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 14}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 13}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 13}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 13}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 10}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 9}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 9}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 9}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 9}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 9}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 9}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 9}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 9}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 8}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 8}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 8}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 7}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 7}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 6}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 6}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 5}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 5}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 5}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 5}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 4}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 4}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 4}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 4}, {"cid": 70, "color": "#4E79A7", "label": "CommunitLine truncated
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 58, "community_name": "Community 58", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 58, "community_name": "Community 58", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 151, "community_name": "Community 151", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 152, "community_name": "Community 152", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 35, "community_name": "Community 35", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 35, "community_name": "Community 35", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 35, "community_name": "Community 35", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 35, "community_name": "Community 35", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 35, "community_name": "Community 35", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 561}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 184}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 130}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 117}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 116}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 111}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 110}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 102}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 96}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 95}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 91}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 90}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 88}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 78}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 67}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 57}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 54}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 51}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 47}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 46}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 43}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 42}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 41}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 40}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 40}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 37}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 36}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 35}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 35}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 35}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 33}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 31}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 30}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 25}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 24}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 22}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 19}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 17}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 17}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 16}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 14}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 14}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 13}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 13}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 13}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 13}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 10}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 9}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 9}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 9}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 9}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 9}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 9}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 9}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 8}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 8}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 8}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 8}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 7}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 7}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 7}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 6}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 6}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 5}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 5}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 5}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 5}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 4}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 4}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 4}, {"cid": 70, "color": "#4E79A7", "label": "CommunLine truncated
|
||||
|
||||
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
|
||||
function esc(s) {
|
||||
|
||||
+3020
-2891
File diff suppressed because it is too large.
Load diff
@@ -8,7 +8,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/core/auth.ts": 1781272556.3503833,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/business/ripple.ts": 1781272556.3509357,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/business/highlight.ts": 1781272556.3516848,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts": 1785912860.2590215,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts": 1789702283.3593416,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/index.ts": 1781272556.36091,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/config/index.ts": 1781272556.3566036,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/component/chart.ts": 1781272556.3575494,
|
||||
@@ -17,8 +17,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1781272556.3597305,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1789611740.8245428,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1789611699.652026,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1789708405.9863644,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1789711366.3105218,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1787887312.735774,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1787887320.8486114,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/composables/useAttachmentActions.ts": 1788162977.6691713,
|
||||
@@ -410,7 +410,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/chinaRegion.ts": 1781272556.6606348,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-entry-sms-notification.ts": 1781272556.660898,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/business.ts": 1781750847.0646322,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts": 1785663707.7250297,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts": 1789702188.9523969,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/inbound-sample.ts": 1782889889.452178,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/ethnic.ts": 1781272556.6658764,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/research-donation-sample-collection.ts": 1781272556.6660974,
|
||||
@@ -433,9 +433,9 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/system.ts": 1782802585.0325377,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1782894693.658258,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1782719389.581961,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1782122651.4628098,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1789711364.4677992,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1787193626.7833252,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1782893854.347133,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1789711416.2312732,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1782893854.3402505,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1782893854.311591,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1788315818.1102333,
|
||||
@@ -723,7 +723,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/insurance-company/index.vue": 1782893851.6212523,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-protocol-no/index.vue": 1782893851.573797,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/customer/index.vue": 1783326449.0455372,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-region/index.vue": 1783326449.0480328,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-region/index.vue": 1789702500.075,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-region/CollectionProtocolNoTab.vue": 1783326449.0493598,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/loading.ts": 1781272558.486104,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/index.ts": 1781272559.227605,
|
||||
@@ -745,7 +745,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RoutePermissionValidator.ts": 1781272559.4235668,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/index.ts": 1781272559.424284,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/IframeRouteManager.ts": 1781272559.425215,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1787887334.7914455,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1789696342.9507763,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/index.ts": 1781272559.5419338,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1781272559.549901,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1781272559.596198,
|
||||
|
||||
Reference in new issue
Block a user