This commit is contained in:
zlwu committed 2026-06-04 12:57:59 +08:00
1 parent afd517ce10
commit eec61cb5dd
82 files changed
+16585 -9026

No files matched your search

+1 -1
View File
@@ -1 +1 @@
91519
88694
+146 -26
View File
@@ -1,7 +1,8 @@
"""公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线)"""
import os
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile, File
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import Session
@@ -22,16 +23,21 @@ from app.core.auth import (
)
from app.core.response import success_response, error_response
from app.core.config import settings
from app.core.storage import storage
from app.core.avatar_url import extract_avatar_storage_key
from app.models.models import SysUser
from app.crud.user import user_crud
from app.core.utils import generate_uuid
from app.core import login_crypto
from app.services.web_auth_roles import iter_user_menu_perm_codes, resolve_web_role_for_user
from app.schemas.auth import ProfileUpdateRequest
from app.crud.user import update_user, change_password
router = APIRouter(prefix="", tags=["公共接口"])
def _avatar_url_field(user: SysUser) -> Optional[str]:
"""返回库中原始头像路径,由前端按 VITE_MINIO_BASE_URL 拼展示 URL。"""
raw = getattr(user, "avatar", None)
if not raw:
return None
@@ -48,6 +54,8 @@ def _build_user_info_payload(user: SysUser, web_role: str) -> dict:
"realName": user.nick_name,
"phone": phone,
"email": (getattr(user, "email", None) or "").strip() or None,
"sex": getattr(user, "sex", None) or "0",
"remark": (getattr(user, "remark", None) or "").strip() or None,
"role": web_role,
"regionId": user.dept_id,
"regionName": None,
@@ -55,6 +63,36 @@ def _build_user_info_payload(user: SysUser, web_role: str) -> dict:
}
def _build_current_user_payload(current_user: dict) -> dict:
"""组装 current-user 接口响应。"""
au = current_user.get("avatar_url")
avatar_url = str(au).strip() if au else None
return {
"id": current_user.get("user_id"),
"username": current_user.get("username"),
"realName": current_user.get("real_name"),
"phone": current_user.get("phone"),
"email": current_user.get("email"),
"sex": current_user.get("sex") or "0",
"remark": current_user.get("remark"),
"role": current_user.get("role"),
"isSuperAdmin": current_user.get("role") == "super_admin",
"regionId": current_user.get("region_id"),
"regionName": None,
"regionIds": [],
"isActive": current_user.get("is_active"),
"avatarUrl": avatar_url if avatar_url else None,
}
async def _resolve_current_user_from_request(request: Request, db: Session) -> dict:
authorization = request.headers.get("Authorization", "")
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="未授权")
token = authorization.replace("Bearer ", "")
return await get_current_user_by_token(token=token, db=db)
def _jwt_user_payload(user: SysUser, web_role: str) -> dict:
return {
"sub": user.id,
@@ -214,36 +252,118 @@ async def logout(request: Request):
@router.get("/web/auth/current-user", response_model=dict)
async def get_current_user_profile(request: Request, db: Session = Depends(get_db)):
"""获取当前用户信息(与 sys_user 字段对应)。"""
authorization = request.headers.get("Authorization", "")
if not authorization.startswith("Bearer "):
return error_response(401, "未授权")
token = authorization.replace("Bearer ", "")
try:
current_user = await get_current_user_by_token(token=token, db=db)
au = current_user.get("avatar_url")
avatar_url = str(au).strip() if au else None
return success_response(
{
"id": current_user.get("user_id"),
"username": current_user.get("username"),
"realName": current_user.get("real_name"),
"phone": current_user.get("phone"),
"email": current_user.get("email"),
"role": current_user.get("role"),
"isSuperAdmin": current_user.get("role") == "super_admin",
"regionId": current_user.get("region_id"),
"regionName": None,
"regionIds": [],
"isActive": current_user.get("is_active"),
"avatarUrl": avatar_url if avatar_url else None,
}
)
current_user = await _resolve_current_user_from_request(request, db)
return success_response(_build_current_user_payload(current_user))
except HTTPException:
return error_response(401, "Token无效或已过期")
@router.put("/web/auth/profile", response_model=dict)
async def update_current_user_profile(
request: Request,
body: ProfileUpdateRequest,
db: Session = Depends(get_db),
):
"""当前用户更新个人信息(手机号、邮箱、性别、备注)。"""
try:
current_user = await _resolve_current_user_from_request(request, db)
user_id = current_user.get("user_id")
if not user_id:
return error_response(401, "未授权")
payload = body.model_dump(exclude_unset=True, by_alias=False)
update_user(
db,
user_id,
phone_no=payload.get("phone_no"),
email=payload.get("email"),
sex=payload.get("sex"),
remark=payload.get("remark"),
avatar=payload.get("avatar"),
user_id_updater=user_id,
)
user = user_crud.get(db, user_id)
if not user:
return error_response(404, "用户不存在")
web_role = resolve_web_role_for_user(db, user.id)
return success_response(_build_user_info_payload(user, web_role))
except HTTPException:
return error_response(401, "Token无效或已过期")
except ValueError as exc:
return error_response(400, str(exc))
@router.post("/web/auth/change-password", response_model=dict)
async def change_current_user_password(
request: Request,
body: dict,
db: Session = Depends(get_db),
):
"""当前用户修改密码。"""
try:
current_user = await _resolve_current_user_from_request(request, db)
user_id = current_user.get("user_id")
if not user_id:
return error_response(401, "未授权")
old_password = (body.get("oldPassword") or body.get("old_password") or "").strip()
new_password = (body.get("newPassword") or body.get("new_password") or "").strip()
if not old_password:
return error_response(400, "请输入原密码")
if len(new_password) < 6:
return error_response(400, "新密码长度不能少于6位")
change_password(db, user_id, old_password, new_password)
return success_response({"message": "密码修改成功"})
except HTTPException:
return error_response(401, "Token无效或已过期")
except ValueError as exc:
return error_response(400, str(exc))
_AVATAR_MAX_BYTES = 2 * 1024 * 1024
_AVATAR_ALLOWED_EXT = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
@router.post("/web/auth/upload-avatar", response_model=dict)
async def upload_current_user_avatar(
request: Request,
file: UploadFile = File(...),
db: Session = Depends(get_db),
):
"""当前用户上传头像(仅图片,最大 2MB)。"""
try:
await _resolve_current_user_from_request(request, db)
except HTTPException:
return error_response(401, "Token无效或已过期")
if not file or not file.filename:
return error_response(400, "请选择图片")
ext = os.path.splitext(file.filename)[1].lower()
if ext not in _AVATAR_ALLOWED_EXT:
return error_response(400, "仅支持 JPG、PNG、GIF、WEBP、BMP 格式")
content_type = (file.content_type or "").lower()
if content_type and not content_type.startswith("image/"):
return error_response(400, "仅支持上传图片文件")
try:
file_content = await file.read()
if not file_content:
return error_response(400, "文件内容为空")
if len(file_content) > _AVATAR_MAX_BYTES:
return error_response(400, "头像大小不能超过 2MB")
url = storage.upload_file(file_content, file.filename, "avatar")
stored_url = extract_avatar_storage_key(url)
return success_response({"url": stored_url})
except Exception as exc:
return error_response(500, f"上传失败: {str(exc)}")
@router.get("/web/auth/menu-codes", response_model=dict)
async def get_web_auth_menu_codes(request: Request, db: Session = Depends(get_db)):
"""获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。"""
+8 -2
View File
@@ -225,7 +225,11 @@ async def get_current_user_by_token(
phone = (getattr(user, "phone_no", None) or "").strip() or None
email = (getattr(user, "email", None) or "").strip() or None
avatar = getattr(user, "avatar", None)
avatar_raw = getattr(user, "avatar", None)
avatar_stored = None
if avatar_raw is not None:
s = str(avatar_raw).strip()
avatar_stored = s if s else None
current_user_dept_id.set(user.dept_id or "")
@@ -237,7 +241,9 @@ async def get_current_user_by_token(
"region_id": user.dept_id,
"phone": phone,
"email": email,
"avatar_url": avatar,
"sex": getattr(user, "sex", None) or "0",
"remark": (getattr(user, "remark", None) or "").strip() or None,
"avatar_url": avatar_stored,
"is_active": user.status == "1",
}
+41
View File
@@ -0,0 +1,41 @@
"""用户头像 URL 解析(sys_user.avatar 可能存 MinIO 相对路径或完整 URL)"""
from typing import Optional
from app.core.config import settings
from app.core.storage import storage
def resolve_avatar_display_url(raw: Optional[str]) -> Optional[str]:
"""服务端内部拼完整 URL(邮件等);Web API 应返回库中原始路径。"""
if raw is None:
return None
s = str(raw).strip()
if not s:
return None
lower = s.lower()
if lower.startswith("http://") or lower.startswith("https://") or s.startswith("/"):
return s
try:
return storage.get_url(s.lstrip("/"))
except Exception:
base = (settings.MINIO_BASE_URL or settings.LOCAL_STORAGE_URL or "").rstrip("/")
if base:
return f"{base}/{s.lstrip('/')}"
return s
def extract_avatar_storage_key(full_url: str) -> str:
"""从 upload 返回值或历史完整 URL 中提取 avatar/ 相对路径。"""
u = (full_url or "").strip()
if not u:
return ""
base_url = (settings.MINIO_BASE_URL or "").rstrip("/")
if base_url and u.startswith(base_url + "/"):
return u[len(base_url) + 1 :].lstrip("/")
marker = "/avatar/"
idx = u.lower().find(marker)
if idx >= 0:
return f"avatar/{u[idx + len(marker) :].lstrip('/')}"
if not u.lower().startswith("http") and not u.startswith("/"):
return u.lstrip("/")
return u
+1
View File
@@ -138,6 +138,7 @@ class Settings(BaseSettings):
"/api/web/admin/products/upload-image,"
"/api/web/admin/categories/upload-image,"
"/api/web/admin/users/upload-avatar,"
"/api/web/auth/upload-avatar,"
"/api/invoice/download"
)
+32 -7
View File
@@ -17,6 +17,8 @@ from app.crud.protocol_donation_convert import (
assert_biz_no_available,
get_partner_sample_codes,
)
from app.services.protocol_payment_installment import calc_fq_installment_due_date
from app.services.protocol_pay_mode_switch import validate_payment_details_for_save
from app.models.research_donation import BizResearchDonation, BizResearchDonationSample
from app.models.models import (
BizAttachment,
@@ -350,7 +352,7 @@ def _payment_to_dict(p: BizProtocolPayment) -> Dict[str, Any]:
"payMode": p.pay_mode or "",
"contractAmount": _dec(p.contract_amount),
"installmentCount": p.installment_count,
"installmentIntervalMonths": p.installment_interval_months,
"installmentIntervalMonths": _dec(p.installment_interval_months),
"lumpSumDeadline": (p.lump_sum_deadline or "").strip() if p.lump_sum_deadline else "",
"installmentStartDate": (p.installment_start_date or "").strip() if p.installment_start_date else "",
"tenantNo": p.tenant_no,
@@ -2537,7 +2539,8 @@ def align_fq_payment_details_payload(
amounts = _calc_installment_due_amounts(pay.contract_amount, count)
start = (pay.installment_start_date or "").strip()
interval = int(pay.installment_interval_months or 0)
interval = pay.installment_interval_months
interval_val = float(interval) if interval is not None else 0
result: List[Dict[str, Any]] = []
for i in range(count):
no = i + 1
@@ -2547,11 +2550,13 @@ def align_fq_payment_details_payload(
if i < len(unnumbered):
raw = dict(unnumbered[i])
raw["installment_no"] = no
if not raw.get("installment_due_date") and start and interval:
raw["installment_due_date"] = _add_months_yyyymmdd(start, i * interval)
if not raw.get("installment_due_date") and start and interval_val:
raw["installment_due_date"] = calc_fq_installment_due_date(start, i, interval_val)
result.append(raw)
continue
due_date = _add_months_yyyymmdd(start, i * interval) if start and interval else ""
due_date = (
calc_fq_installment_due_date(start, i, interval_val) if start and interval_val else ""
)
due = amounts[i] if i < len(amounts) else None
result.append(
{
@@ -2601,8 +2606,8 @@ def upsert_payment(
obj = get_payment_by_protocol_id(db, collection_protocol_id, tenant_no)
_STR_FIELDS = ["pay_mode"]
_STR_DATE_FIELDS = ["lump_sum_deadline", "installment_start_date"]
_DEC_FIELDS = ["contract_amount"]
_INT_FIELDS = ["installment_count", "installment_interval_months"]
_DEC_FIELDS = ["contract_amount", "installment_interval_months"]
_INT_FIELDS = ["installment_count"]
def _apply(target: BizProtocolPayment) -> None:
for k in _STR_FIELDS:
@@ -2658,6 +2663,26 @@ def save_payment_details(
user_id: Optional[str],
details: list,
) -> List[BizProtocolPaymentDetail]:
pay = (
db.query(BizProtocolPayment)
.filter(
and_(
BizProtocolPayment.id == payment_id,
BizProtocolPayment.tenant_no == tenant_no,
BizProtocolPayment.del_flag == 0,
)
)
.first()
)
if pay:
normalized = [_normalize_fields(dict(d)) for d in (details or [])]
validate_payment_details_for_save(
pay.pay_mode or "",
pay.contract_amount,
pay.installment_count,
normalized,
)
existing = list_payment_details(db, payment_id, tenant_no)
for d in existing:
d.del_flag = 1
+19
View File
@@ -418,6 +418,25 @@ def reset_password(db: Session, user_id: str, new_password: str) -> None:
db.commit()
def change_password(db: Session, user_id: str, old_password: str, new_password: str) -> None:
from app.core.security import verify_password
obj = get_user(db, user_id)
if not obj:
raise ValueError("用户不存在")
pwd_hash = getattr(obj, "password", None) or ""
if not verify_password(old_password, pwd_hash):
raise ValueError("原密码不正确")
new_pw = (new_password or "").strip()
if len(new_pw) < 6:
raise ValueError("新密码长度不能少于6位")
obj.password = _hash_password(new_pw)
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
def total_pages(total: int, page_size: int) -> int:
if total <= 0:
return 0
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "label": "avatar_url.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1"}, {"id": "core_avatar_url_resolve_avatar_display_url", "label": "resolve_avatar_display_url()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L8"}, {"id": "core_avatar_url_rationale_1", "label": "\u7528\u6237\u5934\u50cf URL \u89e3\u6790\uff08sys_user.avatar \u53ef\u80fd\u5b58 MinIO \u76f8\u5bf9\u8def\u5f84\u6216\u5b8c\u6574 URL\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "app_core_config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "app_core_storage", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "core_avatar_url_resolve_avatar_display_url", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L8", "weight": 1.0}, {"source": "core_avatar_url_rationale_1", "target": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1", "weight": 1.0}], "raw_calls": [{"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "strip", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L11"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "str", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L11"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "lower", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L14"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "startswith", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L15"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "startswith", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L15"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "startswith", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L15"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "get_url", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L18"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "lstrip", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L18"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "rstrip", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L20"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "lstrip", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L22"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "label": "auth.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L1"}, {"id": "schemas_auth_loginrequest", "label": "LoginRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_auth_loginresponse", "label": "LoginResponse", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13"}, {"id": "schemas_auth_usercreate", "label": "UserCreate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23"}, {"id": "schemas_auth_userresponse", "label": "UserResponse", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L33"}, {"id": "schemas_auth_config", "label": "Config", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L44"}, {"id": "schemas_auth_changepasswordrequest", "label": "ChangePasswordRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L48"}, {"id": "schemas_auth_profileupdaterequest", "label": "ProfileUpdateRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L54"}, {"id": "schemas_auth_resetpasswordrequest", "label": "ResetPasswordRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L65"}, {"id": "schemas_auth_rationale_1", "label": "Pydantic schemas - \u7528\u6237\u8ba4\u8bc1", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L1"}, {"id": "schemas_auth_rationale_55", "label": "\u5f53\u524d\u7528\u6237\u4e2a\u4eba\u4fe1\u606f\u66f4\u65b0\uff08\u4ec5\u5141\u8bb8\u81ea\u52a9\u4fee\u6539\u7684\u5b57\u6bb5\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L55"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_loginrequest", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7", "weight": 1.0}, {"source": "schemas_auth_loginrequest", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_loginresponse", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13", "weight": 1.0}, {"source": "schemas_auth_loginresponse", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_usercreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23", "weight": 1.0}, {"source": "schemas_auth_usercreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_userresponse", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "label": "auth.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L1"}, {"id": "schemas_auth_loginrequest", "label": "LoginRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_auth_loginresponse", "label": "LoginResponse", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13"}, {"id": "schemas_auth_usercreate", "label": "UserCreate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23"}, {"id": "schemas_auth_userresponse", "label": "UserResponse", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L33"}, {"id": "schemas_auth_config", "label": "Config", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L44"}, {"id": "schemas_auth_changepasswordrequest", "label": "ChangePasswordRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L48"}, {"id": "schemas_auth_profileupdaterequest", "label": "ProfileUpdateRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L54"}, {"id": "schemas_auth_resetpasswordrequest", "label": "ResetPasswordRequest", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L64"}, {"id": "schemas_auth_rationale_1", "label": "Pydantic schemas - \u7528\u6237\u8ba4\u8bc1", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L1"}, {"id": "schemas_auth_rationale_55", "label": "\u5f53\u524d\u7528\u6237\u4e2a\u4eba\u4fe1\u606f\u66f4\u65b0\uff08\u4ec5\u5141\u8bb8\u81ea\u52a9\u4fee\u6539\u7684\u5b57\u6bb5\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L55"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_loginrequest", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7", "weight": 1.0}, {"source": "schemas_auth_loginrequest", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_loginresponse", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13", "weight": 1.0}, {"source": "schemas_auth_loginresponse", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_usercreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23", "weight": 1.0}, {"source": "schemas_auth_usercreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_auth_py", "target": "schemas_auth_userresponse", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_public_py", "label": "public.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_avatar_url_field", "label": "_avatar_url_field()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L39"}, {"id": "api_public_build_user_info_payload", "label": "_build_user_info_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L43"}, {"id": "api_public_build_current_user_payload", "label": "_build_current_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L61"}, {"id": "api_public_resolve_current_user_from_request", "label": "_resolve_current_user_from_request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L83"}, {"id": "api_public_jwt_user_payload", "label": "_jwt_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L91"}, {"id": "api_public_web_login_rsa_public_key", "label": "web_login_rsa_public_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L102"}, {"id": "api_public_web_login", "label": "web_login()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L109"}, {"id": "api_public_refresh_token", "label": "refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L167"}, {"id": "api_public_logout", "label": "logout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L229"}, {"id": "api_public_get_current_user_profile", "label": "get_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L248"}, {"id": "api_public_update_current_user_profile", "label": "update_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L258"}, {"id": "api_public_change_current_user_password", "label": "change_current_user_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L294"}, {"id": "api_public_upload_current_user_avatar", "label": "upload_current_user_avatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L326"}, {"id": "api_public_get_web_auth_menu_codes", "label": "get_web_auth_menu_codes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L362"}, {"id": "api_public_rationale_1", "label": "\u516c\u5171\u63a5\u53e3\uff08Web \u767b\u5f55\u3001\u5237\u65b0 Token\u3001\u767b\u51fa\uff1b\u7528\u6237\u6a21\u578b\u5bf9\u9f50 docs/sql/zkxw.sql \u57fa\u7ebf\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_rationale_44", "label": "\u4e0e web-admin `userInfo` \u5b57\u6bb5\u7ea6\u5b9a\u4e00\u81f4\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L44"}, {"id": "api_public_rationale_62", "label": "\u7ec4\u88c5 current-user \u63a5\u53e3\u54cd\u5e94\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L62"}, {"id": "api_public_rationale_103", "label": "Web \u7aef\u767b\u5f55\u7528 RSA \u516c\u94a5\uff08PKCS#1 v1.5\uff0c\u4e0e JSEncrypt \u4e00\u81f4\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L103"}, {"id": "api_public_rationale_114", "label": "Web \u7aef\u767b\u5f55\uff1a\u8d26\u53f7\u4e3a sys_user.user_no\uff0c\u5bc6\u7801\u4e3a sys_user.password\uff08bcrypt\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L114"}, {"id": "api_public_rationale_230", "label": "\u767b\u51fa\uff1a\u540a\u9500 Redis \u4f1a\u8bdd\uff08\u82e5\u5df2\u914d\u7f6e REDIS_HOST\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L230"}, {"id": "api_public_rationale_249", "label": "\u83b7\u53d6\u5f53\u524d\u7528\u6237\u4fe1\u606f\uff08\u4e0e sys_user \u5b57\u6bb5\u5bf9\u5e94\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilongLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_user_py", "label": "user.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L1"}, {"id": "crud_user_hash_password", "label": "_hash_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L18"}, {"id": "crud_user_usercrudcompat", "label": "_UserCRUDCompat", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L22"}, {"id": "crud_user_usercrudcompat_get_by_user_no", "label": ".get_by_user_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L23"}, {"id": "crud_user_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L38"}, {"id": "crud_user_list_users", "label": "list_users()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L58"}, {"id": "crud_user_get_user", "label": "get_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L109"}, {"id": "crud_user_list_post_ids_for_user", "label": "list_post_ids_for_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L117"}, {"id": "crud_user_set_user_post_ids_only", "label": "set_user_post_ids_only()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L127"}, {"id": "crud_user_list_user_ids_bound_to_post", "label": "list_user_ids_bound_to_post()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L159"}, {"id": "crud_user_sync_post_user_bindings", "label": "sync_post_user_bindings()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L174"}, {"id": "crud_user_get_user_with_relations", "label": "get_user_with_relations()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L202"}, {"id": "crud_user_user_no_exists", "label": "user_no_exists()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L227"}, {"id": "crud_user_replace_associations", "label": "_replace_associations()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L236"}, {"id": "crud_user_create_user", "label": "create_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L287"}, {"id": "crud_user_update_user", "label": "update_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L346"}, {"id": "crud_user_soft_delete_user", "label": "soft_delete_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L401"}, {"id": "crud_user_reset_password", "label": "reset_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L411"}, {"id": "crud_user_change_password", "label": "change_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L421"}, {"id": "crud_user_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L440"}, {"id": "crud_user_rationale_1", "label": "\u7528\u6237 CRUD\uff08sys_user + sys_user_role + sys_user_post\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L1"}, {"id": "crud_user_rationale_134", "label": "\u4ec5\u66ff\u6362 sys_user_post\uff0c\u4e0d\u5f71\u54cd sys_user_role\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L134"}, {"id": "crud_user_rationale_181", "label": "\u5c06\u672c\u804c\u4f4d\u7ed1\u5b9a\u7684\u7528\u6237\u96c6\u5408\u8bbe\u4e3a user_ids\uff08\u53ea\u589e\u5220\u672c\u804c\u4f4d\uff0c\u4fdd\u7559\u7528\u6237\u5176\u5b83\u804c\u4f4d\u4e0e\u89d2\u8272\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py", "source_location": "L181"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_user_py", "target": "math", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.pyLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "label": "avatar_url.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1"}, {"id": "core_avatar_url_resolve_avatar_display_url", "label": "resolve_avatar_display_url()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L8"}, {"id": "core_avatar_url_extract_avatar_storage_key", "label": "extract_avatar_storage_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L27"}, {"id": "core_avatar_url_rationale_1", "label": "\u7528\u6237\u5934\u50cf URL \u89e3\u6790\uff08sys_user.avatar \u53ef\u80fd\u5b58 MinIO \u76f8\u5bf9\u8def\u5f84\u6216\u5b8c\u6574 URL\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1"}, {"id": "core_avatar_url_rationale_9", "label": "\u670d\u52a1\u7aef\u5185\u90e8\u62fc\u5b8c\u6574 URL\uff08\u90ae\u4ef6\u7b49\uff09\uff1bWeb API \u5e94\u8fd4\u56de\u5e93\u4e2d\u539f\u59cb\u8def\u5f84\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L9"}, {"id": "core_avatar_url_rationale_28", "label": "\u4ece upload \u8fd4\u56de\u503c\u6216\u5386\u53f2\u5b8c\u6574 URL \u4e2d\u63d0\u53d6 avatar/ \u76f8\u5bf9\u8def\u5f84\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L28"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "app_core_config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "app_core_storage", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "core_avatar_url_resolve_avatar_display_url", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "target": "core_avatar_url_extract_avatar_storage_key", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L27", "weight": 1.0}, {"source": "core_avatar_url_rationale_1", "target": "users_wuzilong_work_00code_zkxw_backend_app_core_avatar_url_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L1", "weight": 1.0}, {"source": "core_avatar_url_rationale_9", "target": "core_avatar_url_resolve_avatar_display_url", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L9", "weight": 1.0}, {"source": "core_avatar_url_rationale_28", "target": "core_avatar_url_extract_avatar_storage_key", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L28", "weight": 1.0}], "raw_calls": [{"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "strip", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L12"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "str", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L12"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "lower", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L15"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "startswith", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L16"}, {"caller_nid": "core_avatar_url_resolve_avatar_display_url", "callee": "startswith", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/avatar_url.py", "source_location": "L16"}, {"caller_nid": "core_avatar_url_resolve_avataLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "label": "auth.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L1"}, {"id": "core_auth_redis_session_enabled", "label": "_redis_session_enabled()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L30"}, {"id": "core_auth_get_redis_client", "label": "_get_redis_client()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L35"}, {"id": "core_auth_session_key", "label": "_session_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L52"}, {"id": "core_auth_refresh_key", "label": "_refresh_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L56"}, {"id": "core_auth_store_session", "label": "store_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L60"}, {"id": "core_auth_revoke_session", "label": "revoke_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L68"}, {"id": "core_auth_extend_session", "label": "extend_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L76"}, {"id": "core_auth_is_session_valid", "label": "is_session_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L92"}, {"id": "core_auth_store_refresh_token", "label": "store_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L107"}, {"id": "core_auth_is_refresh_token_valid", "label": "is_refresh_token_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L115"}, {"id": "core_auth_revoke_refresh_token", "label": "revoke_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L127"}, {"id": "core_auth_create_access_token", "label": "create_access_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L134"}, {"id": "core_auth_create_refresh_token", "label": "create_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L158"}, {"id": "core_auth_decode_token", "label": "decode_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L174"}, {"id": "core_auth_get_current_user_by_token", "label": "get_current_user_by_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L187"}, {"id": "core_auth_get_current_user", "label": "get_current_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L248"}, {"id": "core_auth_get_current_user_simple", "label": "get_current_user_simple()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L256"}, {"id": "core_auth_get_client_ip", "label": "get_client_ip()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L275"}, {"id": "core_auth_rationale_77", "label": "\u5237\u65b0\u4f1a\u8bdd TTL\uff08refreshToken \u7eed\u671f\uff1b\u5df2\u767b\u5f55\u7528\u6237\u6bcf\u6b21 API \u6821\u9a8c\u901a\u8fc7\u540e\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L77"}, {"id": "core_auth_rationale_108", "label": "\u5199\u5165 refreshToken\uff08\u7528\u4e8e one-time \u4f7f\u7528/\u540a\u9500\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L108"}, {"id": "core_auth_rationale_191", "label": "\u6839\u636e token \u83b7\u53d6\u5f53\u524d\u7528\u6237\uff08\u5305\u542b Redis \u4f1a\u8bdd\u6821\u9a8c\uff1b\u4f1a\u8bdd\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L191"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "target": "asyncio", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_pLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_public_py", "label": "public.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_avatar_url_field", "label": "_avatar_url_field()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L36"}, {"id": "api_public_build_user_info_payload", "label": "_build_user_info_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L44"}, {"id": "api_public_build_current_user_payload", "label": "_build_current_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L62"}, {"id": "api_public_resolve_current_user_from_request", "label": "_resolve_current_user_from_request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L84"}, {"id": "api_public_jwt_user_payload", "label": "_jwt_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L92"}, {"id": "api_public_web_login_rsa_public_key", "label": "web_login_rsa_public_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L103"}, {"id": "api_public_web_login", "label": "web_login()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L110"}, {"id": "api_public_refresh_token", "label": "refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L168"}, {"id": "api_public_logout", "label": "logout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L230"}, {"id": "api_public_get_current_user_profile", "label": "get_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L249"}, {"id": "api_public_update_current_user_profile", "label": "update_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L259"}, {"id": "api_public_change_current_user_password", "label": "change_current_user_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L294"}, {"id": "api_public_get_web_auth_menu_codes", "label": "get_web_auth_menu_codes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L322"}, {"id": "api_public_rationale_1", "label": "\u516c\u5171\u63a5\u53e3\uff08Web \u767b\u5f55\u3001\u5237\u65b0 Token\u3001\u767b\u51fa\uff1b\u7528\u6237\u6a21\u578b\u5bf9\u9f50 docs/sql/zkxw.sql \u57fa\u7ebf\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_rationale_45", "label": "\u4e0e web-admin `userInfo` \u5b57\u6bb5\u7ea6\u5b9a\u4e00\u81f4\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L45"}, {"id": "api_public_rationale_63", "label": "\u7ec4\u88c5 current-user \u63a5\u53e3\u54cd\u5e94\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L63"}, {"id": "api_public_rationale_104", "label": "Web \u7aef\u767b\u5f55\u7528 RSA \u516c\u94a5\uff08PKCS#1 v1.5\uff0c\u4e0e JSEncrypt \u4e00\u81f4\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L104"}, {"id": "api_public_rationale_115", "label": "Web \u7aef\u767b\u5f55\uff1a\u8d26\u53f7\u4e3a sys_user.user_no\uff0c\u5bc6\u7801\u4e3a sys_user.password\uff08bcrypt\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L115"}, {"id": "api_public_rationale_231", "label": "\u767b\u51fa\uff1a\u540a\u9500 Redis \u4f1a\u8bdd\uff08\u82e5\u5df2\u914d\u7f6e REDIS_HOST\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L231"}, {"id": "api_public_rationale_250", "label": "\u83b7\u53d6\u5f53\u524d\u7528\u6237\u4fe1\u606f\uff08\u4e0e sys_user \u5b57\u6bb5\u5bf9\u5e94\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L250"}, {"id": "api_public_rationale_264", "label": "\u5f53\u524d\u7528\u6237\u66f4\u65b0\u4e2a\u4eba\u4fe1\u606f\uff08\u624b\u673a\u53f7\u3001\u90aeLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_collection_protocol_py", "label": "collection_protocol.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L1"}, {"id": "crud_collection_protocol_get_protocol_nos_used_in_protocols", "label": "get_protocol_nos_used_in_protocols()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L52"}, {"id": "crud_collection_protocol_get_sample_codes_used_in_protocol_samples", "label": "get_sample_codes_used_in_protocol_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L86"}, {"id": "crud_collection_protocol_validate_sample_codes_not_used", "label": "validate_sample_codes_not_used()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L120"}, {"id": "crud_collection_protocol_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L153"}, {"id": "crud_collection_protocol_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L159"}, {"id": "crud_collection_protocol_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L163"}, {"id": "crud_collection_protocol_optional_decimal", "label": "_optional_decimal()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L171"}, {"id": "crud_collection_protocol_today_yyyymmdd", "label": "_today_yyyymmdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L177"}, {"id": "crud_collection_protocol_normalize_yyyymmdd_date", "label": "_normalize_yyyymmdd_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L181"}, {"id": "crud_collection_protocol_normalize_fields", "label": "_normalize_fields()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L200"}, {"id": "crud_collection_protocol_pop_attachment_urls", "label": "_pop_attachment_urls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L211"}, {"id": "crud_collection_protocol_attachments_to_urls", "label": "attachments_to_urls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L222"}, {"id": "crud_collection_protocol_normalize_attachment_url_key", "label": "_normalize_attachment_url_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L241"}, {"id": "crud_collection_protocol_format_attachment_file_size", "label": "_format_attachment_file_size()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L250"}, {"id": "crud_collection_protocol_lookup_attachment_by_url", "label": "_lookup_attachment_by_url()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L258"}, {"id": "crud_collection_protocol_urls_to_attachment_list", "label": "urls_to_attachment_list()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L282"}, {"id": "crud_collection_protocol_user_nick_name_map", "label": "_user_nick_name_map()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L314"}, {"id": "crud_collection_protocol_protocol_to_dict", "label": "_protocol_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L322"}, {"id": "crud_collection_protocol_payment_to_dict", "label": "_payment_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L347"}, {"id": "crud_collection_protocol_detail_to_dict", "label": "_detail_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py", "source_location": "L365"}, {"id": "crud_collection_protocol_audit_to_dict", "label": "_audit_to_dict()", "file_type": "code", "source_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "label": "auth.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L1"}, {"id": "core_auth_redis_session_enabled", "label": "_redis_session_enabled()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L29"}, {"id": "core_auth_get_redis_client", "label": "_get_redis_client()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L34"}, {"id": "core_auth_session_key", "label": "_session_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L51"}, {"id": "core_auth_refresh_key", "label": "_refresh_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L55"}, {"id": "core_auth_store_session", "label": "store_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L59"}, {"id": "core_auth_revoke_session", "label": "revoke_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L67"}, {"id": "core_auth_extend_session", "label": "extend_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L75"}, {"id": "core_auth_is_session_valid", "label": "is_session_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L91"}, {"id": "core_auth_store_refresh_token", "label": "store_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L106"}, {"id": "core_auth_is_refresh_token_valid", "label": "is_refresh_token_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L114"}, {"id": "core_auth_revoke_refresh_token", "label": "revoke_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L126"}, {"id": "core_auth_create_access_token", "label": "create_access_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L133"}, {"id": "core_auth_create_refresh_token", "label": "create_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L157"}, {"id": "core_auth_decode_token", "label": "decode_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L173"}, {"id": "core_auth_get_current_user_by_token", "label": "get_current_user_by_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L186"}, {"id": "core_auth_get_current_user", "label": "get_current_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L251"}, {"id": "core_auth_get_current_user_simple", "label": "get_current_user_simple()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L259"}, {"id": "core_auth_get_client_ip", "label": "get_client_ip()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L278"}, {"id": "core_auth_rationale_76", "label": "\u5237\u65b0\u4f1a\u8bdd TTL\uff08refreshToken \u7eed\u671f\uff1b\u5df2\u767b\u5f55\u7528\u6237\u6bcf\u6b21 API \u6821\u9a8c\u901a\u8fc7\u540e\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L76"}, {"id": "core_auth_rationale_107", "label": "\u5199\u5165 refreshToken\uff08\u7528\u4e8e one-time \u4f7f\u7528/\u540a\u9500\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L107"}, {"id": "core_auth_rationale_190", "label": "\u6839\u636e token \u83b7\u53d6\u5f53\u524d\u7528\u6237\uff08\u5305\u542b Redis \u4f1a\u8bdd\u6821\u9a8c\uff1b\u4f1a\u8bdd\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L190"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "target": "asyncio", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_pLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "label": "auth.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L1"}, {"id": "core_auth_redis_session_enabled", "label": "_redis_session_enabled()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L29"}, {"id": "core_auth_get_redis_client", "label": "_get_redis_client()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L34"}, {"id": "core_auth_session_key", "label": "_session_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L51"}, {"id": "core_auth_refresh_key", "label": "_refresh_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L55"}, {"id": "core_auth_store_session", "label": "store_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L59"}, {"id": "core_auth_revoke_session", "label": "revoke_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L67"}, {"id": "core_auth_extend_session", "label": "extend_session()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L75"}, {"id": "core_auth_is_session_valid", "label": "is_session_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L91"}, {"id": "core_auth_store_refresh_token", "label": "store_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L106"}, {"id": "core_auth_is_refresh_token_valid", "label": "is_refresh_token_valid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L114"}, {"id": "core_auth_revoke_refresh_token", "label": "revoke_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L126"}, {"id": "core_auth_create_access_token", "label": "create_access_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L133"}, {"id": "core_auth_create_refresh_token", "label": "create_refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L157"}, {"id": "core_auth_decode_token", "label": "decode_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L173"}, {"id": "core_auth_get_current_user_by_token", "label": "get_current_user_by_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L186"}, {"id": "core_auth_get_current_user", "label": "get_current_user()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L247"}, {"id": "core_auth_get_current_user_simple", "label": "get_current_user_simple()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L255"}, {"id": "core_auth_get_client_ip", "label": "get_client_ip()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L274"}, {"id": "core_auth_rationale_76", "label": "\u5237\u65b0\u4f1a\u8bdd TTL\uff08refreshToken \u7eed\u671f\uff1b\u5df2\u767b\u5f55\u7528\u6237\u6bcf\u6b21 API \u6821\u9a8c\u901a\u8fc7\u540e\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L76"}, {"id": "core_auth_rationale_107", "label": "\u5199\u5165 refreshToken\uff08\u7528\u4e8e one-time \u4f7f\u7528/\u540a\u9500\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L107"}, {"id": "core_auth_rationale_190", "label": "\u6839\u636e token \u83b7\u53d6\u5f53\u524d\u7528\u6237\uff08\u5305\u542b Redis \u4f1a\u8bdd\u6821\u9a8c\uff1b\u4f1a\u8bdd\u6ed1\u52a8\u7eed\u671f\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L190"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_py", "target": "asyncio", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_auth_pLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_models_py", "label": "models.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L1"}, {"id": "models_models_userrole", "label": "UserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L10"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysuser", "label": "SysUser", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L22"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysrole", "label": "SysRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L48"}, {"id": "models_models_sysuserrole", "label": "SysUserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L68"}, {"id": "models_models_sysrolemenu", "label": "SysRoleMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L87"}, {"id": "models_models_sysroledatascopedetail", "label": "SysRoleDataScopeDetail", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L106"}, {"id": "models_models_sysmenu", "label": "SysMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L126"}, {"id": "models_models_sysdept", "label": "SysDept", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L152"}, {"id": "models_models_systenant", "label": "SysTenant", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L175"}, {"id": "models_models_syssite", "label": "SysSite", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L195"}, {"id": "models_models_syspost", "label": "SysPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L220"}, {"id": "models_models_sysuserpost", "label": "SysUserPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L240"}, {"id": "models_models_bizitemunit", "label": "BizItemUnit", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L259"}, {"id": "models_models_bizexpresscompany", "label": "BizExpressCompany", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L278"}, {"id": "models_models_bizcustomer", "label": "BizCustomer", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L297"}, {"id": "models_models_bizcellsampletype", "label": "BizCellSampleType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L320"}, {"id": "models_models_bizcollectionregion", "label": "BizCollectionRegion", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L341"}, {"id": "models_models_bizcollectionhospital", "label": "BizCollectionHospital", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L359"}, {"id": "models_models_bizcollectionprotocolno", "label": "BizCollectionProtocolNo", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L378"}, {"id": "models_models_bizbiznosequence", "label": "BizBizNoSequence", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L400"}, {"id": "models_models_bizsamplecode", "label": "BizSampleCode", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L420"}, {"id": "models_models_sysdicttype", "label": "SysDictType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L448"}, {"id": "models_models_sysethnic", "label": "SysEthnic", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L467"}, {"id": "models_models_sysdictdata", "label": "SysDictData", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location"Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "label": "protocol_payment_installment.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L1"}, {"id": "services_protocol_payment_installment_add_days_yyyymmdd", "label": "add_days_yyyymmdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L15"}, {"id": "services_protocol_payment_installment_add_months_yyyymmdd", "label": "add_months_yyyymmdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L28"}, {"id": "services_protocol_payment_installment_add_interval_months_yyyymmdd", "label": "add_interval_months_yyyymmdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L43"}, {"id": "services_protocol_payment_installment_calc_fq_installment_due_date", "label": "calc_fq_installment_due_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L63"}, {"id": "services_protocol_payment_installment_rationale_1", "label": "\u5206\u671f\u7f34\u8d39\uff1a\u5e94\u7f34\u65e5\u8ba1\u7b97\uff08\u6574\u6570\u6708\u6309\u65e5\u5386\u9012\u589e\uff0c\u5c0f\u6570\u6708\u6309 30 \u5929/\u6708\u6362\u7b97\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L1"}, {"id": "services_protocol_payment_installment_rationale_44", "label": "\u6574\u6570\u90e8\u5206\u6309\u65e5\u5386\u52a0\u6708\uff0c\u5c0f\u6570\u90e8\u5206\u6309 30 \u5929/\u6708\u6362\u7b97\u4e3a\u5929\u6570\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L44"}, {"id": "services_protocol_payment_installment_rationale_68", "label": "\u7b2c 1 \u671f=\u5f00\u59cb\u65f6\u95f4\uff1b\u7b2c 2 \u671f\u8d77\u5728\u95f4\u9694\u9012\u589e\u57fa\u7840\u4e0a\u63d0\u524d 10 \u5929\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L68"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "calendar", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "decimal", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "services_protocol_payment_installment_add_days_yyyymmdd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "services_protocol_payment_installment_add_months_yyyymmdd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L28", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "services_protocol_payment_installment_add_interval_months_yyyymmdd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L43", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_protocol_payment_installment_py", "target": "services_protocol_payment_installment_calc_fq_installment_due_date", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py", "source_location": "L63", "weight": 1.0}, {"source": "services_protocol_payment_instalLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "label": "config.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L1"}, {"id": "core_config_settings", "label": "Settings", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L7"}, {"id": "basesettings", "label": "BaseSettings", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "core_config_config", "label": "Config", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L145"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "pydantic_settings", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "pathlib", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "os", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "core_config_settings", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L7", "weight": 1.0}, {"source": "core_config_settings", "target": "basesettings", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_core_config_py", "target": "core_config_config", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py", "source_location": "L145", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_public_py", "label": "public.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_avatar_url_field", "label": "_avatar_url_field()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L38"}, {"id": "api_public_build_user_info_payload", "label": "_build_user_info_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L46"}, {"id": "api_public_build_current_user_payload", "label": "_build_current_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L64"}, {"id": "api_public_resolve_current_user_from_request", "label": "_resolve_current_user_from_request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L86"}, {"id": "api_public_jwt_user_payload", "label": "_jwt_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L94"}, {"id": "api_public_web_login_rsa_public_key", "label": "web_login_rsa_public_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L105"}, {"id": "api_public_web_login", "label": "web_login()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L112"}, {"id": "api_public_refresh_token", "label": "refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L170"}, {"id": "api_public_logout", "label": "logout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L232"}, {"id": "api_public_get_current_user_profile", "label": "get_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L251"}, {"id": "api_public_update_current_user_profile", "label": "update_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L261"}, {"id": "api_public_change_current_user_password", "label": "change_current_user_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L297"}, {"id": "api_public_upload_current_user_avatar", "label": "upload_current_user_avatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L329"}, {"id": "api_public_get_web_auth_menu_codes", "label": "get_web_auth_menu_codes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L365"}, {"id": "api_public_rationale_1", "label": "\u516c\u5171\u63a5\u53e3\uff08Web \u767b\u5f55\u3001\u5237\u65b0 Token\u3001\u767b\u51fa\uff1b\u7528\u6237\u6a21\u578b\u5bf9\u9f50 docs/sql/zkxw.sql \u57fa\u7ebf\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_rationale_47", "label": "\u4e0e web-admin `userInfo` \u5b57\u6bb5\u7ea6\u5b9a\u4e00\u81f4\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L47"}, {"id": "api_public_rationale_65", "label": "\u7ec4\u88c5 current-user \u63a5\u53e3\u54cd\u5e94\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L65"}, {"id": "api_public_rationale_106", "label": "Web \u7aef\u767b\u5f55\u7528 RSA \u516c\u94a5\uff08PKCS#1 v1.5\uff0c\u4e0e JSEncrypt \u4e00\u81f4\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L106"}, {"id": "api_public_rationale_117", "label": "Web \u7aef\u767b\u5f55\uff1a\u8d26\u53f7\u4e3a sys_user.user_no\uff0c\u5bc6\u7801\u4e3a sys_user.password\uff08bcrypt\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L117"}, {"id": "api_public_rationale_233", "label": "\u767b\u51fa\uff1a\u540a\u9500 Redis \u4f1a\u8bdd\uff08\u82e5\u5df2\u914d\u7f6e REDIS_HOST\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L233"}, {"id": "api_public_rationale_252", "label": "\u83b7\u53d6\u5f53\u524d\u7528\u6237\u4fe1\u606f\uff08\u4e0e sys_user \u5b57\u6bb5\u5bf9\u5e94\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilongLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_public_py", "label": "public.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_avatar_url_field", "label": "_avatar_url_field()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L39"}, {"id": "api_public_build_user_info_payload", "label": "_build_user_info_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L48"}, {"id": "api_public_build_current_user_payload", "label": "_build_current_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L66"}, {"id": "api_public_resolve_current_user_from_request", "label": "_resolve_current_user_from_request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L88"}, {"id": "api_public_jwt_user_payload", "label": "_jwt_user_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L96"}, {"id": "api_public_web_login_rsa_public_key", "label": "web_login_rsa_public_key()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L107"}, {"id": "api_public_web_login", "label": "web_login()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L114"}, {"id": "api_public_refresh_token", "label": "refresh_token()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L172"}, {"id": "api_public_logout", "label": "logout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L234"}, {"id": "api_public_get_current_user_profile", "label": "get_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L253"}, {"id": "api_public_update_current_user_profile", "label": "update_current_user_profile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L263"}, {"id": "api_public_change_current_user_password", "label": "change_current_user_password()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L299"}, {"id": "api_public_upload_current_user_avatar", "label": "upload_current_user_avatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L331"}, {"id": "api_public_get_web_auth_menu_codes", "label": "get_web_auth_menu_codes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L368"}, {"id": "api_public_rationale_1", "label": "\u516c\u5171\u63a5\u53e3\uff08Web \u767b\u5f55\u3001\u5237\u65b0 Token\u3001\u767b\u51fa\uff1b\u7528\u6237\u6a21\u578b\u5bf9\u9f50 docs/sql/zkxw.sql \u57fa\u7ebf\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L1"}, {"id": "api_public_rationale_40", "label": "\u8fd4\u56de\u5e93\u4e2d\u539f\u59cb\u5934\u50cf\u8def\u5f84\uff0c\u7531\u524d\u7aef\u6309 VITE_MINIO_BASE_URL \u62fc\u5c55\u793a URL\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L40"}, {"id": "api_public_rationale_49", "label": "\u4e0e web-admin `userInfo` \u5b57\u6bb5\u7ea6\u5b9a\u4e00\u81f4\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L49"}, {"id": "api_public_rationale_67", "label": "\u7ec4\u88c5 current-user \u63a5\u53e3\u54cd\u5e94\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L67"}, {"id": "api_public_rationale_108", "label": "Web \u7aef\u767b\u5f55\u7528 RSA \u516c\u94a5\uff08PKCS#1 v1.5\uff0c\u4e0e JSEncrypt \u4e00\u81f4\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L108"}, {"id": "api_public_rationale_119", "label": "Web \u7aef\u767b\u5f55\uff1a\u8d26\u53f7\u4e3a sys_user.user_no\uff0c\u5bc6\u7801\u4e3a sys_user.password\uff08bcrypt\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py", "source_location": "L119"}, {"id": "api_public_rationale_235", "label": "\u767b\u51fa\uff1a\u540a\u9500 Redis \u4f1a\u8bdd\uff08\u82e5\u5df2\u914d\u7f6e REDIS_HOST\uff09\u3002", "file_type": "rationale", Line truncated
+1 -1
View File
@@ -824,7 +824,7 @@ class BizProtocolPayment(Base):
pay_mode = Column(String(8), nullable=False, comment="dj趸交 fq分期")
contract_amount = Column(Numeric(18, 2), nullable=True, comment="合同金额")
installment_count = Column(Integer, nullable=True, comment="分期期数")
installment_interval_months = Column(Integer, nullable=True, comment="每期间隔(月)")
installment_interval_months = Column(Numeric(10, 2), nullable=True, comment="每期间隔(月)")
lump_sum_deadline = Column(CHAR(8), nullable=True, comment="趸交截止时间YYYYMMDD")
installment_start_date = Column(CHAR(8), nullable=True, comment="分期开始YYYYMMDD")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
+12 -1
View File
@@ -1,5 +1,5 @@
"""Pydantic schemas - 用户认证"""
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional
from datetime import datetime
@@ -51,6 +51,17 @@ class ChangePasswordRequest(BaseModel):
new_password: str = Field(..., min_length=6)
class ProfileUpdateRequest(BaseModel):
"""当前用户个人信息更新(仅允许自助修改的字段)"""
model_config = ConfigDict(populate_by_name=True)
phone_no: Optional[str] = Field(None, alias="phoneNo", max_length=11)
email: Optional[str] = Field(None, max_length=50)
sex: Optional[str] = Field(None, pattern=r"^[012]$")
remark: Optional[str] = Field(None, max_length=512)
avatar: Optional[str] = Field(None, alias="avatarUrl", max_length=512)
class ResetPasswordRequest(BaseModel):
"""重置密码请求(运营操作)"""
user_id: int
@@ -0,0 +1,84 @@
"""缴费模式切换校验(与前端 protocol-pay-mode-switch.ts 规则一致)"""
from __future__ import annotations
from decimal import Decimal
from typing import Any, Dict, Optional
LOCKED_STATUSES = frozenset({"jfqrz", "yjf", "tfqrz", "ytf"})
BLOCKING_STATUSES = frozenset({"jfqrz", "tfqrz", "ytf"})
def _dec(v: Any) -> float:
if v is None:
return 0.0
return float(Decimal(str(v)))
def validate_payment_details_for_save(
pay_mode: str,
contract_amount: Optional[Any],
installment_count: Optional[int],
details: Optional[list],
) -> None:
"""保存缴费明细前的 P0 校验。"""
rows = [dict(d) for d in (details or [])]
contract = _dec(contract_amount)
for r in rows:
status = (r.get("pay_status") or r.get("payStatus") or "dfj").strip()
if status in BLOCKING_STATUSES:
raise ValueError("存在缴费确认中或退费处理中的明细,无法保存")
paid_sum = sum(
_dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
for r in rows
if (r.get("pay_status") or r.get("payStatus")) == "yjf"
)
dfj_rows = [
r
for r in rows
if (r.get("pay_status") or r.get("payStatus") or "dfj") == "dfj"
]
yjf_rows = [r for r in rows if (r.get("pay_status") or r.get("payStatus")) == "yjf"]
if pay_mode == "dj":
if len(dfj_rows) > 1:
raise ValueError("趸交模式仅允许一条待缴费明细")
remaining = round(contract - paid_sum, 2)
if dfj_rows:
due = _dec(
dfj_rows[0].get("due_amount")
if dfj_rows[0].get("due_amount") is not None
else dfj_rows[0].get("dueAmount")
)
expected = remaining if yjf_rows else contract
if abs(due - expected) > 0.015:
raise ValueError(
f"趸交待缴金额应为 {expected:.2f},当前为 {due:.2f}"
)
elif pay_mode == "fq":
count = int(installment_count or 0)
if count < 1:
raise ValueError("分期模式须填写分期期数")
if len(rows) != count:
raise ValueError(f"分期明细条数应等于分期期数({count})")
fq_forbidden = frozenset({"jfqrz", "tfqrz", "ytf"})
for r in rows:
status = (r.get("pay_status") or r.get("payStatus") or "dfj").strip()
if status in fq_forbidden:
raise ValueError("分期模式下存在不可保存的缴费状态")
total_due = sum(
_dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
for r in rows
)
if contract and abs(total_due - contract) > 0.015:
raise ValueError("分期各期应缴金额之和应等于合同金额")
if yjf_rows:
max_paid_no = max(
int(r.get("installment_no") or r.get("installmentNo") or 0)
for r in yjf_rows
)
if max_paid_no > count:
raise ValueError("分期期数不能少于已缴费的最大期次")
@@ -0,0 +1,73 @@
"""分期缴费:应缴日计算(整数月按日历递增,小数月按 30 天/月换算)"""
from __future__ import annotations
import calendar
from decimal import Decimal, ROUND_DOWN
from typing import Union
Number = Union[int, float, Decimal, str]
DAYS_PER_MONTH = 30
FQ_ADVANCE_DAYS = 10
def add_days_yyyymmdd(date_str: str, days: int) -> str:
raw = (date_str or "").strip().replace("-", "")
if len(raw) != 8:
return ""
from datetime import date, timedelta
y = int(raw[0:4])
m = int(raw[4:6])
d = int(raw[6:8])
dt = date(y, m, d) + timedelta(days=days)
return f"{dt.year:04d}{dt.month:02d}{dt.day:02d}"
def add_months_yyyymmdd(date_str: str, months: int) -> str:
raw = (date_str or "").strip().replace("-", "")
if len(raw) != 8:
return ""
y = int(raw[0:4])
m = int(raw[4:6])
d = int(raw[6:8])
month_index = m - 1 + months
ny = y + month_index // 12
nm = month_index % 12 + 1
last_day = calendar.monthrange(ny, nm)[1]
nd = min(d, last_day)
return f"{ny:04d}{nm:02d}{nd:02d}"
def add_interval_months_yyyymmdd(date_str: str, total_months: Number) -> str:
"""整数部分按日历加月,小数部分按 30 天/月换算为天数。"""
raw = (date_str or "").strip().replace("-", "")
if len(raw) != 8:
return ""
total = Decimal(str(total_months))
if total == 0:
return raw
whole = int(total.to_integral_value(rounding=ROUND_DOWN))
frac = total - Decimal(whole)
result = raw
if whole:
result = add_months_yyyymmdd(result, whole)
if frac:
days = int(round(float(frac) * DAYS_PER_MONTH))
if days:
result = add_days_yyyymmdd(result, days)
return result
def calc_fq_installment_due_date(
start_date: str,
index: int,
interval_months: Number,
) -> str:
"""第 1 期=开始时间;第 2 期起在间隔递增基础上提前 10 天。"""
total_months = Decimal(str(index)) * Decimal(str(interval_months))
base = add_interval_months_yyyymmdd(start_date, total_months)
if index >= 1:
return add_days_yyyymmdd(base, -FQ_ADVANCE_DAYS)
return base
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+176 -101
View File
@@ -1,12 +1,12 @@
# Graph Report - app (2026-06-03)
# Graph Report - app (2026-06-04)
## Corpus Check
- 179 files · ~187,032 words
- 181 files · ~187,864 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3386 nodes · 15106 edges · 40 communities detected
- Extraction: 37% EXTRACTED · 63% INFERRED · 0% AMBIGUOUS · INFERRED: 9448 edges (avg confidence: 0.61)
- 3412 nodes · 15181 edges · 55 communities detected
- Extraction: 38% EXTRACTED · 62% INFERRED · 0% AMBIGUOUS · INFERRED: 9487 edges (avg confidence: 0.61)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -44,17 +44,32 @@
- [[_COMMUNITY_Community 31|Community 31]]
- [[_COMMUNITY_Community 32|Community 32]]
- [[_COMMUNITY_Community 33|Community 33]]
- [[_COMMUNITY_Community 34|Community 34]]
- [[_COMMUNITY_Community 35|Community 35]]
- [[_COMMUNITY_Community 36|Community 36]]
- [[_COMMUNITY_Community 37|Community 37]]
- [[_COMMUNITY_Community 38|Community 38]]
- [[_COMMUNITY_Community 39|Community 39]]
- [[_COMMUNITY_Community 40|Community 40]]
- [[_COMMUNITY_Community 41|Community 41]]
- [[_COMMUNITY_Community 42|Community 42]]
- [[_COMMUNITY_Community 43|Community 43]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 47|Community 47]]
- [[_COMMUNITY_Community 48|Community 48]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 54|Community 54]]
- [[_COMMUNITY_Community 55|Community 55]]
- [[_COMMUNITY_Community 56|Community 56]]
## God Nodes (most connected - your core abstractions)
1. `success_response()` - 524 edges
2. `error_response()` - 424 edges
3. `SysUser` - 359 edges
1. `success_response()` - 527 edges
2. `error_response()` - 427 edges
3. `SysUser` - 363 edges
4. `BizQcTaskResult` - 272 edges
5. `BizQcMaterialUsage` - 264 edges
6. `BizQcMaterial` - 262 edges
@@ -68,215 +83,275 @@
core/api_request_log_middleware.py → models/models.py
- `get_menu_list()` --calls--> `success_response()` [INFERRED]
api/menu.py → core/response.py
- `公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线)` --uses--> `SysUser` [INFERRED]
api/public.py → models/models.py
- `与 web-admin `userInfo` 字段约定一致。` --uses--> `SysUser` [INFERRED]
api/public.py → models/models.py
- `Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致)` --uses--> `SysUser` [INFERRED]
api/public.py → models/models.py
- `中试服务交付管理 API(交付信息、交付物料、交付方式)` --uses--> `SysUser` [INFERRED]
api/pilot_delivery.py → models/models.py
- `保留匹配节点及其全部上级,供关键词检索后仍能正确组树。` --uses--> `SysDept` [INFERRED]
crud/dept.py → models/models.py
- `物品单位 CRUD(biz_item_unit)` --uses--> `BizItemUnit` [INFERRED]
crud/item_unit.py → models/models.py
## Communities
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (633): attachment_download_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu() (+625 more)
Nodes (596): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+588 more)
### Community 1 - "Community 1"
Cohesion: 0.01
Nodes (463): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+455 more)
Nodes (523): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+515 more)
### Community 2 - "Community 2"
Cohesion: 0.05
Nodes (285): 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, Base (+277 more)
Nodes (387): AuditCreateBody, BatchDetailIdsBody, BatchRefundFinanceSubmitBody, FinanceConfirmBody, FullmoonTodoInsuranceBody, ProtocolCreateBody, ProtocolUpdateBody, 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API (+379 more)
### Community 3 - "Community 3"
Cohesion: 0.02
Nodes (205): generate_uuid(), batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status() (+197 more)
Nodes (214): list_collection_regions(), list_protocol_nos(), _ensure_customer_for_mother(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id() (+206 more)
### Community 4 - "Community 4"
Cohesion: 0.07
Nodes (223): AuditCreateBody, BatchDetailIdsBody, BatchRefundFinanceSubmitBody, FinanceConfirmBody, FullmoonTodoInsuranceBody, ProtocolCreateBody, ProtocolUpdateBody, 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API (+215 more)
Cohesion: 0.05
Nodes (178): generate_uuid(), _deduct_fee_on_delivery(), 创建交付时扣减费用。 流程: 1. schedule → plan → application 2. application.chan, activate_channel_agreement(), _apply_gmcp_order_quotas(), _apply_purchase_txn_holdings(), _apply_unsubscribe_txn_holdings(), _batch_gmcp_current_holding_amounts() (+170 more)
### Community 5 - "Community 5"
Cohesion: 0.02
Nodes (157): get_designer_data(), delete(), get_multi_with_filters(), create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict() (+149 more)
Cohesion: 0.03
Nodes (167): _format_attachment_file_size(), urls_to_attachment_list(), list_production_tasks(), apply_task_result(), complete_task(), _conclusion_label(), create_qc_task_manual(), create_task_for_line() (+159 more)
### Community 6 - "Community 6"
Cohesion: 0.03
Nodes (161): api_create_outbound_qc(), _format_attachment_file_size(), urls_to_attachment_list(), batch_review(), _fmt_dt(), get_schedule_detail(), _get_user_name(), list_reviews_paginated() (+153 more)
Nodes (103): _avatar_url_field(), _build_current_user_payload(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线) (+95 more)
### Community 7 - "Community 7"
Cohesion: 0.03
Nodes (144): api_save_application(), eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), _apply_submit_receipt() (+136 more)
Nodes (108): get_protocol_nos_used_in_protocols(), create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), 字典类型 CRUD(sys_dict_type), _row_to_dict(), soft_delete_dict_type() (+100 more)
### Community 8 - "Community 8"
Cohesion: 0.03
Nodes (128): _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), 数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3, 根据用户角色的数据权限过滤查询。 Args: query: SQLAlchemy query 对象 model: 查询, 从 SysUser 读取用户的 dept_id。 (+120 more)
Nodes (63): _error_json(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, ApiCryptoError, _b64url_decode() (+55 more)
### Community 9 - "Community 9"
Cohesion: 0.03
Nodes (67): _error_json(), lifespan(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, ApiCryptoError (+59 more)
Cohesion: 0.04
Nodes (94): api_save_application(), eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), auto_generate_schedules() (+86 more)
### Community 10 - "Community 10"
Cohesion: 0.03
Nodes (89): generate_id(), camel_to_snake(), CRUDBase, get_model_attr(), HistoryCRUD, get_protocol_nos_used_in_protocols(), create_dict_type(), dict_type_code_exists() (+81 more)
Cohesion: 0.05
Nodes (82): 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() (+74 more)
### Community 11 - "Community 11"
Cohesion: 0.06
Nodes (87): AccessLogCreateBody, AccessLogUpdateBody, batch_create_storage_slots(), CleanRoomCreateBody, CleanRoomUpdateBody, create_access_log(), create_storage_slot(), delete_access_log() (+79 more)
### Community 12 - "Community 12"
Cohesion: 0.35
Nodes (61): BatchStockInBody, BatchStockInItem, BatchStockOutBody, BatchStockOutItem, ConsumableCreateBody, ConsumablePackCreateBody, ConsumablePackUpdateBody, ConsumableUpdateBody (+53 more)
### Community 12 - "Community 12"
Cohesion: 0.07
Nodes (62): batch_delete_research_donations(), change_donation_status(), convert_from_storage(), convert_to_storage(), create_research_donation(), delete_research_donation(), _get_current_user_id(), get_research_donation_detail() (+54 more)
### Community 13 - "Community 13"
Cohesion: 0.06
Nodes (57): _avatar_url_field(), _build_user_info_payload(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。 (+49 more)
Cohesion: 0.07
Nodes (51): get_designer_data(), ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, list_results_by_task() (+43 more)
### Community 14 - "Community 14"
Cohesion: 0.06
Nodes (60): 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() (+52 more)
Cohesion: 0.16
Nodes (32): _create_insurance_company(), _create_insurance_company_invoice(), _create_insurance_project_price(), delete_insurance_company(), delete_insurance_company_invoice(), delete_insurance_project_price(), _fmt_dt(), _generate_uuid() (+24 more)
### Community 15 - "Community 15"
Cohesion: 0.08
Nodes (55): batch_delete_research_donations(), change_donation_status(), convert_from_storage(), convert_to_storage(), create_research_donation(), delete_research_donation(), _get_current_user_id(), get_research_donation_detail() (+47 more)
### Community 16 - "Community 16"
Cohesion: 0.12
Nodes (30): _apply_qc_status_filter(), _base_query(), _derive_qc_conclusion(), _ensure_notification(), _eq_ci(), _fetch_completed_tasks(), _fmt_dt(), generate_sms_content() (+22 more)
### Community 17 - "Community 17"
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 16 - "Community 16"
Cohesion: 0.16
Nodes (26): add_step(), copy_step(), create_route(), _create_step_consumable(), _create_step_data_collection(), _create_step_output(), _create_step_reagent(), get_route() (+18 more)
### Community 17 - "Community 17"
Cohesion: 0.14
Nodes (23): batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status(), list_consumables() (+15 more)
### Community 18 - "Community 18"
Cohesion: 0.13
Nodes (18): _access_log_to_dict(), create_access_log(), create_clean_room(), get_access_log(), get_clean_room(), list_access_logs(), list_clean_rooms(), room_code_exists() (+10 more)
### Community 19 - "Community 19"
Cohesion: 0.16
Nodes (19): get_by_schedule(), get_method(), _get_tenant_no(), list_by_application(), list_materials(), 中试服务交付管理 API(交付信息、交付物料、交付方式), soft_delete(), create_delivery() (+11 more)
### Community 20 - "Community 20"
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 19 - "Community 19"
Cohesion: 0.18
Nodes (18): company_code_exists(), create_company(), get_company(), list_companies(), list_company_options(), _row_to_dict(), soft_delete_company(), update_company() (+10 more)
### Community 20 - "Community 20"
Cohesion: 0.25
Nodes (13): code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), list_materials(), _norm_status(), options_list() (+5 more)
### Community 21 - "Community 21"
Cohesion: 0.21
Nodes (16): batch_stock_in(), batch_stock_out(), _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), list_materials() (+8 more)
### Community 22 - "Community 22"
Cohesion: 0.25
Nodes (14): batch_stock_in(), batch_stock_out(), create_reagent(), _dec(), get_reagent(), _is_enabled_status(), list_reagents(), _norm_status() (+6 more)
### Community 23 - "Community 23"
Cohesion: 0.2
Nodes (14): deduct_for_task(), _display_remark(), list_usages(), list_usages_by_task(), _parse_receiver_purpose_from_remark(), 查询质检任务关联的物料使用记录(task_complete)。, 撤销任务此前 task_complete 扣减,便于重新填报物料使用。, 兼容旧数据:领用人/用途曾拼接在 remark 中。 (+6 more)
### Community 24 - "Community 24"
Cohesion: 0.25
Nodes (13): batch_issue_protocol_nos(), batch_revoke_protocol_nos(), create_protocol_no(), get_protocol_no(), list_protocol_nos(), _parse_issued_time(), protocol_no_exists(), 采集协议号池 CRUD(biz_collection_protocol_no) (+5 more)
### Community 22 - "Community 22"
### Community 25 - "Community 25"
Cohesion: 0.24
Nodes (9): 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import Wech, 发送模板消息 Args: openid: 用户openid templ, send_delivery_notice(), send_delivery_warning(), send_order_status(), send_payment_success(), send_refund_result(), send_urge_notification() (+1 more)
### Community 23 - "Community 23"
Cohesion: 0.24
Nodes (9): _ensure_customer_for_mother(), create_customer(), get_customer(), id_card_taken(), list_customers(), 客户 CRUD(biz_customer), _row_to_dict(), soft_delete_customer() (+1 more)
### Community 26 - "Community 26"
Cohesion: 0.23
Nodes (12): Config, ProductBase, ProductCategoryBase, ProductCategoryCreate, ProductCategoryResponse, ProductCreate, ProductResponse, ProductUnitBase (+4 more)
### Community 24 - "Community 24"
### Community 27 - "Community 27"
Cohesion: 0.27
Nodes (11): create_consumable_pack(), get_consumable_pack(), get_pack_detail(), _item_row_to_dict(), list_all_packs(), list_consumable_packs(), pack_code_exists(), 耗材包 CRUD(biz_consumable_pack + biz_consumable_pack_item) (+3 more)
### Community 28 - "Community 28"
Cohesion: 0.24
Nodes (12): appointment_no_exists(), create_exhibition_appointment(), _format_visit_date(), _generate_appointment_no(), get_exhibition_appointment(), list_exhibition_appointments(), 展览预约 CRUD(biz_exhibition_appointment), 生成预约编号:YYYYMMDD + 6位序号(如 20260515000001) (+4 more)
### Community 29 - "Community 29"
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 30 - "Community 30"
Cohesion: 0.27
Nodes (11): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。 (+3 more)
### Community 31 - "Community 31"
Cohesion: 0.31
Nodes (8): create_sop_document(), get_sop_document(), list_sop_documents(), SOP文档 CRUD(biz_sop_document), _row_to_dict(), soft_delete_sop_document(), sop_code_exists(), update_sop_document()
### Community 32 - "Community 32"
Cohesion: 0.31
Nodes (8): create_express_company(), express_code_exists(), get_express_company(), list_express_companies(), 快递公司 CRUD(biz_express_company), _row_to_dict(), soft_delete_express_company(), update_express_company()
### Community 33 - "Community 33"
Cohesion: 0.38
Nodes (9): create_check_item(), get_check_item(), item_code_exists(), list_active_check_items(), list_check_items(), _order_query(), _row_to_dict(), soft_delete_check_item() (+1 more)
### Community 25 - "Community 25"
### Community 34 - "Community 34"
Cohesion: 0.27
Nodes (8): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), 字典数据 CRUD(sys_dict_data), _row_to_dict(), soft_delete_dict_data(), update_dict_data()
### Community 35 - "Community 35"
Cohesion: 0.31
Nodes (8): category_code_exists(), create_process_category(), get_process_category(), list_process_categories(), 工艺工序分类 CRUD(biz_process_category), _row_to_dict(), soft_delete_process_category(), update_process_category()
### Community 36 - "Community 36"
Cohesion: 0.31
Nodes (8): create_item_unit(), get_item_unit(), list_item_units(), 物品单位 CRUD(biz_item_unit), _row_to_dict(), soft_delete_item_unit(), unit_code_exists(), update_item_unit()
### Community 37 - "Community 37"
Cohesion: 0.31
Nodes (8): create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict(), soft_delete_cell_sample_type(), type_code_exists(), update_cell_sample_type()
### Community 38 - "Community 38"
Cohesion: 0.31
Nodes (8): create_output_type(), get_output_type(), list_output_types(), 工艺产出物类型 CRUD(biz_output_type), _row_to_dict(), soft_delete_output_type(), type_code_exists(), update_output_type()
### Community 39 - "Community 39"
Cohesion: 0.36
Nodes (9): deduct_for_task(), _display_remark(), list_usages(), list_usages_by_task(), _parse_receiver_purpose_from_remark(), _resolve_receiver_purpose(), revert_task_material_usages(), _row_to_dict() (+1 more)
Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
### Community 26 - "Community 26"
Cohesion: 0.39
Nodes (8): create_lab_item(), get_lab_item(), item_code_exists(), list_active_lab_items(), list_lab_items(), _row_to_dict(), soft_delete_lab_item(), update_lab_item()
### Community 40 - "Community 40"
Cohesion: 0.33
Nodes (7): _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), _row_to_dict(), soft_delete_menu(), update_menu()
### Community 27 - "Community 27"
### Community 41 - "Community 41"
Cohesion: 0.36
Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
### Community 42 - "Community 42"
Cohesion: 0.29
Nodes (2): BaseModel, SoftDeleteModel
### Community 28 - "Community 28"
### Community 43 - "Community 43"
Cohesion: 0.38
Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
### Community 29 - "Community 29"
### Community 44 - "Community 44"
Cohesion: 0.5
Nodes (3): BaseSettings, Config, Settings
### Community 30 - "Community 30"
### Community 45 - "Community 45"
Cohesion: 0.5
Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
### Community 31 - "Community 31"
### Community 46 - "Community 46"
Cohesion: 0.67
Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,13 个一级菜单)
### Community 32 - "Community 32"
### Community 47 - "Community 47"
Cohesion: 1.0
Nodes (1): 请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
### Community 33 - "Community 33"
### Community 48 - "Community 48"
Cohesion: 1.0
Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
### Community 36 - "Community 36"
### Community 51 - "Community 51"
Cohesion: 1.0
Nodes (1): 发送订单状态通知 Args: openid: 用户openid ord
### Community 37 - "Community 37"
### Community 52 - "Community 52"
Cohesion: 1.0
Nodes (1): 发送支付成功通知 Args: openid: 用户openid ord
### Community 38 - "Community 38"
### Community 53 - "Community 53"
Cohesion: 1.0
Nodes (1): 发送配送通知 Args: openid: 配送员openid task
### Community 39 - "Community 39"
### Community 54 - "Community 54"
Cohesion: 1.0
Nodes (1): 发送配送时效预警 Args: openid: 配送员openid ta
### Community 40 - "Community 40"
### Community 55 - "Community 55"
Cohesion: 1.0
Nodes (1): 发送退款结果通知 Args: openid: 用户openid ord
### Community 41 - "Community 41"
### Community 56 - "Community 56"
Cohesion: 1.0
Nodes (1): 发送催单通知给配送员 Args: deliver_openid: 配送员openid
## Knowledge Gaps
- **99 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+94 more)
- **106 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+101 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 27`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
- **Thin community `Community 42`** (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 30`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
- **Thin community `Community 45`** (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 31`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,13 个一级菜单)`
- **Thin community `Community 46`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,13 个一级菜单)`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 32`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
- **Thin community `Community 47`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 33`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
- **Thin community `Community 48`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 36`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
- **Thin community `Community 51`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 37`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
- **Thin community `Community 52`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 38`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
- **Thin community `Community 53`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 39`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
- **Thin community `Community 54`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 40`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
- **Thin community `Community 55`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 41`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
- **Thin community `Community 56`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 4`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 15`, `Community 16`, `Community 21`?**
_High betweenness centrality (0.113) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 3` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 23`, `Community 24`, `Community 25`, `Community 26`?**
_High betweenness centrality (0.073) - this node is a cross-community bridge._
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 31`?**
_High betweenness centrality (0.067) - this node is a cross-community bridge._
- **Are the 523 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
_`success_response()` has 523 INFERRED edges - model-reasoned connections that need verification._
- **Are the 514 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `extract_login_credentials()`) actually correct?**
_`str` has 514 INFERRED edges - model-reasoned connections that need verification._
- **Are the 423 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
_`error_response()` has 423 INFERRED edges - model-reasoned connections that need verification._
- **Are the 357 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
_`SysUser` has 357 INFERRED edges - model-reasoned connections that need verification._
- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 6`, `Community 7`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 17`, `Community 19`, `Community 23`, `Community 24`, `Community 29`?**
_High betweenness centrality (0.117) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 4` to `Community 0`, `Community 1`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 16`, `Community 17`, `Community 18`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 36`, `Community 37`, `Community 38`, `Community 39`, `Community 40`, `Community 41`?**
_High betweenness centrality (0.069) - this node is a cross-community bridge._
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 3`, `Community 6`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 46`, `Community 14`, `Community 19`?**
_High betweenness centrality (0.068) - this node is a cross-community bridge._
- **Are the 526 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
_`success_response()` has 526 INFERRED edges - model-reasoned connections that need verification._
- **Are the 521 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
_`str` has 521 INFERRED edges - model-reasoned connections that need verification._
- **Are the 426 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
_`error_response()` has 426 INFERRED edges - model-reasoned connections that need verification._
- **Are the 361 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
_`SysUser` has 361 INFERRED edges - model-reasoned connections that need verification._
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">3386 nodes &middot; 15106 edges &middot; 46 communities</div>
<div id="stats">3412 nodes &middot; 15181 edges &middot; 61 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 34, "community_name": "Community 34", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 7}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_error_json", "label": "_error_json()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "_error_json()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 3}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "sqlalchemy_exception_handler()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "unhandled_exception_handler()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 5}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_rationale_81", "label": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "app_main_rationale_92", "label": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 9, "community_name": "Community 9", "source_file": "tasks/__init__.py", "file_type": "code", "degLine truncated
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_error_json", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_sqlalchemy_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_unhandled_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_error_json", "to": "app_main_sqlalchemy_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_error_json", "to": "app_main_unhandled_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "app_main_rationale_81", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "app_main_rationale_92", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_unhandled_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "str", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "labeLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 656}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 499}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 291}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 226}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 225}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 198}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 175}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 153}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 151}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 121}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 112}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 92}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 68}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 64}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 64}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 59}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 34}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 31}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 21}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 20}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 15}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 15}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 13}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 12}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 10}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 10}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 9}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 8}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 7}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 4}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 4}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 3}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 2}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 2}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 1}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 1}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 1}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 1}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 1}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 1}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 1}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 1}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 1}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 1}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 1}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 1}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 0}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 0}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 0}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 0}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 0}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 0}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 0}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 0}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 0}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 0}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 0}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 0}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 0}];
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 49, "community_name": "Community 49", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 7}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_error_json", "label": "_error_json()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "_error_json()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 3}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "sqlalchemy_exception_handler()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "unhandled_exception_handler()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 5}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_rationale_81", "label": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "app_main_rationale_92", "label": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 6, "community_name": "Community 6", "source_file": "tasks/__init__.py", "file_type": "code", "degLine truncated
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_error_json", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_sqlalchemy_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_unhandled_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_error_json", "to": "app_main_sqlalchemy_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_error_json", "to": "app_main_unhandled_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "app_main_rationale_81", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "app_main_rationale_92", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_unhandled_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "str", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "labeLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 620}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 558}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 393}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 253}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 187}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 181}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 124}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 123}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 115}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 99}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 88}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 68}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 67}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 64}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 33}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 31}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 28}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 27}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 22}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 22}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 21}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 16}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 15}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 15}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 13}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 13}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 13}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 13}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 12}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 12}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 10}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 10}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 10}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 10}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 10}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 10}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 10}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 9}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 9}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 9}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 8}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 7}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 4}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 4}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 2}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 2}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 1}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 1}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 1}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 1}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 1}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 1}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 1}];
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
File diff suppressed because it is too large. Load diff
+9 -7
View File
@@ -2,12 +2,13 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/__init__.py": 1778578257.989357,
"/Users/wuzilong/work/00code/zkxw/backend/app/main.py": 1780457411.2283201,
"/Users/wuzilong/work/00code/zkxw/backend/app/tasks/__init__.py": 1778579841.437791,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py": 1779245791.6811426,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py": 1780540737.3359916,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/api_crypto.py": 1778578258.039314,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/login_crypto.py": 1778578258.041774,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py": 1779313263.9269438,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/config.py": 1780539434.4590578,
"/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,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/database.py": 1779242347.136184,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/security.py": 1778578258.044949,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/request_payload.py": 1778578258.04292,
@@ -26,7 +27,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/core/wechat_message.py": 1778578258.0458734,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/material.py": 1779283385.245847,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py": 1779022399.0175831,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1780462850.084297,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1780545639.1395557,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/__init__.py": 1779418058.876012,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/research_donation.py": 1780458012.8329656,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_material.py": 1779928228.734251,
@@ -35,7 +36,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py": 1779491680.5729434,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_task.py": 1779597960.4337523,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/process_route.py": 1779367496.1718137,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py": 1778578258.0939043,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py": 1780539434.444314,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/delivery.py": 1778578258.0949655,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/outbound_release_review.py": 1779968230.0408957,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/price_list.py": 1778578258.096609,
@@ -76,7 +77,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_review.py": 1779968239.1429183,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/post.py": 1779265648.38155,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_check_result.py": 1779255909.8554008,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py": 1778775532.8870275,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py": 1780539127.1805215,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_work_order.py": 1780479191.5054278,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/material.py": 1779283386.3879013,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/dict_type.py": 1779265621.5212462,
@@ -89,7 +90,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1779927441.9434412,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_third_party_item.py": 1779265661.482352,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/reagent.py": 1779512669.9847593,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py": 1780479210.0456562,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py": 1780545654.7291799,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material_usage.py": 1779332036.2049248,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/biz_no_generator.py": 1779882385.4674172,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_infusion_confirm.py": 1779828818.7646794,
@@ -146,7 +147,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/clinical_payment.py": 1779969576.9773977,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py": 1779526635.1455016,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/outbound_release_review.py": 1779968232.5759368,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1779313409.0685387,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1780540754.892583,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/material.py": 1779526648.041865,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py": 1779965447.565143,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/collection_protocol.py": 1779882422.004045,
@@ -177,5 +178,6 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/services/workflow_callback.py": 1778727966.7277644,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/workflow_engine.py": 1778634664.6293883,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/web_auth_roles.py": 1778775532.8878627,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py": 1780545628.43657,
"/Users/wuzilong/work/00code/zkxw/backend/app/ws/__init__.py": 1778578258.105995
}
@@ -0,0 +1,110 @@
"""缴费模式切换校验"""
import unittest
from app.services.protocol_pay_mode_switch import validate_payment_details_for_save
class TestValidatePaymentDetailsForSave(unittest.TestCase):
def test_dj_single_dfj_full_contract(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "dfj", "dueAmount": 10000}],
)
def test_dj_rejects_multiple_dfj(self):
with self.assertRaises(ValueError) as ctx:
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 5000},
{"payStatus": "dfj", "dueAmount": 5000},
],
)
self.assertIn("仅允许一条待缴费", str(ctx.exception))
def test_dj_partial_paid_remaining(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "yjf", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 8000},
],
)
def test_dj_partial_paid_wrong_remaining(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "yjf", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 10000},
],
)
def test_fq_count_mismatch(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"fq",
6000,
6,
[{"payStatus": "dfj", "dueAmount": 6000}],
)
def test_fq_partial_paid_valid(self):
validate_payment_details_for_save(
"fq",
10000,
4,
[
{"payStatus": "yjf", "installmentNo": 1, "dueAmount": 2500},
{"payStatus": "yjf", "installmentNo": 2, "dueAmount": 2500},
{"payStatus": "dfj", "installmentNo": 3, "dueAmount": 2500},
{"payStatus": "dfj", "installmentNo": 4, "dueAmount": 2500},
],
)
def test_fq_jfqrz_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"fq",
6000,
2,
[
{"payStatus": "jfqrz", "dueAmount": 3000},
{"payStatus": "dfj", "dueAmount": 3000},
],
)
def test_fq_ytf_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"fq",
6000,
2,
[
{"payStatus": "ytf", "dueAmount": 3000},
{"payStatus": "dfj", "dueAmount": 3000},
],
)
def test_blocking_status_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "jfqrz", "dueAmount": 10000}],
)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,44 @@
"""分期应缴日:小数月间隔(30 天/月)与提前 10 天规则"""
import unittest
from app.services.protocol_payment_installment import calc_fq_installment_due_date
class TestCalcFqInstallmentDueDate(unittest.TestCase):
def test_first_period_equals_start_date(self):
self.assertEqual(calc_fq_installment_due_date("20260604", 0, 0.5), "20260604")
def test_fractional_interval_half_month_six_periods(self):
start = "20260604"
interval = 0.5
expected = [
"20260604",
"20260609",
"20260624",
"20260709",
"20260725",
"20260809",
]
for i, exp in enumerate(expected):
self.assertEqual(
calc_fq_installment_due_date(start, i, interval),
exp,
msg=f"period {i + 1}",
)
def test_fractional_interval_0_1_advance_10_days(self):
# +3 天 = 20260607,再提前 10 天
self.assertEqual(calc_fq_installment_due_date("20260604", 1, 0.1), "20260528")
def test_integer_interval_six_months(self):
# +6 日历月 = 20261204,再提前 10 天
self.assertEqual(calc_fq_installment_due_date("20260604", 1, 6), "20261124")
def test_fractional_interval_0_3(self):
# index=2 → 0.6 月 → 18 天 → 20260622,再提前 10 天
self.assertEqual(calc_fq_installment_due_date("20260604", 2, 0.3), "20260612")
if __name__ == "__main__":
unittest.main()
+5
View File
@@ -144,6 +144,7 @@ zkxw-docker-deploy-<版本>/
│ └── mysql/init/
├── docs/sql/zkxw.sql # 首次部署自动建库用
├── 部署说明.md
├── 部署指导手册.html # 客户安装手册(浏览器打开,推荐交付)
└── VERSION.txt
```
@@ -412,6 +413,10 @@ docker compose up -d
### 发版方打包时 pip / pnpm 很慢或超时
- 已默认 **阿里云 PyPI** + **npmmirror**,并启用 BuildKit 缓存。
- 若前端构建在 `corepack prepare pnpm@...` 报错(请求 `registry.npmjs.org` 失败),请更新到含 `COREPACK_NPM_REGISTRY` 的 Dockerfile,或手动指定:
```bash
NPM_REGISTRY=https://registry.npmmirror.com ./scripts/package-docker-deploy.sh v1.0.0
```
- **在 Mac(M 芯片)上交叉构建 `linux/amd64` 很慢**(后端 pip 常见 15~40 分钟),日志会长时间不动,**不要误按 Ctrl+C**。
- **推荐**:把仓库放到 **Linux x86 云主机**(与甲方同架构),在该机器执行 `./scripts/package-docker-deploy.sh`,通常 5~15 分钟完成。
+2
View File
@@ -5,6 +5,8 @@ ARG NGINX_IMAGE=docker.m.daocloud.io/library/nginx:1.27-alpine
FROM ${NODE_IMAGE} AS builder
ARG NPM_REGISTRY=https://registry.npmmirror.com
# corepack 默认走 registry.npmjs.org,国内/受限网络会失败;须显式指定镜像
ENV COREPACK_NPM_REGISTRY=${NPM_REGISTRY}
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
+499
View File
@@ -0,0 +1,499 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>中科兴皖 Docker 部署指导手册</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #0f172a;
--text-muted: #64748b;
--border: #e2e8f0;
--warn-bg: #fffbeb;
--warn-border: #f59e0b;
--danger-bg: #fef2f2;
--danger-border: #ef4444;
--code-bg: #f8fafc;
--radius: 10px;
--font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--font);
font-size: 15px;
line-height: 1.65;
color: var(--text);
background: #f1f5f9;
}
.layout {
display: grid;
grid-template-columns: 260px 1fr;
min-height: 100vh;
max-width: 1280px;
margin: 0 auto;
background: #fff;
box-shadow: 0 0 40px rgba(15, 23, 42, 0.06);
}
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; }
.sidebar { position: static; height: auto; border-right: none; border-bottom: 1px solid var(--border); }
}
.sidebar {
position: sticky;
top: 0;
height: 100vh;
overflow-y: auto;
padding: 24px 20px;
background: #fafbfc;
border-right: 1px solid var(--border);
}
.sidebar h1 {
font-size: 17px;
margin: 0 0 4px;
color: var(--primary-dark);
}
.sidebar .sub {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 20px;
}
.sidebar nav a {
display: block;
padding: 6px 10px;
margin: 2px 0;
font-size: 13px;
color: var(--text);
text-decoration: none;
border-radius: 6px;
}
.sidebar nav a:hover { background: var(--primary-bg); color: var(--primary-dark); }
main { padding: 32px 40px 64px; min-width: 0; }
@media (max-width: 600px) { main { padding: 20px 16px 48px; } }
h2 {
font-size: 22px;
margin: 40px 0 16px;
padding-bottom: 8px;
border-bottom: 2px solid var(--primary-bg);
color: var(--primary-dark);
}
h2:first-of-type { margin-top: 0; }
h3 { font-size: 17px; margin: 24px 0 12px; }
p { margin: 0 0 12px; }
ul, ol { margin: 0 0 16px; padding-left: 1.4em; }
li { margin-bottom: 6px; }
table {
width: 100%;
border-collapse: collapse;
margin: 12px 0 20px;
font-size: 14px;
}
th, td {
border: 1px solid var(--border);
padding: 10px 12px;
text-align: left;
vertical-align: top;
}
th { background: var(--primary-bg); font-weight: 600; }
pre, code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
font-size: 13px;
}
pre {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 14px 16px;
overflow-x: auto;
margin: 12px 0 20px;
line-height: 1.5;
}
code { background: var(--code-bg); padding: 2px 6px; border-radius: 4px; }
pre code { background: none; padding: 0; }
.hero {
background: linear-gradient(135deg, var(--primary-bg) 0%, #fff 60%);
border: 1px solid #a7f3d0;
border-radius: var(--radius);
padding: 24px 28px;
margin-bottom: 28px;
}
.hero h2 { border: none; margin: 0 0 8px; padding: 0; font-size: 24px; }
.hero p { margin: 0; color: var(--text-muted); }
.badge {
display: inline-block;
background: var(--primary);
color: #fff;
font-size: 12px;
font-weight: 600;
padding: 3px 10px;
border-radius: 999px;
margin-right: 8px;
vertical-align: middle;
}
.steps { counter-reset: step; list-style: none; padding: 0; }
.steps > li {
counter-increment: step;
position: relative;
padding: 16px 16px 16px 52px;
margin-bottom: 12px;
background: #fafbfc;
border: 1px solid var(--border);
border-radius: var(--radius);
}
.steps > li::before {
content: counter(step);
position: absolute;
left: 16px;
top: 16px;
width: 28px;
height: 28px;
line-height: 28px;
text-align: center;
background: var(--primary);
color: #fff;
font-weight: 700;
font-size: 14px;
border-radius: 50%;
}
.steps h3 { margin: 0 0 8px; font-size: 16px; }
.box {
border-radius: 8px;
padding: 14px 16px;
margin: 16px 0;
font-size: 14px;
}
.box-warn { background: var(--warn-bg); border-left: 4px solid var(--warn-border); }
.box-danger { background: var(--danger-bg); border-left: 4px solid var(--danger-border); }
.box-info { background: var(--primary-bg); border-left: 4px solid var(--primary); }
.checklist { list-style: none; padding: 0; }
.checklist li {
padding: 8px 0 8px 28px;
position: relative;
border-bottom: 1px dashed var(--border);
}
.checklist li::before {
content: "☐";
position: absolute;
left: 0;
color: var(--primary);
font-size: 18px;
}
.arch {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
font-family: ui-monospace, monospace;
font-size: 13px;
line-height: 1.45;
white-space: pre;
overflow-x: auto;
}
.tree {
font-family: ui-monospace, monospace;
font-size: 13px;
background: var(--code-bg);
padding: 16px;
border-radius: 8px;
border: 1px solid var(--border);
}
footer {
margin-top: 48px;
padding-top: 20px;
border-top: 1px solid var(--border);
font-size: 13px;
color: var(--text-muted);
}
</style>
</head>
<body>
<div class="layout">
<aside class="sidebar">
<h1>中科兴皖</h1>
<p class="sub">Docker 部署指导手册<br />适用:甲方运维人员</p>
<nav>
<a href="#overview">概述</a>
<a href="#requirements">环境要求</a>
<a href="#package">交付包说明</a>
<a href="#mirror">镜像加速(国内)</a>
<a href="#install">安装步骤</a>
<a href="#config">配置说明</a>
<a href="#verify">验收检查</a>
<a href="#architecture">服务架构</a>
<a href="#profiles">部署模式</a>
<a href="#ops">日常运维</a>
<a href="#backup">备份与升级</a>
<a href="#faq">常见问题</a>
</nav>
</aside>
<main>
<div class="hero">
<h2><span class="badge">Docker</span>中科兴皖系统部署指导</h2>
<p>
本手册帮助您在 Linux 服务器上完成<strong>预构建镜像</strong>方式的安装。
交付包不含前后端源码,首次启动需联网拉取 MySQL / Redis / MinIO 等基础镜像。
对外访问端口默认为 <strong>8112</strong>。
</p>
</div>
<section id="overview">
<h2>1. 概述</h2>
<table>
<tr><th>项目</th><th>说明</th></tr>
<tr><td>部署方式</td><td><code>docker load</code> 导入应用镜像 + <code>docker compose up -d</code></td></tr>
<tr><td>应用镜像</td><td><code>zkxw-backend</code>、<code>zkxw-web</code>(已预构建,无需编译)</td></tr>
<tr><td>CPU 架构</td><td><code>linux/amd64</code>(常见 x86 云主机;ARM 服务器需联系交付方重新打包)</td></tr>
<tr><td>对外端口</td><td><strong>8112</strong>(Web 管理端 + API 反代)</td></tr>
<tr><td>默认账号</td><td><code>admin</code> / <code>111111</code>(首次登录后请立即修改)</td></tr>
</table>
<div class="box box-info">
<strong>说明:</strong>MySQL、Redis、MinIO 仅在 Docker 内部网络通信,<strong>不占用</strong>宿主机 3306 / 6379 / 9000 端口,一般不会与贵方已有中间件冲突。
</div>
</section>
<section id="requirements">
<h2>2. 环境要求</h2>
<table>
<tr><th>项</th><th>要求</th></tr>
<tr><td>操作系统</td><td>Linux x86_64(推荐 Ubuntu 22.04、CentOS 7+)</td></tr>
<tr><td>Docker</td><td>≥ 24.0</td></tr>
<tr><td>Docker Compose</td><td>v2(使用 <code>docker compose</code> 子命令)</td></tr>
<tr><td>网络</td><td>可访问 Docker Hub,或已配置国内 Registry 镜像加速</td></tr>
<tr><td>硬件建议</td><td>4 核 CPU、8 GB 内存、磁盘 ≥ 50 GB</td></tr>
<tr><td>端口</td><td>宿主机 <strong>8112</strong> 未被占用;防火墙 / 安全组已放行 TCP 8112</td></tr>
</table>
<p>安装前请执行以下命令确认环境:</p>
<pre><code>docker --version
docker compose version
docker info | grep -A2 "Registry Mirrors" # 国内环境建议已配置加速</code></pre>
</section>
<section id="package">
<h2>3. 交付包目录说明</h2>
<p>解压 <code>zkxw-docker-deploy-&lt;版本&gt;.tar.gz</code> 后,目录结构如下:</p>
<div class="tree">zkxw-docker-deploy-&lt;版本&gt;/
├── images/
│ └── zkxw-app-images.tar # 预构建前后端镜像(必做 docker load)
├── deploy/docker/
│ ├── docker-compose.yml
│ ├── .env.example # 复制为 .env 后修改密码
│ ├── mysql/init/
│ └── 部署指导手册.html # 本手册
├── docs/sql/zkxw.sql # 首次部署自动建库(非业务源码)
├── 部署说明.md # Markdown 版说明(可选阅读)
└── VERSION.txt # 版本与镜像 tag 信息</div>
<div class="box box-warn">
<strong>不包含:</strong>后端 Python 源码、前端 Vue 源码、Dockerfile。数据库脚本仅为表结构与基线数据。
</div>
</section>
<section id="mirror">
<h2>4. Docker 镜像加速(国内强烈建议)</h2>
<p>
应用镜像通过 <code>docker load</code> 本地导入;但首次 <code>docker compose up</code> 仍需从网络拉取
<code>mysql:5.7.36</code>、<code>redis:7-alpine</code>、<code>minio/minio</code> 等基础镜像。
国内服务器建议配置 Registry 镜像加速。
</p>
<p>编辑 <code>/etc/docker/daemon.json</code>(示例,任选可用加速地址):</p>
<pre><code>{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.m.daocloud.io"
]
}</code></pre>
<pre><code>sudo systemctl daemon-reload
sudo systemctl restart docker</code></pre>
<p>若贵司已有阿里云 ACR 等专属加速地址,请优先使用官方提供的 URL。</p>
</section>
<section id="install">
<h2>5. 安装步骤</h2>
<p>以下命令假设交付包已上传至 <code>/opt/zkxw</code>,请按实际路径调整。</p>
<ol class="steps">
<li>
<h3>解压交付包</h3>
<pre><code>mkdir -p /opt/zkxw && cd /opt/zkxw
tar -xzf zkxw-docker-deploy-*.tar.gz
cd zkxw-docker-deploy-*</code></pre>
</li>
<li>
<h3>导入应用镜像</h3>
<pre><code>docker load -i images/zkxw-app-images.tar
docker images | grep zkxw-</code></pre>
<p>请记下镜像 tag(如 <code>zkxw-backend:v1.0.0</code>),下一步配置须一致。</p>
</li>
<li>
<h3>配置环境变量</h3>
<pre><code>cd deploy/docker
cp .env.example .env
vi .env</code></pre>
<p>至少修改:<code>MYSQL_ROOT_PASSWORD</code>、<code>DATABASE_URL</code> 中的密码、<code>SECRET_KEY</code>、<code>MINIO_SECRET_KEY</code>。</p>
<p>确认 <code>ZKXW_BACKEND_IMAGE</code>、<code>ZKXW_WEB_IMAGE</code> 与 <code>docker images</code> 显示的 tag 一致(可参考包内 <code>VERSION.txt</code>)。</p>
</li>
<li>
<h3>启动全部服务</h3>
<pre><code>docker compose up -d</code></pre>
<p>首次启动会自动拉取基础镜像,MySQL 导入 <code>zkxw.sql</code> 约需 <strong>1~3 分钟</strong>,请耐心等待。</p>
</li>
<li>
<h3>(可选)查看启动进度</h3>
<pre><code>docker compose ps
docker compose logs -f mysql # 可选,Ctrl+C 仅退出日志,不停止服务
docker compose logs -f backend</code></pre>
</li>
</ol>
</section>
<section id="config">
<h2>6. 配置说明(.env 要点)</h2>
<p>默认模式 <code>COMPOSE_PROFILES=full</code>:自带 MySQL、Redis、MinIO。</p>
<table>
<tr><th>变量</th><th>说明</th></tr>
<tr><td><code>HTTP_PORT</code></td><td>对外 HTTP 端口,默认 <strong>8112</strong></td></tr>
<tr><td><code>ZKXW_BACKEND_IMAGE</code></td><td>后端镜像名:tag,须与 docker load 一致</td></tr>
<tr><td><code>ZKXW_WEB_IMAGE</code></td><td>前端镜像名:tag,须与 docker load 一致</td></tr>
<tr><td><code>MYSQL_ROOT_PASSWORD</code></td><td>MySQL root 密码(full 模式)</td></tr>
<tr><td><code>DATABASE_URL</code></td><td>数据库连接串,密码须与上面一致</td></tr>
<tr><td><code>SECRET_KEY</code></td><td>JWT 密钥,生产环境务必改为长随机串</td></tr>
<tr><td><code>REDIS_HOST</code></td><td>full 模式填 <code>redis</code></td></tr>
<tr><td><code>MINIO_ENDPOINT</code></td><td>full 模式填 <code>minio:9000</code></td></tr>
<tr><td><code>MINIO_BASE_URL</code></td><td>保持 <code>/minio/zkxw</code>(由 Web 镜像内 Nginx 反代)</td></tr>
<tr><td><code>API_ENCRYPT_ENABLED</code></td><td>默认 <code>false</code>,Docker 交付为明文 API</td></tr>
</table>
<div class="box box-danger">
<strong>切勿将 .env 提交到版本库。</strong>修改密码后若服务已启动,执行 <code>docker compose up -d</code> 并 <code>docker compose restart backend</code>。
</div>
</section>
<section id="verify">
<h2>7. 验收检查</h2>
<ul class="checklist">
<li><code>docker compose ps</code> 中 mysql、redis、minio、backend、web 均为 running(backend/web 可为 healthy)</li>
<li>服务器本机:<code>curl -s http://127.0.0.1:8112/health</code> 返回正常</li>
<li>浏览器访问 <code>http://&lt;服务器IP&gt;:8112/</code> 可打开登录页</li>
<li>使用 <code>admin</code> / <code>111111</code> 登录成功(登录后请改密)</li>
<li>防火墙 / 安全组已放行 TCP 8112</li>
</ul>
<table>
<tr><th>地址</th><th>说明</th></tr>
<tr><td><code>http://&lt;IP&gt;:8112/</code></td><td>Web 管理端</td></tr>
<tr><td><code>http://&lt;IP&gt;:8112/health</code></td><td>健康检查</td></tr>
<tr><td><code>http://&lt;IP&gt;:8112/docs</code></td><td>Swagger API 文档</td></tr>
</table>
</section>
<section id="architecture">
<h2>8. 服务架构</h2>
<div class="arch">浏览器 :8112
└── web(Nginx + 前端静态)
├── /api/* → backend:8111(FastAPI)
└── /minio/zkxw/* → minio:9000(附件,容器内网络)
backend → mysql:3306(业务库 zkxw)
→ redis:6379(会话)
→ minio:9000(对象存储)</div>
</section>
<section id="profiles">
<h2>9. 部署模式(高级)</h2>
<p>通过 <code>.env</code> 中 <code>COMPOSE_PROFILES</code> 切换,默认无需修改。</p>
<h3>9.1 full(默认,全栈)</h3>
<pre><code>COMPOSE_PROFILES=full</code></pre>
<p>启动 MySQL + Redis + MinIO + 后端 + 前端,适合独立部署一台服务器。</p>
<h3>9.2 app(仅应用,连接已有中间件)</h3>
<p>若贵方已有 MySQL、Redis、MinIO,可设为 <code>COMPOSE_PROFILES=app</code>,并将 <code>DATABASE_URL</code>、<code>REDIS_HOST</code>、<code>MINIO_*</code> 改为实际地址。具体示例见 <code>deploy/docker/.env.example</code> 注释。</p>
<h3>9.3 app,storage(外部库 + 自带 MinIO)</h3>
<p>已有 MySQL/Redis,仅 MinIO 由本 compose 提供时使用。</p>
<div class="box box-warn">
使用外部 MinIO 且不在 Docker 网络内时,可能需要交付方重新构建 Web 镜像。一般建议先按默认 <code>full</code> 模式完成安装。
</div>
</section>
<section id="ops">
<h2>10. 日常运维命令</h2>
<p>以下命令均在 <code>deploy/docker</code> 目录执行:</p>
<pre><code># 查看状态
docker compose ps
# 查看日志
docker compose logs -f backend
docker compose logs -f web
# 重启
docker compose restart
# 停止(不删数据卷)
docker compose down
# 进入后端容器
docker compose exec backend bash</code></pre>
<div class="box box-danger">
<strong>警告:</strong><code>docker compose down -v</code> 会删除所有数据卷(MySQL、MinIO 等),导致数据丢失。仅在首次部署失败且可接受清空数据时使用。
</div>
</section>
<section id="backup">
<h2>11. 备份与升级</h2>
<h3>11.1 数据卷</h3>
<table>
<tr><th>卷名(前缀 zkxw_)</th><th>内容</th></tr>
<tr><td>mysql_data</td><td>MySQL 业务数据</td></tr>
<tr><td>redis_data</td><td>Redis 持久化</td></tr>
<tr><td>minio_data</td><td>附件对象</td></tr>
<tr><td>backend_uploads</td><td>本地上传目录(若使用本地存储)</td></tr>
</table>
<h3>11.2 MySQL 备份示例</h3>
<pre><code>docker compose exec mysql mysqldump -uroot -p zkxw > zkxw_backup.sql</code></pre>
<h3>11.3 版本升级</h3>
<ol>
<li>备份数据库与 MinIO 数据</li>
<li>解压新版本交付包,<code>docker load -i images/zkxw-app-images.tar</code></li>
<li>更新 <code>.env</code> 中 <code>ZKXW_BACKEND_IMAGE</code>、<code>ZKXW_WEB_IMAGE</code></li>
<li>在 <code>deploy/docker</code> 执行 <code>docker compose up -d</code></li>
<li><code>curl -s http://127.0.0.1:8112/health</code> 验收</li>
</ol>
<p>若仅有数据库结构变更,请使用交付方提供的增量 SQL,<strong>不要</strong>删除已有 mysql_data 卷。</p>
</section>
<section id="faq">
<h2>12. 常见问题</h2>
<h3>无法访问 8112</h3>
<ul>
<li>检查云主机安全组 / 防火墙是否放行 TCP 8112</li>
<li><code>docker compose ps</code> 确认 web 为 running</li>
<li><code>docker compose logs web</code> 查看 Nginx 日志</li>
</ul>
<h3>/api 返回 502</h3>
<ul>
<li>后端未就绪:<code>docker compose logs backend</code></li>
<li>首次部署 MySQL 仍在导入:等待 1~3 分钟后重试</li>
</ul>
<h3>拉取 mysql/redis/minio 镜像失败或很慢</h3>
<p>请按第 4 节配置 Docker Registry 镜像加速后,重新执行 <code>docker compose up -d</code>。</p>
<h3>附件无法显示</h3>
<ul>
<li>确认 <code>STORAGE_TYPE=minio</code></li>
<li><code>docker compose logs minio-init</code> 应显示 bucket 创建成功</li>
<li>浏览器中附件 URL 应为 <code>/minio/zkxw/...</code></li>
</ul>
<h3>首次部署数据库初始化失败(可清空重来)</h3>
<pre><code>cd deploy/docker
docker compose down -v
docker compose up -d</code></pre>
<p>此操作会清空所有数据,仅适用于尚未正式上线的环境。</p>
</section>
<footer>
<p>中科兴皖 · Docker 部署指导手册 · 与交付包内 <code>deploy/docker/README.md</code> 保持一致</p>
<p>技术支持请联系项目交付方。安装完成后请修改默认管理员密码。</p>
</footer>
</main>
</div>
</body>
</html>
@@ -0,0 +1,3 @@
-- 每期间隔(月)支持小数,如 0.1、0.5
ALTER TABLE `biz_protocol_payment`
MODIFY COLUMN `installment_interval_months` decimal(10, 2) DEFAULT NULL COMMENT '每期间隔(月)';
+1 -1
View File
@@ -3046,7 +3046,7 @@ CREATE TABLE `biz_protocol_payment` (
`pay_mode` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'dj趸交 fq分期',
`contract_amount` decimal(18,2) DEFAULT NULL COMMENT '合同金额',
`installment_count` int(11) DEFAULT NULL COMMENT '分期期数',
`installment_interval_months` int(11) DEFAULT NULL COMMENT '每期间隔(月)',
`installment_interval_months` decimal(10,2) DEFAULT NULL COMMENT '每期间隔(月)',
`lump_sum_deadline` char(8) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '趸交截止时间YYYYMMDD',
`installment_start_date` char(8) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '分期开始时间YYYYMMDD',
`tenant_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
@@ -0,0 +1,227 @@
# 采集协议单 · 缴费模式切换设计
**日期**:2026-06-04
**范围**:`web-admin` 采集协议单新增/编辑弹窗(`ProtocolFormDialog.vue`)及后端缴费主表/明细保存
**状态**:已实现(P0)
---
## 1. 背景
采集协议单缴费主表 `pay_mode` 有两种取值:
| 值 | 含义 | 明细约定 |
|----|------|----------|
| `dj` | 趸交(一次性) | **1 条**有效明细 |
| `fq` | 分期 | **N 条**明细,N = `installment_count` |
用户在**编辑已保存协议**时可能切换缴费模式。当前实现仅做字段联动(金额同步、日期重算),**不区分明细缴费状态**,会导致:
- 分期改趸交后仍保留多条明细且每条金额 = 合同全额;
- 已缴明细被重算应缴日/期次;
- 趸交已缴后仍可改分期,账实不符。
本设计定义切换规则与实现边界。
---
## 2. 决策记录
| 议题 | 决定 |
|------|------|
| 小数月间隔换算 | 30 天/月(已实现,见 `protocol-payment-installment.ts`) |
| 第 2 期起提前 10 天 | 保留 |
| 分期部分已缴后改趸交,未缴期次 | **A:作废**(软删/不再展示,不进分期预警) |
| 已缴/确认中明细 | **保留**,只读,不参与重算 |
| 趸交已缴后改分期 | **禁止** |
| 趸交确认中改分期 | **禁止**(提示先完成或撤回财务确认) |
---
## 3. 明细状态与「锁定」定义
| pay_status | 含义 | 切换时 |
|------------|------|--------|
| `dfj` | 待缴费 | 可作废、可重建 |
| `jfqrz` | 缴费确认中 | **锁定**(视为已占用,禁止模式切换) |
| `yjf` | 已缴费 | **锁定**(保留,只读) |
| `tfqrz` | 退费确认中 | **锁定**(禁止切换) |
| `ytf` | 已退费 | **锁定**(禁止切换) |
**剩余应缴金额**:
```
remaining = contractAmount − Σ(dueAmount WHERE pay_status IN ('yjf'))
```
`jfqrz` 阶段切换一律阻断;若业务后续允许「确认中算占用」,再单独开变更。
---
## 4. 场景规则
### 4.1 趸交 → 分期(dj → fq)
| 前置条件 | 处理 |
|----------|------|
| 无明细或全部 `dfj` | **允许**。作废全部现有明细;按分期参数生成 N 条新 `dfj` 明细;主表 `pay_mode=fq`。 |
| 存在 `jfqrz` / `yjf` / `tfqrz` / `ytf` | **禁止**。提示原因。 |
切换后:第 i 期 `installmentNo=i`,应缴日按 `calcFqInstallmentDueDate`,应缴金额按合同均分(末期待尾差)。
### 4.2 分期 → 趸交(fq → dj),一期未缴
| 前置条件 | 处理 |
|----------|------|
| 全部 `dfj` | **允许**。作废全部 N 条分期明细;**新建 1 条**趸交明细:`dueAmount=contractAmount`,`installmentNo=null`,应缴日=`lumpSumDeadline`。 |
| 存在任何非 `dfj` | 走 4.3 |
### 4.3 分期 → 趸交(fq → dj),部分已缴
| 前置条件 | 处理 |
|----------|------|
| 有 `yjf`,无 `jfqrz`/`tfqrz`/`ytf` | **允许**。 |
| 存在 `jfqrz`/`tfqrz`/`ytf` | **禁止**。 |
**允许时步骤**:
1. **保留**所有 `yjf` 明细(只读展示,保留原 `installmentNo`、转账信息)。
2. **作废**所有 `dfj` 分期明细(不进入表单列表;保存时 `del_flag=1`)。
3. **新建 1 条**趸交明细:`dueAmount=remaining`,`payStatus=dfj`,`installmentNo=null`,应缴日=`lumpSumDeadline`。
4. 主表 `pay_mode=dj`;分期参数字段可保留只读快照(便于查看历史计划)。
若 `remaining ≤ 0`:禁止切换,提示「已缴清,无需改趸交」。
### 4.4 趸交 → 分期,趸交已缴
**禁止**。提示「趸交已结清,无法改为分期」。
---
## 5. 交互设计
### 5.1 切换前校验
用户点击「分期 / 趸交」radio 时:
1. 若目标模式与当前相同 → 无操作。
2. 调用 `evaluatePayModeSwitch(currentMode, targetMode, details, contractAmount)`。
3. 返回 `blocked` → `ElMessageBox.alert` 说明原因,**回滚 radio**。
4. 返回 `allowed` → `ElMessageBox.confirm` 展示影响摘要,确认后执行变换。
**确认文案示例(fq→dj,部分已缴)**:
> 将作废 4 期未缴计划,保留 2 期已缴记录,并新增 1 条趸交明细(剩余 ¥X,XXX.XX)。是否继续?
### 5.2 编辑态保护
- 锁定明细:禁用删除、应缴金额、缴费状态、期次、应缴日编辑。
- 趸交模式下隐藏「添加明细」(或禁用,保证仅 1 条有效未缴行 + 已缴历史行)。
- 切到分期时**不**强制 `resetCountInterval`;优先保留主表已保存的期数/间隔/开始时间。
### 5.3 表单数据结构(过渡期)
```typescript
interface PaymentDetailRow {
// ...existing fields
_payModeLocked?: boolean // yjf 等锁定行
_voided?: boolean // 前端待作废,保存时不提交或提交 void 标记
}
```
保存 payload:仅提交**未作废**明细 + 锁定保留行;作废行由后端按 id 软删(P1)或前端不提交、后端 diff(P2)。
---
## 6. 后端改造(分阶段)
### P0(与前端同步上线)
在 `save_payment_details` / 协议 bundle 保存前增加校验:
- `pay_mode=dj`:有效明细中「未锁定 + 未作废」的**待缴**行 ≤ 1;`Σ dueAmount(dfj 行)` = `remaining`(无已缴时 = 合同金额)。
- `pay_mode=fq`:有效 `dfj` 行数 = `installment_count`(无已缴切换场景);或允许「已缴 k 期 + 剩余 N−k 期 dfj」(dj→fq 不适用,仅 fq 内改参数时需另议)。
- 禁止客户端把 `yjf` 行 `pay_status` 改回 `dfj`。
### P1
- 作废明细:`del_flag=1`,分期预警自然排除。
- 保存时带 `detail.id` upsert,避免每次编辑换 UUID。
### P2
- 专用 API:`POST .../payment/change-mode`
- 操作日志:记录模式变更、作废条数、剩余金额
---
## 7. 核心算法(前端纯函数)
建议新增 `web-admin/src/utils/protocol-pay-mode-switch.ts`:
```typescript
type SwitchResult =
| { ok: true; details: DetailLike[]; summary: string }
| { ok: false; reason: string }
function evaluatePayModeSwitch(...): SwitchResult | 'need_confirm'
function applyPayModeSwitch(...): SwitchResult
```
**禁止**在 `onPayModeChange` 中直接 `forEach` 重写全部明细;改为 `applyPayModeSwitch` 返回新数组。
`tryGenerateInstallmentDetails` / `syncPaymentDueAmounts`:
- 跳过 `_payModeLocked` 或 `payStatus !== 'dfj'` 的行;
- 仅对未锁定 `dfj` 行重算金额/日期。
---
## 8. 测试用例
| # | 场景 | 期望 |
|---|------|------|
| T1 | dj dfj×1 → fq 6期 | 1 条作废,6 条新 dfj,金额均分 |
| T2 | fq 6×dfj → dj | 6 条作废,1 条 dj dfj 全额 |
| T3 | fq 2×yjf + 4×dfj → dj | 2 条 yjf 保留,4 条作废,1 条 dj remaining |
| T4 | dj yjf×1 → fq | blocked |
| T5 | fq 含 jfqrz → dj | blocked |
| T6 | fq 全部 yjf → dj | blocked(remaining=0) |
| T7 | 切换取消 | radio 回滚,明细不变 |
---
## 9. 不在本次范围
- 退费后再改模式的专流程 UI
- 小程序端缴费模式切换
---
## 10. 实现顺序建议
1. `protocol-pay-mode-switch.ts` + 单元测试(Node/vitest 或 backend pytest 镜像逻辑)
2. 改造 `onPayModeChange` + 确认弹窗
3. 改造 `tryGenerateInstallmentDetails` / `syncPaymentDueAmounts` 跳过锁定行
4. 后端 P0 校验
5. 联调四个场景 + 分期预警回归
---
## 11. 分期参数变更(非模式切换)
编辑已保存的**分期**协议时,用户可能修改「分期期数 / 每期间隔 / 分期开始时间」,规则如下:
| 情况 | 处理 |
|------|------|
| 全部 `dfj` | 确认后按新参数**整表重建** N 条明细 |
| 部分 `yjf` | 保留已缴行;**作废**全部 `dfj`;按 **剩余金额** 均分生成 `(新期数 − 已缴期数)` 条未缴明细;应缴日按已缴期数续算 index |
| 期数小于已缴最大期次 | **禁止** |
| 含 `jfqrz`/`tfqrz`/`ytf` | **禁止** |
| 仅改间隔/开始时间(期数不变) | 确认后保留 yjf,重建未缴行(金额+日期) |
实现:`web-admin/src/utils/protocol-fq-params-change.ts`,表单 `@change` → `handleFqParamsChange()`。
---
**审阅**:请确认本 spec 无遗漏后,进入 `writing-plans` 生成实现任务清单。
+3
View File
@@ -39,6 +39,8 @@ cp -R "${ROOT}/deploy/docker/mysql" "${STAGING}/deploy/docker/"
cp "${ROOT}/docs/sql/zkxw.sql" "${STAGING}/docs/sql/zkxw.sql"
cp "${ROOT}/deploy/docker/README.md" "${STAGING}/部署说明.md"
cp "${ROOT}/deploy/docker/部署指导手册.html" "${STAGING}/部署指导手册.html"
cp "${ROOT}/deploy/docker/部署指导手册.html" "${STAGING}/deploy/docker/部署指导手册.html"
cat > "${STAGING}/VERSION.txt" <<EOF
package=${PKG_NAME}
@@ -64,6 +66,7 @@ echo "包内不含前后端源码,包含:"
echo " - images/zkxw-app-images.tar"
echo " - deploy/docker/ (compose + .env.example + mysql init)"
echo " - docs/sql/zkxw.sql"
echo " - 部署指导手册.html(客户安装手册,浏览器打开)"
echo ""
echo "甲方服务器执行:"
echo " tar -xzf ${PKG_NAME}.tar.gz"
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+89 -110
View File
@@ -1,11 +1,11 @@
# Graph Report - src (2026-06-03)
# Graph Report - src (2026-06-04)
## Corpus Check
- Large corpus: 680 files · ~424,746 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
- Large corpus: 683 files · ~427,683 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 1957 nodes · 2519 edges · 51 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 557 edges (avg confidence: 0.8)
- 2000 nodes · 2611 edges · 48 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 579 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -43,7 +43,7 @@
- [[_COMMUNITY_Community 32|Community 32]]
- [[_COMMUNITY_Community 33|Community 33]]
- [[_COMMUNITY_Community 34|Community 34]]
- [[_COMMUNITY_Community 36|Community 36]]
- [[_COMMUNITY_Community 35|Community 35]]
- [[_COMMUNITY_Community 37|Community 37]]
- [[_COMMUNITY_Community 38|Community 38]]
- [[_COMMUNITY_Community 39|Community 39]]
@@ -51,19 +51,16 @@
- [[_COMMUNITY_Community 41|Community 41]]
- [[_COMMUNITY_Community 42|Community 42]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 48|Community 48]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 55|Community 55]]
- [[_COMMUNITY_Community 57|Community 57]]
- [[_COMMUNITY_Community 54|Community 54]]
## God Nodes (most connected - your core abstractions)
1. `get()` - 206 edges
2. `push()` - 36 edges
2. `push()` - 39 edges
3. `WebSocketClient` - 26 edges
4. `emit()` - 16 edges
5. `MenuProcessor` - 16 edges
@@ -75,53 +72,53 @@
## Surprising Connections (you probably didn't know these)
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
views/basic-info/customer/index.vue → utils/fullscreen-loading.ts
views/production/consumable/ConsumablePackTab.vue → utils/fullscreen-loading.ts
- `handleDownloadAttachment()` --calls--> `downloadAttachmentFile()` [INFERRED]
views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue → utils/download-attachment.ts
- `subscribeTokenRefresh()` --calls--> `push()` [INFERRED]
utils/http/index.ts → views/operations/storage-facility/index.vue
- `handleUnauthorizedResponse()` --calls--> `fetchRefreshToken()` [INFERRED]
utils/http/index.ts → api/auth.ts
- `fetchLeaveList()` --calls--> `get()` [INFERRED]
api/leave.ts → views/production/process/ProcessRouteTab.vue
- `fetchLeaveDetail()` --calls--> `get()` [INFERRED]
api/leave.ts → views/production/process/ProcessRouteTab.vue
- `fetchConsumableList()` --calls--> `get()` [INFERRED]
api/production.ts → views/production/process/ProcessRouteTab.vue
- `fetchReagentList()` --calls--> `get()` [INFERRED]
api/production.ts → views/production/process/ProcessRouteTab.vue
## Communities
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (103): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList() (+95 more)
Nodes (111): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList() (+103 more)
### Community 1 - "Community 1"
Cohesion: 0.03
Nodes (91): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchCheckupInstitutionProjects(), fetchPilotCheckupActualItems(), fetchPilotCheckupDetail() (+83 more)
Nodes (93): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchCheckupInstitutionProjects(), fetchPilotCheckupDetail(), markPilotCheckupChecked() (+85 more)
### Community 2 - "Community 2"
Cohesion: 0.03
Nodes (40): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), clickTab(), handleTouchEnd(), setTransition() (+32 more)
Nodes (38): clickTab(), handleTouchEnd(), setTransition(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), IframeRouteManager, RoutePermissionValidator (+30 more)
### Community 3 - "Community 3"
Cohesion: 0.03
Nodes (36): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents(), fetchAllReagentStockLogs() (+28 more)
Nodes (37): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents(), fetchAllReagentStockLogs() (+29 more)
### Community 4 - "Community 4"
Cohesion: 0.03
Nodes (35): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+27 more)
Nodes (41): fetchAttachmentDownloadUrl(), fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts() (+33 more)
### Community 5 - "Community 5"
Cohesion: 0.04
Nodes (28): updateInfusionScheduleStatus(), submitOutboundReleaseReview(), fetchStorageReleaseReviewDetail(), fetchStorageReleaseReviewList(), submitStorageReleaseReview(), disableDevTools(), handleLock(), handleUnlock() (+20 more)
Cohesion: 0.03
Nodes (35): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+27 more)
### Community 6 - "Community 6"
Cohesion: 0.04
Nodes (32): handleBlur(), handleFocus(), handleInput(), fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed() (+24 more)
Nodes (35): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), submitOutboundReleaseReview(), fetchStorageReleaseReviewDetail(), fetchStorageReleaseReviewList(), submitStorageReleaseReview() (+27 more)
### Community 7 - "Community 7"
Cohesion: 0.06
Nodes (34): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+26 more)
Cohesion: 0.04
Nodes (35): handleBlur(), handleFocus(), handleInput(), fetchEthnicList(), formatAmount(), fmtMoney(), auditorDisplay(), dash() (+27 more)
### Community 8 - "Community 8"
Cohesion: 0.06
Nodes (29): for(), set(), walk(), annotateForkEdges(), chainApiToElements(), walkAncestorBranch(), walkAncestorParents(), if() (+21 more)
Nodes (11): fetchGetLatestUpgradeLog(), getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager (+3 more)
### Community 9 - "Community 9"
Cohesion: 0.06
@@ -132,24 +129,24 @@ Cohesion: 0.1
Nodes (2): WebSocketClient, TableCache
### Community 11 - "Community 11"
Cohesion: 0.07
Nodes (21): auditorDisplay(), dash(), expressName(), formatYm(), formatYmd(), sampleTypeName(), siteName(), unitName() (+13 more)
Cohesion: 0.08
Nodes (33): fetchRefreshToken(), toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url() (+25 more)
### Community 12 - "Community 12"
Cohesion: 0.06
Nodes (24): fetchAttachmentDownloadUrl(), handleDownloadAttachment(), fullFileUrl(), handleDownloadAttachment(), handlePreviewAttachment(), isVideoFile(), handleReceptionAttachmentDownload(), handleDownloadAttachment() (+16 more)
Cohesion: 0.07
Nodes (20): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), collectIds(), postorder(), preorder(), applyTreeExpand() (+12 more)
### Community 13 - "Community 13"
Cohesion: 0.09
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
Cohesion: 0.07
Nodes (26): fetchCreateSite(), fetchDeleteSite(), fetchSiteList(), fetchUpdateSite(), fetchUserList(), formatHandoverUserLabel(), formatNowDatetime(), fullFileUrl() (+18 more)
### Community 14 - "Community 14"
Cohesion: 0.07
Nodes (25): fetchCreateSite(), fetchDeleteSite(), fetchSiteList(), fetchUpdateSite(), fetchUserList(), formatHandoverUserLabel(), formatNowDatetime(), fullFileUrl() (+17 more)
Nodes (22): for(), set(), walk(), annotateForkEdges(), chainApiToElements(), walkAncestorBranch(), walkAncestorParents(), if() (+14 more)
### Community 15 - "Community 15"
Cohesion: 0.08
Nodes (20): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), collectIds(), postorder(), preorder(), applyTreeExpand() (+12 more)
Cohesion: 0.13
Nodes (26): applyFqParamsChange(), countByStatus(), evaluateFqParamsChange(), formatMoneySummary(), generateInitialFqDetails(), getPaidInstallmentRows(), maxPaidInstallmentNo(), activeDetails() (+18 more)
### Community 16 - "Community 16"
Cohesion: 0.08
@@ -157,56 +154,56 @@ Nodes (11): maskIdCard(), buildOutboundPilotContextSections(), applicationStatus
### Community 17 - "Community 17"
Cohesion: 0.12
Nodes (22): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), handleBatchComplete(), handleBatchStart(), handleBatchVoid() (+14 more)
Nodes (19): fetchGetUserInfo(), fetchLogin(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchUserInfo(), buildWebEncryptedLoginBody(), defaultAvatarPathByRole(), hasCustomUserAvatar() (+11 more)
### Community 18 - "Community 18"
Cohesion: 0.12
Nodes (20): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleBatchDelete() (+12 more)
Nodes (22): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), handleBatchComplete(), handleBatchStart(), handleBatchVoid() (+14 more)
### Community 19 - "Community 19"
Cohesion: 0.12
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
Nodes (20): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleBatchDelete() (+12 more)
### Community 20 - "Community 20"
Cohesion: 0.12
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
### Community 21 - "Community 21"
Cohesion: 0.13
Nodes (5): fetchGetMenuList(), fetchGetMenuCodes(), MenuProcessor, useAppMode(), useAuth()
### Community 22 - "Community 22"
Cohesion: 0.1
Nodes (6): fetchProductionTaskDetail(), fetchProductionTaskList(), fetchUpdateProductionTask(), handleClose(), handleSubmit(), loadTaskDetail()
### Community 21 - "Community 21"
### Community 23 - "Community 23"
Cohesion: 0.15
Nodes (16): fetchPilotCheckupList(), canEditActualCheckupInfo(), canEditReserve(), canFillCheckupInfo(), canMarkChecked(), handleDropdownCommand(), handleEditActual(), handleEditReserve() (+8 more)
### Community 22 - "Community 22"
### Community 24 - "Community 24"
Cohesion: 0.12
Nodes (12): fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks(), fetchProcessDefList() (+4 more)
### Community 23 - "Community 23"
### Community 25 - "Community 25"
Cohesion: 0.13
Nodes (8): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), loadData(), onReviewSubmitted()
### Community 24 - "Community 24"
Cohesion: 0.16
Nodes (14): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleBatchDelete(), handleDelete(), handleDropdownCommand() (+6 more)
### Community 25 - "Community 25"
### Community 26 - "Community 26"
Cohesion: 0.16
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
### Community 26 - "Community 26"
### Community 27 - "Community 27"
Cohesion: 0.2
Nodes (2): ComponentLoader, RouteTransformer
### Community 27 - "Community 27"
### Community 28 - "Community 28"
Cohesion: 0.27
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
### Community 28 - "Community 28"
### Community 29 - "Community 29"
Cohesion: 0.18
Nodes (9): addSample(), applyDonationBundleToForm(), confirmBatchTransportApply(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd(), loadEthnicOptions(), normalizeSampleRow() (+1 more)
### Community 29 - "Community 29"
Cohesion: 0.14
Nodes (8): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), useCeremony(), useTheme(), themeAnimation(), toggleTheme(), toggleTransition()
### Community 30 - "Community 30"
Cohesion: 0.18
Nodes (5): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData()
@@ -223,124 +220,106 @@ Nodes (3): fetchMaterialList(), fetchMaterialOptions(), fetchMaterialStockLogLis
Cohesion: 0.2
Nodes (2): fetchQcMaterialList(), fetchQcMaterialOptions()
### Community 36 - "Community 36"
### Community 35 - "Community 35"
Cohesion: 0.28
Nodes (1): FireworkSystem
### Community 37 - "Community 37"
Cohesion: 0.39
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
### Community 37 - "Community 37"
### Community 38 - "Community 38"
Cohesion: 0.36
Nodes (1): TableHeightCalculator
### Community 38 - "Community 38"
### Community 39 - "Community 39"
Cohesion: 0.25
Nodes (2): fetchQcTaskDetail(), fetchQcTaskList()
### Community 39 - "Community 39"
### Community 40 - "Community 40"
Cohesion: 0.25
Nodes (3): fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList()
### Community 40 - "Community 40"
### Community 41 - "Community 41"
Cohesion: 0.33
Nodes (2): logApiCryptoBootInfo(), maskSecret()
### Community 41 - "Community 41"
Cohesion: 0.29
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
### Community 42 - "Community 42"
Cohesion: 0.47
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
### Community 44 - "Community 44"
Cohesion: 0.33
Nodes (2): fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive()
### Community 45 - "Community 45"
Cohesion: 0.33
Nodes (2): fetchQcThirdPartyItemList(), fetchQcThirdPartyItemsActive()
### Community 46 - "Community 46"
Cohesion: 0.33
Nodes (2): fetchQcThirdPartyCompanyList(), fetchQcThirdPartyCompanyOptions()
### Community 48 - "Community 48"
### Community 46 - "Community 46"
Cohesion: 0.5
Nodes (2): focusInput(), handleKeydown()
### Community 49 - "Community 49"
### Community 48 - "Community 48"
Cohesion: 0.67
Nodes (2): if(), logSidebarMenu()
### Community 51 - "Community 51"
### Community 50 - "Community 50"
Cohesion: 0.5
Nodes (1): fetchStorageEntrySmsList()
### Community 52 - "Community 52"
### Community 51 - "Community 51"
Cohesion: 0.5
Nodes (1): fetchSampleSmsNotificationList()
### Community 53 - "Community 53"
### Community 52 - "Community 52"
Cohesion: 0.5
Nodes (1): fetchQcSmsList()
### Community 55 - "Community 55"
### Community 54 - "Community 54"
Cohesion: 1.0
Nodes (2): getSettingDefaults(), resetToDefaults()
### Community 57 - "Community 57"
Cohesion: 1.0
Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
## Knowledge Gaps
- **Thin community `Community 10`** (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 26`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
- **Thin community `Community 27`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 34`** (10 nodes): `fetchBatchStockInQcMaterial()`, `fetchBatchStockOutQcMaterial()`, `fetchCreateQcMaterial()`, `fetchDeleteQcMaterial()`, `fetchQcMaterialList()`, `fetchQcMaterialOptions()`, `fetchStockInQcMaterial()`, `fetchStockOutQcMaterial()`, `fetchUpdateQcMaterial()`, `qc-material.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 37`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
- **Thin community `Community 35`** (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 38`** (8 nodes): `fetchCompleteQcTask()`, `fetchCreateQcTask()`, `fetchDeleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
- **Thin community `Community 38`** (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 40`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
- **Thin community `Community 39`** (8 nodes): `fetchCompleteQcTask()`, `fetchCreateQcTask()`, `fetchDeleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 41`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
- **Thin community `Community 41`** (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 44`** (6 nodes): `fetchCreateSampleReceptionCheckItem()`, `fetchDeleteSampleReceptionCheckItem()`, `fetchSampleReceptionCheckItemList()`, `fetchSampleReceptionCheckItemsActive()`, `fetchUpdateSampleReceptionCheckItem()`, `sample-reception-check-item.ts`
- **Thin community `Community 44`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 45`** (6 nodes): `fetchCreateQcThirdPartyItem()`, `fetchDeleteQcThirdPartyItem()`, `fetchQcThirdPartyItemList()`, `fetchQcThirdPartyItemsActive()`, `fetchUpdateQcThirdPartyItem()`, `qc-third-party-item.ts`
- **Thin community `Community 46`** (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 46`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
- **Thin community `Community 48`** (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 48`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
- **Thin community `Community 50`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 49`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
- **Thin community `Community 51`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 51`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
- **Thin community `Community 52`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 52`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 53`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 55`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 57`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
- **Thin community `Community 54`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.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 `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 8`, `Community 9`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 30`, `Community 32`, `Community 33`, `Community 34`, `Community 38`, `Community 39`, `Community 41`, `Community 44`, `Community 45`, `Community 46`, `Community 51`, `Community 52`, `Community 53`?**
_High betweenness centrality (0.280) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 1` to `Community 0`, `Community 3`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 14`, `Community 16`, `Community 28`?**
_High betweenness centrality (0.131) - this node is a cross-community bridge._
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 30`, `Community 32`, `Community 33`, `Community 34`, `Community 39`, `Community 40`, `Community 44`, `Community 50`, `Community 51`, `Community 52`?**
_High betweenness centrality (0.276) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 1` to `Community 0`, `Community 35`, `Community 3`, `Community 4`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 29`?**
_High betweenness centrality (0.144) - this node is a cross-community bridge._
- **Why does `handleDynamicRoutes()` connect `Community 2` to `Community 17`, `Community 21`?**
_High betweenness centrality (0.033) - this node is a cross-community bridge._
- **Are the 205 inferred relationships involving `get()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `buildTreePathLabelFromFlat()`) actually correct?**
_`get()` has 205 INFERRED edges - model-reasoned connections that need verification._
- **Are the 35 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `fetchAllPagedRecords()`) actually correct?**
_`push()` has 35 INFERRED edges - model-reasoned connections that need verification._
- **Are the 38 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `fetchAllPagedRecords()`) actually correct?**
_`push()` has 38 INFERRED edges - model-reasoned connections that need verification._
- **Are the 15 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
_`emit()` has 15 INFERRED edges - model-reasoned connections that need verification._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.03 - nodes in this community are weakly interconnected._
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">1957 nodes &middot; 2519 edges &middot; 301 communities</div>
<div id="stats">2000 nodes &middot; 2611 edges &middot; 298 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 40, "community_name": "Community 40", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 40, "community_name": "Community 40", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 40, "community_name": "Community 40", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 40, "community_name": "Community 40", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 40, "community_name": "Community 40", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 40, "community_name": "Community 40", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 88, "community_name": "Community 88", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 89, "community_name": "Community 89", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 19, "community_name": "Community 19", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 19, "community_name": "Community 19", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 19, "community_name": "Community 19", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 19, "community_name": "Community 19", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 19, "community_name": "Community 19", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine 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": 310}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 130}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 104}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 81}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 73}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 67}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 61}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 57}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 45}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 45}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 42}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 41}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 39}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 37}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 37}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 35}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 33}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 26}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 24}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 22}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 22}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 22}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 19}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 18}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 18}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 17}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 17}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 16}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 16}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 15}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 14}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 13}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 12}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 11}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 9}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 8}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 8}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 8}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 8}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 7}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 7}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 6}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 6}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 6}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 6}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 6}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 6}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 5}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 4}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 4}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 4}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 4}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 4}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 4}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 3}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 3}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 3}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 3}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 3}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 3}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 3}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 3}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 3}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count":Line truncated
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 41, "community_name": "Community 41", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 41, "community_name": "Community 41", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 41, "community_name": "Community 41", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 41, "community_name": "Community 41", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 41, "community_name": "Community 41", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 41, "community_name": "Community 41", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 85, "community_name": "Community 85", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 86, "community_name": "Community 86", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 20, "community_name": "Community 20", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 20, "community_name": "Community 20", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine 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": 331}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 128}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 89}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 82}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 73}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 73}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 72}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 67}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 55}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 45}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 42}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 41}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 39}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 38}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 37}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 32}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 31}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 27}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 26}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 24}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 22}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 22}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 22}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 22}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 19}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 18}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 17}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 17}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 16}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 16}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 14}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 13}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 12}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 11}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 9}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 9}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 8}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 8}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 8}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 8}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 7}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 6}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 6}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 6}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 6}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 5}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 5}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 4}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 4}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 4}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 4}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 4}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 4}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 3}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 3}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 3}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 3}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 3}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 3}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 3}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": Line truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
File diff suppressed because it is too large. Load diff
+72 -69
View File
@@ -1,6 +1,6 @@
{
"/Users/wuzilong/work/00code/zkxw/web-admin/src/App.vue": 1779790323.6203642,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/main.ts": 1779258114.8468678,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/App.vue": 1780480414.9445062,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/main.ts": 1780480414.9551268,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/env.d.ts": 1778582067.998984,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/shims-loose.d.ts": 1778582068.0215242,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/index.ts": 1778582067.9976711,
@@ -8,26 +8,26 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/core/auth.ts": 1778582067.9971414,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/business/ripple.ts": 1778582067.996821,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/directives/business/highlight.ts": 1778582067.9966211,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts": 1779155643.5621417,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts": 1780539154.013632,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/index.ts": 1778582068.02854,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/config/index.ts": 1778582068.0266159,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/component/chart.ts": 1778582068.026058,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/component/index.ts": 1778582068.0262403,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/response.ts": 1778582068.025649,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/response.ts": 1780480414.9570467,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1780477949.7778664,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1780477857.5482304,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1780546861.7453368,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1780546851.8236628,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1780480414.9574106,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/setting.ts": 1778582067.9954739,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/index.ts": 1779344841.362722,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/index.ts": 1780480414.9542737,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/modules/component.ts": 1778582067.9943824,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/modules/festival.ts": 1778582067.9949014,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/modules/headerBar.ts": 1778582067.9952137,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/modules/fastEnter.ts": 1779232181.1371717,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/modules/fastEnter.ts": 1780480414.9544525,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/assets/images.ts": 1778582067.993645,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/plugins/echarts.ts": 1778582068.010295,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/plugins/index.ts": 1778582068.010531,
@@ -40,9 +40,9 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/enums/appEnum.ts": 1778582067.9982593,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/patch-el-select-filterable.ts": 1779790341.162103,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts": 1779790341.466299,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts": 1779790341.943788,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts": 1780540742.1887667,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1779117454.818312,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1779938225.7809103,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1780480414.9585993,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1779199125.4894068,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1779826490.6921933,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1778775532.895782,
@@ -55,8 +55,10 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/money-format.ts": 1779695444.9027524,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/category-path-display.ts": 1779790340.9231393,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-labels.ts": 1779199108.173778,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts": 1780547381.0547066,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/loginRsa.ts": 1778582068.0373487,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts": 1780310646.0954366,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts": 1780546639.7181845,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts": 1780545638.298603,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/download-attachment.ts": 1779159591.1376455,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/ui/colors.ts": 1778582068.046773,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/ui/emojo.ts": 1778582068.0469406,
@@ -71,43 +73,43 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/form/validator.ts": 1778582068.0335238,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/constants/links.ts": 1778582068.031627,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/constants/index.ts": 1778582068.0312057,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/worktab.ts": 1779240471.2587266,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/jump.ts": 1778775532.8961763,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/route.ts": 1779790341.1550379,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/worktab.ts": 1780480414.958444,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/jump.ts": 1780480414.9581196,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/route.ts": 1780480414.9582925,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/navigation/index.ts": 1778582068.0376182,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage/storage.ts": 1778582068.0412395,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage/storage-config.ts": 1778582068.040829,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage/index.ts": 1778582068.040637,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage/storage-key-manager.ts": 1778582068.0410247,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/mittBus.ts": 1778582068.0423212,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/upgrade.ts": 1779790341.6936553,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/upgrade.ts": 1780480414.9587884,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/error-handle.ts": 1778582068.0419197,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/index.ts": 1778582068.0421178,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sys/console.ts": 1778582068.0417144,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/table/tableConfig.ts": 1778582068.0443969,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/table/tableCache.ts": 1778582068.044193,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/table/tableUtils.ts": 1778582068.0454955,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/status.ts": 1778582068.036632,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/status.ts": 1780480414.957961,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/crypto.ts": 1778582068.0353796,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts": 1779463885.9788811,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/error.ts": 1778582068.0356607,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts": 1780480414.9578006,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/error.ts": 1780480414.9575992,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/socket/index.ts": 1778582068.0397933,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router/sidebar-menu-filter.ts": 1778582068.0391986,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/tables/art-table/index.vue": 1779525781.573567,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/tables/art-table-header/index.vue": 1779790330.385015,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-search-bar/index.vue": 1779525781.574576,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-form/index.vue": 1779525781.575067,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-button-more/index.vue": 1779525781.5755153,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-excel-import/index.vue": 1779525781.575924,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-excel-export/index.vue": 1779525781.576321,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-drag-verify/index.vue": 1779525781.576787,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-wang-editor/index.vue": 1779790328.3615272,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-button-table/index.vue": 1779525781.5775588,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/tables/art-table-header/index.vue": 1780480414.9533372,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-search-bar/index.vue": 1780480414.9481184,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-form/index.vue": 1780480414.9478915,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-button-more/index.vue": 1780480414.9465926,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-excel-import/index.vue": 1780480414.9476178,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-excel-export/index.vue": 1780480414.9474266,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-drag-verify/index.vue": 1780480414.9471714,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-wang-editor/index.vue": 1780480414.9483848,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/forms/art-button-table/index.vue": 1780480414.9469068,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/widget/art-icon-button/index.vue": 1779525781.578175,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-bar-chart-card/index.vue": 1779525781.5786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-data-list-card/index.vue": 1779525781.5789726,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-timeline-list-card/index.vue": 1779525781.5793364,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-stats-card/index.vue": 1779525781.5797477,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-stats-card/index.vue": 1780480414.9464056,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-image-card/index.vue": 1779525781.5800624,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-donut-chart-card/index.vue": 1779525781.5803144,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/cards/art-progress-card/index.vue": 1779525781.5805767,
@@ -121,7 +123,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/charts/art-radar-chart/index.vue": 1779525781.5834749,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/charts/art-h-bar-chart/index.vue": 1779525781.5837238,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/others/art-watermark/index.vue": 1779525781.5842316,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/others/art-menu-right/index.vue": 1779790330.3317213,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/others/art-menu-right/index.vue": 1780480414.9530756,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/banners/art-card-banner/index.vue": 1779525781.5850756,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/banners/art-basic-banner/index.vue": 1779525781.5857205,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-global-component/index.vue": 1779525781.5863822,
@@ -131,45 +133,46 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/composables/useSettingsState.ts": 1778582067.9773157,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/composables/useSettingsHandlers.ts": 1778582067.9766955,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/ThemeSettings.vue": 1779525781.5871665,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingHeader.vue": 1779525781.587754,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingItem.vue": 1779525781.5882142,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingHeader.vue": 1780480414.9523888,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingItem.vue": 1780480414.9525797,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue": 1779525781.5885482,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/BasicSettings.vue": 1779525781.588895,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingDrawer.vue": 1779525781.5892572,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SettingActions.vue": 1779525781.5898669,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/ColorSettings.vue": 1779525781.5901814,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/ColorSettings.vue": 1780480414.952198,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue": 1779525781.5907402,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SectionTitle.vue": 1779525781.5913193,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue": 1779525781.59174,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/ContainerSettings.vue": 1779525781.5920076,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue": 1779525781.592554,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-chat-window/index.vue": 1779525781.59301,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-page-content/index.vue": 1779942912.5296464,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue": 1780480414.9528105,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-chat-window/index.vue": 1780480414.949035,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-page-content/index.vue": 1780480414.9517472,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue": 1779525781.5938804,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue": 1779525781.5942907,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-mixed-menu/index.vue": 1779525781.5947194,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue": 1779790328.9606564,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue": 1779525781.59551,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue": 1780480414.950054,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue": 1780480414.9512155,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-fireworks-effect/index.vue": 1779525781.5963395,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-global-search/index.vue": 1779525781.5967863,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-notification/index.vue": 1779525781.597402,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-global-search/index.vue": 1780480414.949282,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-notification/index.vue": 1780480414.9515367,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-fast-enter/index.vue": 1779525781.5979466,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-screen-lock/index.vue": 1779525781.598362,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-screen-lock/index.vue": 1780480414.9519973,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-breadcrumb/index.vue": 1779525781.5987902,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/index.vue": 1779525781.5991871,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue": 1779790328.7361135,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/index.vue": 1780480414.949582,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue": 1780540514.104913,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue": 1780540511.8256087,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/theme/theme-svg/index.vue": 1779525781.6001027,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/result/ArtResultPage.vue": 1779525781.6004124,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/exception/ExceptionIllustration.vue": 1779790330.5225084,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/exception/ArtException.vue": 1779790330.512786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/login/AuthTopBar.vue": 1779790330.5399241,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/exception/ArtException.vue": 1780480414.9538875,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/login/AuthTopBar.vue": 1780480414.9540887,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/views/login/LoginLeftView.vue": 1779525781.6012564,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/text-effect/art-text-scroll/index.vue": 1779525781.6015408,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/text-effect/art-text-scroll/index.vue": 1780480414.953665,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/text-effect/art-count-to/index.vue": 1779525781.6018777,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/text-effect/art-festival-text-scroll/index.vue": 1779525781.6024969,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-svg-icon/index.vue": 1779525781.603243,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-back-to-top/index.vue": 1779525781.6038082,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-logo/index.vue": 1779790326.538098,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-svg-icon/index.vue": 1780480414.9462214,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-back-to-top/index.vue": 1780480414.9458482,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/base/art-logo/index.vue": 1780480414.9460301,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/media/art-video-player/index.vue": 1779525781.6045322,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/media/art-cutter-img/index.vue": 1779525781.6051006,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/index.vue": 1779790325.7480125,
@@ -236,15 +239,15 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/system.ts": 1779790323.5636237,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1779283387.4196396,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1780462889.9246686,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1778582067.8610063,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1780539439.6854532,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1780478257.9926786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1779938231.136978,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1780480414.9591243,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1779866933.9349332,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1779859546.0906804,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue": 1779525781.613681,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1780462937.905501,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue": 1779525781.6162472,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1780458686.0600615,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1780547422.9811244,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue": 1779525781.6193507,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-info/index.vue": 1780457198.009363,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-reception-info/index.vue": 1780325524.917126,
@@ -279,17 +282,17 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationViewDialog.vue": 1780478943.7240853,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue": 1780458686.0616076,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue": 1779525781.6357327,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/dashboard/console/index.vue": 1779857063.389745,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/dashboard/console/index.vue": 1780480414.9592953,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/tenant/index.vue": 1779858296.9440098,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/order/index.vue": 1779856873.200306,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/unit/index.vue": 1779858300.774436,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/role/index.vue": 1779858382.6288393,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/role/index.vue": 1780480414.9603326,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/post/index.vue": 1779858367.1503367,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/category/index.vue": 1779859746.1898513,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/express/index.vue": 1779858275.4881697,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/index.vue": 1779856885.5607417,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/index.vue": 1780480414.960575,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/detail.vue": 1779790371.4357243,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/modules/user-dialog.vue": 1779790371.5651114,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/modules/user-dialog.vue": 1780480414.960775,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/user/modules/user-search.vue": 1779525781.643876,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/product/index.vue": 1779859799.4894958,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/workflow/my-applications/index.vue": 1779856891.19533,
@@ -299,7 +302,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/workflow/components/ApprovalDialog.vue": 1779790371.6886575,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/workflow/designer/FlowDesigner.vue": 1779790371.8114932,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/workflow/process/index.vue": 1779859839.5932422,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/menu/index.vue": 1779858346.0258229,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/menu/index.vue": 1780480414.960126,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/site/index.vue": 1779858286.7791865,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/dept/index.vue": 1779858319.5899463,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/dict/index.vue": 1779857764.622668,
@@ -352,9 +355,9 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue": 1780478944.9533937,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779864358.234948,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779864494.1133437,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/500/index.vue": 1779546148.0553653,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/403/index.vue": 1779546144.3909206,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/404/index.vue": 1779546146.242008,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/500/index.vue": 1780480414.959776,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/403/index.vue": 1780480414.95946,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/404/index.vue": 1780480414.9596128,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-type/index.vue": 1779871660.7926793,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-type/CellProductPriceTab.vue": 1779871667.0159404,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-type/ProjectPriceSystemTab.vue": 1779864896.060257,
@@ -410,27 +413,27 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-region/CollectionProtocolNoTab.vue": 1779865978.3539536,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/loading.ts": 1778582067.932299,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/index.ts": 1778582068.0218356,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/worktab.ts": 1779790340.7550395,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/worktab.ts": 1780480414.9568844,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/table.ts": 1779257771.098434,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/menu.ts": 1779790340.600478,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/menu.ts": 1780480414.9564605,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/setting.ts": 1778582068.0225098,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts": 1779938228.2554607,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts": 1780540187.1384017,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routesAlias.ts": 1778582068.0211735,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/index.ts": 1779232037.727516,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/index.ts": 1780480414.9557922,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RouteTransformer.ts": 1778582068.0132053,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RouteValidator.ts": 1778582068.013567,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/ComponentLoader.ts": 1778582068.0119128,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/MenuProcessor.ts": 1779882681.9204168,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/MenuProcessor.ts": 1780480414.955383,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RouteRegistry.ts": 1778582068.0129957,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RoutePermissionValidator.ts": 1778582068.0127926,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/index.ts": 1778582068.0138035,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/IframeRouteManager.ts": 1778582068.0121944,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1780457411.241232,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/index.ts": 1778598510.6043763,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1779546186.993006,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1779546226.1088154,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/index.ts": 1780480414.9561174,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1780480414.9559598,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1780480414.9562852,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/asyncRoutes.ts": 1778582068.0206802,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/beforeEach.ts": 1779938234.2979362,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/beforeEach.ts": 1780480414.9556284,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/afterEach.ts": 1778582068.0146143,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/my-copy.svg": 1778582067.9349968,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/search.svg": 1778582067.9393654,
+47
View File
@@ -38,6 +38,9 @@ export function fetchGetUserInfo() {
username: string
realName: string
phone: string
email?: string
sex?: string
remark?: string
role: string
regionId: string
regionName: string
@@ -50,6 +53,50 @@ export function fetchGetUserInfo() {
})
}
/** 当前用户更新个人信息 */
export function fetchUpdateProfile(data: {
phoneNo?: string
email?: string
sex?: string
remark?: string
avatarUrl?: string
}) {
return request.put<{
id: string
username: string
realName: string
phone: string
email?: string
sex?: string
remark?: string
role: string
regionId: string
regionName: string
avatarUrl?: string | null
}>({
url: '/web/auth/profile',
data,
showSuccessMessage: true
})
}
/** 当前用户上传头像 */
export function fetchUploadProfileAvatar(data: FormData) {
return request.post<{ url: string }>({
url: '/web/auth/upload-avatar',
data
})
}
/** 当前用户修改密码 */
export function fetchChangePassword(data: { oldPassword: string; newPassword: string }) {
return request.post({
url: '/web/auth/change-password',
data,
showSuccessMessage: true
})
}
/**
* 获取当前用户菜单 code 权限
*/
@@ -1,6 +1,7 @@
<!-- 用户菜单 -->
<template>
<ElPopover
<div class="user-menu-root">
<ElPopover
ref="userMenuPopover"
placement="bottom-end"
:width="240"
@@ -13,16 +14,21 @@
>
<template #reference>
<div class="user-avatar-wrapper">
<el-avatar :size="34" :src="avatarDisplayUrl">
{{ userInfo.realName?.charAt(0) || userInfo.username?.charAt(0) || '管' }}
<el-avatar :size="34" :src="avatarDisplayUrl || undefined" :key="avatarDisplayUrl">
<span v-if="showAvatarInitial">{{ avatarInitialText }}</span>
</el-avatar>
</div>
</template>
<template #default>
<div class="pt-3">
<div class="flex-c pb-1 px-0">
<el-avatar :size="40" :src="avatarDisplayUrl" style="margin-right: 12px">
{{ userInfo.realName?.charAt(0) || userInfo.username?.charAt(0) || '管' }}
<el-avatar
:size="40"
:src="avatarDisplayUrl || undefined"
:key="avatarDisplayUrl"
style="margin-right: 12px"
>
<span v-if="showAvatarInitial">{{ avatarInitialText }}</span>
</el-avatar>
<div class="w-[calc(100%-60px)] h-full">
<span class="block text-sm font-medium text-g-800 truncate">
@@ -43,6 +49,15 @@
<span>{{ userInfo.regionName }}</span>
</li>
<div class="w-full h-px my-2 bg-g-300/80"></div>
<li class="btn-item" @click="openProfileDialog">
<ArtSvgIcon icon="edit" />
<span>个人信息</span>
</li>
<li class="btn-item" @click="openPasswordDialog">
<ArtSvgIcon icon="ri:lock-password-line" />
<span>修改密码</span>
</li>
<div class="w-full h-px my-2 bg-g-300/80"></div>
<div class="log-out c-p" @click="loginOut">
{{ $t('topBar.user.logout') }}
</div>
@@ -50,13 +65,23 @@
</div>
</template>
</ElPopover>
<UserProfileDialog v-model:visible="profileDialogVisible" />
<UserPasswordDialog v-model:visible="passwordDialogVisible" />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { ElMessageBox } from 'element-plus'
import { useUserStore } from '@/store/modules/user'
import { resolveUserAvatarUrl } from '@/utils/user-avatar'
import {
resolveUserAvatarFromInfo,
pickUserAvatarRaw,
hasCustomUserAvatar
} from '@/utils/user-avatar'
import UserProfileDialog from './UserProfileDialog.vue'
import UserPasswordDialog from './UserPasswordDialog.vue'
defineOptions({ name: 'ArtUserMenu' })
@@ -64,11 +89,31 @@
const { getUserInfo: userInfo } = storeToRefs(userStore)
const avatarDisplayUrl = computed(() =>
resolveUserAvatarUrl(userInfo.value.avatarUrl, userInfo.value.role)
const avatarDisplayUrl = computed(() => resolveUserAvatarFromInfo(userInfo.value))
const showAvatarInitial = computed(() => !hasCustomUserAvatar(pickUserAvatarRaw(userInfo.value)))
const avatarInitialText = computed(
() => userInfo.value.realName?.charAt(0) || userInfo.value.username?.charAt(0) || '管'
)
const userMenuPopover = ref()
const profileDialogVisible = ref(false)
const passwordDialogVisible = ref(false)
function openProfileDialog() {
closeUserMenu()
setTimeout(() => {
profileDialogVisible.value = true
}, 150)
}
function openPasswordDialog() {
closeUserMenu()
setTimeout(() => {
passwordDialogVisible.value = true
}, 150)
}
/**
* 获取角色名称
@@ -0,0 +1,149 @@
<!-- 当前用户修改密码 -->
<template>
<el-dialog
v-model="dialogVisible"
title="修改密码"
width="480px"
class="dialog-form-layout"
align-center
destroy-on-close
:close-on-click-modal="false"
@close="handleClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="原密码" prop="oldPassword" class="form-col-span-2">
<el-input
v-model="formData.oldPassword"
type="password"
show-password
placeholder="请输入原密码"
autocomplete="current-password"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword" class="form-col-span-2">
<el-input
v-model="formData.newPassword"
type="password"
show-password
placeholder="至少 6 位"
autocomplete="new-password"
/>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmPassword" class="form-col-span-2">
<el-input
v-model="formData.confirmPassword"
type="password"
show-password
placeholder="请再次输入新密码"
autocomplete="new-password"
/>
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="handleClose">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
<el-icon><Check /></el-icon>
确定
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { Close, Check } from '@element-plus/icons-vue'
import { fetchChangePassword } from '@/api/auth'
const props = defineProps<{
visible: boolean
}>()
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void
}>()
const formRef = ref<FormInstance>()
const submitting = ref(false)
const formData = reactive({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const formRules: FormRules = {
oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, message: '新密码长度不能少于 6 位', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
{
validator: (_rule, value, callback) => {
if (value !== formData.newPassword) {
callback(new Error('两次输入的新密码不一致'))
return
}
callback()
},
trigger: 'blur'
}
]
}
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
function resetForm() {
formData.oldPassword = ''
formData.newPassword = ''
formData.confirmPassword = ''
formRef.value?.clearValidate?.()
}
function handleClose() {
dialogVisible.value = false
}
async function handleSubmit() {
if (!formRef.value) return
try {
await formRef.value.validate()
submitting.value = true
await fetchChangePassword({
oldPassword: formData.oldPassword,
newPassword: formData.newPassword
})
dialogVisible.value = false
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
submitting.value = false
}
}
watch(
() => props.visible,
(val) => {
if (val) {
resetForm()
}
}
)
</script>
@@ -0,0 +1,326 @@
<!-- 当前用户个人信息编辑 -->
<template>
<el-dialog
v-model="dialogVisible"
title="个人信息"
width="560px"
class="dialog-form-layout"
align-center
destroy-on-close
:close-on-click-modal="false"
@close="handleClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="头像" class="form-col-span-2">
<div class="profile-avatar-row">
<el-avatar
:size="72"
:src="avatarPreviewUrl || undefined"
:key="avatarPreviewKey"
>
<span v-if="showAvatarInitial">{{ avatarInitial }}</span>
</el-avatar>
<div class="profile-avatar-actions">
<div class="profile-avatar-btns">
<el-upload
ref="avatarUploadRef"
accept="image/*"
:show-file-list="false"
:auto-upload="false"
:disabled="avatarUploading"
@change="handleAvatarChange"
>
<el-button class="btn-ghost" :loading="avatarUploading">
<el-icon><Upload /></el-icon>
上传头像
</el-button>
</el-upload>
<el-button
v-if="formData.avatarUrl"
class="btn-ghost"
:disabled="avatarUploading"
@click="handleClearAvatar"
>
<el-icon><Delete /></el-icon>
恢复默认
</el-button>
</div>
<p class="profile-avatar-tip">支持 JPG、PNG 等图片,大小不超过 2MB</p>
</div>
</div>
</el-form-item>
<el-form-item label="手机号" prop="phoneNo">
<el-input v-model="formData.phoneNo" placeholder="选填" maxlength="11" clearable />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="选填" maxlength="50" clearable />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择" style="width: 100%">
<el-option label="男" value="0" />
<el-option label="女" value="1" />
<el-option label="未知" value="2" />
</el-select>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
placeholder="选填"
maxlength="512"
show-word-limit
/>
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="handleClose">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
<el-icon><Check /></el-icon>
保存
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch, onBeforeUnmount } from 'vue'
import type { FormInstance, FormRules, UploadFile, UploadInstance } from 'element-plus'
import { Close, Check, Upload, Delete } from '@element-plus/icons-vue'
import { fetchGetUserInfo, fetchUpdateProfile, fetchUploadProfileAvatar } from '@/api/auth'
import { useUserStore } from '@/store/modules/user'
import {
pickUserAvatarRaw,
pickUploadAvatarUrl,
hasCustomUserAvatar,
resolveUserAvatarUrl
} from '@/utils/user-avatar'
const props = defineProps<{
visible: boolean
}>()
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void
}>()
const userStore = useUserStore()
const formRef = ref<FormInstance>()
const avatarUploadRef = ref<UploadInstance>()
const submitting = ref(false)
const avatarUploading = ref(false)
const avatarLocalPreview = ref('')
const displayRole = ref('')
const formData = reactive({
phoneNo: '',
email: '',
sex: '0',
remark: '',
avatarUrl: ''
})
const formRules: FormRules = {
phoneNo: [
{
validator: (_rule, value, callback) => {
const v = (value || '').trim()
if (v && !/^1\d{10}$/.test(v)) {
callback(new Error('请输入正确的手机号'))
return
}
callback()
},
trigger: 'blur'
}
],
email: [
{
validator: (_rule, value, callback) => {
const v = (value || '').trim()
if (v && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
callback(new Error('请输入正确的邮箱'))
return
}
callback()
},
trigger: 'blur'
}
]
}
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
const avatarPreviewUrl = computed(() => {
if (avatarLocalPreview.value) return avatarLocalPreview.value
return resolveUserAvatarUrl(formData.avatarUrl, displayRole.value)
})
const avatarPreviewKey = computed(
() => avatarLocalPreview.value || formData.avatarUrl || 'default-avatar'
)
const showAvatarInitial = computed(
() => !avatarLocalPreview.value && !hasCustomUserAvatar(formData.avatarUrl)
)
const avatarInitial = computed(() => {
const info = userStore.getUserInfo
return info.realName?.charAt(0) || info.username?.charAt(0) || '管'
})
function revokeAvatarLocalPreview() {
if (avatarLocalPreview.value) {
URL.revokeObjectURL(avatarLocalPreview.value)
avatarLocalPreview.value = ''
}
}
function handleClose() {
dialogVisible.value = false
}
function handleClearAvatar() {
formData.avatarUrl = ''
revokeAvatarLocalPreview()
avatarUploadRef.value?.clearFiles()
}
async function loadProfile() {
const data = await fetchGetUserInfo()
formData.phoneNo = data.phone || ''
formData.email = data.email || ''
formData.sex = data.sex || '0'
formData.remark = data.remark || ''
formData.avatarUrl = pickUserAvatarRaw(data) || pickUserAvatarRaw(userStore.getUserInfo) || ''
displayRole.value = data.role || userStore.getUserInfo.role || ''
revokeAvatarLocalPreview()
avatarUploadRef.value?.clearFiles()
}
async function handleAvatarChange(uploadFile: UploadFile) {
const file = uploadFile.raw
if (!file) return
if (uploadFile.status && uploadFile.status !== 'ready') return
if (!file.type.startsWith('image/')) {
ElMessage.error('仅支持上传图片文件')
avatarUploadRef.value?.clearFiles()
return
}
if (file.size > 2 * 1024 * 1024) {
ElMessage.error('头像大小不能超过 2MB')
avatarUploadRef.value?.clearFiles()
return
}
avatarUploading.value = true
revokeAvatarLocalPreview()
avatarLocalPreview.value = URL.createObjectURL(file)
try {
const fd = new FormData()
fd.append('file', file)
const res = await fetchUploadProfileAvatar(fd)
const stored = pickUploadAvatarUrl(res)
if (!stored) {
ElMessage.error('上传成功但未返回图片地址')
handleClearAvatar()
return
}
formData.avatarUrl = stored
ElMessage.success('头像上传成功')
} catch {
handleClearAvatar()
} finally {
avatarUploading.value = false
avatarUploadRef.value?.clearFiles()
}
}
async function handleSubmit() {
if (!formRef.value) return
try {
await formRef.value.validate()
submitting.value = true
const updated = await fetchUpdateProfile({
phoneNo: formData.phoneNo.trim() || undefined,
email: formData.email.trim() || undefined,
sex: formData.sex,
remark: formData.remark.trim() || undefined,
avatarUrl: formData.avatarUrl || ''
})
userStore.setUserInfo({
phone: updated.phone,
email: updated.email,
sex: updated.sex,
remark: updated.remark,
avatarUrl: pickUserAvatarRaw(updated)
})
dialogVisible.value = false
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
submitting.value = false
}
}
watch(
() => props.visible,
(val) => {
if (val) {
loadProfile().catch(() => {})
formRef.value?.clearValidate?.()
} else {
revokeAvatarLocalPreview()
}
}
)
onBeforeUnmount(() => {
revokeAvatarLocalPreview()
})
</script>
<style scoped lang="scss">
.profile-avatar-row {
display: flex;
gap: 16px;
align-items: center;
}
.profile-avatar-actions {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.profile-avatar-btns {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.profile-avatar-tip {
margin: 0;
font-size: 12px;
color: var(--mint-text-muted, #64748b);
line-height: 1.5;
}
</style>
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "label": "UserProfileDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L1"}, {"id": "widget_userprofiledialog_revokeavatarlocalpreview", "label": "revokeAvatarLocalPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L168"}, {"id": "widget_userprofiledialog_handleclose", "label": "handleClose()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L175"}, {"id": "widget_userprofiledialog_handleclearavatar", "label": "handleClearAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L179"}, {"id": "widget_userprofiledialog_loadprofile", "label": "loadProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L185"}, {"id": "widget_userprofiledialog_handleavatarchange", "label": "handleAvatarChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L197"}, {"id": "widget_userprofiledialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L230"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L94", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L95", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L96", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L97", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L98", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_revokeavatarlocalpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L168", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleclose", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L175", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "label": "user-avatar.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L1"}, {"id": "utils_user_avatar_defaultavatarpathbyrole", "label": "defaultAvatarPathByRole()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L7"}, {"id": "utils_user_avatar_pickuseravatarraw", "label": "pickUserAvatarRaw()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L12"}, {"id": "utils_user_avatar_resolvestoragefileurl", "label": "resolveStorageFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L21"}, {"id": "utils_user_avatar_resolveuseravatarurl", "label": "resolveUserAvatarUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L30"}, {"id": "utils_user_avatar_resolveuseravatarfrominfo", "label": "resolveUserAvatarFromInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L40"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_defaultavatarpathbyrole", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_pickuseravatarraw", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolvestoragefileurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L21", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarfrominfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L40", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarurl", "target": "utils_user_avatar_resolvestoragefileurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L35", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarurl", "target": "utils_user_avatar_defaultavatarpathbyrole", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L36", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarfrominfo", "target": "utils_user_avatar_resolveuseravatarurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L43", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarfrominfo", "target": "utils_user_avatar_pickuseravatarraw", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L43", "weight": 1.0}], "raw_calls": [{"caller_nid": "utils_user_avatar_pickuseravatarraw", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L17"}, {"caller_nid": "utils_user_avatar_pickuseravatarraw", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L17"}, {"caller_nid": "utils_user_avatar_resolvestoragefileurl", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L22"}, {"caller_nid": "utils_user_avatar_resolvestoragefileurl", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L22"}, {"caller_nid": "utils_user_avatar_resolvestoragefileurl", "callee": "test", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_loLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "label": "UserProfileDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L1"}, {"id": "widget_userprofiledialog_revokeavatarlocalpreview", "label": "revokeAvatarLocalPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L187"}, {"id": "widget_userprofiledialog_handleclose", "label": "handleClose()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L194"}, {"id": "widget_userprofiledialog_handleclearavatar", "label": "handleClearAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L198"}, {"id": "widget_userprofiledialog_loadprofile", "label": "loadProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L204"}, {"id": "widget_userprofiledialog_handleavatarchange", "label": "handleAvatarChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L216"}, {"id": "widget_userprofiledialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L255"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L100", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L101", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L102", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L103", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L104", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_revokeavatarlocalpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L187", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleclose", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L194", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_hLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "label": "auth.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L1"}, {"id": "api_auth_fetchlogin", "label": "fetchLogin()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L9"}, {"id": "api_auth_fetchgetuserinfo", "label": "fetchGetUserInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L35"}, {"id": "api_auth_fetchupdateprofile", "label": "fetchUpdateProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L57"}, {"id": "api_auth_fetchuploadprofileavatar", "label": "fetchUploadProfileAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L84"}, {"id": "api_auth_fetchchangepassword", "label": "fetchChangePassword()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L92"}, {"id": "api_auth_fetchgetmenucodes", "label": "fetchGetMenuCodes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L103"}, {"id": "api_auth_fetchrefreshtoken", "label": "fetchRefreshToken()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L118"}, {"id": "api_auth_fetchlogout", "label": "fetchLogout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L132"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_loginrsa", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchlogin", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchgetuserinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchupdateprofile", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L57", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchuploadprofileavatar", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L84", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchchangepassword", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L92", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchgetmenucodes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L103", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchrefreshtoken", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L118", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchlogout", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L132", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_auth_fetchlogin", "callee": "buildWebEncryptedLoginBody", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L10"}, {"caller_nid": "api_auth_fetchlogin", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "label": "UserProfileDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L1"}, {"id": "widget_userprofiledialog_handleclose", "label": "handleClose()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L120"}, {"id": "widget_userprofiledialog_loadprofile", "label": "loadProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L124"}, {"id": "widget_userprofiledialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L132"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L62", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L63", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L64", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L65", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleclose", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L120", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_loadprofile", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L124", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handlesubmit", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L132", "weight": 1.0}], "raw_calls": [{"caller_nid": "widget_userprofiledialog_loadprofile", "callee": "fetchGetUserInfo", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L125"}, {"caller_nid": "widget_userprofiledialog_handlesubmit", "callee": "validate", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L135"}, {"caller_nid": "widget_userprofiledialog_handlesubmit", "callee": "fetchUpdateProfile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L137"}, {"caller_nid": "widget_userprofiledialog_handlesubmit", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L138"}, {"caller_nid": "widget_userprofiledialog_handLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "label": "user-avatar.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L1"}, {"id": "utils_user_avatar_joinpublicasset", "label": "joinPublicAsset()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L2"}, {"id": "utils_user_avatar_defaultavatarpathbyrole", "label": "defaultAvatarPathByRole()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L14"}, {"id": "utils_user_avatar_normalizeavatarstoragekey", "label": "normalizeAvatarStorageKey()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L22"}, {"id": "utils_user_avatar_pickuseravatarraw", "label": "pickUserAvatarRaw()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L44"}, {"id": "utils_user_avatar_hascustomuseravatar", "label": "hasCustomUserAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L53"}, {"id": "utils_user_avatar_pickuploadavatarurl", "label": "pickUploadAvatarUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L58"}, {"id": "utils_user_avatar_resolvestoragefileurl", "label": "resolveStorageFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L69"}, {"id": "utils_user_avatar_resolveuseravatarurl", "label": "resolveUserAvatarUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L78"}, {"id": "utils_user_avatar_resolveuseravatarfrominfo", "label": "resolveUserAvatarFromInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L88"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_joinpublicasset", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_defaultavatarpathbyrole", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L14", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_normalizeavatarstoragekey", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L22", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_pickuseravatarraw", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L44", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_hascustomuseravatar", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L53", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_pickuploadavatarurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L58", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolvestoragefileurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L69", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L78", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarfrominfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "sLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "label": "ProtocolFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolformdialog_fmtmoney", "label": "fmtMoney()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1218"}, {"id": "protocol_protocolformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1290"}, {"id": "protocol_protocolformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1310"}, {"id": "protocol_protocolformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1317"}, {"id": "protocol_protocolformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1328"}, {"id": "protocol_protocolformdialog_expressname", "label": "expressName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1356"}, {"id": "protocol_protocolformdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2131"}, {"id": "protocol_protocolformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2668"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1152", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1153", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1165", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1171", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1172", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1173", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_ethnic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1185", "weight": 1.0}, {"source":Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "label": "protocol-fq-params-change.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L1"}, {"id": "utils_protocol_fq_params_change_formatmoneysummary", "label": "formatMoneySummary()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L26"}, {"id": "utils_protocol_fq_params_change_countbystatus", "label": "countByStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L30"}, {"id": "utils_protocol_fq_params_change_fqparamssnapshot", "label": "fqParamsSnapshot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L34"}, {"id": "utils_protocol_fq_params_change_issamefqparams", "label": "isSameFqParams()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L42"}, {"id": "utils_protocol_fq_params_change_getpaidinstallmentrows", "label": "getPaidInstallmentRows()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L50"}, {"id": "utils_protocol_fq_params_change_maxpaidinstallmentno", "label": "maxPaidInstallmentNo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L56"}, {"id": "utils_protocol_fq_params_change_evaluatefqparamschange", "label": "evaluateFqParamsChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L63"}, {"id": "utils_protocol_fq_params_change_applyfqparamschange", "label": "applyFqParamsChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L143"}, {"id": "utils_protocol_fq_params_change_generateinitialfqdetails", "label": "generateInitialFqDetails()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L182"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "utils_protocol_fq_params_change_formatmoneysummary", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "utils_protocol_fq_params_change_countbystatus", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "utils_protocol_fq_params_change_fqparamssnapshot", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L34", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "utils_protocol_fq_params_change_issamefqparams", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L42", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_fq_params_change_ts", "target": "utils_protocol_fq_params_change_getpaidinstallmentrows", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-fq-params-change.ts", "source_location": "L50", "wLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "label": "ArtUserMenu.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L1"}, {"id": "widget_artusermenu_openprofiledialog", "label": "openProfileDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L91"}, {"id": "widget_artusermenu_openpassworddialog", "label": "openPasswordDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L98"}, {"id": "widget_artusermenu_getrolename", "label": "getRoleName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L108"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openprofiledialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L91", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openpassworddialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L98", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_getrolename", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L108", "weight": 1.0}], "raw_calls": [{"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L92"}, {"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L93"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L99"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L100"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "label": "user-avatar.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L1"}, {"id": "utils_user_avatar_joinpublicasset", "label": "joinPublicAsset()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L2"}, {"id": "utils_user_avatar_defaultavatarpathbyrole", "label": "defaultAvatarPathByRole()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L14"}, {"id": "utils_user_avatar_pickuseravatarraw", "label": "pickUserAvatarRaw()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L19"}, {"id": "utils_user_avatar_hascustomuseravatar", "label": "hasCustomUserAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L28"}, {"id": "utils_user_avatar_pickuploadavatarurl", "label": "pickUploadAvatarUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L33"}, {"id": "utils_user_avatar_resolvestoragefileurl", "label": "resolveStorageFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L44"}, {"id": "utils_user_avatar_resolveuseravatarurl", "label": "resolveUserAvatarUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L53"}, {"id": "utils_user_avatar_resolveuseravatarfrominfo", "label": "resolveUserAvatarFromInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L63"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_joinpublicasset", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_defaultavatarpathbyrole", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L14", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_pickuseravatarraw", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_hascustomuseravatar", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L28", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_pickuploadavatarurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L33", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolvestoragefileurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L44", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L53", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar_ts", "target": "utils_user_avatar_resolveuseravatarfrominfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L63", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarurl", "target": "utils_user_avatar_resolvestoragefileurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts", "source_location": "L58", "weight": 1.0}, {"source": "utils_user_avatar_resolveuseravatarurl", "target": "utils_user_avatar_defaultavatarpathbyrole", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utilLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "label": "auth.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L1"}, {"id": "api_auth_fetchlogin", "label": "fetchLogin()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L9"}, {"id": "api_auth_fetchgetuserinfo", "label": "fetchGetUserInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L35"}, {"id": "api_auth_fetchupdateprofile", "label": "fetchUpdateProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L57"}, {"id": "api_auth_fetchchangepassword", "label": "fetchChangePassword()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L83"}, {"id": "api_auth_fetchgetmenucodes", "label": "fetchGetMenuCodes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L94"}, {"id": "api_auth_fetchrefreshtoken", "label": "fetchRefreshToken()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L109"}, {"id": "api_auth_fetchlogout", "label": "fetchLogout()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L123"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_loginrsa", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchlogin", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchgetuserinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchupdateprofile", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L57", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchchangepassword", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L83", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchgetmenucodes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L94", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchrefreshtoken", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L109", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth_ts", "target": "api_auth_fetchlogout", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L123", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_auth_fetchlogin", "callee": "buildWebEncryptedLoginBody", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L10"}, {"caller_nid": "api_auth_fetchlogin", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L11"}, {"caller_nid": "api_auth_fetchgetuserinfo", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L36"}, {"caller_nid": "api_auth_fetchupdateprofile", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts", "source_location": "L63"}, {"caller_nid": "api_auth_fetchchangepassword", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts",Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "label": "ArtUserMenu.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L1"}, {"id": "widget_artusermenu_openprofiledialog", "label": "openProfileDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L89"}, {"id": "widget_artusermenu_openpassworddialog", "label": "openPasswordDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L96"}, {"id": "widget_artusermenu_getrolename", "label": "getRoleName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L106"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openprofiledialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L89", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openpassworddialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L96", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_getrolename", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L106", "weight": 1.0}], "raw_calls": [{"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L90"}, {"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L91"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L97"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L98"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_api_d_ts", "label": "api.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_types_api_d_ts", "target": "vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api.d.ts", "source_location": "L793", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "label": "protocol-pay-mode-switch.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L1"}, {"id": "utils_protocol_pay_mode_switch_ispaydetaillocked", "label": "isPayDetailLocked()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L30"}, {"id": "utils_protocol_pay_mode_switch_markpaydetaillocks", "label": "markPayDetailLocks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L35"}, {"id": "utils_protocol_pay_mode_switch_sumpaidamount", "label": "sumPaidAmount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L41"}, {"id": "utils_protocol_pay_mode_switch_calcremainingamount", "label": "calcRemainingAmount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L47"}, {"id": "utils_protocol_pay_mode_switch_formatmoneysummary", "label": "formatMoneySummary()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L56"}, {"id": "utils_protocol_pay_mode_switch_activedetails", "label": "activeDetails()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L60"}, {"id": "utils_protocol_pay_mode_switch_hasblockingstatus", "label": "hasBlockingStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L64"}, {"id": "utils_protocol_pay_mode_switch_countbystatus", "label": "countByStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L68"}, {"id": "utils_protocol_pay_mode_switch_createpaymentdetailrow", "label": "createPaymentDetailRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L72"}, {"id": "utils_protocol_pay_mode_switch_evaluatepaymodeswitch", "label": "evaluatePayModeSwitch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L96"}, {"id": "utils_protocol_pay_mode_switch_applypaymodeswitch", "label": "applyPayModeSwitch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L162"}, {"id": "utils_protocol_pay_mode_switch_canaddpaymentdetail", "label": "canAddPaymentDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L210"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_ispaydetaillocked", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_markpaydetaillocks", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_sumpaidamount", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L41", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_calcremainingamount", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L47", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utiLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "label": "user.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "pinia", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_enums_appenum", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_worktab", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_types_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_router_guards_beforeeach", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L21", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_menu", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L22", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_storage_storage_config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_router_routesalias", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L27", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "label": "ArtUserMenu.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L1"}, {"id": "widget_artusermenu_openprofiledialog", "label": "openProfileDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L104"}, {"id": "widget_artusermenu_openpassworddialog", "label": "openPasswordDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L111"}, {"id": "widget_artusermenu_getrolename", "label": "getRoleName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L121"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openprofiledialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L104", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_openpassworddialog", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L111", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_artusermenu_vue", "target": "widget_artusermenu_getrolename", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L121", "weight": 1.0}], "raw_calls": [{"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L105"}, {"caller_nid": "widget_artusermenu_openprofiledialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L106"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "closeUserMenu", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L112"}, {"caller_nid": "widget_artusermenu_openpassworddialog", "callee": "setTimeout", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue", "source_location": "L113"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "label": "UserProfileDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L1"}, {"id": "widget_userprofiledialog_revokeavatarlocalpreview", "label": "revokeAvatarLocalPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L168"}, {"id": "widget_userprofiledialog_handleclose", "label": "handleClose()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L175"}, {"id": "widget_userprofiledialog_handleclearavatar", "label": "handleClearAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L179"}, {"id": "widget_userprofiledialog_loadprofile", "label": "loadProfile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L185"}, {"id": "widget_userprofiledialog_handleavatarchange", "label": "handleAvatarChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L197"}, {"id": "widget_userprofiledialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L230"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L94", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L95", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L96", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L97", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_user_avatar", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L98", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_revokeavatarlocalpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L168", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleclose", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-header-bar/widget/UserProfileDialog.vue", "source_location": "L175", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_header_bar_widget_userprofiledialog_vue", "target": "widget_userprofiledialog_handleLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "label": "protocol-payment-installment.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L1"}, {"id": "utils_protocol_payment_installment_addmonthsymd", "label": "addMonthsYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L5"}, {"id": "utils_protocol_payment_installment_adddaysymd", "label": "addDaysYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L19"}, {"id": "utils_protocol_payment_installment_addintervalmonthsymd", "label": "addIntervalMonthsYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L34"}, {"id": "utils_protocol_payment_installment_calcfqinstallmentduedate", "label": "calcFqInstallmentDueDate()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L55"}, {"id": "utils_protocol_payment_installment_calcinstallmentdueamounts", "label": "calcInstallmentDueAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L67"}, {"id": "utils_protocol_payment_installment_alignfqpaymentdetailsfordisplay", "label": "alignFqPaymentDetailsForDisplay()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L93"}, {"id": "utils_protocol_payment_installment_sortfqpaymentdetails", "label": "sortFqPaymentDetails()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L160"}, {"id": "utils_protocol_payment_installment_syncfqpaymentdetailrowcount", "label": "syncFqPaymentDetailRowCount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L170"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_addmonthsymd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_adddaysymd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_addintervalmonthsymd", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L34", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_calcfqinstallmentduedate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L55", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_calcinstallmentdueamounts", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L67", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_alignfqpaymentdetailsfordisplay", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L93", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utils_protocol_payment_installment_sortfqpaymentdetails", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L160", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "target": "utLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "label": "ProtocolFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolformdialog_fmtmoney", "label": "fmtMoney()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1213"}, {"id": "protocol_protocolformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1278"}, {"id": "protocol_protocolformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1298"}, {"id": "protocol_protocolformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1305"}, {"id": "protocol_protocolformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1316"}, {"id": "protocol_protocolformdialog_expressname", "label": "expressName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1344"}, {"id": "protocol_protocolformdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2070"}, {"id": "protocol_protocolformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2607"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1152", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1153", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1165", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1171", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1172", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1173", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_ethnic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1185", "weight": 1.0}, {"source":Line truncated
+6 -3
View File
@@ -24,6 +24,7 @@ import { StorageConfig } from '@/utils/storage/storage-config'
import { fetchLogout } from '@/api/auth'
import { RoutesAlias } from '@/router/routesAlias'
import { resolveSafeRedirectTarget } from '@/utils/router'
import { pickUserAvatarRaw } from '@/utils/user-avatar'
/**
* 用户状态管理
@@ -57,11 +58,13 @@ export const useUserStore = defineStore(
* @param newInfo 新的用户信息
*/
const setUserInfo = (newInfo: Partial<Api.Auth.UserInfo> & Record<string, any>) => {
const merged = { ...(info.value || {}), ...(newInfo || {}) }
const avatarRaw = pickUserAvatarRaw(newInfo) || pickUserAvatarRaw(merged)
info.value = {
...(info.value || {}),
...(newInfo || {}),
...merged,
buttons: newInfo?.buttons ?? info.value?.buttons ?? [],
userName: newInfo?.userName ?? newInfo?.username ?? info.value?.userName ?? ''
userName: newInfo?.userName ?? newInfo?.username ?? info.value?.userName ?? '',
avatarUrl: avatarRaw || undefined
} as Api.Auth.UserInfo
}
+3 -1
View File
@@ -9,10 +9,12 @@ declare namespace Api {
username: string
realName?: string
phone?: string
email?: string
sex?: string
remark?: string
role: string
regionId?: string
regionName?: string
email?: string
roles?: string[]
/** 自定义头像 URL,空则前端使用 `/avatart.jpg` */
avatarUrl?: string | null
@@ -0,0 +1,204 @@
/**
* 分期参数变更(期数 / 间隔 / 开始时间),非缴费模式切换
* 规则见 docs/superpowers/specs/2026-06-04-protocol-pay-mode-switch-design.md §分期参数变更
*/
import {
calcFqInstallmentDueDate,
calcInstallmentDueAmounts,
sortFqPaymentDetails
} from '@/utils/protocol-payment-installment'
import {
calcRemainingAmount,
createPaymentDetailRow,
type DetailLike,
type EvaluateResult
} from '@/utils/protocol-pay-mode-switch'
export interface FqParams {
installmentCount: number | null
installmentIntervalMonths: number | null
installmentStartDate: string
}
const BLOCKING_STATUSES = new Set(['jfqrz', 'tfqrz', 'ytf'])
function formatMoneySummary(v: number): string {
return `¥${v.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`
}
function countByStatus(details: DetailLike[], status: string): number {
return details.filter((d) => String(d.payStatus || 'dfj') === status).length
}
export function fqParamsSnapshot(params: FqParams): FqParams {
return {
installmentCount: params.installmentCount,
installmentIntervalMonths: params.installmentIntervalMonths,
installmentStartDate: params.installmentStartDate || ''
}
}
export function isSameFqParams(a: FqParams, b: FqParams): boolean {
return (
Number(a.installmentCount) === Number(b.installmentCount) &&
Number(a.installmentIntervalMonths) === Number(b.installmentIntervalMonths) &&
String(a.installmentStartDate || '') === String(b.installmentStartDate || '')
)
}
export function getPaidInstallmentRows(details: DetailLike[]): DetailLike[] {
return [...details]
.filter((d) => String(d.payStatus) === 'yjf')
.sort((a, b) => Number(a.installmentNo || 0) - Number(b.installmentNo || 0))
}
function maxPaidInstallmentNo(details: DetailLike[]): number {
return getPaidInstallmentRows(details).reduce(
(max, d) => Math.max(max, Number(d.installmentNo || 0)),
0
)
}
export function evaluateFqParamsChange(
_oldParams: FqParams,
newParams: FqParams,
details: DetailLike[],
contractAmount: number | null
): EvaluateResult {
const rows = details.filter((d) => !d._voided)
const newCount = Number(newParams.installmentCount)
const interval = Number(newParams.installmentIntervalMonths)
const startDate = String(newParams.installmentStartDate || '')
if (!newCount || newCount < 1) {
return { action: 'blocked', reason: '请填写有效的分期期数。' }
}
if (!interval || interval <= 0) {
return { action: 'blocked', reason: '请填写有效的每期间隔(月)。' }
}
if (!startDate) {
return { action: 'blocked', reason: '请填写分期开始时间。' }
}
if (rows.some((d) => BLOCKING_STATUSES.has(String(d.payStatus || '')))) {
return {
action: 'blocked',
reason: '存在缴费确认中或退费处理中的明细,请先完成相关流程后再修改分期参数。'
}
}
const paidRows = getPaidInstallmentRows(rows)
const maxPaidNo = maxPaidInstallmentNo(rows)
if (maxPaidNo > 0 && newCount < maxPaidNo) {
return {
action: 'blocked',
reason: `已有第 ${maxPaidNo} 期缴费记录,分期期数不能少于 ${maxPaidNo}。`
}
}
const dfjCount = countByStatus(rows, 'dfj')
if (paidRows.length === 0) {
if (rows.length === 0) {
return { action: 'noop' }
}
return {
action: 'confirm',
summary: `将按新参数(${newCount} 期)重新生成缴费明细,原有 ${rows.length} 条待缴计划将被替换。是否继续?`
}
}
const remaining = calcRemainingAmount(contractAmount, rows)
const newUnpaidCount = newCount - paidRows.length
if (newUnpaidCount < 0) {
return {
action: 'blocked',
reason: `已有 ${paidRows.length} 期缴费记录,分期期数不能少于 ${paidRows.length}。`
}
}
if (remaining <= 0 && dfjCount > 0) {
return { action: 'blocked', reason: '已缴清,无需调整分期计划。' }
}
const countChanged = Number(_oldParams.installmentCount) !== newCount
if (countChanged) {
return {
action: 'confirm',
summary: `将保留 ${paidRows.length} 期已缴记录,作废 ${dfjCount} 期未缴计划,并重新生成 ${newUnpaidCount} 期未缴明细(剩余 ${formatMoneySummary(remaining)})。是否继续?`
}
}
return {
action: 'confirm',
summary: `将保留已缴记录,并更新 ${dfjCount} 期未缴计划的应缴时间与金额(剩余 ${formatMoneySummary(remaining)})。是否继续?`
}
}
/** 按新分期参数重建明细:保留 yjf,作废原 dfj,生成剩余期次 */
export function applyFqParamsChange(
params: FqParams,
details: DetailLike[],
contractAmount: number | null
): DetailLike[] {
const count = Number(params.installmentCount) || 0
const interval = Number(params.installmentIntervalMonths) || 0
const startDate = params.installmentStartDate || ''
if (!count || !interval || !startDate) return details
const paidRows = getPaidInstallmentRows(details).map((d) => ({
...d,
_payModeLocked: true
}))
const remainingCount = count - paidRows.length
const remaining = calcRemainingAmount(contractAmount, paidRows)
const amounts =
remainingCount > 0
? calcInstallmentDueAmounts(remaining, remainingCount)
: []
const result: DetailLike[] = [...paidRows]
for (let i = 0; i < remainingCount; i++) {
const periodIndex = paidRows.length + i
result.push(
createPaymentDetailRow({
dueAmount: amounts[i] ?? null,
installmentNo: periodIndex + 1,
installmentDueDate: calcFqInstallmentDueDate(startDate, periodIndex, interval),
expanded: false
})
)
}
return sortFqPaymentDetails(result)
}
/** 首次填充分期参数时生成明细(无需确认) */
export function generateInitialFqDetails(
params: FqParams,
contractAmount: number | null
): DetailLike[] {
const count = Number(params.installmentCount) || 0
const interval = Number(params.installmentIntervalMonths) || 0
const startDate = params.installmentStartDate || ''
if (!count || !interval || !startDate) return []
const amounts = calcInstallmentDueAmounts(contractAmount, count)
const result: DetailLike[] = []
for (let i = 0; i < count; i++) {
result.push(
createPaymentDetailRow({
dueAmount: amounts[i] ?? null,
installmentNo: i + 1,
installmentDueDate: calcFqInstallmentDueDate(startDate, i, interval),
expanded: i === 0
})
)
}
return result
}
@@ -0,0 +1,216 @@
/**
* 采集协议单 · 缴费模式切换(dj ↔ fq)
* 规则见 docs/superpowers/specs/2026-06-04-protocol-pay-mode-switch-design.md
*/
import {
calcFqInstallmentDueDate,
calcInstallmentDueAmounts
} from '@/utils/protocol-payment-installment'
export type PayMode = 'dj' | 'fq'
export type DetailLike = Record<string, any>
const LOCKED_STATUSES = new Set(['jfqrz', 'yjf', 'tfqrz', 'ytf'])
const BLOCKING_STATUSES = new Set(['jfqrz', 'tfqrz', 'ytf'])
export interface PayModeSwitchContext {
contractAmount: number | null
lumpSumDeadline: string
installmentCount: number | null
installmentIntervalMonths: number | null
installmentStartDate: string
}
export type EvaluateResult =
| { action: 'noop' }
| { action: 'blocked'; reason: string }
| { action: 'confirm'; summary: string }
export function isPayDetailLocked(detail: DetailLike): boolean {
if (detail._payModeLocked) return true
return LOCKED_STATUSES.has(String(detail.payStatus || ''))
}
export function markPayDetailLocks(details: DetailLike[]): void {
for (const d of details) {
d._payModeLocked = isPayDetailLocked(d)
}
}
export function sumPaidAmount(details: DetailLike[]): number {
return details
.filter((d) => String(d.payStatus) === 'yjf')
.reduce((sum, d) => sum + (Number(d.dueAmount) || 0), 0)
}
export function calcRemainingAmount(
contractAmount: number | null,
details: DetailLike[]
): number {
if (contractAmount == null) return 0
const remaining = Number(contractAmount) - sumPaidAmount(details)
return Math.round(remaining * 100) / 100
}
function formatMoneySummary(v: number): string {
return `¥${v.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`
}
function activeDetails(details: DetailLike[]): DetailLike[] {
return details.filter((d) => !d._voided)
}
function hasBlockingStatus(details: DetailLike[]): boolean {
return details.some((d) => BLOCKING_STATUSES.has(String(d.payStatus || '')))
}
function countByStatus(details: DetailLike[], status: string): number {
return details.filter((d) => String(d.payStatus || 'dfj') === status).length
}
export function createPaymentDetailRow(options: {
dueAmount: number | null
installmentDueDate?: string
installmentNo?: number | null
payStatus?: string
expanded?: boolean
}): DetailLike {
const now = new Date()
const pad = (n: number) => String(n).padStart(2, '0')
const defaultTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
return {
transferType: '',
dueAmount: options.dueAmount,
transferAmount: null,
transferTime: defaultTime,
payStatus: options.payStatus ?? 'dfj',
installmentNo: options.installmentNo ?? null,
installmentDueDate: options.installmentDueDate ?? '',
_expanded: options.expanded ?? false,
_payModeLocked: false,
attachments: []
}
}
export function evaluatePayModeSwitch(
fromMode: PayMode,
toMode: PayMode,
details: DetailLike[],
ctx: PayModeSwitchContext
): EvaluateResult {
if (fromMode === toMode) return { action: 'noop' }
const rows = activeDetails(details)
if (hasBlockingStatus(rows)) {
return {
action: 'blocked',
reason: '存在缴费确认中或退费处理中的明细,请先完成相关流程后再切换缴费模式。'
}
}
if (fromMode === 'dj' && toMode === 'fq') {
if (rows.some((d) => isPayDetailLocked(d))) {
return {
action: 'blocked',
reason: '趸交已有缴费记录,无法改为分期。'
}
}
const count = ctx.installmentCount || 6
return {
action: 'confirm',
summary: `将清除当前趸交计划,按 ${count} 期分期重新生成缴费明细。是否继续?`
}
}
if (fromMode === 'fq' && toMode === 'dj') {
const dfjCount = countByStatus(rows, 'dfj')
const yjfCount = countByStatus(rows, 'yjf')
if (yjfCount > 0 && dfjCount === 0) {
return {
action: 'blocked',
reason: '分期已全部缴清,无需改为趸交。'
}
}
const remaining = calcRemainingAmount(ctx.contractAmount, rows)
if (yjfCount > 0 && remaining <= 0) {
return {
action: 'blocked',
reason: '已缴清,无需改为趸交。'
}
}
if (yjfCount > 0) {
return {
action: 'confirm',
summary: `将作废 ${dfjCount} 期未缴计划,保留 ${yjfCount} 期已缴记录,并新增 1 条趸交明细(剩余 ${formatMoneySummary(remaining)})。是否继续?`
}
}
return {
action: 'confirm',
summary: `将作废 ${dfjCount || rows.length} 期分期计划,并合并为 1 条趸交明细。是否继续?`
}
}
return { action: 'noop' }
}
export function applyPayModeSwitch(
fromMode: PayMode,
toMode: PayMode,
details: DetailLike[],
ctx: PayModeSwitchContext
): DetailLike[] {
const rows = activeDetails(details)
if (fromMode === 'dj' && toMode === 'fq') {
const count = Number(ctx.installmentCount) || 6
const interval = Number(ctx.installmentIntervalMonths) || 6
const startDate = ctx.installmentStartDate || ''
const amounts = calcInstallmentDueAmounts(ctx.contractAmount, count)
const result: DetailLike[] = []
for (let i = 0; i < count; i++) {
result.push(
createPaymentDetailRow({
dueAmount: amounts[i] ?? null,
installmentNo: i + 1,
installmentDueDate:
startDate && interval ? calcFqInstallmentDueDate(startDate, i, interval) : '',
expanded: i === 0
})
)
}
return result
}
if (fromMode === 'fq' && toMode === 'dj') {
const yjfRows = rows
.filter((d) => String(d.payStatus) === 'yjf')
.map((d) => ({ ...d, _payModeLocked: true }))
const remaining = calcRemainingAmount(ctx.contractAmount, yjfRows)
const deadline = ctx.lumpSumDeadline || ''
const dueAmount = yjfRows.length ? remaining : ctx.contractAmount
const newDjRow = createPaymentDetailRow({
dueAmount,
installmentNo: null,
installmentDueDate: deadline,
expanded: yjfRows.length === 0
})
return [...yjfRows, newDjRow]
}
return rows
}
/** 趸交模式下是否还可添加待缴明细 */
export function canAddPaymentDetail(payMode: string, details: DetailLike[]): boolean {
if (payMode === 'fq') return true
const dfjCount = details.filter(
(d) => !isPayDetailLocked(d) && String(d.payStatus || 'dfj') === 'dfj'
).length
return dfjCount < 1
}
@@ -1,5 +1,7 @@
/** 分期缴费:期数对齐、应缴日/金额计算(与 ProtocolFormDialog 逻辑一致) */
const DAYS_PER_MONTH = 30
export function addMonthsYmd(dateStr: string, months: number): string {
const compact = String(dateStr || '').replace(/-/g, '')
if (compact.length !== 8) return ''
@@ -28,13 +30,34 @@ export function addDaysYmd(dateStr: string, days: number): string {
return `${yy}${mm}${dd}`
}
/** 分期应缴费时间:第 1 期=开始时间,第 2 期起在按月递增基础上提前 10 天 */
/** 整数部分按日历加月,小数部分按 30 天/月换算为天数 */
export function addIntervalMonthsYmd(dateStr: string, totalMonths: number): string {
const compact = String(dateStr || '').replace(/-/g, '')
if (compact.length !== 8 || !Number.isFinite(totalMonths) || totalMonths === 0) {
return compact.length === 8 ? compact : ''
}
const wholeMonths = Math.trunc(totalMonths)
const fracMonths = totalMonths - wholeMonths
let result = compact
if (wholeMonths !== 0) {
result = addMonthsYmd(result, wholeMonths)
}
if (fracMonths !== 0) {
const days = Math.round(fracMonths * DAYS_PER_MONTH)
if (days !== 0) {
result = addDaysYmd(result, days)
}
}
return result
}
/** 分期应缴费时间:第 1 期=开始时间,第 2 期起在间隔递增基础上提前 10 天 */
export function calcFqInstallmentDueDate(
startDate: string,
index: number,
intervalMonths: number
): string {
const base = addMonthsYmd(startDate, index * intervalMonths)
const base = addIntervalMonthsYmd(startDate, index * intervalMonths)
if (index >= 1) {
return addDaysYmd(base, -10)
}
+80 -5
View File
@@ -1,17 +1,92 @@
/** 未设置头像时使用 `public/avatart.jpg`(根路径 `/avatart.jpg`) */
export const DEFAULT_USER_AVATAR_URL = '/avatart.jpg'
/** 未设置头像时使用 `public/avatart.jpg` */
function joinPublicAsset(path: string): string {
const base = import.meta.env.BASE_URL || '/'
const prefix = base.endsWith('/') ? base : `${base}/`
return `${prefix}${path.replace(/^\//, '')}`
}
export const DEFAULT_USER_AVATAR_URL = joinPublicAsset('avatart.jpg')
/** 本地/部署环境由 .env 配置;docker 默认走 Nginx `/minio/zkxw` 反代 */
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '/minio/zkxw').replace(/\/$/, '')
/** @deprecated 各角色默认已统一为默认头像图,保留参数仅为兼容旧调用 */
export function defaultAvatarPathByRole(_role: string | undefined): string {
return DEFAULT_USER_AVATAR_URL
}
/** 展示用头像:`avatarUrl` 非空则用其值,否则用默认图 */
/**
* 将库中头像值规范为存储相对路径(如 avatar/202606/04/xxx.jpeg)。
* 兼容历史数据里写入的完整 URL(如 https://www.chengyantek.cn/zkxw/avatar/...)。
*/
export function normalizeAvatarStorageKey(raw: string | null | undefined): string {
const u = raw != null ? String(raw).trim() : ''
if (!u) return ''
if (!/^https?:\/\//i.test(u) && !u.startsWith('/')) return u
const marker = '/avatar/'
const lower = u.toLowerCase()
const idx = lower.indexOf(marker)
if (idx >= 0) {
return `avatar/${u.slice(idx + marker.length)}`
}
if (u.startsWith('/')) {
const parts = u.split('/').filter(Boolean)
const avatarIdx = parts.findIndex((p) => p === 'avatar')
if (avatarIdx >= 0) return parts.slice(avatarIdx).join('/')
}
return u
}
/** 从用户信息对象读取原始头像字段(兼容 avatar / avatarUrl) */
export function pickUserAvatarRaw(
source: { avatarUrl?: string | null; avatar?: string | null } | null | undefined
): string {
if (!source) return ''
const raw = source.avatarUrl ?? source.avatar
return normalizeAvatarStorageKey(raw != null ? String(raw).trim() : '')
}
/** 是否已设置自定义头像(非空即视为已设置) */
export function hasCustomUserAvatar(avatarUrl: string | null | undefined): boolean {
return Boolean(normalizeAvatarStorageKey(avatarUrl))
}
/** 解析上传接口返回的图片地址 */
export function pickUploadAvatarUrl(res: unknown): string {
if (res == null) return ''
if (typeof res === 'string') return normalizeAvatarStorageKey(res)
const record = res as Record<string, unknown>
const nested = record.data as Record<string, unknown> | undefined
const raw =
record.url ?? record.fileUrl ?? record.file_url ?? nested?.url ?? nested?.fileUrl
return typeof raw === 'string' ? normalizeAvatarStorageKey(raw) : ''
}
/** MinIO 相对路径或完整 URL → 浏览器可访问地址 */
export function resolveStorageFileUrl(url: string | null | undefined): string {
const key = normalizeAvatarStorageKey(url)
if (!key) return ''
if (/^https?:\/\//i.test(key)) return key
if (key.startsWith('/')) return key
return `${minioBaseUrl}/${key.replace(/^\//, '')}`
}
/** 展示用头像:优先用户表头像,否则默认图 */
export function resolveUserAvatarUrl(
avatarUrl: string | null | undefined,
role: string | undefined
): string {
const u = avatarUrl != null ? String(avatarUrl).trim() : ''
if (u) return u
const key = normalizeAvatarStorageKey(avatarUrl)
if (key) return resolveStorageFileUrl(key)
return defaultAvatarPathByRole(role)
}
/** 从用户对象直接得到展示用头像 URL */
export function resolveUserAvatarFromInfo(
source: { avatarUrl?: string | null; avatar?: string | null; role?: string } | null | undefined
): string {
return resolveUserAvatarUrl(pickUserAvatarRaw(source), source?.role)
}
@@ -758,7 +758,7 @@
step="any"
:min="1"
style="width: 100%"
@change="tryGenerateInstallmentDetails"
@change="handleFqParamsChange"
/>
</el-form-item>
<el-form-item label="每期间隔(月)">
@@ -766,9 +766,9 @@
v-model="formData.installmentIntervalMonths"
type="number"
step="any"
:min="1"
:min="0.1"
style="width: 100%"
@change="tryGenerateInstallmentDetails"
@change="handleFqParamsChange"
/>
</el-form-item>
<el-form-item label="分期开始时间">
@@ -780,7 +780,7 @@
placeholder="选择开始时间"
clearable
style="width: 100%"
@change="tryGenerateInstallmentDetails"
@change="handleFqParamsChange"
/>
</el-form-item>
</template>
@@ -790,7 +790,11 @@
<!-- 缴费明细 -->
<div class="section-subtitle" style="margin-top: 20px">缴费明细</div>
<div style="margin-bottom: 14px">
<el-button type="primary" @click="addPaymentDetail">
<el-button
type="primary"
:disabled="!canAddPaymentDetail(formData.payMode, formData.paymentDetails)"
@click="addPaymentDetail"
>
<el-icon><Plus /></el-icon>
添加明细
</el-button>
@@ -834,6 +838,7 @@
{{ paymentDetailSummary(detail) }}
</span>
<el-button
v-if="!isPayDetailLocked(detail)"
link
type="danger"
size="small"
@@ -872,6 +877,7 @@
<AmountInput
v-model="detail.transferAmount"
:min="0"
:disabled="isPayDetailLocked(detail)"
placeholder="请输入转账金额"
/>
</el-form-item>
@@ -882,6 +888,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择时间"
clearable
:disabled="isPayDetailLocked(detail)"
style="width: 100%"
/>
</el-form-item>
@@ -889,6 +896,7 @@
<el-select
v-model="detail.payStatus"
placeholder="状态"
:disabled="isPayDetailLocked(detail)"
style="width: 100%"
>
<el-option label="待缴费" value="dfj" />
@@ -906,12 +914,13 @@
format="YYYY-MM-DD"
placeholder="选择应缴费时间"
clearable
:disabled="isPayDetailLocked(detail)"
style="width: 100%"
/>
</el-form-item>
</div>
<!-- 每条明细的附件上传区 -->
<div class="payment-detail-card__attachments">
<div v-if="!isPayDetailLocked(detail)" class="payment-detail-card__attachments">
<div class="attachment-upload">
<div class="attachment-list">
<el-upload
@@ -1175,10 +1184,24 @@
} from '@/api/admin'
import { fetchEthnicList, type EthnicOption } from '@/api/ethnic'
import { useUserStore } from '@/store/modules/user'
import { syncFqPaymentDetailRowCount } from '@/utils/protocol-payment-installment'
import {
calcFqInstallmentDueDate,
syncFqPaymentDetailRowCount
} from '@/utils/protocol-payment-installment'
applyPayModeSwitch,
canAddPaymentDetail,
evaluatePayModeSwitch,
isPayDetailLocked,
markPayDetailLocks,
sumPaidAmount,
type PayMode
} from '@/utils/protocol-pay-mode-switch'
import {
applyFqParamsChange,
evaluateFqParamsChange,
fqParamsSnapshot,
generateInitialFqDetails,
isSameFqParams,
type FqParams
} from '@/utils/protocol-fq-params-change'
import AmountInput from '@/components/common/amount-input/index.vue'
import { formatMoneyDisplay } from '@/utils/money-format'
import {
@@ -1199,6 +1222,16 @@
const userStore = useUserStore()
/** 上次确认生效的缴费模式(用于切换取消/拦截时回滚) */
const lastConfirmedPayMode = ref<PayMode>('dj')
/** 上次确认生效的分期参数(用于编辑时修改期数/间隔/开始时间回滚) */
const lastConfirmedFqParams = ref<FqParams>({
installmentCount: null,
installmentIntervalMonths: null,
installmentStartDate: ''
})
const customerId = ref<string | null>(null)
const customerOptions = ref<
Array<{ id: string; customerName: string; phoneNo: string; idCardNo: string }>
@@ -1544,6 +1577,12 @@
batchTransportVisible.value = false
Object.assign(batchTransport, createEmptyTransport())
activeFormTab.value = 'basic'
lastConfirmedPayMode.value = 'dj'
lastConfirmedFqParams.value = {
installmentCount: null,
installmentIntervalMonths: null,
installmentStartDate: ''
}
}
function scrollToSection(key: string) {
@@ -1627,6 +1666,7 @@
}
function addPaymentDetail() {
if (!canAddPaymentDetail(formData.payMode, formData.paymentDetails)) return
const now = new Date()
const pad = (n: number) => String(n).padStart(2, '0')
const defaultTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
@@ -1702,77 +1742,180 @@
return Number.isFinite(n) ? Math.trunc(n) : undefined
}
function toFloatOrUndefined(val: unknown): number | undefined {
if (val == null || val === '') return undefined
const n = Number(val)
return Number.isFinite(n) ? n : undefined
}
function fqPaymentContext() {
return {
payMode: formData.payMode,
installmentCount: toIntOrUndefined(formData.installmentCount) ?? null,
contractAmount: formData.contractAmount,
installmentStartDate: formData.installmentStartDate,
installmentIntervalMonths: toIntOrUndefined(formData.installmentIntervalMonths) ?? null
installmentIntervalMonths: toFloatOrUndefined(formData.installmentIntervalMonths) ?? null
}
}
/** 分期模式:三个参数都填好后生成/补齐缴费明细行(已有明细时仅补齐期次,不整表覆盖) */
function tryGenerateInstallmentDetails() {
if (formData.payMode !== 'fq') return
const count = formData.installmentCount
const interval = formData.installmentIntervalMonths
const startDate = formData.installmentStartDate
if (!count || !interval || !startDate) return
function payModeSwitchContext() {
return {
contractAmount: formData.contractAmount,
lumpSumDeadline: formData.lumpSumDeadline,
installmentCount: toIntOrUndefined(formData.installmentCount) ?? null,
installmentIntervalMonths: toFloatOrUndefined(formData.installmentIntervalMonths) ?? null,
installmentStartDate: formData.installmentStartDate
}
}
if (formData.paymentDetails.length) {
syncFqPaymentDetailRowCount(fqPaymentContext(), formData.paymentDetails)
syncPaymentDueAmounts()
formData.paymentDetails.forEach((d, i) => {
d.installmentNo = i + 1
d.installmentDueDate = calcFqInstallmentDueDate(startDate, i, interval)
})
function getFqParamsFromForm(): FqParams {
return {
installmentCount: toIntOrUndefined(formData.installmentCount) ?? null,
installmentIntervalMonths: toFloatOrUndefined(formData.installmentIntervalMonths) ?? null,
installmentStartDate: formData.installmentStartDate || ''
}
}
function restoreFqParams(params: FqParams) {
formData.installmentCount = params.installmentCount
formData.installmentIntervalMonths = params.installmentIntervalMonths
formData.installmentStartDate = params.installmentStartDate || ''
}
function syncLastConfirmedFqParams() {
lastConfirmedFqParams.value = fqParamsSnapshot(getFqParamsFromForm())
}
/**
* 分期参数变更(期数 / 间隔 / 开始时间):校验 → 确认 → 重建未缴期次
* 与缴费模式切换独立;保留 yjf 已缴行,作废原 dfj 后按剩余金额重建。
*/
async function handleFqParamsChange() {
if (formData.payMode !== 'fq') return
const newParams = getFqParamsFromForm()
const oldParams = lastConfirmedFqParams.value
if (isSameFqParams(oldParams, newParams)) return
if (
!newParams.installmentCount ||
!newParams.installmentIntervalMonths ||
!newParams.installmentStartDate
) {
return
}
const dueAmounts = calcInstallmentDueAmounts(formData.contractAmount, count)
const details = []
for (let i = 0; i < count; i++) {
details.push({
transferType: '',
dueAmount: dueAmounts[i] ?? null,
transferAmount: null,
transferTime: '',
payStatus: 'dfj',
installmentNo: i + 1,
installmentDueDate: calcFqInstallmentDueDate(startDate, i, interval),
attachments: [] as Array<{
id?: string
fileName: string
fileUrl: string
fileSize: number
fileSizeDisplay?: string
fileType?: string
_uploading?: boolean
}>
})
if (formData.paymentDetails.length === 0) {
formData.paymentDetails = generateInitialFqDetails(newParams, formData.contractAmount)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
syncLastConfirmedFqParams()
return
}
initPaymentDetailExpandFlags(details)
formData.paymentDetails = details
const evaluation = evaluateFqParamsChange(
oldParams,
newParams,
formData.paymentDetails,
formData.contractAmount
)
if (evaluation.action === 'noop') {
syncLastConfirmedFqParams()
return
}
if (evaluation.action === 'blocked') {
restoreFqParams(oldParams)
await ElMessageBox.alert(evaluation.reason, '无法修改分期参数', { type: 'warning' })
return
}
if (evaluation.action === 'confirm') {
try {
await ElMessageBox.confirm(evaluation.summary, '修改分期参数', {
confirmButtonText: '确认修改',
cancelButtonText: '取消',
type: 'warning'
})
} catch {
restoreFqParams(oldParams)
return
}
}
formData.paymentDetails = applyFqParamsChange(
newParams,
formData.paymentDetails,
formData.contractAmount
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
syncLastConfirmedFqParams()
}
/** 缴费模式切换回调 */
function onPayModeChange(val: string) {
if (val === 'fq') {
applyFqPaymentDefaults({ resetCountInterval: true })
tryGenerateInstallmentDetails()
} else {
// 趸交模式:将趸交截止时间同步到所有明细的应缴费时间
syncPaymentDueAmounts()
/** 缴费模式切换:校验 → 确认 → 变换明细 */
async function onPayModeChange(val: string) {
const toMode = val as PayMode
const fromMode = lastConfirmedPayMode.value
if (toMode === fromMode) return
const evaluation = evaluatePayModeSwitch(
fromMode,
toMode,
formData.paymentDetails,
payModeSwitchContext()
)
if (evaluation.action === 'blocked') {
formData.payMode = fromMode
await ElMessageBox.alert(evaluation.reason, '无法切换缴费模式', { type: 'warning' })
return
}
if (evaluation.action === 'confirm') {
try {
await ElMessageBox.confirm(evaluation.summary, '切换缴费模式', {
confirmButtonText: '确认切换',
cancelButtonText: '取消',
type: 'warning'
})
} catch {
formData.payMode = fromMode
return
}
}
if (toMode === 'fq') {
applyFqPaymentDefaults({ resetCountInterval: false })
}
formData.paymentDetails = applyPayModeSwitch(
fromMode,
toMode,
formData.paymentDetails,
payModeSwitchContext()
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
if (toMode === 'dj') {
syncLumpSumDueDate()
}
lastConfirmedPayMode.value = toMode
if (toMode === 'fq') {
syncLastConfirmedFqParams()
}
}
/** 趸交模式:将趸交截止时间同步到所有缴费明细的应缴费时间 */
/** 趸交模式:将趸交截止时间同步到待缴明细的应缴费时间 */
function syncLumpSumDueDate() {
if (formData.payMode !== 'dj') return
const deadline = formData.lumpSumDeadline || ''
formData.paymentDetails.forEach((d) => {
if (isPayDetailLocked(d)) return
d.installmentDueDate = deadline
})
}
@@ -1885,6 +2028,7 @@
formData.installmentStartDate = String(pay.installmentStartDate || '')
if (formData.payMode === 'fq') {
applyFqPaymentDefaults()
syncLastConfirmedFqParams()
}
}
@@ -1896,8 +2040,10 @@
}))
initPaymentDetailExpandFlags(details)
formData.paymentDetails = details
markPayDetailLocks(formData.paymentDetails)
}
syncFqPaymentDetailRowCount(fqPaymentContext(), formData.paymentDetails)
lastConfirmedPayMode.value = (formData.payMode === 'fq' ? 'fq' : 'dj') as PayMode
const pa =
(row as any).protocolAttachments || (bundle.protocol as any)?.protocolAttachments || ''
@@ -2057,11 +2203,13 @@
contractAmount: formData.contractAmount ?? undefined,
lumpSumDeadline: formData.lumpSumDeadline || undefined,
installmentCount: toIntOrUndefined(formData.installmentCount),
installmentIntervalMonths: toIntOrUndefined(formData.installmentIntervalMonths),
installmentIntervalMonths: toFloatOrUndefined(formData.installmentIntervalMonths),
installmentStartDate: formData.installmentStartDate || undefined
},
paymentDetails: formData.paymentDetails.map(({ _expanded, ...d }) => ({
transferType: d.transferType,
paymentDetails: formData.paymentDetails.map(
({ _expanded, _payModeLocked, _voided, ...d }) => ({
id: d.id,
transferType: d.transferType,
dueAmount: d.dueAmount,
transferAmount: d.transferAmount,
transferTime: d.transferTime,
@@ -2087,7 +2235,7 @@
fileSizeDisplay: a.fileSizeDisplay,
fileType: a.fileType
}))
}))
}))
}
if (isEdit.value && editingId.value) {
@@ -2527,21 +2675,29 @@
return amounts
}
/** 按缴费模式同步各明细应缴费金额 */
/** 按缴费模式同步各明细应缴费金额(跳过已锁定明细) */
function syncPaymentDueAmounts() {
const contract = formData.contractAmount
if (contract == null) return
if (formData.payMode === 'dj') {
formData.paymentDetails.forEach((d) => {
d.dueAmount = contract
const paidSum = sumPaidAmount(formData.paymentDetails)
const remaining = Math.round((Number(contract) - paidSum) * 100) / 100
const dfjRows = formData.paymentDetails.filter(
(d) => !isPayDetailLocked(d) && String(d.payStatus || 'dfj') === 'dfj'
)
const targetAmount = paidSum > 0 ? remaining : contract
dfjRows.forEach((d, i) => {
if (i === 0) d.dueAmount = targetAmount
})
return
}
if (formData.payMode === 'fq') {
if (formData.paymentDetails.some((d) => isPayDetailLocked(d))) return
const count = formData.installmentCount || formData.paymentDetails.length
if (!count) return
const amounts = calcInstallmentDueAmounts(Number(contract), count)
const amounts = calcInstallmentDueAmounts(Number(contract), Number(count))
formData.paymentDetails.forEach((d, i) => {
if (isPayDetailLocked(d)) return
if (amounts[i] !== undefined) d.dueAmount = amounts[i]
})
}