This commit is contained in:
zlwu committed 2026-06-07 00:40:01 +08:00
1 parent f9676f8759
commit 725774ef0c
155 files changed
+50675 -5242

No files matched your search

+1 -1
View File
@@ -1 +1 @@
85220
35826
+2
View File
@@ -6,6 +6,7 @@ from app.api.workflow import router as workflow_router
from app.api.leave import router as leave_router
from app.api.crm import router as crm_router
from app.api.monitor import router as monitor_router
from app.api.bx import bx_router
__all__ = [
"public_router",
@@ -15,4 +16,5 @@ __all__ = [
"leave_router",
"crm_router",
"monitor_router",
"bx_router",
]
+19
View File
@@ -0,0 +1,19 @@
"""报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。"""
from fastapi import APIRouter
from app.api.bx import advance, attachment, expense_item, payment, reimburse, workflow
from app.schemas.bx.common import ApiResponse
bx_router = APIRouter(tags=["报销管理"])
bx_router.include_router(expense_item.router)
bx_router.include_router(workflow.router)
bx_router.include_router(advance.router)
bx_router.include_router(attachment.router)
bx_router.include_router(reimburse.router)
bx_router.include_router(payment.router)
@bx_router.get("/bx/health", response_model=ApiResponse[dict])
def bx_health():
"""P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。"""
return ApiResponse(data={"status": "ok", "module": "bx"})
+345
View File
@@ -0,0 +1,345 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.models.bx_models import BxAdvanceApply, BxWorkflowDef
from app.schemas.bx.advance import (
AdvanceApplyCreate,
AdvanceApplyOut,
AdvanceApplyUpdate,
AdvanceApprovalCreate,
AdvanceApprovalOut,
AdvanceApproverPendingStatsOut,
AdvanceMyStatsOut,
)
from app.schemas.bx.common import ApiResponse, PagedData
from app.services.bx.advance import AdvanceService
from app.services.bx.expense_item import ExpenseItemService
from app.utils.bx_codegen import generate_apply_code
from app.utils.bx_date_range import parse_create_time_day_bounds
router = APIRouter(prefix="/advances", tags=["备用金管理"])
def _tenant(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
def _fill_offset_amounts(db: Session, tenant_no: str, rows, out_rows: List[AdvanceApplyOut]) -> None:
if not out_rows:
return
offset_map = AdvanceService.offset_amount_map(db, tenant_no, [r.id for r in rows])
for out_row in out_rows:
out_row.offset_amount = offset_map.get(out_row.id, 0.0)
@router.get("/", response_model=ApiResponse[PagedData[AdvanceApplyOut]])
def list_advances(
page: int = 1,
pageSize: int = 10,
status: Optional[str] = None,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = []
if status:
filters.append(BxAdvanceApply.status == status)
if keyword:
filters.append(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
if applicantId:
filters.append(BxAdvanceApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxAdvanceApply.create_time >= d0)
filters.append(BxAdvanceApply.create_time < d1)
rows, total = AdvanceService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
out_rows = [AdvanceApplyOut.model_validate(r) for r in rows]
_fill_offset_amounts(db, tenant_no, rows, out_rows)
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/my", response_model=ApiResponse[PagedData[AdvanceApplyOut]])
def my_advances(
page: int = 1,
pageSize: int = 10,
status: Optional[str] = None,
keyword: str = "",
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = [
or_(
BxAdvanceApply.applicant_id == user.id,
BxAdvanceApply.create_by == user.id,
)
]
if status:
filters.append(BxAdvanceApply.status == status)
if keyword:
filters.append(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxAdvanceApply.create_time >= d0)
filters.append(BxAdvanceApply.create_time < d1)
rows, total = AdvanceService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
out_rows = [AdvanceApplyOut.model_validate(r) for r in rows]
_fill_offset_amounts(db, tenant_no, rows, out_rows)
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/my/stats", response_model=ApiResponse[AdvanceMyStatsOut])
def my_advance_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = AdvanceService.my_apply_stats(db, user.id, tenant_no)
return ApiResponse(data=AdvanceMyStatsOut.model_validate(data))
@router.get("/stats/status-counts", response_model=ApiResponse[dict])
def advance_status_counts(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
return ApiResponse(data=AdvanceService.global_status_counts(db, tenant_no))
@router.get("/pending-approval", response_model=ApiResponse[PagedData[AdvanceApplyOut]])
def pending_approval(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
applicant_id = str(applicantId).strip() if applicantId else None
rows, total = AdvanceService.pending_approval(
db,
user,
tenant_no,
page=page,
page_size=pageSize,
keyword=keyword,
applicant_id=applicant_id,
apply_date=applyDate,
)
out_rows = [AdvanceApplyOut.model_validate(r) for r in rows]
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/pending-approval/stats", response_model=ApiResponse[AdvanceApproverPendingStatsOut])
def advance_pending_approval_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = AdvanceService.pending_approval_stats(db, user, tenant_no)
return ApiResponse(data=AdvanceApproverPendingStatsOut.model_validate(data))
@router.get("/approved-by-me", response_model=ApiResponse[PagedData[AdvanceApplyOut]])
def approved_by_me(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
applicant_id = str(applicantId).strip() if applicantId else None
rows, total = AdvanceService.approved_by_me(
db,
user,
tenant_no,
page=page,
page_size=pageSize,
keyword=keyword,
applicant_id=applicant_id,
apply_date=applyDate,
)
out_rows = [AdvanceApplyOut.model_validate(r) for r in rows]
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/{advance_id}", response_model=ApiResponse[AdvanceApplyOut])
def get_advance(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
advance = AdvanceService.get_by_id(db, advance_id, tenant_no)
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
out = AdvanceApplyOut.model_validate(advance)
offset_map = AdvanceService.offset_amount_map(db, tenant_no, [advance_id])
out.offset_amount = offset_map.get(advance_id, 0.0)
return ApiResponse(data=out)
@router.get("/{advance_id}/approvals", response_model=ApiResponse[List[AdvanceApprovalOut]])
def get_approvals(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not AdvanceService.get_by_id(db, advance_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
approvals = AdvanceService.get_approvals(db, advance_id, tenant_no)
return ApiResponse(data=[AdvanceApprovalOut.model_validate(a) for a in approvals])
@router.get("/{advance_id}/approval-history", response_model=ApiResponse[List[AdvanceApprovalOut]])
def get_approval_history(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not AdvanceService.get_by_id(db, advance_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
history = AdvanceService.get_approval_history(db, advance_id, tenant_no)
return ApiResponse(data=[AdvanceApprovalOut.model_validate(a) for a in history])
@router.post("/", response_model=ApiResponse[AdvanceApplyOut])
def create_advance(
body: AdvanceApplyCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = body.model_dump(exclude_unset=True)
eid = data.get("expense_item_id")
if eid:
if not ExpenseItemService.get_by_id(db, eid, tenant_no):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"费用项目 {eid} 不存在")
data["applicant_id"] = user.id
data["applicant_name"] = user.name
data["apply_code"] = generate_apply_code(db, "BYJ", tenant_no)
data["status"] = "draft"
wf = (
db.query(BxWorkflowDef)
.filter(
BxWorkflowDef.biz_type == "advance",
BxWorkflowDef.tenant_no == tenant_no,
BxWorkflowDef.del_flag == 0,
)
.first()
)
if wf:
data["wf_id"] = wf.id
advance = AdvanceService.create(db, data, user=user)
db.commit()
db.refresh(advance)
return ApiResponse(data=AdvanceApplyOut.model_validate(advance))
@router.put("/{advance_id}", response_model=ApiResponse[AdvanceApplyOut])
def update_advance(
advance_id: str,
body: AdvanceApplyUpdate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = body.model_dump(exclude_unset=True)
if data.get("expense_item_id"):
eid = data["expense_item_id"]
if not ExpenseItemService.get_by_id(db, eid, tenant_no):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"费用项目 {eid} 不存在")
advance = AdvanceService.update(db, advance_id, data, user=user)
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
db.commit()
db.refresh(advance)
return ApiResponse(data=AdvanceApplyOut.model_validate(advance))
@router.post("/{advance_id}/submit", response_model=ApiResponse[AdvanceApplyOut])
def submit_advance(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
advance = AdvanceService.submit(db, advance_id, user)
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
db.commit()
db.refresh(advance)
return ApiResponse(data=AdvanceApplyOut.model_validate(advance))
@router.post("/{advance_id}/approve", response_model=ApiResponse[AdvanceApplyOut])
def approve_advance(
advance_id: str,
body: AdvanceApprovalCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
advance = AdvanceService.approve(db, advance_id, user, body.approval_opinion or "")
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在或无权审批")
db.commit()
db.refresh(advance)
return ApiResponse(data=AdvanceApplyOut.model_validate(advance))
@router.post("/{advance_id}/reject", response_model=ApiResponse[AdvanceApplyOut])
def reject_advance(
advance_id: str,
body: AdvanceApprovalCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
advance = AdvanceService.reject(db, advance_id, user, body.approval_opinion or "")
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在或无权审批")
db.commit()
db.refresh(advance)
return ApiResponse(data=AdvanceApplyOut.model_validate(advance))
@router.delete("/{advance_id}", response_model=ApiResponse[None])
def delete_advance(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not AdvanceService.get_by_id(db, advance_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
AdvanceService.soft_delete(db, advance_id, user=user)
db.commit()
return ApiResponse(data=None)
+151
View File
@@ -0,0 +1,151 @@
import mimetypes
from typing import List
from urllib.parse import quote
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.schemas.bx.attachment import AttachmentOut
from app.schemas.bx.common import ApiResponse
from app.services.bx.attachment import AttachmentService
from app.services.bx.storage import (
download_file_bytes,
get_preview_url,
upload_bx_file,
)
router = APIRouter(prefix="/attachments", tags=["附件管理"])
def _tenant(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
def _parse_id_list(ids: str) -> List[str]:
return [x.strip() for x in (ids or "").split(",") if x.strip()]
@router.post("/upload", response_model=ApiResponse[AttachmentOut])
async def upload_file(
file: UploadFile = File(...),
biz_type: str = "",
biz_id: str = "0",
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
if not file or not file.filename:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="请选择文件")
file_data = await file.read()
content_type = (
file.content_type
or mimetypes.guess_type(file.filename or "")[0]
or "application/octet-stream"
)
relative_path, _full_url = upload_bx_file(
file_data, file.filename or "unknown", biz_type=biz_type or "attachment"
)
biz_id_value = str(biz_id).strip() if biz_id else "0"
data = {
"biz_type": biz_type or "",
"biz_id": biz_id_value or "0",
"file_name": file.filename or "unknown",
"file_path": relative_path,
"file_type": content_type,
"file_size": len(file_data),
}
attachment = AttachmentService.create(db, data, user=user)
db.commit()
db.refresh(attachment)
return ApiResponse(data=AttachmentOut.model_validate(attachment))
@router.get("/download/{attachment_id}")
def download_file(
attachment_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
attachment = AttachmentService.get_by_id(db, attachment_id, tenant_no)
if not attachment:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
try:
file_data = download_file_bytes(attachment.file_path)
except FileNotFoundError as exc:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="文件不存在") from exc
except Exception as exc:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="文件读取失败") from exc
encoded_name = quote(attachment.file_name or "download")
headers = {"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_name}"}
media_type = attachment.file_type or "application/octet-stream"
return StreamingResponse(iter([file_data]), media_type=media_type, headers=headers)
@router.delete("/{attachment_id}", response_model=ApiResponse[bool])
def delete_attachment(
attachment_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not AttachmentService.get_by_id(db, attachment_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
success = AttachmentService.soft_delete(db, attachment_id, user=user)
db.commit()
return ApiResponse(data=success)
@router.get("/preview/{attachment_id}", response_model=ApiResponse[dict])
def preview_attachment(
attachment_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
"""返回图片/附件预览 URL(MinIO 预签名或本地公开地址,对齐门户 storage 能力)。"""
tenant_no = _tenant(db, user)
attachment = AttachmentService.get_by_id(db, attachment_id, tenant_no)
if not attachment:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
try:
url = get_preview_url(attachment.file_path, expires_seconds=3600)
except Exception as exc:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="获取预览地址失败") from exc
return ApiResponse(
data={
"url": url,
"file_name": attachment.file_name,
"file_type": attachment.file_type,
}
)
@router.get("/list", response_model=ApiResponse[List[AttachmentOut]])
def list_by_biz(
biz_type: str = "",
biz_id: str = "0",
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
biz_id_value = str(biz_id).strip() if biz_id else "0"
attachments = AttachmentService.get_by_biz(db, biz_type=biz_type, biz_id=biz_id_value, tenant_no=tenant_no)
return ApiResponse(data=[AttachmentOut.model_validate(a) for a in attachments])
@router.put("/bind-biz", response_model=ApiResponse[int])
def bind_biz(
ids: str = Query(...),
biz_type: str = Query(...),
biz_id: str = Query(...),
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
id_list = _parse_id_list(ids)
biz_id_value = str(biz_id).strip() or "0"
count = AttachmentService.bind_biz(db, id_list, biz_type, biz_id_value, user=user)
db.commit()
return ApiResponse(data=count)
+88
View File
@@ -0,0 +1,88 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.schemas.bx.common import ApiResponse
from app.schemas.bx.expense_item import ExpenseItemCreate, ExpenseItemOut, ExpenseItemUpdate
from app.services.bx.expense_item import ExpenseItemService
router = APIRouter(prefix="/expense-items", tags=["费用项目管理"])
@router.get("/tree", response_model=ApiResponse[List[ExpenseItemOut]])
def get_expense_item_tree(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
tree = ExpenseItemService.get_item_tree(db, tenant_no)
return ApiResponse(data=[ExpenseItemOut.model_validate(i) for i in tree])
@router.get("/{item_id}", response_model=ApiResponse[ExpenseItemOut])
def get_expense_item(
item_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
item = ExpenseItemService.get_by_id(db, item_id, tenant_no)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
return ApiResponse(data=ExpenseItemOut.model_validate(item))
@router.post("/", response_model=ApiResponse[ExpenseItemOut])
def create_expense_item(
body: ExpenseItemCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
try:
item = ExpenseItemService.create(db, body.model_dump(exclude_unset=True), user=user)
db.commit()
db.refresh(item)
except ValueError as exc:
db.rollback()
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
return ApiResponse(data=ExpenseItemOut.model_validate(item))
@router.put("/{item_id}", response_model=ApiResponse[ExpenseItemOut])
def update_expense_item(
item_id: str,
body: ExpenseItemUpdate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
try:
item = ExpenseItemService.update(
db, item_id, body.model_dump(exclude_unset=True), user=user
)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
db.commit()
db.refresh(item)
except ValueError as exc:
db.rollback()
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
return ApiResponse(data=ExpenseItemOut.model_validate(item))
@router.delete("/{item_id}", response_model=ApiResponse[None])
def delete_expense_item(
item_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
item = ExpenseItemService.get_by_id(db, item_id, tenant_no)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
ExpenseItemService.soft_delete(db, item_id, user=user)
db.commit()
return ApiResponse(data=None)
+414
View File
@@ -0,0 +1,414 @@
import csv
import io
from datetime import datetime
from typing import Any, List, Optional
from urllib.parse import quote
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import StreamingResponse
from sqlalchemy import or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.models.bx_models import BxAdvanceApply, BxReimburseApply, BxReimburseOffset
from app.schemas.bx.advance import (
AdvanceFinanceIssueStatsOut,
AdvanceIssueOut,
AdvanceIssueRequest,
AdvanceOffsetRecordOut,
)
from app.schemas.bx.common import ApiResponse, PagedData
from app.schemas.bx.reimburse import (
BatchOffsetRequest,
OffsetRequest,
PayRequest,
PaymentFinanceStatsOut,
ReimburseApplyOut,
ReimburseOffsetRecordOut,
)
from app.services.bx.advance import AdvanceService
from app.services.bx.attachment import AttachmentService
from app.services.bx.reimburse import ReimburseService
from app.utils.bx_date_range import parse_create_time_day_bounds
router = APIRouter(prefix="/payments", tags=["支付处理"])
def _tenant(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
def _wrap_reimburse_rows(outs: List[ReimburseApplyOut]) -> List[dict]:
return [{"type": "reimburse", "data": o.model_dump(by_alias=True)} for o in outs]
@router.get("/pending", response_model=ApiResponse[PagedData[Any]])
def list_pending_payments(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = [BxReimburseApply.status == "approved"]
if keyword:
filters.append(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
if applicantId:
filters.append(BxReimburseApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxReimburseApply.create_time >= d0)
filters.append(BxReimburseApply.create_time < d1)
rows, total = ReimburseService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
outs = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(
data=PagedData(rows=_wrap_reimburse_rows(outs), total=total, page=page, page_size=pageSize)
)
@router.get("/stats", response_model=ApiResponse[PaymentFinanceStatsOut])
def payment_finance_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = ReimburseService.finance_payment_stats(db, tenant_no)
return ApiResponse(data=PaymentFinanceStatsOut.model_validate(data))
@router.get("/processed", response_model=ApiResponse[PagedData[Any]])
def list_processed_payments(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = [BxReimburseApply.status.in_(["processed", "paid", "offset"])]
if keyword:
filters.append(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
if applicantId:
filters.append(BxReimburseApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxReimburseApply.create_time >= d0)
filters.append(BxReimburseApply.create_time < d1)
rows, total = ReimburseService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
outs = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(
data=PagedData(rows=_wrap_reimburse_rows(outs), total=total, page=page, page_size=pageSize)
)
@router.post("/{payment_id}/pay", response_model=ApiResponse[ReimburseApplyOut])
def pay(
payment_id: str,
body: Optional[PayRequest] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
pay_remark = body.pay_remark if body else ""
actual_amount = body.actual_amount if body else None
reimburse = ReimburseService.pay(
db, payment_id, user, pay_remark=pay_remark or "", actual_amount=actual_amount
)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="支付记录不存在")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.post("/{payment_id}/offset", response_model=ApiResponse[ReimburseApplyOut])
def offset(
payment_id: str,
body: OffsetRequest,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.offset(
db,
payment_id,
body.advance_id,
body.offset_amount,
user,
remark=body.remark or "",
)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="支付记录不存在")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.post("/{payment_id}/batch-offset", response_model=ApiResponse[ReimburseApplyOut])
def batch_offset(
payment_id: str,
body: BatchOffsetRequest,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.batch_offset(
db,
payment_id,
[item.model_dump() for item in body.items],
body.pay_amount,
user,
remark=body.remark or "",
)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="支付记录不存在")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.get("/advance-issues", response_model=ApiResponse[PagedData[AdvanceIssueOut]])
def list_advance_issues(
page: int = 1,
pageSize: int = 10,
issue_status: Optional[str] = None,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = []
if issue_status == "pending":
filters.append(BxAdvanceApply.status == "approved")
filters.append(or_(BxAdvanceApply.issue_status.is_(None), BxAdvanceApply.issue_status != "issued"))
elif issue_status == "issued":
filters.append(BxAdvanceApply.status == "issued")
else:
filters.append(or_(BxAdvanceApply.status == "approved", BxAdvanceApply.status == "issued"))
if keyword:
filters.append(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
if applicantId:
filters.append(BxAdvanceApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxAdvanceApply.create_time >= d0)
filters.append(BxAdvanceApply.create_time < d1)
rows, total = AdvanceService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
out_rows = [AdvanceIssueOut.model_validate(r) for r in rows]
if out_rows:
offset_map = AdvanceService.offset_amount_map(db, tenant_no, [r.id for r in rows])
for out_row in out_rows:
out_row.offset_amount = offset_map.get(out_row.id, 0.0)
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/advance-issues/stats", response_model=ApiResponse[AdvanceFinanceIssueStatsOut])
def advance_issue_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = AdvanceService.finance_issue_stats(db, tenant_no)
return ApiResponse(data=AdvanceFinanceIssueStatsOut.model_validate(data))
@router.get("/advance-issues/export")
def export_advance_issues(
issue_status: Optional[str] = None,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = []
if issue_status == "pending":
filters.append(BxAdvanceApply.status == "approved")
filters.append(or_(BxAdvanceApply.issue_status.is_(None), BxAdvanceApply.issue_status != "issued"))
elif issue_status == "issued":
filters.append(BxAdvanceApply.status == "issued")
else:
filters.append(or_(BxAdvanceApply.status == "approved", BxAdvanceApply.status == "issued"))
if keyword:
filters.append(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
if applicantId:
filters.append(BxAdvanceApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxAdvanceApply.create_time >= d0)
filters.append(BxAdvanceApply.create_time < d1)
rows, _ = AdvanceService.get_paged(db, tenant_no, page=1, page_size=99999, filters=filters)
out_rows = [AdvanceIssueOut.model_validate(r) for r in rows]
buf = io.StringIO()
writer = csv.writer(buf)
writer.writerow(
[
"申请单号",
"申请人",
"申请原因",
"申请金额",
"审批通过时间",
"发放状态",
"发放时间",
"发放操作人",
"实际发放金额",
"发放备注",
]
)
for row in out_rows:
writer.writerow(
[
row.apply_code,
row.applicant_name,
row.reason,
row.amount,
row.approve_time or "",
"已发放" if row.issue_status == "issued" else "待发放",
row.issue_time or "",
row.issue_user_name or "",
row.actual_amount or "",
row.issue_remark or "",
]
)
buf.seek(0)
filename = f"备用金发放列表_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
encoded_name = quote(filename)
return StreamingResponse(
iter([buf.getvalue()]),
media_type="text/csv; charset=utf-8",
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_name}"},
)
@router.post("/advance/{advance_id}/issue", response_model=ApiResponse[AdvanceIssueOut])
def issue_advance(
advance_id: str,
body: AdvanceIssueRequest,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
issue_atts = AttachmentService.get_by_biz(db, "advance_issue", advance_id, tenant_no)
if not issue_atts:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="请至少上传 1 个发放附件")
advance = AdvanceService.issue(
db, advance_id, user, body.issue_remark or "", body.actual_amount
)
if not advance:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="备用金申请不存在")
db.commit()
db.refresh(advance)
out = AdvanceIssueOut.model_validate(advance)
offset_map = AdvanceService.offset_amount_map(db, tenant_no, [advance_id])
out.offset_amount = offset_map.get(advance_id, 0.0)
return ApiResponse(data=out)
@router.get("/advance/{advance_id}/offset-records", response_model=ApiResponse[List[AdvanceOffsetRecordOut]])
def list_advance_offset_records(
advance_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
rows = (
db.query(BxReimburseOffset, BxReimburseApply)
.join(
BxReimburseApply,
BxReimburseApply.id == BxReimburseOffset.reimburse_id,
)
.filter(
BxReimburseOffset.advance_id == advance_id,
BxReimburseOffset.tenant_no == tenant_no,
BxReimburseOffset.del_flag == 0,
BxReimburseApply.del_flag == 0,
BxReimburseApply.tenant_no == tenant_no,
)
.order_by(BxReimburseOffset.create_time.desc(), BxReimburseOffset.id.desc())
.all()
)
records = [
AdvanceOffsetRecordOut(
reimburse_id=reimburse.id,
reimburse_apply_code=reimburse.apply_code,
reimburse_title=reimburse.title,
reimburse_applicant_name=reimburse.applicant_name,
offset_amount=float(offset.offset_amount or 0),
remark=offset.remark,
operator=offset.create_by,
create_time=offset.create_time,
)
for offset, reimburse in rows
]
return ApiResponse(data=records)
@router.get("/{payment_id}/offset-records", response_model=ApiResponse[List[ReimburseOffsetRecordOut]])
def list_reimburse_offset_records(
payment_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
rows = (
db.query(BxReimburseOffset, BxAdvanceApply)
.join(BxAdvanceApply, BxAdvanceApply.id == BxReimburseOffset.advance_id)
.filter(
BxReimburseOffset.reimburse_id == payment_id,
BxReimburseOffset.tenant_no == tenant_no,
BxReimburseOffset.del_flag == 0,
BxAdvanceApply.del_flag == 0,
BxAdvanceApply.tenant_no == tenant_no,
)
.order_by(BxReimburseOffset.create_time.desc(), BxReimburseOffset.id.desc())
.all()
)
records = [
ReimburseOffsetRecordOut(
advance_id=advance.id,
advance_apply_code=advance.apply_code,
advance_applicant_name=advance.applicant_name,
advance_reason=advance.reason,
offset_amount=float(offset.offset_amount or 0),
remark=offset.remark,
operator=offset.create_by,
create_time=offset.create_time,
)
for offset, advance in rows
]
return ApiResponse(data=records)
+373
View File
@@ -0,0 +1,373 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.models.bx_models import BxReimburseApply, BxWorkflowDef
from app.schemas.bx.common import ApiResponse, PagedData
from app.schemas.bx.reimburse import (
ReimburseApplyCreate,
ReimburseApplyOut,
ReimburseApplyUpdate,
ReimburseApprovalCreate,
ReimburseApprovalOut,
ReimburseApproverPendingStatsOut,
ReimburseMyStatsOut,
)
from app.services.bx.expense_item import ExpenseItemService
from app.services.bx.reimburse import ReimburseService
from app.utils.bx_codegen import generate_apply_code, generate_title
from app.utils.bx_date_range import parse_create_time_day_bounds
router = APIRouter(prefix="/reimburses", tags=["报销管理"])
def _tenant(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
def _build_details_data(db: Session, tenant_no: str, body_details) -> List[dict]:
details_data: List[dict] = []
for d in body_details:
item = ExpenseItemService.get_by_id(db, d.expense_item_id, tenant_no)
if not item:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"费用项目 {d.expense_item_id} 不存在",
)
details_data.append(
{
"expense_id": d.expense_item_id,
"expense_name": item.item_name,
"amount": d.amount,
"remark": d.remark,
}
)
return details_data
@router.get("/generate-title", response_model=ApiResponse[str])
def get_generated_title(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
title = generate_title(db, user.name, tenant_no)
return ApiResponse(data=title)
@router.get("/", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
def list_reimburses(
page: int = 1,
pageSize: int = 10,
status: Optional[str] = None,
keyword: str = "",
expense_id: Optional[str] = None,
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = []
if status:
filters.append(BxReimburseApply.status == status)
if keyword:
filters.append(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
if expense_id:
filters.append(BxReimburseApply.expense_id == str(expense_id).strip())
if applicantId:
filters.append(BxReimburseApply.applicant_id == str(applicantId).strip())
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxReimburseApply.create_time >= d0)
filters.append(BxReimburseApply.create_time < d1)
rows, total = ReimburseService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
out_rows = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/my", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
def my_reimburses(
page: int = 1,
pageSize: int = 10,
status: Optional[str] = None,
keyword: str = "",
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
filters = [
or_(
BxReimburseApply.applicant_id == user.id,
BxReimburseApply.create_by == user.id,
)
]
if status:
filters.append(BxReimburseApply.status == status)
if keyword:
filters.append(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
bounds = parse_create_time_day_bounds(applyDate)
if bounds:
d0, d1 = bounds
filters.append(BxReimburseApply.create_time >= d0)
filters.append(BxReimburseApply.create_time < d1)
rows, total = ReimburseService.get_paged(db, tenant_no, page=page, page_size=pageSize, filters=filters)
out_rows = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/my/stats", response_model=ApiResponse[ReimburseMyStatsOut])
def my_reimburse_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = ReimburseService.my_apply_stats(db, user.id, tenant_no)
return ApiResponse(data=ReimburseMyStatsOut.model_validate(data))
@router.get("/stats/status-counts", response_model=ApiResponse[dict])
def reimburse_status_counts(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
return ApiResponse(data=ReimburseService.global_status_counts(db, tenant_no))
@router.get("/pending-approval", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
def pending_approval(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
applicant_id = str(applicantId).strip() if applicantId else None
rows, total = ReimburseService.pending_approval(
db,
user,
tenant_no,
page=page,
page_size=pageSize,
keyword=keyword,
applicant_id=applicant_id,
apply_date=applyDate,
)
out_rows = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/pending-approval/stats", response_model=ApiResponse[ReimburseApproverPendingStatsOut])
def pending_approval_stats(
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
data = ReimburseService.pending_approval_stats(db, user, tenant_no)
return ApiResponse(data=ReimburseApproverPendingStatsOut.model_validate(data))
@router.get("/approved-by-me", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
def approved_by_me(
page: int = 1,
pageSize: int = 10,
keyword: str = "",
applicantId: Optional[str] = None,
applyDate: Optional[str] = None,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
applicant_id = str(applicantId).strip() if applicantId else None
rows, total = ReimburseService.approved_by_me(
db,
user,
tenant_no,
page=page,
page_size=pageSize,
keyword=keyword,
applicant_id=applicant_id,
apply_date=applyDate,
)
out_rows = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
return ApiResponse(data=PagedData(rows=out_rows, total=total, page=page, page_size=pageSize))
@router.get("/{reimburse_id}", response_model=ApiResponse[ReimburseApplyOut])
def get_reimburse(
reimburse_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.get_by_id(db, reimburse_id, tenant_no)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
out = ReimburseService.build_apply_out_with_details(db, tenant_no, reimburse)
return ApiResponse(data=out)
@router.get("/{reimburse_id}/approvals", response_model=ApiResponse[List[ReimburseApprovalOut]])
def get_approvals(
reimburse_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not ReimburseService.get_by_id(db, reimburse_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
approvals = ReimburseService.get_approvals(db, reimburse_id, tenant_no)
return ApiResponse(data=[ReimburseApprovalOut.model_validate(a) for a in approvals])
@router.get("/{reimburse_id}/approval-history", response_model=ApiResponse[List[ReimburseApprovalOut]])
def get_approval_history(
reimburse_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not ReimburseService.get_by_id(db, reimburse_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
history = ReimburseService.get_approval_history(db, reimburse_id, tenant_no)
return ApiResponse(data=[ReimburseApprovalOut.model_validate(a) for a in history])
@router.post("/", response_model=ApiResponse[ReimburseApplyOut])
def create_reimburse(
body: ReimburseApplyCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
details_data = _build_details_data(db, tenant_no, body.details)
data = {
"apply_code": generate_apply_code(db, "BX", tenant_no),
"status": "draft",
"remark": body.remark,
}
if body.title:
data["title"] = body.title
wf = (
db.query(BxWorkflowDef)
.filter(
BxWorkflowDef.biz_type == "reimburse",
BxWorkflowDef.tenant_no == tenant_no,
BxWorkflowDef.del_flag == 0,
)
.first()
)
if wf:
data["wf_id"] = wf.id
reimburse = ReimburseService.create_with_details(db, data, details_data, user=user)
db.commit()
db.refresh(reimburse)
out = ReimburseService.build_apply_out_with_details(db, tenant_no, reimburse)
return ApiResponse(data=out)
@router.put("/{reimburse_id}", response_model=ApiResponse[ReimburseApplyOut])
def update_reimburse(
reimburse_id: str,
body: ReimburseApplyUpdate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
details_data = None
if body.details is not None:
details_data = _build_details_data(db, tenant_no, body.details)
main_data = {}
if body.remark is not None:
main_data["remark"] = body.remark
reimburse = ReimburseService.update_with_details(
db, reimburse_id, main_data, details_data, user=user
)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
db.commit()
db.refresh(reimburse)
out = ReimburseService.build_apply_out_with_details(db, tenant_no, reimburse)
return ApiResponse(data=out)
@router.post("/{reimburse_id}/submit", response_model=ApiResponse[ReimburseApplyOut])
def submit_reimburse(
reimburse_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.submit(db, reimburse_id, user)
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.post("/{reimburse_id}/approve", response_model=ApiResponse[ReimburseApplyOut])
def approve_reimburse(
reimburse_id: str,
body: ReimburseApprovalCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.approve(db, reimburse_id, user, body.approval_opinion or "")
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在或无权审批")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.post("/{reimburse_id}/reject", response_model=ApiResponse[ReimburseApplyOut])
def reject_reimburse(
reimburse_id: str,
body: ReimburseApprovalCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
reimburse = ReimburseService.reject(db, reimburse_id, user, body.approval_opinion or "")
if not reimburse:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在或无权审批")
db.commit()
db.refresh(reimburse)
return ApiResponse(data=ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse))
@router.delete("/{reimburse_id}", response_model=ApiResponse[None])
def delete_reimburse(
reimburse_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = _tenant(db, user)
if not ReimburseService.get_by_id(db, reimburse_id, tenant_no):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
ReimburseService.soft_delete(db, reimburse_id, user=user)
db.commit()
return ApiResponse(data=None)
+130
View File
@@ -0,0 +1,130 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext, get_bx_current_user
from app.core.database import get_db
from app.core.tenant_context import get_tenant_no
from app.schemas.bx.common import ApiResponse, PagedData
from app.schemas.bx.workflow import (
WorkflowDefCreate,
WorkflowDefOut,
WorkflowDefUpdate,
WorkflowNodeCreate,
)
from app.services.bx.workflow import WorkflowService
router = APIRouter(prefix="/workflows", tags=["工作流配置"])
@router.get("/", response_model=ApiResponse[PagedData[WorkflowDefOut]])
def list_workflows(
page: int = 1,
pageSize: int = 10,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
rows, total = WorkflowService.get_paged(db, tenant_no, page=page, page_size=pageSize)
out_rows = [WorkflowDefOut.model_validate(r) for r in rows]
paged_data = PagedData(rows=out_rows, total=total, page=page, page_size=pageSize)
return ApiResponse(data=paged_data)
@router.get("/biz/{biz_type}", response_model=ApiResponse[WorkflowDefOut])
def get_workflow_by_biz_type(
biz_type: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
workflow = WorkflowService.ensure_by_biz_type(db, biz_type, user=user)
if not workflow:
return ApiResponse(data=None)
return ApiResponse(data=WorkflowDefOut.model_validate(workflow))
@router.get("/{workflow_id}", response_model=ApiResponse[WorkflowDefOut])
def get_workflow(
workflow_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
workflow = WorkflowService.get_by_id(db, workflow_id, tenant_no)
if not workflow:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="工作流不存在")
return ApiResponse(data=WorkflowDefOut.model_validate(workflow))
@router.post("/", response_model=ApiResponse[WorkflowDefOut])
def create_workflow(
body: WorkflowDefCreate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
data = body.model_dump(exclude_unset=True)
nodes_data = data.pop("nodes", []) or []
workflow = WorkflowService.create(db, data, user=user)
if nodes_data:
WorkflowService.save_nodes(db, workflow.id, nodes_data, user=user)
db.commit()
db.refresh(workflow)
return ApiResponse(data=WorkflowDefOut.model_validate(workflow))
@router.put("/{workflow_id}", response_model=ApiResponse[WorkflowDefOut])
def update_workflow(
workflow_id: str,
body: WorkflowDefUpdate,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
data = body.model_dump(exclude_unset=True)
nodes_data = data.pop("nodes", None)
workflow = WorkflowService.update(db, workflow_id, data, user=user)
if not workflow:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="工作流不存在")
if nodes_data is not None:
WorkflowService.save_nodes(db, workflow_id, nodes_data, user=user)
db.commit()
db.refresh(workflow)
return ApiResponse(data=WorkflowDefOut.model_validate(workflow))
@router.put("/{workflow_id}/nodes", response_model=ApiResponse[WorkflowDefOut])
def save_workflow_nodes(
workflow_id: str,
body: List[WorkflowNodeCreate],
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
nodes_data = [n.model_dump(exclude_unset=True) for n in body]
try:
WorkflowService.save_nodes(db, workflow_id, nodes_data, user=user)
except ValueError as exc:
db.rollback()
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
workflow = WorkflowService.get_by_id(db, workflow_id, tenant_no)
if not workflow:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="工作流不存在")
db.commit()
db.refresh(workflow)
return ApiResponse(data=WorkflowDefOut.model_validate(workflow))
@router.delete("/{workflow_id}", response_model=ApiResponse[None])
def delete_workflow(
workflow_id: str,
db: Session = Depends(get_db),
user: BxUserContext = Depends(get_bx_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
workflow = WorkflowService.get_by_id(db, workflow_id, tenant_no)
if not workflow:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="工作流不存在")
WorkflowService.soft_delete(db, workflow_id, user=user)
db.commit()
return ApiResponse(data=None)
+3
View File
@@ -10,6 +10,9 @@ BIZ_TYPE_DEFAULT_FOLDER: dict[str, str] = {
"crm_customer_follow_attachment": "crm/customer/follow",
"crm_opportunity_follow_attachment": "crm/opportunity/follow",
"crm_clue_follow_attachment": "crm/clue/follow",
"advance": "bx/advance",
"reimburse_apply": "bx/reimburse",
"advance_issue": "bx/advance-issue",
}
+40
View File
@@ -0,0 +1,40 @@
"""报销模块认证适配:门户 JWT 用户 → bx 业务上下文。"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict
from fastapi import Depends
from app.core.menu_guard import require_admin_or_super_admin
@dataclass
class BxUserContext:
"""bx Service/Router 使用的当前用户上下文(sys_user.id 为 UUID 字符串)。"""
id: str
name: str
dept_id: str
role: str
tenant_no: str
def to_tenant_user_dict(self) -> Dict[str, Any]:
"""供 get_tenant_no 等门户工具使用。"""
return {
"user_id": self.id,
"role": self.role,
"tenant_no": self.tenant_no,
}
def get_bx_current_user(
user: dict = Depends(require_admin_or_super_admin),
) -> BxUserContext:
return BxUserContext(
id=(user.get("user_id") or "").strip(),
name=(user.get("real_name") or user.get("username") or "").strip(),
dept_id=(user.get("region_id") or "").strip(),
role=(user.get("role") or "").strip(),
tenant_no=(user.get("tenant_no") or "").strip(),
)
+8 -1
View File
@@ -138,7 +138,14 @@ class Settings(BaseSettings):
"/api/web/admin/products/upload-image,"
"/api/web/admin/categories/upload-image,"
"/api/web/admin/users/upload-avatar,"
"/api/invoice/download"
"/api/invoice/download,"
"/api/bx/health,"
"/api/expense-items,"
"/api/workflows,"
"/api/advances,"
"/api/reimburses,"
"/api/payments,"
"/api/attachments"
)
class Config:
+3
View File
@@ -13,6 +13,7 @@ from app.core.database import SessionLocal
from app.core.api_request_log_middleware import ApiRequestLogMiddleware
from app.core.api_crypto_middleware import ApiCryptoMiddleware
from app.models import models # noqa: F401
import app.models.bx_models # noqa: F401
from app.api import (
public_router,
menu_router,
@@ -21,6 +22,7 @@ from app.api import (
leave_router,
crm_router,
monitor_router,
bx_router,
)
@@ -112,6 +114,7 @@ app.include_router(admin_router, prefix=settings.API_V1_PREFIX)
app.include_router(workflow_router, prefix=settings.API_V1_PREFIX)
app.include_router(leave_router, prefix=settings.API_V1_PREFIX)
app.include_router(crm_router, prefix=settings.API_V1_PREFIX)
app.include_router(bx_router, prefix=settings.API_V1_PREFIX)
app.include_router(monitor_router, prefix=settings.API_V1_PREFIX)
+269
View File
@@ -0,0 +1,269 @@
"""报销(bx)模块数据模型 — 对齐 docs/sql/20260606_bx_tables.sql"""
from datetime import datetime
from sqlalchemy import BigInteger, Column, DateTime, DECIMAL, ForeignKey, Integer, SmallInteger, String
from sqlalchemy.orm import relationship
from app.core.database import Base
class BxExpenseItem(Base):
__tablename__ = "bx_expense_item"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
parent_id = Column(String(64), nullable=False, default="0", comment="父级项目ID")
item_name = Column(String(128), nullable=False, comment="项目名称")
item_code = Column(String(64), nullable=True, comment="项目编码")
level = Column(SmallInteger, nullable=False, default=1, comment="层级")
sort_order = Column(Integer, nullable=False, default=0, comment="排序号")
status = Column(SmallInteger, nullable=False, default=1, comment="状态")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxWorkflowDef(Base):
__tablename__ = "bx_workflow_def"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
biz_type = Column(String(32), nullable=False, comment="业务类型")
wf_name = Column(String(128), nullable=False, comment="工作流名称")
status = Column(SmallInteger, nullable=False, default=1, comment="状态")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
nodes = relationship(
"BxWorkflowNode",
back_populates="workflow_def",
primaryjoin="and_(BxWorkflowDef.id == BxWorkflowNode.wf_id, BxWorkflowNode.del_flag == 0)",
foreign_keys="BxWorkflowNode.wf_id",
order_by="BxWorkflowNode.node_order",
lazy="selectin",
)
class BxWorkflowNode(Base):
__tablename__ = "bx_workflow_node"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
wf_id = Column(
String(64),
ForeignKey("bx_workflow_def.id"),
nullable=False,
comment="工作流定义ID",
)
node_name = Column(String(128), nullable=False, comment="节点名称")
node_order = Column(Integer, nullable=False, default=0, comment="节点顺序")
approver_id = Column(String(64), nullable=True, comment="审批人用户ID")
approver_name = Column(String(64), nullable=True, comment="审批人姓名")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
workflow_def = relationship(
"BxWorkflowDef",
back_populates="nodes",
foreign_keys=[wf_id],
)
class BxAdvanceApply(Base):
__tablename__ = "bx_advance_apply"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
apply_code = Column(String(32), nullable=False, comment="申请单号")
applicant_id = Column(String(64), nullable=False, comment="申请人用户ID")
applicant_name = Column(String(64), nullable=False, comment="申请人姓名")
reason = Column(String(1024), nullable=False, comment="申请原因")
purpose = Column(String(1024), nullable=True, comment="申请目的")
expense_item_id = Column(String(64), nullable=True, comment="费用项目ID")
amount = Column(DECIMAL(14, 2), nullable=False, comment="申请金额")
status = Column(String(32), nullable=False, default="draft", comment="状态")
current_node = Column(String(128), nullable=True, comment="当前审批节点")
wf_id = Column(String(64), nullable=True, comment="工作流定义ID")
issue_status = Column(String(32), nullable=True, comment="发放状态")
issue_time = Column(DateTime, nullable=True, comment="发放时间")
issue_user_id = Column(String(64), nullable=True, comment="发放操作人ID")
issue_user_name = Column(String(64), nullable=True, comment="发放操作人姓名")
issue_remark = Column(String(512), nullable=True, comment="发放备注")
actual_amount = Column(DECIMAL(14, 2), nullable=True, comment="实际发放金额")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxAdvanceApproval(Base):
__tablename__ = "bx_advance_approval"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
apply_id = Column(String(64), nullable=False, comment="备用金申请单ID")
node_order = Column(Integer, nullable=False, comment="节点顺序")
node_name = Column(String(128), nullable=False, comment="节点名称")
approver_id = Column(String(64), nullable=False, comment="审批人用户ID")
approver_name = Column(String(64), nullable=False, comment="审批人姓名")
approval_status = Column(String(32), nullable=False, comment="审批结果")
approval_opinion = Column(String(1024), nullable=True, comment="审批意见")
approval_time = Column(DateTime, nullable=False, comment="审批时间")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(DateTime, nullable=True, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxReimburseApply(Base):
__tablename__ = "bx_reimburse_apply"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
apply_code = Column(String(32), nullable=False, comment="报销单号")
title = Column(String(256), nullable=False, comment="报销标题")
applicant_id = Column(String(64), nullable=False, comment="申请人用户ID")
applicant_name = Column(String(64), nullable=False, comment="申请人姓名")
expense_id = Column(String(64), nullable=False, comment="费用项目ID")
expense_name = Column(String(128), nullable=False, comment="费用项目名称")
amount = Column(DECIMAL(14, 2), nullable=False, comment="报销金额")
invoice_count = Column(Integer, nullable=False, default=0, comment="发票数量")
attach_count = Column(Integer, nullable=False, default=0, comment="附件数量")
status = Column(String(32), nullable=False, default="draft", comment="状态")
current_node = Column(String(128), nullable=True, comment="当前审批节点")
wf_id = Column(String(64), nullable=True, comment="工作流定义ID")
pay_status = Column(String(32), nullable=True, comment="支付状态")
pay_time = Column(DateTime, nullable=True, comment="支付时间")
pay_user_id = Column(String(64), nullable=True, comment="支付操作人ID")
pay_user_name = Column(String(64), nullable=True, comment="支付操作人姓名")
actual_amount = Column(DECIMAL(14, 2), nullable=True, comment="实际发放金额")
offset_advance_id = Column(String(64), nullable=True, comment="冲抵备用金单ID")
offset_amount = Column(DECIMAL(14, 2), nullable=True, comment="冲抵金额")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注说明")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxReimburseDetail(Base):
__tablename__ = "bx_reimburse_detail"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
reimburse_id = Column(String(64), nullable=False, comment="报销单ID")
expense_id = Column(String(64), nullable=False, comment="费用项目ID")
expense_name = Column(String(128), nullable=False, comment="费用项目名称")
amount = Column(DECIMAL(14, 2), nullable=False, comment="报销金额")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxReimburseApproval(Base):
__tablename__ = "bx_reimburse_approval"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
apply_id = Column(String(64), nullable=False, comment="报销申请单ID")
node_order = Column(Integer, nullable=False, comment="节点顺序")
node_name = Column(String(128), nullable=False, comment="节点名称")
approver_id = Column(String(64), nullable=False, comment="审批人用户ID")
approver_name = Column(String(64), nullable=False, comment="审批人姓名")
approval_status = Column(String(32), nullable=False, comment="审批结果")
approval_opinion = Column(String(1024), nullable=True, comment="审批意见")
approval_time = Column(DateTime, nullable=False, comment="审批时间")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(DateTime, nullable=True, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxReimburseOffset(Base):
__tablename__ = "bx_reimburse_offset"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
reimburse_id = Column(String(64), nullable=False, comment="报销申请单ID")
advance_id = Column(String(64), nullable=False, comment="备用金申请单ID")
offset_amount = Column(DECIMAL(14, 2), nullable=False, comment="冲抵金额")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(DateTime, nullable=True, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BxAttachment(Base):
__tablename__ = "bx_attachment"
id = Column(String(64), primary_key=True, comment="主键ID(UUID)")
biz_type = Column(String(32), nullable=False, comment="业务类型")
biz_id = Column(String(64), nullable=False, comment="业务单据ID")
file_name = Column(String(256), nullable=False, comment="文件名")
file_path = Column(String(512), nullable=False, comment="文件存储路径")
file_type = Column(String(128), nullable=True, comment="文件MIME类型")
file_size = Column(BigInteger, nullable=True, comment="文件大小")
attach_type = Column(String(32), nullable=True, default="attachment", comment="附件类型")
sort_order = Column(Integer, nullable=False, default=0, comment="排序号")
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号(公共)")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人")
update_time = Column(DateTime, nullable=True, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
+1
View File
@@ -0,0 +1 @@
"""报销(bx)Pydantic 模型 — 响应协议与 bx 原系统保持一致"""
+156
View File
@@ -0,0 +1,156 @@
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic.alias_generators import to_camel
from app.schemas.bx.common import DatetimeToStr, OptDatetimeToStr
def _coerce_optional_id(value):
if value is None:
return None
s = str(value).strip()
return s or None
class AdvanceApplyCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
reason: str
purpose: Optional[str] = None
amount: float
remark: Optional[str] = None
expense_item_id: Optional[str] = None
@field_validator("expense_item_id", mode="before")
@classmethod
def validate_expense_item_id(cls, value):
return _coerce_optional_id(value)
class AdvanceApplyUpdate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
reason: Optional[str] = None
purpose: Optional[str] = None
amount: Optional[float] = None
remark: Optional[str] = None
expense_item_id: Optional[str] = None
@field_validator("expense_item_id", mode="before")
@classmethod
def validate_expense_item_id(cls, value):
return _coerce_optional_id(value)
class AdvanceApplyOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
apply_code: str
applicant_id: str
applicant_name: str
reason: str
purpose: Optional[str] = None
expense_item_id: Optional[str] = None
amount: float
status: str
current_node: Optional[str] = None
apply_time: DatetimeToStr = Field(validation_alias="create_time")
remark: Optional[str] = None
actual_amount: Optional[float] = None
offset_amount: Optional[float] = None
issue_status: Optional[str] = None
issue_time: OptDatetimeToStr = None
issue_user_name: Optional[str] = None
issue_remark: Optional[str] = None
class AdvanceApprovalCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
approval_status: Optional[str] = None
approval_opinion: Optional[str] = None
class AdvanceApprovalOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
advance_id: str
node_name: str
node_order: int
approval_status: str
approval_opinion: Optional[str] = None
approver_name: str
approval_time: OptDatetimeToStr = None
class AdvanceMyStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
total_count: int
pending_count: int
approved_pass_count: int
rejected_count: int
pending_issue_count: int
issued_count: int
issued_amount: float
month_apply_amount: float
month_apply_count: int
offset_total: float
class AdvanceApproverPendingStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
pending_total: int
month_pending_amount: float
class AdvanceIssueOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
apply_code: str
applicant_id: str
applicant_name: str
reason: str
amount: float
apply_time: DatetimeToStr = Field(validation_alias="create_time")
approve_time: DatetimeToStr = Field(validation_alias="update_time")
issue_status: Optional[str] = None
issue_time: OptDatetimeToStr = None
issue_user_name: Optional[str] = None
issue_remark: Optional[str] = None
actual_amount: Optional[float] = None
offset_amount: Optional[float] = None
class AdvanceIssueRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
issue_remark: Optional[str] = None
actual_amount: Optional[float] = None
class AdvanceOffsetRecordOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
reimburse_id: str
reimburse_apply_code: str
reimburse_title: Optional[str] = None
reimburse_applicant_name: Optional[str] = None
offset_amount: float
remark: Optional[str] = None
operator: Optional[str] = None
create_time: OptDatetimeToStr = None
class AdvanceFinanceIssueStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
pending_count: int
issued_count: int
pending_amount: float
issued_month_amount: float
+19
View File
@@ -0,0 +1,19 @@
from typing import Optional
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel
from app.schemas.bx.common import DatetimeToStr
class AttachmentOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
file_name: str
file_path: str
file_size: Optional[int] = None
file_type: Optional[str] = None
biz_type: Optional[str] = None
biz_id: Optional[str] = None
create_time: DatetimeToStr
+46
View File
@@ -0,0 +1,46 @@
"""bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。"""
from datetime import datetime
from typing import Annotated, Generic, List, Optional, TypeVar
from pydantic import BaseModel, ConfigDict, PlainSerializer
from pydantic.alias_generators import to_camel
T = TypeVar("T")
def _dt_to_str(v: Optional[datetime]) -> Optional[str]:
if v is None:
return None
return v.strftime("%Y-%m-%d %H:%M:%S")
DatetimeToStr = Annotated[datetime, PlainSerializer(_dt_to_str, return_type=str)]
OptDatetimeToStr = Annotated[Optional[datetime], PlainSerializer(_dt_to_str, return_type=Optional[str])]
class PagedQuery(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
page: int = 1
page_size: int = 10
class ApiResponse(BaseModel, Generic[T]):
code: int = 0
msg: str = "success"
data: Optional[T] = None
class PagedData(BaseModel, Generic[T]):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
rows: List[T]
total: int
page: int
page_size: int
class PagedResponse(BaseModel, Generic[T]):
code: int = 0
msg: str = "success"
data: Optional[PagedData[T]] = None
+64
View File
@@ -0,0 +1,64 @@
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, field_validator
from pydantic.alias_generators import to_camel
def _coerce_id(value) -> str:
if value is None:
return "0"
return str(value).strip()
class ExpenseItemCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
parent_id: str = "0"
item_name: str
item_code: str
level: int = 1
sort_order: int = 0
status: int = 1
remark: Optional[str] = None
@field_validator("parent_id", mode="before")
@classmethod
def validate_parent_id(cls, value):
return _coerce_id(value)
class ExpenseItemUpdate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
parent_id: Optional[str] = None
item_name: Optional[str] = None
item_code: Optional[str] = None
level: Optional[int] = None
sort_order: Optional[int] = None
status: Optional[int] = None
remark: Optional[str] = None
@field_validator("parent_id", mode="before")
@classmethod
def validate_parent_id(cls, value):
if value is None:
return None
return _coerce_id(value)
class ExpenseItemOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
parent_id: str
item_name: str
item_code: Optional[str] = None
level: int
sort_order: int
status: int
remark: Optional[str] = None
child_count: int = 0
children: Optional[List["ExpenseItemOut"]] = None
ExpenseItemOut.model_rebuild()
+193
View File
@@ -0,0 +1,193 @@
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic.alias_generators import to_camel
from app.schemas.bx.common import DatetimeToStr, OptDatetimeToStr
def _coerce_id(value):
if value is None:
return None
return str(value).strip()
class ReimburseDetailCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
expense_item_id: str
amount: float
remark: Optional[str] = None
@field_validator("expense_item_id", mode="before")
@classmethod
def validate_expense_item_id(cls, value):
s = _coerce_id(value)
if not s:
raise ValueError("费用项目不能为空")
return s
class ReimburseDetailOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
expense_id: str
expense_name: str
amount: float
remark: Optional[str] = None
class ReimburseApplyCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
title: Optional[str] = None
details: List[ReimburseDetailCreate] = Field(..., min_length=1)
remark: Optional[str] = None
@field_validator("details")
@classmethod
def details_must_not_be_empty(cls, value):
if not value or len(value) < 1:
raise ValueError("至少需要一条报销明细")
return value
class ReimburseApplyUpdate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
details: Optional[List[ReimburseDetailCreate]] = None
remark: Optional[str] = None
class ReimburseApplyOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
apply_code: str
applicant_id: str
applicant_name: str
title: str
expense_id: Optional[str] = None
expense_name: str
amount: float
current_node: Optional[str] = None
apply_time: DatetimeToStr = Field(validation_alias="create_time")
status: Optional[str] = None
remark: Optional[str] = None
invoice_count: int = 0
attach_count: int = 0
pay_status: Optional[str] = None
pay_time: OptDatetimeToStr = None
pay_user_name: Optional[str] = None
actual_amount: Optional[float] = None
offset_advance_id: Optional[str] = None
offset_amount: Optional[float] = None
details: Optional[List[ReimburseDetailOut]] = None
class ReimburseApprovalCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
approval_status: Optional[str] = None
approval_opinion: Optional[str] = None
class ReimburseApprovalOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
reimburse_id: str
node_name: str
node_order: int
approval_status: str
approval_opinion: Optional[str] = None
approver_name: str
approval_time: OptDatetimeToStr = None
class ReimburseMyStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
total_count: int
pending_count: int
approved_count: int
rejected_count: int
approved_amount: float
month_apply_amount: float
month_apply_count: int
class ReimburseApproverPendingStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
pending_total: int
month_pending_amount: float
class PayRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
pay_remark: Optional[str] = None
actual_amount: Optional[float] = None
class OffsetRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
advance_id: str
offset_amount: float
remark: Optional[str] = None
@field_validator("advance_id", mode="before")
@classmethod
def validate_advance_id(cls, value):
s = _coerce_id(value)
if not s:
raise ValueError("备用金单不能为空")
return s
class OffsetItem(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
advance_id: str
offset_amount: float
@field_validator("advance_id", mode="before")
@classmethod
def validate_advance_id(cls, value):
s = _coerce_id(value)
if not s:
raise ValueError("备用金单不能为空")
return s
class BatchOffsetRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
items: List[OffsetItem]
pay_amount: float = 0
remark: Optional[str] = None
class ReimburseOffsetRecordOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
advance_id: str
advance_apply_code: str
advance_applicant_name: Optional[str] = None
advance_reason: Optional[str] = None
offset_amount: float
remark: Optional[str] = None
operator: Optional[str] = None
create_time: OptDatetimeToStr = None
class PaymentFinanceStatsOut(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
pending_count: int
paid_count: int
offset_count: int
month_processed_amount: float
+62
View File
@@ -0,0 +1,62 @@
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, field_validator
from pydantic.alias_generators import to_camel
def _coerce_optional_id(value):
if value is None:
return None
return str(value).strip()
class WorkflowNodeCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
node_name: str
node_order: int
approver_id: Optional[str] = None
approver_name: Optional[str] = None
@field_validator("approver_id", mode="before")
@classmethod
def validate_approver_id(cls, value):
return _coerce_optional_id(value)
class WorkflowNodeOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
node_name: str
node_order: int
approver_id: Optional[str] = None
approver_name: Optional[str] = None
class WorkflowDefCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
biz_type: str
wf_name: str
status: int = 1
nodes: Optional[List[WorkflowNodeCreate]] = None
class WorkflowDefUpdate(BaseModel):
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
biz_type: Optional[str] = None
wf_name: Optional[str] = None
status: Optional[int] = None
nodes: Optional[List[WorkflowNodeCreate]] = None
class WorkflowDefOut(BaseModel):
model_config = ConfigDict(from_attributes=True, populate_by_name=True, alias_generator=to_camel)
id: str
biz_type: str
wf_name: str
status: int
nodes: List[WorkflowNodeOut] = []
+1
View File
@@ -0,0 +1 @@
"""报销模块 Service 包。"""
+606
View File
@@ -0,0 +1,606 @@
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional, Sequence, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext
from app.core.tenant_context import get_tenant_no
from app.core.utils import generate_uuid
from app.models.bx_models import (
BxAdvanceApply,
BxAdvanceApproval,
BxReimburseOffset,
BxWorkflowDef,
BxWorkflowNode,
)
from app.services.bx.approval_cycle import filter_current_cycle_approvals
from app.services.bx.base import BxCrudService
from app.utils.bx_codegen import generate_apply_code
from app.utils.bx_date_range import current_month_bounds, parse_create_time_day_bounds
class AdvanceService:
@staticmethod
def _tenant_no(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
@staticmethod
def get_by_id(db: Session, advance_id: str, tenant_no: str) -> Optional[BxAdvanceApply]:
return BxCrudService.get_by_id(BxAdvanceApply, db, advance_id, tenant_no)
@staticmethod
def get_paged(
db: Session,
tenant_no: str,
page: int,
page_size: int,
filters: Optional[List[Any]] = None,
) -> Tuple[Sequence[BxAdvanceApply], int]:
return BxCrudService.get_paged(BxAdvanceApply, db, tenant_no, page, page_size, filters)
@staticmethod
def create(
db: Session,
obj_in: dict,
*,
user: BxUserContext,
) -> BxAdvanceApply:
tenant_no = AdvanceService._tenant_no(db, user)
return BxCrudService.create(
BxAdvanceApply,
db,
obj_in,
tenant_no=tenant_no,
user_id=user.id,
dept_id=user.dept_id,
)
@staticmethod
def update(
db: Session,
advance_id: str,
obj_in: dict,
*,
user: BxUserContext,
) -> Optional[BxAdvanceApply]:
tenant_no = AdvanceService._tenant_no(db, user)
return BxCrudService.update(
BxAdvanceApply,
db,
advance_id,
tenant_no,
obj_in,
user_id=user.id,
)
@staticmethod
def soft_delete(db: Session, advance_id: str, *, user: BxUserContext) -> bool:
tenant_no = AdvanceService._tenant_no(db, user)
return BxCrudService.soft_delete(
BxAdvanceApply, db, advance_id, tenant_no, user_id=user.id
)
@staticmethod
def pending_approval(
db: Session,
user: BxUserContext,
tenant_no: str,
page: int,
page_size: int,
keyword: str = "",
applicant_id: Optional[str] = None,
apply_date: Optional[str] = None,
):
subquery = (
db.query(BxAdvanceApply.id)
.join(
BxWorkflowNode,
and_(
BxWorkflowNode.wf_id == BxAdvanceApply.wf_id,
BxWorkflowNode.node_name == BxAdvanceApply.current_node,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
),
)
.filter(
BxAdvanceApply.del_flag == 0,
BxAdvanceApply.tenant_no == tenant_no,
BxAdvanceApply.status == "pending",
BxWorkflowNode.approver_id == user.id,
)
)
if keyword:
subquery = subquery.filter(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
if applicant_id:
subquery = subquery.filter(BxAdvanceApply.applicant_id == applicant_id)
bounds = parse_create_time_day_bounds(apply_date)
if bounds:
d0, d1 = bounds
subquery = subquery.filter(
BxAdvanceApply.create_time >= d0, BxAdvanceApply.create_time < d1
)
subquery = subquery.subquery()
total = db.query(func.count()).select_from(subquery).scalar() or 0
rows = (
db.query(BxAdvanceApply)
.join(subquery, BxAdvanceApply.id == subquery.c.id)
.order_by(BxAdvanceApply.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return rows, total
@staticmethod
def approved_by_me(
db: Session,
user: BxUserContext,
tenant_no: str,
page: int,
page_size: int,
keyword: str = "",
applicant_id: Optional[str] = None,
apply_date: Optional[str] = None,
):
subquery = (
db.query(BxAdvanceApproval.apply_id)
.filter(
BxAdvanceApproval.approver_id == user.id,
BxAdvanceApproval.tenant_no == tenant_no,
BxAdvanceApproval.del_flag == 0,
)
.distinct()
.subquery()
)
query = (
db.query(BxAdvanceApply)
.join(subquery, BxAdvanceApply.id == subquery.c.apply_id)
.filter(BxAdvanceApply.del_flag == 0, BxAdvanceApply.tenant_no == tenant_no)
)
if keyword:
query = query.filter(
or_(
BxAdvanceApply.apply_code.contains(keyword),
BxAdvanceApply.applicant_name.contains(keyword),
)
)
if applicant_id:
query = query.filter(BxAdvanceApply.applicant_id == applicant_id)
bounds = parse_create_time_day_bounds(apply_date)
if bounds:
d0, d1 = bounds
query = query.filter(BxAdvanceApply.create_time >= d0, BxAdvanceApply.create_time < d1)
total = query.count()
rows = (
query.order_by(BxAdvanceApply.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return rows, total
@staticmethod
def _workflow_nodes(db: Session, wf_id: str, tenant_no: str) -> List[BxWorkflowNode]:
return (
db.query(BxWorkflowNode)
.filter(
BxWorkflowNode.wf_id == wf_id,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
)
.order_by(BxWorkflowNode.node_order)
.all()
)
@staticmethod
def submit(db: Session, apply_id: str, user: BxUserContext) -> Optional[BxAdvanceApply]:
tenant_no = AdvanceService._tenant_no(db, user)
apply_obj = AdvanceService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
apply_obj.status = "pending"
if not apply_obj.apply_code:
apply_obj.apply_code = generate_apply_code(db, "BYJ", tenant_no)
apply_obj.update_by = user.id
if apply_obj.wf_id:
nodes = AdvanceService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
if nodes:
apply_obj.current_node = nodes[0].node_name
db.flush()
return apply_obj
@staticmethod
def approve(
db: Session, apply_id: str, approver: BxUserContext, opinion: str
) -> Optional[BxAdvanceApply]:
tenant_no = AdvanceService._tenant_no(db, approver)
apply_obj = AdvanceService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return None
nodes = AdvanceService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
if not nodes:
return None
current_idx = None
for i, node in enumerate(nodes):
if node.node_name == apply_obj.current_node:
current_idx = i
break
if current_idx is None:
return None
current_node = nodes[current_idx]
if current_node.approver_id != approver.id:
return None
approval = BxAdvanceApproval(
id=generate_uuid(),
apply_id=apply_id,
approver_id=approver.id,
approver_name=approver.name,
approval_opinion=opinion,
node_name=current_node.node_name,
node_order=current_node.node_order,
approval_status="approved",
approval_time=datetime.now(),
tenant_no=tenant_no,
create_by=approver.id,
create_dept_id=approver.dept_id or "",
)
db.add(approval)
if current_idx == len(nodes) - 1:
apply_obj.status = "approved"
apply_obj.current_node = None
else:
apply_obj.current_node = nodes[current_idx + 1].node_name
apply_obj.update_by = approver.id
db.flush()
return apply_obj
@staticmethod
def reject(
db: Session, apply_id: str, approver: BxUserContext, opinion: str
) -> Optional[BxAdvanceApply]:
tenant_no = AdvanceService._tenant_no(db, approver)
apply_obj = AdvanceService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return None
nodes = AdvanceService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
current_node = None
for node in nodes:
if node.node_name == apply_obj.current_node:
current_node = node
break
if current_node is None or current_node.approver_id != approver.id:
return None
approval = BxAdvanceApproval(
id=generate_uuid(),
apply_id=apply_id,
approver_id=approver.id,
approver_name=approver.name,
approval_opinion=opinion,
node_name=current_node.node_name,
node_order=current_node.node_order,
approval_status="rejected",
approval_time=datetime.now(),
tenant_no=tenant_no,
create_by=approver.id,
create_dept_id=approver.dept_id or "",
)
db.add(approval)
apply_obj.status = "rejected"
apply_obj.current_node = None
apply_obj.update_by = approver.id
db.flush()
return apply_obj
@staticmethod
def _approval_record_dict(approval: BxAdvanceApproval, apply_id: str) -> Dict[str, Any]:
return {
"id": approval.id,
"advance_id": apply_id,
"node_name": approval.node_name,
"node_order": approval.node_order,
"approval_status": approval.approval_status,
"approval_opinion": approval.approval_opinion,
"approver_name": approval.approver_name,
"approval_time": approval.approval_time,
}
@staticmethod
def _latest_approval_per_node(
approvals: Sequence[BxAdvanceApproval],
) -> Dict[int, BxAdvanceApproval]:
"""同一节点多次审批时,取 approval_time 最新的一条作为当前节点状态。"""
approval_map: Dict[int, BxAdvanceApproval] = {}
for approval in approvals:
existing = approval_map.get(approval.node_order)
if existing is None:
approval_map[approval.node_order] = approval
continue
approval_time = approval.approval_time or datetime.min
existing_time = existing.approval_time or datetime.min
if approval_time >= existing_time:
approval_map[approval.node_order] = approval
return approval_map
@staticmethod
def _query_approvals(db: Session, apply_id: str, tenant_no: str) -> List[BxAdvanceApproval]:
return (
db.query(BxAdvanceApproval)
.filter(
BxAdvanceApproval.apply_id == apply_id,
BxAdvanceApproval.tenant_no == tenant_no,
BxAdvanceApproval.del_flag == 0,
)
.all()
)
@staticmethod
def get_approvals(db: Session, apply_id: str, tenant_no: str) -> List[Dict[str, Any]]:
apply_obj = AdvanceService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return []
wf = BxCrudService.get_by_id(BxWorkflowDef, db, apply_obj.wf_id, tenant_no)
if not wf:
return []
nodes = AdvanceService._workflow_nodes(db, wf.id, tenant_no)
if not nodes:
return []
approvals = AdvanceService._query_approvals(db, apply_id, tenant_no)
cycle_approvals = filter_current_cycle_approvals(approvals, apply_obj.status)
approval_map = AdvanceService._latest_approval_per_node(cycle_approvals)
result: List[Dict[str, Any]] = []
for node in nodes:
approval = approval_map.get(node.node_order)
if approval:
result.append(AdvanceService._approval_record_dict(approval, apply_id))
else:
result.append(
{
"id": "0",
"advance_id": apply_id,
"node_name": node.node_name,
"node_order": node.node_order,
"approval_status": "pending",
"approval_opinion": None,
"approver_name": node.approver_name or "",
"approval_time": None,
}
)
return result
@staticmethod
def get_approval_history(db: Session, apply_id: str, tenant_no: str) -> List[Dict[str, Any]]:
"""返回全部审批动作,按时间正序排列,用于展示完整审批历史。"""
if not AdvanceService.get_by_id(db, apply_id, tenant_no):
return []
approvals = (
db.query(BxAdvanceApproval)
.filter(
BxAdvanceApproval.apply_id == apply_id,
BxAdvanceApproval.tenant_no == tenant_no,
BxAdvanceApproval.del_flag == 0,
)
.order_by(BxAdvanceApproval.approval_time.asc(), BxAdvanceApproval.create_time.asc())
.all()
)
return [AdvanceService._approval_record_dict(a, apply_id) for a in approvals]
@staticmethod
def global_status_counts(db: Session, tenant_no: str) -> Dict[str, int]:
rows = (
db.query(BxAdvanceApply.status, func.count())
.filter(BxAdvanceApply.del_flag == 0, BxAdvanceApply.tenant_no == tenant_no)
.group_by(BxAdvanceApply.status)
.all()
)
return {str(s or ""): int(c) for s, c in rows}
@staticmethod
def my_apply_stats(db: Session, user_id: str, tenant_no: str) -> dict:
base = and_(
BxAdvanceApply.del_flag == 0,
BxAdvanceApply.tenant_no == tenant_no,
or_(
BxAdvanceApply.applicant_id == user_id,
BxAdvanceApply.create_by == user_id,
),
)
total_count = db.query(func.count()).select_from(BxAdvanceApply).filter(base).scalar() or 0
pending = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.status == "pending")
.scalar()
or 0
)
approved_pass = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.status == "approved")
.scalar()
or 0
)
rejected_count = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.status == "rejected")
.scalar()
or 0
)
pending_issue = approved_pass
issued = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.status == "issued")
.scalar()
or 0
)
issued_amount = (
db.query(func.coalesce(func.sum(BxAdvanceApply.amount), 0))
.filter(base, BxAdvanceApply.status == "issued")
.scalar()
or 0
)
m0, m1 = current_month_bounds()
month_apply_amount = (
db.query(func.coalesce(func.sum(BxAdvanceApply.amount), 0))
.filter(base, BxAdvanceApply.create_time >= m0, BxAdvanceApply.create_time < m1)
.scalar()
or 0
)
month_apply_count = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.create_time >= m0, BxAdvanceApply.create_time < m1)
.scalar()
or 0
)
offset_total = (
db.query(func.coalesce(func.sum(BxReimburseOffset.offset_amount), 0))
.join(BxAdvanceApply, BxReimburseOffset.advance_id == BxAdvanceApply.id)
.filter(
BxAdvanceApply.applicant_id == user_id,
BxAdvanceApply.del_flag == 0,
BxAdvanceApply.tenant_no == tenant_no,
BxReimburseOffset.tenant_no == tenant_no,
BxReimburseOffset.del_flag == 0,
)
.scalar()
or 0
)
return {
"total_count": int(total_count),
"pending_count": int(pending),
"approved_pass_count": int(approved_pass),
"rejected_count": int(rejected_count),
"pending_issue_count": int(pending_issue),
"issued_count": int(issued),
"issued_amount": float(issued_amount),
"month_apply_amount": float(month_apply_amount),
"month_apply_count": int(month_apply_count),
"offset_total": float(offset_total),
}
@staticmethod
def pending_approval_stats(db: Session, user: BxUserContext, tenant_no: str) -> dict:
subquery = (
db.query(BxAdvanceApply.id)
.join(
BxWorkflowNode,
and_(
BxWorkflowNode.wf_id == BxAdvanceApply.wf_id,
BxWorkflowNode.node_name == BxAdvanceApply.current_node,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
),
)
.filter(
BxAdvanceApply.del_flag == 0,
BxAdvanceApply.tenant_no == tenant_no,
BxAdvanceApply.status == "pending",
BxWorkflowNode.approver_id == user.id,
)
).subquery()
total = db.query(func.count()).select_from(subquery).scalar() or 0
m0, m1 = current_month_bounds()
month_sum = (
db.query(func.coalesce(func.sum(BxAdvanceApply.amount), 0))
.join(subquery, BxAdvanceApply.id == subquery.c.id)
.filter(BxAdvanceApply.create_time >= m0, BxAdvanceApply.create_time < m1)
.scalar()
or 0
)
return {"pending_total": int(total), "month_pending_amount": float(month_sum)}
@staticmethod
def offset_amount_map(db: Session, tenant_no: str, advance_ids: List[str]) -> Dict[str, float]:
if not advance_ids:
return {}
rows = (
db.query(
BxReimburseOffset.advance_id,
func.coalesce(func.sum(BxReimburseOffset.offset_amount), 0),
)
.filter(
BxReimburseOffset.advance_id.in_(advance_ids),
BxReimburseOffset.tenant_no == tenant_no,
BxReimburseOffset.del_flag == 0,
)
.group_by(BxReimburseOffset.advance_id)
.all()
)
return {str(advance_id): float(amount) for advance_id, amount in rows}
@staticmethod
def issue(
db: Session,
advance_id: str,
user: BxUserContext,
remark: str = "",
actual_amount: Optional[float] = None,
) -> Optional[BxAdvanceApply]:
tenant_no = AdvanceService._tenant_no(db, user)
apply_obj = AdvanceService.get_by_id(db, advance_id, tenant_no)
if not apply_obj:
return None
apply_obj.status = "issued"
apply_obj.update_by = user.id
apply_obj.issue_status = "issued"
apply_obj.issue_time = datetime.now()
apply_obj.issue_user_id = user.id
apply_obj.issue_user_name = user.name
if remark:
apply_obj.issue_remark = remark
if actual_amount is not None:
apply_obj.actual_amount = actual_amount
db.flush()
return apply_obj
@staticmethod
def finance_issue_stats(db: Session, tenant_no: str) -> dict:
base = and_(BxAdvanceApply.del_flag == 0, BxAdvanceApply.tenant_no == tenant_no)
pending_f = and_(
BxAdvanceApply.status == "approved",
or_(BxAdvanceApply.issue_status.is_(None), BxAdvanceApply.issue_status != "issued"),
)
pending_n = db.query(func.count()).select_from(BxAdvanceApply).filter(base, pending_f).scalar() or 0
issued_n = (
db.query(func.count())
.select_from(BxAdvanceApply)
.filter(base, BxAdvanceApply.status == "issued")
.scalar()
or 0
)
pending_amount = (
db.query(func.coalesce(func.sum(BxAdvanceApply.amount), 0))
.filter(base, pending_f)
.scalar()
or 0
)
m0, m1 = current_month_bounds()
amt_expr = func.coalesce(BxAdvanceApply.actual_amount, BxAdvanceApply.amount)
issued_month = (
db.query(func.coalesce(func.sum(amt_expr), 0))
.filter(
base,
BxAdvanceApply.status == "issued",
BxAdvanceApply.issue_time.isnot(None),
BxAdvanceApply.issue_time >= m0,
BxAdvanceApply.issue_time < m1,
)
.scalar()
or 0
)
return {
"pending_count": int(pending_n),
"issued_count": int(issued_n),
"pending_amount": float(pending_amount),
"issued_month_amount": float(issued_month),
}
+42
View File
@@ -0,0 +1,42 @@
"""审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。"""
from __future__ import annotations
from datetime import datetime
from typing import List, Sequence, TypeVar
T = TypeVar("T")
def filter_current_cycle_approvals(
approvals: Sequence[T],
apply_status: str,
*,
status_attr: str = "approval_status",
time_attr: str = "approval_time",
) -> List[T]:
"""审批进度仅展示当前轮次;完整历史由 get_approval_history 返回。"""
rejections = [a for a in approvals if getattr(a, status_attr) == "rejected"]
if not rejections:
return list(approvals)
sorted_rejections = sorted(
rejections,
key=lambda a: getattr(a, time_attr) or datetime.min,
)
last_rejection_time = getattr(sorted_rejections[-1], time_attr) or datetime.min
if apply_status != "rejected":
return [
a
for a in approvals
if (getattr(a, time_attr) or datetime.min) > last_rejection_time
]
cycle_start = datetime.min
if len(sorted_rejections) >= 2:
cycle_start = getattr(sorted_rejections[-2], time_attr) or datetime.min
return [
a
for a in approvals
if (getattr(a, time_attr) or datetime.min) > cycle_start
]
+119
View File
@@ -0,0 +1,119 @@
from __future__ import annotations
from typing import List, Optional
from sqlalchemy import case as sql_case, func, or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext
from app.core.tenant_context import get_tenant_no
from app.models.bx_models import BxAttachment
from app.services.bx.base import BxCrudService
class AttachmentService:
@staticmethod
def _tenant_no(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
@staticmethod
def get_by_id(db: Session, attachment_id: str, tenant_no: str) -> Optional[BxAttachment]:
return BxCrudService.get_by_id(BxAttachment, db, attachment_id, tenant_no)
@staticmethod
def create(
db: Session,
obj_in: dict,
*,
user: BxUserContext,
) -> BxAttachment:
tenant_no = AttachmentService._tenant_no(db, user)
return BxCrudService.create(
BxAttachment,
db,
obj_in,
tenant_no=tenant_no,
user_id=user.id,
dept_id=user.dept_id,
)
@staticmethod
def soft_delete(db: Session, attachment_id: str, *, user: BxUserContext) -> bool:
tenant_no = AttachmentService._tenant_no(db, user)
return BxCrudService.soft_delete(
BxAttachment, db, attachment_id, tenant_no, user_id=user.id
)
@staticmethod
def get_by_biz(db: Session, biz_type: str, biz_id: str, tenant_no: str) -> List[BxAttachment]:
query = db.query(BxAttachment).filter(
BxAttachment.biz_type == biz_type,
BxAttachment.biz_id == biz_id,
BxAttachment.tenant_no == tenant_no,
BxAttachment.del_flag == 0,
)
return query.order_by(BxAttachment.sort_order, BxAttachment.create_time).all()
@staticmethod
def bind_biz(
db: Session,
ids: List[str],
biz_type: str,
biz_id: str,
*,
user: BxUserContext,
) -> int:
tenant_no = AttachmentService._tenant_no(db, user)
if not ids:
return 0
count = (
db.query(BxAttachment)
.filter(
BxAttachment.id.in_(ids),
BxAttachment.tenant_no == tenant_no,
BxAttachment.del_flag == 0,
)
.update(
{
BxAttachment.biz_type: biz_type,
BxAttachment.biz_id: biz_id,
BxAttachment.update_by: user.id,
},
synchronize_session="fetch",
)
)
db.flush()
return int(count or 0)
@staticmethod
def count_invoice_attach_by_biz_ids(
db: Session, biz_type: str, biz_ids: List[str], tenant_no: str
) -> dict[str, tuple[int, int]]:
if not biz_ids:
return {}
if biz_type == "reimburse_apply":
inv_cond = or_(
BxAttachment.attach_type == "invoice",
BxAttachment.file_type.isnot(None) & BxAttachment.file_type.like("image/%"),
BxAttachment.file_type == "application/pdf",
)
else:
inv_cond = BxAttachment.attach_type == "invoice"
inv_case = sql_case((inv_cond, 1), else_=0)
att_case = sql_case((inv_cond, 0), else_=1)
rows = (
db.query(
BxAttachment.biz_id,
func.coalesce(func.sum(inv_case), 0).label("inv"),
func.coalesce(func.sum(att_case), 0).label("att"),
)
.filter(
BxAttachment.biz_type == biz_type,
BxAttachment.biz_id.in_(biz_ids),
BxAttachment.tenant_no == tenant_no,
BxAttachment.del_flag == 0,
)
.group_by(BxAttachment.biz_id)
.all()
)
return {str(r.biz_id): (int(r.inv), int(r.att)) for r in rows}
+92
View File
@@ -0,0 +1,92 @@
"""报销模块 Service 基类 — 租户隔离 + UUID + 软删除。"""
from __future__ import annotations
from typing import Any, List, Optional, Sequence, Tuple, Type, TypeVar
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
M = TypeVar("M")
class BxCrudService:
@staticmethod
def get_by_id(model: Type[M], db: Session, id: str, tenant_no: str) -> Optional[M]:
query = db.query(model).filter(model.id == id, model.tenant_no == tenant_no)
if hasattr(model, "del_flag"):
query = query.filter(model.del_flag == 0)
return query.first()
@staticmethod
def get_paged(
model: Type[M],
db: Session,
tenant_no: str,
page: int,
page_size: int,
filters: Optional[List[Any]] = None,
) -> Tuple[Sequence[M], int]:
query = db.query(model).filter(model.tenant_no == tenant_no)
if hasattr(model, "del_flag"):
query = query.filter(model.del_flag == 0)
if filters:
query = query.filter(*filters)
total = query.count()
if hasattr(model, "sort_order"):
query = query.order_by(model.sort_order.asc(), model.create_time.desc())
else:
query = query.order_by(model.create_time.desc())
rows = query.offset((page - 1) * page_size).limit(page_size).all()
return rows, total
@staticmethod
def create(
model: Type[M],
db: Session,
obj_in: dict,
*,
tenant_no: str,
user_id: str,
dept_id: str,
) -> M:
data = dict(obj_in)
data.setdefault("id", generate_uuid())
data["tenant_no"] = tenant_no
data["create_by"] = user_id
data["create_dept_id"] = dept_id or ""
obj = model(**data)
db.add(obj)
db.flush()
return obj
@staticmethod
def update(
model: Type[M],
db: Session,
id: str,
tenant_no: str,
obj_in: dict,
*,
user_id: str,
) -> Optional[M]:
obj = BxCrudService.get_by_id(model, db, id, tenant_no)
if not obj:
return None
for key, value in obj_in.items():
setattr(obj, key, value)
if hasattr(obj, "update_by"):
obj.update_by = user_id
db.flush()
return obj
@staticmethod
def soft_delete(model: Type[M], db: Session, id: str, tenant_no: str, *, user_id: str = "") -> bool:
obj = BxCrudService.get_by_id(model, db, id, tenant_no)
if not obj:
return False
obj.del_flag = 1
if hasattr(obj, "update_by") and user_id:
obj.update_by = user_id
db.flush()
return True
+114
View File
@@ -0,0 +1,114 @@
from __future__ import annotations
from typing import List, Optional
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext
from app.core.tenant_context import get_tenant_no
from app.models.bx_models import BxExpenseItem
from app.services.bx.base import BxCrudService
class ExpenseItemService:
@staticmethod
def _tenant_no(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
@staticmethod
def get_by_id(db: Session, item_id: str, tenant_no: str) -> Optional[BxExpenseItem]:
return BxCrudService.get_by_id(BxExpenseItem, db, item_id, tenant_no)
@staticmethod
def create(
db: Session,
obj_in: dict,
*,
user: BxUserContext,
) -> BxExpenseItem:
tenant_no = ExpenseItemService._tenant_no(db, user)
data = dict(obj_in)
parent_id = data.get("parent_id") or "0"
data["parent_id"] = parent_id
if parent_id and parent_id != "0":
parent = ExpenseItemService.get_by_id(db, parent_id, tenant_no)
if not parent:
raise ValueError("父级费用项目不存在")
data.setdefault("level", (parent.level or 1) + 1)
else:
data["parent_id"] = "0"
data.setdefault("level", 1)
return BxCrudService.create(
BxExpenseItem,
db,
data,
tenant_no=tenant_no,
user_id=user.id,
dept_id=user.dept_id,
)
@staticmethod
def update(
db: Session,
item_id: str,
obj_in: dict,
*,
user: BxUserContext,
) -> Optional[BxExpenseItem]:
tenant_no = ExpenseItemService._tenant_no(db, user)
data = dict(obj_in)
if "parent_id" in data and data["parent_id"] is not None:
parent_id = data["parent_id"] or "0"
data["parent_id"] = parent_id
if parent_id != "0":
if parent_id == item_id:
raise ValueError("不能将项目设置为自己的父级")
parent = ExpenseItemService.get_by_id(db, parent_id, tenant_no)
if not parent:
raise ValueError("父级费用项目不存在")
return BxCrudService.update(
BxExpenseItem,
db,
item_id,
tenant_no,
data,
user_id=user.id,
)
@staticmethod
def soft_delete(db: Session, item_id: str, *, user: BxUserContext) -> bool:
tenant_no = ExpenseItemService._tenant_no(db, user)
return BxCrudService.soft_delete(
BxExpenseItem, db, item_id, tenant_no, user_id=user.id
)
@staticmethod
def get_item_tree(db: Session, tenant_no: str) -> List[BxExpenseItem]:
items = (
db.query(BxExpenseItem)
.filter(BxExpenseItem.del_flag == 0, BxExpenseItem.tenant_no == tenant_no)
.order_by(BxExpenseItem.sort_order.asc(), BxExpenseItem.create_time.desc())
.all()
)
return ExpenseItemService._build_tree(items)
@staticmethod
def _build_tree(items: List[BxExpenseItem]) -> List[BxExpenseItem]:
item_map = {item.id: item for item in items}
roots: List[BxExpenseItem] = []
for item in items:
parent_id = item.parent_id or "0"
if not parent_id or parent_id == "0":
roots.append(item)
else:
parent = item_map.get(parent_id)
if parent:
if not hasattr(parent, "children") or parent.children is None:
parent.children = []
parent.children.append(item)
for item in items:
if hasattr(item, "children") and item.children:
item.child_count = len(item.children)
else:
item.child_count = 0
return roots
+765
View File
@@ -0,0 +1,765 @@
from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from typing import Any, Dict, List, Optional, Sequence, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext
from app.core.tenant_context import get_tenant_no
from app.core.utils import generate_uuid
from app.models.bx_models import (
BxReimburseApply,
BxReimburseApproval,
BxReimburseDetail,
BxReimburseOffset,
BxWorkflowDef,
BxWorkflowNode,
)
from app.schemas.bx.reimburse import ReimburseApplyOut, ReimburseDetailOut
from app.services.bx.approval_cycle import filter_current_cycle_approvals
from app.services.bx.attachment import AttachmentService
from app.services.bx.base import BxCrudService
from app.utils.bx_codegen import generate_apply_code, generate_title
from app.utils.bx_date_range import current_month_bounds, parse_create_time_day_bounds
_REIMBURSE_MY_STATS_PASSED_STATUSES = ("approved", "processed", "paid", "offset")
class ReimburseService:
@staticmethod
def _tenant_no(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
@staticmethod
def get_by_id(db: Session, reimburse_id: str, tenant_no: str) -> Optional[BxReimburseApply]:
return BxCrudService.get_by_id(BxReimburseApply, db, reimburse_id, tenant_no)
@staticmethod
def get_paged(
db: Session,
tenant_no: str,
page: int,
page_size: int,
filters: Optional[List[Any]] = None,
) -> Tuple[Sequence[BxReimburseApply], int]:
return BxCrudService.get_paged(BxReimburseApply, db, tenant_no, page, page_size, filters)
@staticmethod
def soft_delete(db: Session, reimburse_id: str, *, user: BxUserContext) -> bool:
tenant_no = ReimburseService._tenant_no(db, user)
return BxCrudService.soft_delete(
BxReimburseApply, db, reimburse_id, tenant_no, user_id=user.id
)
@staticmethod
def _workflow_nodes(db: Session, wf_id: str, tenant_no: str) -> List[BxWorkflowNode]:
return (
db.query(BxWorkflowNode)
.filter(
BxWorkflowNode.wf_id == wf_id,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
)
.order_by(BxWorkflowNode.node_order)
.all()
)
@staticmethod
def pending_approval(
db: Session,
user: BxUserContext,
tenant_no: str,
page: int,
page_size: int,
keyword: str = "",
applicant_id: Optional[str] = None,
apply_date: Optional[str] = None,
):
subquery = (
db.query(BxReimburseApply.id)
.join(
BxWorkflowNode,
and_(
BxWorkflowNode.wf_id == BxReimburseApply.wf_id,
BxWorkflowNode.node_name == BxReimburseApply.current_node,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
),
)
.filter(
BxReimburseApply.del_flag == 0,
BxReimburseApply.tenant_no == tenant_no,
BxReimburseApply.status == "pending",
BxWorkflowNode.approver_id == user.id,
)
)
if keyword:
subquery = subquery.filter(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
if applicant_id:
subquery = subquery.filter(BxReimburseApply.applicant_id == applicant_id)
bounds = parse_create_time_day_bounds(apply_date)
if bounds:
d0, d1 = bounds
subquery = subquery.filter(
BxReimburseApply.create_time >= d0, BxReimburseApply.create_time < d1
)
subquery = subquery.subquery()
total = db.query(func.count()).select_from(subquery).scalar() or 0
rows = (
db.query(BxReimburseApply)
.join(subquery, BxReimburseApply.id == subquery.c.id)
.order_by(BxReimburseApply.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return rows, total
@staticmethod
def approved_by_me(
db: Session,
user: BxUserContext,
tenant_no: str,
page: int,
page_size: int,
keyword: str = "",
applicant_id: Optional[str] = None,
apply_date: Optional[str] = None,
):
subquery = (
db.query(BxReimburseApproval.apply_id)
.filter(
BxReimburseApproval.approver_id == user.id,
BxReimburseApproval.tenant_no == tenant_no,
BxReimburseApproval.del_flag == 0,
)
.distinct()
.subquery()
)
query = (
db.query(BxReimburseApply)
.join(subquery, BxReimburseApply.id == subquery.c.apply_id)
.filter(BxReimburseApply.del_flag == 0, BxReimburseApply.tenant_no == tenant_no)
)
if keyword:
query = query.filter(
or_(
BxReimburseApply.apply_code.contains(keyword),
BxReimburseApply.applicant_name.contains(keyword),
)
)
if applicant_id:
query = query.filter(BxReimburseApply.applicant_id == applicant_id)
bounds = parse_create_time_day_bounds(apply_date)
if bounds:
d0, d1 = bounds
query = query.filter(BxReimburseApply.create_time >= d0, BxReimburseApply.create_time < d1)
total = query.count()
rows = (
query.order_by(BxReimburseApply.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return rows, total
@staticmethod
def submit(db: Session, apply_id: str, user: BxUserContext) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, user)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
apply_obj.status = "pending"
if not apply_obj.apply_code:
apply_obj.apply_code = generate_apply_code(db, "BX", tenant_no)
apply_obj.update_by = user.id
if apply_obj.wf_id:
nodes = ReimburseService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
if nodes:
apply_obj.current_node = nodes[0].node_name
db.flush()
return apply_obj
@staticmethod
def approve(
db: Session, apply_id: str, approver: BxUserContext, opinion: str
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, approver)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return None
nodes = ReimburseService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
if not nodes:
return None
current_idx = None
for i, node in enumerate(nodes):
if node.node_name == apply_obj.current_node:
current_idx = i
break
if current_idx is None:
return None
current_node = nodes[current_idx]
if current_node.approver_id != approver.id:
return None
approval = BxReimburseApproval(
id=generate_uuid(),
apply_id=apply_id,
approver_id=approver.id,
approver_name=approver.name,
approval_opinion=opinion,
node_name=current_node.node_name,
node_order=current_node.node_order,
approval_status="approved",
approval_time=datetime.now(),
tenant_no=tenant_no,
create_by=approver.id,
create_dept_id=approver.dept_id or "",
)
db.add(approval)
if current_idx == len(nodes) - 1:
apply_obj.status = "approved"
apply_obj.current_node = None
else:
apply_obj.current_node = nodes[current_idx + 1].node_name
apply_obj.update_by = approver.id
db.flush()
return apply_obj
@staticmethod
def reject(
db: Session, apply_id: str, approver: BxUserContext, opinion: str
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, approver)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return None
nodes = ReimburseService._workflow_nodes(db, apply_obj.wf_id, tenant_no)
current_node = None
for node in nodes:
if node.node_name == apply_obj.current_node:
current_node = node
break
if current_node is None or current_node.approver_id != approver.id:
return None
approval = BxReimburseApproval(
id=generate_uuid(),
apply_id=apply_id,
approver_id=approver.id,
approver_name=approver.name,
approval_opinion=opinion,
node_name=current_node.node_name,
node_order=current_node.node_order,
approval_status="rejected",
approval_time=datetime.now(),
tenant_no=tenant_no,
create_by=approver.id,
create_dept_id=approver.dept_id or "",
)
db.add(approval)
apply_obj.status = "rejected"
apply_obj.current_node = None
apply_obj.update_by = approver.id
db.flush()
return apply_obj
@staticmethod
def _approval_record_dict(approval: BxReimburseApproval, apply_id: str) -> Dict[str, Any]:
return {
"id": approval.id,
"reimburse_id": apply_id,
"node_name": approval.node_name,
"node_order": approval.node_order,
"approval_status": approval.approval_status,
"approval_opinion": approval.approval_opinion,
"approver_name": approval.approver_name,
"approval_time": approval.approval_time,
}
@staticmethod
def _latest_approval_per_node(
approvals: Sequence[BxReimburseApproval],
) -> Dict[int, BxReimburseApproval]:
"""同一节点多次审批时,取 approval_time 最新的一条作为当前节点状态。"""
approval_map: Dict[int, BxReimburseApproval] = {}
for approval in approvals:
existing = approval_map.get(approval.node_order)
if existing is None:
approval_map[approval.node_order] = approval
continue
approval_time = approval.approval_time or datetime.min
existing_time = existing.approval_time or datetime.min
if approval_time >= existing_time:
approval_map[approval.node_order] = approval
return approval_map
@staticmethod
def _query_approvals(db: Session, apply_id: str, tenant_no: str) -> List[BxReimburseApproval]:
return (
db.query(BxReimburseApproval)
.filter(
BxReimburseApproval.apply_id == apply_id,
BxReimburseApproval.tenant_no == tenant_no,
BxReimburseApproval.del_flag == 0,
)
.all()
)
@staticmethod
def get_approvals(db: Session, apply_id: str, tenant_no: str) -> List[Dict[str, Any]]:
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj or not apply_obj.wf_id:
return []
wf = BxCrudService.get_by_id(BxWorkflowDef, db, apply_obj.wf_id, tenant_no)
if not wf:
return []
nodes = ReimburseService._workflow_nodes(db, wf.id, tenant_no)
if not nodes:
return []
approvals = ReimburseService._query_approvals(db, apply_id, tenant_no)
cycle_approvals = filter_current_cycle_approvals(approvals, apply_obj.status)
approval_map = ReimburseService._latest_approval_per_node(cycle_approvals)
result: List[Dict[str, Any]] = []
for node in nodes:
approval = approval_map.get(node.node_order)
if approval:
result.append(ReimburseService._approval_record_dict(approval, apply_id))
else:
result.append(
{
"id": "0",
"reimburse_id": apply_id,
"node_name": node.node_name,
"node_order": node.node_order,
"approval_status": "pending",
"approval_opinion": None,
"approver_name": node.approver_name or "",
"approval_time": None,
}
)
return result
@staticmethod
def get_approval_history(db: Session, apply_id: str, tenant_no: str) -> List[Dict[str, Any]]:
"""返回全部审批动作,按时间正序排列,用于展示完整审批历史。"""
if not ReimburseService.get_by_id(db, apply_id, tenant_no):
return []
approvals = (
db.query(BxReimburseApproval)
.filter(
BxReimburseApproval.apply_id == apply_id,
BxReimburseApproval.tenant_no == tenant_no,
BxReimburseApproval.del_flag == 0,
)
.order_by(BxReimburseApproval.approval_time.asc(), BxReimburseApproval.create_time.asc())
.all()
)
return [ReimburseService._approval_record_dict(a, apply_id) for a in approvals]
@staticmethod
def create_with_details(
db: Session,
obj_in: dict,
details: List[dict],
*,
user: BxUserContext,
) -> BxReimburseApply:
tenant_no = ReimburseService._tenant_no(db, user)
data = dict(obj_in)
total_amount = sum(float(d.get("amount", 0) or 0) for d in details)
title = data.pop("title", None) or generate_title(db, user.name, tenant_no)
first_detail = details[0] if details else {}
apply_obj = BxReimburseApply(
id=generate_uuid(),
title=title,
amount=total_amount,
expense_id=str(first_detail.get("expense_id") or ""),
expense_name=first_detail.get("expense_name") or "",
applicant_id=user.id,
applicant_name=user.name,
tenant_no=tenant_no,
create_by=user.id,
create_dept_id=user.dept_id or "",
**data,
)
db.add(apply_obj)
db.flush()
for d in details:
detail = BxReimburseDetail(
id=generate_uuid(),
reimburse_id=apply_obj.id,
expense_id=str(d["expense_id"]),
expense_name=d["expense_name"],
amount=d["amount"],
remark=d.get("remark"),
tenant_no=tenant_no,
create_by=user.id,
create_dept_id=user.dept_id or "",
)
db.add(detail)
db.flush()
return apply_obj
@staticmethod
def update_with_details(
db: Session,
apply_id: str,
obj_in: dict,
details: Optional[List[dict]],
*,
user: BxUserContext,
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, user)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
for key, value in obj_in.items():
setattr(apply_obj, key, value)
if details is not None:
db.query(BxReimburseDetail).filter(
BxReimburseDetail.reimburse_id == apply_id,
BxReimburseDetail.tenant_no == tenant_no,
BxReimburseDetail.del_flag == 0,
).update({"del_flag": 1}, synchronize_session=False)
total_amount = Decimal("0")
first_expense_name = ""
first_expense_id = ""
for i, d in enumerate(details):
detail = BxReimburseDetail(
id=generate_uuid(),
reimburse_id=apply_id,
expense_id=str(d["expense_id"]),
expense_name=d["expense_name"],
amount=d["amount"],
remark=d.get("remark"),
tenant_no=tenant_no,
create_by=user.id,
create_dept_id=user.dept_id or "",
)
db.add(detail)
total_amount += Decimal(str(d["amount"]))
if i == 0:
first_expense_name = d["expense_name"]
first_expense_id = str(d["expense_id"])
apply_obj.amount = total_amount
apply_obj.expense_name = first_expense_name
apply_obj.expense_id = first_expense_id
apply_obj.update_by = user.id
db.flush()
return apply_obj
@staticmethod
def get_details(db: Session, reimburse_id: str, tenant_no: str) -> List[BxReimburseDetail]:
return (
db.query(BxReimburseDetail)
.filter(
BxReimburseDetail.reimburse_id == reimburse_id,
BxReimburseDetail.tenant_no == tenant_no,
BxReimburseDetail.del_flag == 0,
)
.order_by(BxReimburseDetail.create_time)
.all()
)
@staticmethod
def out_rows_with_attachment_counts(
db: Session, tenant_no: str, rows: List[BxReimburseApply]
) -> List[ReimburseApplyOut]:
if not rows:
return []
ids = [r.id for r in rows]
counts = AttachmentService.count_invoice_attach_by_biz_ids(
db, "reimburse_apply", ids, tenant_no
)
out_list: List[ReimburseApplyOut] = []
for row in rows:
out = ReimburseApplyOut.model_validate(row)
inv, att = counts.get(row.id, (0, 0))
out_list.append(out.model_copy(update={"invoice_count": inv, "attach_count": att}))
return out_list
@staticmethod
def out_one_with_attachment_counts(
db: Session, tenant_no: str, row: BxReimburseApply
) -> ReimburseApplyOut:
return ReimburseService.out_rows_with_attachment_counts(db, tenant_no, [row])[0]
@staticmethod
def build_apply_out_with_details(
db: Session, tenant_no: str, reimburse: BxReimburseApply
) -> ReimburseApplyOut:
out = ReimburseService.out_one_with_attachment_counts(db, tenant_no, reimburse)
detail_rows = ReimburseService.get_details(db, reimburse.id, tenant_no)
out.details = [ReimburseDetailOut.model_validate(d) for d in detail_rows]
return out
@staticmethod
def global_status_counts(db: Session, tenant_no: str) -> Dict[str, int]:
rows = (
db.query(BxReimburseApply.status, func.count())
.filter(BxReimburseApply.del_flag == 0, BxReimburseApply.tenant_no == tenant_no)
.group_by(BxReimburseApply.status)
.all()
)
return {str(s or ""): int(c) for s, c in rows}
@staticmethod
def my_apply_stats(db: Session, user_id: str, tenant_no: str) -> dict:
base = and_(
BxReimburseApply.del_flag == 0,
BxReimburseApply.tenant_no == tenant_no,
or_(
BxReimburseApply.applicant_id == user_id,
BxReimburseApply.create_by == user_id,
),
)
total_count = db.query(func.count()).select_from(BxReimburseApply).filter(base).scalar() or 0
pending = (
db.query(func.count())
.select_from(BxReimburseApply)
.filter(base, BxReimburseApply.status == "pending")
.scalar()
or 0
)
approved_count = (
db.query(func.count())
.select_from(BxReimburseApply)
.filter(base, BxReimburseApply.status.in_(_REIMBURSE_MY_STATS_PASSED_STATUSES))
.scalar()
or 0
)
rejected_count = (
db.query(func.count())
.select_from(BxReimburseApply)
.filter(base, BxReimburseApply.status == "rejected")
.scalar()
or 0
)
approved_amount = (
db.query(func.coalesce(func.sum(BxReimburseApply.amount), 0))
.filter(base, BxReimburseApply.status.in_(_REIMBURSE_MY_STATS_PASSED_STATUSES))
.scalar()
or 0
)
m0, m1 = current_month_bounds()
month_apply_amount = (
db.query(func.coalesce(func.sum(BxReimburseApply.amount), 0))
.filter(base, BxReimburseApply.create_time >= m0, BxReimburseApply.create_time < m1)
.scalar()
or 0
)
month_apply_count = (
db.query(func.count())
.select_from(BxReimburseApply)
.filter(base, BxReimburseApply.create_time >= m0, BxReimburseApply.create_time < m1)
.scalar()
or 0
)
return {
"total_count": int(total_count),
"pending_count": int(pending),
"approved_count": int(approved_count),
"rejected_count": int(rejected_count),
"approved_amount": float(approved_amount),
"month_apply_amount": float(month_apply_amount),
"month_apply_count": int(month_apply_count),
}
@staticmethod
def pending_approval_stats(db: Session, user: BxUserContext, tenant_no: str) -> dict:
subquery = (
db.query(BxReimburseApply.id)
.join(
BxWorkflowNode,
and_(
BxWorkflowNode.wf_id == BxReimburseApply.wf_id,
BxWorkflowNode.node_name == BxReimburseApply.current_node,
BxWorkflowNode.tenant_no == tenant_no,
BxWorkflowNode.del_flag == 0,
),
)
.filter(
BxReimburseApply.del_flag == 0,
BxReimburseApply.tenant_no == tenant_no,
BxReimburseApply.status == "pending",
BxWorkflowNode.approver_id == user.id,
)
).subquery()
total = db.query(func.count()).select_from(subquery).scalar() or 0
m0, m1 = current_month_bounds()
month_sum = (
db.query(func.coalesce(func.sum(BxReimburseApply.amount), 0))
.join(subquery, BxReimburseApply.id == subquery.c.id)
.filter(BxReimburseApply.create_time >= m0, BxReimburseApply.create_time < m1)
.scalar()
or 0
)
return {"pending_total": int(total), "month_pending_amount": float(month_sum)}
@staticmethod
def pay(
db: Session,
apply_id: str,
user: BxUserContext,
pay_remark: str = "",
actual_amount: Optional[float] = None,
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, user)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
apply_obj.status = "processed"
apply_obj.update_by = user.id
apply_obj.pay_status = "paid"
apply_obj.pay_time = datetime.now()
apply_obj.pay_user_id = user.id
apply_obj.pay_user_name = user.name
if actual_amount is not None:
apply_obj.actual_amount = actual_amount
db.flush()
return apply_obj
@staticmethod
def offset(
db: Session,
apply_id: str,
advance_id: str,
amount: float,
user: BxUserContext,
remark: str = "",
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, user)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
apply_obj.status = "offset"
apply_obj.offset_advance_id = advance_id
apply_obj.offset_amount = amount
apply_obj.update_by = user.id
offset_record = BxReimburseOffset(
id=generate_uuid(),
reimburse_id=apply_id,
advance_id=advance_id,
offset_amount=amount,
remark=remark or None,
tenant_no=tenant_no,
create_by=user.name,
create_dept_id=user.dept_id or "",
)
db.add(offset_record)
db.flush()
return apply_obj
@staticmethod
def batch_offset(
db: Session,
apply_id: str,
items: List[dict],
pay_amount: float,
user: BxUserContext,
remark: str = "",
) -> Optional[BxReimburseApply]:
tenant_no = ReimburseService._tenant_no(db, user)
apply_obj = ReimburseService.get_by_id(db, apply_id, tenant_no)
if not apply_obj:
return None
has_offsets = bool(items)
total_offset = 0.0
first_advance_id = None
for item in items:
advance_id = str(item["advance_id"])
offset_amount = float(item["offset_amount"])
if first_advance_id is None:
first_advance_id = advance_id
total_offset += offset_amount
offset_record = BxReimburseOffset(
id=generate_uuid(),
reimburse_id=apply_id,
advance_id=advance_id,
offset_amount=offset_amount,
remark=remark or None,
tenant_no=tenant_no,
create_by=user.name,
create_dept_id=user.dept_id or "",
)
db.add(offset_record)
if first_advance_id is not None:
apply_obj.offset_advance_id = first_advance_id
apply_obj.offset_amount = total_offset
if pay_amount > 0:
apply_obj.pay_status = "paid"
apply_obj.pay_time = datetime.now()
apply_obj.pay_user_id = user.id
apply_obj.pay_user_name = user.name
apply_obj.actual_amount = pay_amount
apply_obj.status = "processed"
elif has_offsets:
apply_obj.actual_amount = 0
apply_obj.status = "processed"
apply_obj.update_by = user.id
db.flush()
return apply_obj
@staticmethod
def finance_payment_stats(db: Session, tenant_no: str) -> dict:
base = and_(BxReimburseApply.del_flag == 0, BxReimburseApply.tenant_no == tenant_no)
pending = (
db.query(func.count())
.select_from(BxReimburseApply)
.filter(base, BxReimburseApply.status == "approved")
.scalar()
or 0
)
bank_paid = BxReimburseApply.pay_time.isnot(None)
paid = (
db.query(func.count()).select_from(BxReimburseApply).filter(base, bank_paid).scalar() or 0
)
offset_settled = or_(
BxReimburseApply.status == "offset",
and_(
BxReimburseApply.status == "processed",
BxReimburseApply.pay_time.is_(None),
func.coalesce(BxReimburseApply.offset_amount, 0) > 0,
),
)
offset = (
db.query(func.count()).select_from(BxReimburseApply).filter(base, offset_settled).scalar() or 0
)
m0, m1 = current_month_bounds()
month_paid = (
db.query(func.coalesce(func.sum(BxReimburseApply.amount), 0))
.filter(base, bank_paid, BxReimburseApply.pay_time >= m0, BxReimburseApply.pay_time < m1)
.scalar()
or 0
)
month_offset = (
db.query(func.coalesce(func.sum(BxReimburseApply.amount), 0))
.filter(base, offset_settled, BxReimburseApply.update_time >= m0, BxReimburseApply.update_time < m1)
.scalar()
or 0
)
return {
"pending_count": int(pending),
"paid_count": int(paid),
"offset_count": int(offset),
"month_processed_amount": float(month_paid) + float(month_offset),
}
+103
View File
@@ -0,0 +1,103 @@
"""报销附件存储适配 — 复用门户 app.core.storage,对外保持 bx 协议。"""
from __future__ import annotations
from datetime import timedelta
from io import BytesIO
from pathlib import Path
from app.core.config import settings
from app.core.storage import LocalStorage, MinIOStorage, get_storage, storage
def normalize_storage_path(file_url: str) -> str:
"""将 upload 返回的完整 URL 归一化为存储相对路径(与 admin 附件 upload 一致)。"""
u = (file_url or "").strip()
if not u:
return ""
if "?" in u:
u = u.split("?", 1)[0]
base_url = (settings.MINIO_BASE_URL or "").rstrip("/")
if base_url and u.startswith(base_url + "/"):
return u[len(base_url) + 1 :]
local_base = settings.LOCAL_STORAGE_URL.rstrip("/")
if u.startswith(local_base + "/"):
return u[len(local_base) + 1 :]
if u.startswith("http://") or u.startswith("https://"):
store = get_storage()
if isinstance(store, MinIOStorage):
return store._extract_object_key(u)
return u.lstrip("/")
def upload_bx_file(file_content: bytes, filename: str, *, biz_type: str = "") -> tuple[str, str]:
"""
上传文件到对象存储。
返回 (relative_path, access_url)。
"""
folder = "bx"
if biz_type:
folder = f"bx/{biz_type.strip('/')}"
full_url = storage.upload_file(file_content, filename, folder)
relative_path = normalize_storage_path(full_url)
return relative_path, full_url
def get_access_url(file_path: str) -> str:
"""获取可访问 URL(本地/MinIO 公开地址)。"""
store = get_storage()
key = normalize_storage_path(file_path)
if not key:
return ""
return store.get_url(key)
def get_preview_url(file_path: str, *, expires_seconds: int = 3600) -> str:
"""图片/附件预览 URL:MinIO 预签名 inline,本地/其他存储用 get_url。"""
store = get_storage()
key = normalize_storage_path(file_path)
if not key:
return ""
if isinstance(store, MinIOStorage):
return store.client.presigned_get_object(
bucket_name=store.bucket_name,
object_name=key,
expires=timedelta(seconds=expires_seconds),
)
return store.get_url(key)
def get_download_url(file_path: str, file_name: str, *, expires_seconds: int = 3600) -> str:
"""带文件名的下载 URL(对齐门户 attachment/download-url)。"""
store = get_storage()
key = normalize_storage_path(file_path)
return store.presigned_download_url(key, file_name, expires_seconds)
def download_file_bytes(file_path: str) -> bytes:
"""读取文件二进制,供 StreamingResponse 下载。"""
store = get_storage()
key = normalize_storage_path(file_path)
if not key:
raise ValueError("无效的文件路径")
if isinstance(store, MinIOStorage):
response = store.client.get_object(store.bucket_name, key)
try:
return response.read()
finally:
response.close()
response.release_conn()
if isinstance(store, LocalStorage):
file_full_path = Path(store.upload_dir) / key
if not file_full_path.is_file():
raise FileNotFoundError(f"文件不存在: {key}")
return file_full_path.read_bytes()
# 七牛/OSS 等:回退 HTTP 拉取(开发环境较少用到)
import urllib.request
url = store.get_url(key)
with urllib.request.urlopen(url, timeout=60) as resp:
return resp.read()
def file_stream(file_path: str) -> BytesIO:
return BytesIO(download_file_bytes(file_path))
+169
View File
@@ -0,0 +1,169 @@
from __future__ import annotations
from typing import List, Optional, Sequence, Tuple
from sqlalchemy.orm import Session
from app.core.bx_auth import BxUserContext
from app.core.tenant_context import get_tenant_no
from app.core.utils import generate_uuid
from app.models.bx_models import BxWorkflowDef, BxWorkflowNode
from app.services.bx.base import BxCrudService
_DEFAULT_WORKFLOWS = {
"advance": {
"wf_name": "备用金审批流程",
"remark": "备用金申请默认审批流程",
},
"reimburse": {
"wf_name": "报销单审批流程",
"remark": "报销申请默认审批流程",
},
}
class WorkflowService:
@staticmethod
def _tenant_no(db: Session, user: BxUserContext) -> str:
return get_tenant_no(db, user.to_tenant_user_dict())
@staticmethod
def get_by_id(db: Session, workflow_id: str, tenant_no: str) -> Optional[BxWorkflowDef]:
return BxCrudService.get_by_id(BxWorkflowDef, db, workflow_id, tenant_no)
@staticmethod
def get_paged(
db: Session,
tenant_no: str,
page: int,
page_size: int,
) -> Tuple[Sequence[BxWorkflowDef], int]:
return BxCrudService.get_paged(BxWorkflowDef, db, tenant_no, page, page_size)
@staticmethod
def create(
db: Session,
obj_in: dict,
*,
user: BxUserContext,
) -> BxWorkflowDef:
tenant_no = WorkflowService._tenant_no(db, user)
return BxCrudService.create(
BxWorkflowDef,
db,
obj_in,
tenant_no=tenant_no,
user_id=user.id,
dept_id=user.dept_id,
)
@staticmethod
def update(
db: Session,
workflow_id: str,
obj_in: dict,
*,
user: BxUserContext,
) -> Optional[BxWorkflowDef]:
tenant_no = WorkflowService._tenant_no(db, user)
return BxCrudService.update(
BxWorkflowDef,
db,
workflow_id,
tenant_no,
obj_in,
user_id=user.id,
)
@staticmethod
def soft_delete(db: Session, workflow_id: str, *, user: BxUserContext) -> bool:
tenant_no = WorkflowService._tenant_no(db, user)
deleted = BxCrudService.soft_delete(
BxWorkflowDef, db, workflow_id, tenant_no, user_id=user.id
)
if deleted:
db.query(BxWorkflowNode).filter(
BxWorkflowNode.wf_id == workflow_id,
BxWorkflowNode.tenant_no == tenant_no,
).update({"del_flag": 1}, synchronize_session=False)
db.flush()
return deleted
@staticmethod
def get_by_biz_type(db: Session, biz_type: str, tenant_no: str) -> Optional[BxWorkflowDef]:
return (
db.query(BxWorkflowDef)
.filter(
BxWorkflowDef.biz_type == biz_type,
BxWorkflowDef.tenant_no == tenant_no,
BxWorkflowDef.del_flag == 0,
)
.first()
)
@staticmethod
def ensure_by_biz_type(
db: Session,
biz_type: str,
*,
user: BxUserContext,
) -> Optional[BxWorkflowDef]:
"""按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。"""
tenant_no = WorkflowService._tenant_no(db, user)
workflow = WorkflowService.get_by_biz_type(db, biz_type, tenant_no)
if workflow:
return workflow
meta = _DEFAULT_WORKFLOWS.get(biz_type)
if not meta:
return None
workflow = WorkflowService.create(
db,
{
"biz_type": biz_type,
"wf_name": meta["wf_name"],
"remark": meta["remark"],
"status": 1,
},
user=user,
)
db.commit()
db.refresh(workflow)
return workflow
@staticmethod
def save_nodes(
db: Session,
wf_id: str,
nodes: List[dict],
*,
user: BxUserContext,
) -> List[BxWorkflowNode]:
tenant_no = WorkflowService._tenant_no(db, user)
workflow = WorkflowService.get_by_id(db, wf_id, tenant_no)
if not workflow:
raise ValueError("工作流不存在")
db.query(BxWorkflowNode).filter(
BxWorkflowNode.wf_id == wf_id,
BxWorkflowNode.tenant_no == tenant_no,
).delete(synchronize_session=False)
created: List[BxWorkflowNode] = []
for node_data in nodes:
data = dict(node_data)
data["id"] = generate_uuid()
data["wf_id"] = wf_id
data["tenant_no"] = tenant_no
data["create_by"] = user.id
data["create_dept_id"] = user.dept_id or ""
if data.get("approver_id") is not None:
data["approver_id"] = str(data["approver_id"]).strip() or None
node = BxWorkflowNode(**data)
db.add(node)
created.append(node)
db.flush()
db.expire(workflow, ["nodes"])
return created
+46
View File
@@ -0,0 +1,46 @@
"""报销单号/标题生成(从 bx-backend 迁移,增加 tenant_no 隔离)。"""
from datetime import datetime
from sqlalchemy import text
from sqlalchemy.orm import Session
def generate_apply_code(db: Session, prefix: str, tenant_no: str) -> str:
"""
生成顺序单号:{prefix}{YYYYMMDD}{3位序号}。
prefix 示例:BYJ(备用金)、BX(报销)。
"""
today = datetime.now().strftime("%Y%m%d")
like_pattern = f"{prefix}{today}%"
result = db.execute(
text(
"SELECT MAX(CAST(SUBSTRING(apply_code, -3) AS UNSIGNED)) FROM ("
"SELECT apply_code FROM bx_advance_apply "
"WHERE tenant_no = :tenant_no AND apply_code LIKE :p1 AND del_flag = 0 "
"UNION ALL "
"SELECT apply_code FROM bx_reimburse_apply "
"WHERE tenant_no = :tenant_no AND apply_code LIKE :p2 AND del_flag = 0"
") t"
),
{"tenant_no": tenant_no, "p1": like_pattern, "p2": like_pattern},
).scalar()
seq = (result or 0) + 1
return f"{prefix}{today}{seq:03d}"
def generate_title(db: Session, applicant_name: str, tenant_no: str) -> str:
today = datetime.now().strftime("%Y%m%d")
like_pattern = f"%报销单{today}%"
result = db.execute(
text(
"SELECT COUNT(*) FROM bx_reimburse_apply "
"WHERE tenant_no = :tenant_no AND title LIKE :p AND del_flag = 0"
),
{"tenant_no": tenant_no, "p": like_pattern},
).scalar()
seq = (result or 0) + 1
return f"{applicant_name}报销单{today}{seq:03d}"
+26
View File
@@ -0,0 +1,26 @@
"""报销模块日期范围工具(从 bx-backend 迁移)。"""
from __future__ import annotations
from datetime import datetime, timedelta
def current_month_bounds(now: datetime | None = None) -> tuple[datetime, datetime]:
"""当前自然月 [month_start, next_month_start)。"""
if now is None:
now = datetime.now()
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
if now.month == 12:
month_end = datetime(now.year + 1, 1, 1)
else:
month_end = datetime(now.year, now.month + 1, 1)
return month_start, month_end
def parse_create_time_day_bounds(apply_date: str | None) -> tuple[datetime, datetime] | None:
if not apply_date or not str(apply_date).strip():
return None
try:
day_start = datetime.strptime(str(apply_date).strip(), "%Y-%m-%d")
return day_start, day_start + timedelta(days=1)
except ValueError:
return None
+25 -1
View File
@@ -103,6 +103,30 @@
"101": "Community 101",
"102": "Community 102",
"103": "Community 103",
"104": "Community 104",
"105": "Community 105",
"106": "Community 106",
"107": "Community 107",
"108": "Community 108",
"109": "Community 109",
"113": "Community 113"
"110": "Community 110",
"111": "Community 111",
"112": "Community 112",
"113": "Community 113",
"114": "Community 114",
"115": "Community 115",
"116": "Community 116",
"117": "Community 117",
"118": "Community 118",
"119": "Community 119",
"120": "Community 120",
"121": "Community 121",
"122": "Community 122",
"123": "Community 123",
"124": "Community 124",
"125": "Community 125",
"126": "Community 126",
"127": "Community 127",
"128": "Community 128",
"129": "Community 129"
}
+249 -142
View File
@@ -1,16 +1,16 @@
# Graph Report - backend (2026-06-06)
## Corpus Check
- 122 files · ~93,527 words
- 149 files · ~105,173 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 2643 nodes · 18676 edges · 106 communities (103 shown, 3 thin omitted)
- Extraction: 45% EXTRACTED · 55% INFERRED · 0% AMBIGUOUS · INFERRED: 10230 edges (avg confidence: 0.51)
- 3047 nodes · 20600 edges · 130 communities (125 shown, 5 thin omitted)
- Extraction: 47% EXTRACTED · 53% INFERRED · 0% AMBIGUOUS · INFERRED: 10854 edges (avg confidence: 0.51)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `dacfdc9f`
- Built from commit: `f9676f87`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -112,94 +112,117 @@
- [[_COMMUNITY_Community 101|Community 101]]
- [[_COMMUNITY_Community 102|Community 102]]
- [[_COMMUNITY_Community 103|Community 103]]
- [[_COMMUNITY_Community 104|Community 104]]
- [[_COMMUNITY_Community 105|Community 105]]
- [[_COMMUNITY_Community 106|Community 106]]
- [[_COMMUNITY_Community 107|Community 107]]
- [[_COMMUNITY_Community 108|Community 108]]
- [[_COMMUNITY_Community 109|Community 109]]
- [[_COMMUNITY_Community 110|Community 110]]
- [[_COMMUNITY_Community 111|Community 111]]
- [[_COMMUNITY_Community 112|Community 112]]
- [[_COMMUNITY_Community 113|Community 113]]
- [[_COMMUNITY_Community 114|Community 114]]
- [[_COMMUNITY_Community 116|Community 116]]
- [[_COMMUNITY_Community 117|Community 117]]
- [[_COMMUNITY_Community 118|Community 118]]
- [[_COMMUNITY_Community 119|Community 119]]
- [[_COMMUNITY_Community 120|Community 120]]
- [[_COMMUNITY_Community 121|Community 121]]
- [[_COMMUNITY_Community 122|Community 122]]
- [[_COMMUNITY_Community 123|Community 123]]
- [[_COMMUNITY_Community 124|Community 124]]
- [[_COMMUNITY_Community 125|Community 125]]
- [[_COMMUNITY_Community 126|Community 126]]
- [[_COMMUNITY_Community 127|Community 127]]
- [[_COMMUNITY_Community 128|Community 128]]
- [[_COMMUNITY_Community 129|Community 129]]
## God Nodes (most connected - your core abstractions)
1. `Session` - 256 edges
2. `success_response()` - 246 edges
3. `Session` - 243 edges
4. `BaseModel` - 242 edges
1. `BaseModel` - 281 edges
2. `Session` - 256 edges
3. `success_response()` - 246 edges
4. `Session` - 243 edges
5. `error_response()` - 234 edges
6. `SysUser` - 180 edges
7. `get_tenant_no()` - 140 edges
8. `int` - 111 edges
9. `CustomerListBody` - 108 edges
10. `CustomerCreateBody` - 108 edges
7. `get_tenant_no()` - 170 edges
8. `generate_uuid()` - 114 edges
9. `int` - 111 edges
10. `CustomerListBody` - 108 edges
## Surprising Connections (you probably didn't know these)
- `bool` --uses--> `SysUser` [INFERRED]
app/crud/sample_code.py → app/models/models.py
- `date` --uses--> `CrmContact` [INFERRED]
app/crud/crm/contact.py → app/models/crm_models.py
- `bool` --uses--> `CrmInvoiceInfo` [INFERRED]
app/crud/crm/invoice_info.py → app/models/crm_models.py
- `pool_recovery_job()` --calls--> `execute_pool_rules()` [INFERRED]
app/tasks/customer_pool.py → app/crud/crm/pool_rule.py
- `Session` --uses--> `CrmCustomer` [INFERRED]
app/tasks/customer_pool.py → app/models/crm_models.py
- `Any` --uses--> `SysApiRequestLog` [INFERRED]
app/core/api_request_log_middleware.py → app/models/models.py
- `bytes` --uses--> `SysApiRequestLog` [INFERRED]
app/core/api_request_log_middleware.py → app/models/models.py
- `bool` --uses--> `SysApiRequestLog` [INFERRED]
app/core/api_request_log_middleware.py → app/models/models.py
- `UploadFile` --uses--> `BxUserContext` [INFERRED]
app/api/bx/attachment.py → app/core/bx_auth.py
- `int` --uses--> `BxUserContext` [INFERRED]
app/api/bx/workflow.py → app/core/bx_auth.py
## Communities (106 total, 3 thin omitted)
## Communities (130 total, 5 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.15
Nodes (42): Any, int, Session, str, Any, bool, CRM 公海池规则 CRUD + 规则执行逻辑, int (+34 more)
Cohesion: 0.25
Nodes (27): Any, bool, CRM 公海池规则 CRUD + 规则执行逻辑, int, Session, str, generate_uuid(), create_pool_rule() (+19 more)
### Community 1 - "Community 1"
Cohesion: 0.09
Nodes (84): Any, ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, Any (+76 more)
Cohesion: 0.14
Nodes (28): Any, Any, bool, str, WfProcessInstance, Any, bool, Session (+20 more)
### Community 2 - "Community 2"
Cohesion: 0.22
Nodes (11): Any, bool, int, Session, str, bool, camel_to_snake(), CRUDBase (+3 more)
Cohesion: 0.23
Nodes (10): Any, bool, int, Session, str, camel_to_snake(), CRUDBase, get_model_attr() (+2 more)
### Community 3 - "Community 3"
Cohesion: 0.13
Nodes (15): Any, bool, int, str, get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.html, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total_amo (+7 more)
### Community 4 - "Community 4"
Cohesion: 0.20
Nodes (29): bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), Session, str, barcode_exists(), category_code_exists(), _category_row_to_dict(), create_category() (+21 more)
Cohesion: 0.14
Nodes (41): Any, bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), int, Session, str, barcode_exists(), _build_category_tree() (+33 more)
### Community 5 - "Community 5"
Cohesion: 0.09
Nodes (33): Any, bool, bytes, int, str, Exception, bool, bytes (+25 more)
Cohesion: 0.14
Nodes (5): bool, BaseStorage, OSSStorage, QiniuStorage, 文件存储工具模块 支持多种存储方式: 1. 本地存储 (local) - 默认 2. 七牛云存储 (qiniu) - 推荐生产环境使用 3. 阿里云OSS (
### Community 6 - "Community 6"
Cohesion: 0.09
Nodes (94): attachment_download_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post(), create_role_management() (+86 more)
Cohesion: 0.11
Nodes (72): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post(), create_role_management(), create_site() (+64 more)
### Community 7 - "Community 7"
Cohesion: 0.22
Nodes (9): create_new_version(), Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id (+1 more)
### Community 8 - "Community 8"
Cohesion: 0.15
Nodes (34): Session, str, Any, bool, int, Session, str, apply_data_scope() (+26 more)
Cohesion: 0.11
Nodes (50): Session, str, Any, bool, int, Session, str, Any (+42 more)
### Community 9 - "Community 9"
Cohesion: 0.33
Nodes (15): Any, int, Session, str, BizLeaveRequest, create_leave(), get_leave(), list_leaves() (+7 more)
### Community 10 - "Community 10"
Cohesion: 0.09
Nodes (56): register_contract_sub_routes(), Any, bool, datetime, int, Session, str, _advance_contract_after_approval() (+48 more)
Cohesion: 0.08
Nodes (68): Any, bool, CrmContract, datetime, int, Query, Session, str (+60 more)
### Community 11 - "Community 11"
Cohesion: 0.05
Nodes (168): _apply_default_invoice_info_to_customers(), category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate() (+160 more)
Nodes (167): _apply_default_invoice_info_to_customers(), category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate() (+159 more)
### Community 12 - "Community 12"
Cohesion: 0.06
Nodes (68): str, CRM 客户管理 Pydantic Schemas, BaseModel, PageResult, ResponseModel, ContactByCustomerBody, ContactDetailBody, ContactSetPrimaryBody (+60 more)
Cohesion: 0.15
Nodes (52): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, Any, int (+44 more)
### Community 13 - "Community 13"
Cohesion: 0.36
Nodes (213): Any, bool, str, UploadFile, CRM 线索管理 Pydantic Schemas, CRM 商机管理 Pydantic Schemas, CRM 客户公海池规则 Pydantic Schemas, CRM 产品模块 Pydantic Schemas (+205 more)
### Community 14 - "Community 14"
Cohesion: 0.18
Nodes (39): Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作), CrmCustomer, date, datetime, float, int (+31 more)
Cohesion: 0.17
Nodes (45): Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作), CrmCustomer, date, datetime, float, int (+37 more)
### Community 15 - "Community 15"
Cohesion: 0.08
@@ -210,16 +233,16 @@ Cohesion: 0.12
Nodes (31): CRM 报价单 API 路由 — 挂接到 crm.router, CRM 报价单 Pydantic Schemas, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody, QuoteApprovalRefreshNamesBody, QuoteApprovalWithdrawBody, QuoteCreateBody (+23 more)
### Community 17 - "Community 17"
Cohesion: 0.26
Nodes (22): Any, bool, datetime, int, Session, str, BizSampleCode, batch_generate_sample_codes() (+14 more)
Cohesion: 0.11
Nodes (59): BxUserContext, int, Session, str, BxUserContext, int, Session, str (+51 more)
### Community 18 - "Community 18"
Cohesion: 0.17
Nodes (34): Any, bool, int, Session, str, SysUser, create_user(), get_user() (+26 more)
### Community 19 - "Community 19"
Cohesion: 0.35
Nodes (9): check_customer_protect_time(), _execute_single_rule(), _get_all_tenant_nos(), pool_recovery_job(), 公海池自动回收任务(由 APScheduler 调度) 遍历所有租户的启用规则,执行自动回收。, 每天凌晨1点:检查保护期到期客户,跟进次数不足则退回公海, 每天早上9点:保护期到期提醒(3天内到期且跟进不足), remind_customer_recycle() (+1 more)
Cohesion: 0.23
Nodes (21): Any, bool, CRM 供方信息 CRUD — 对照 CrmSupplierInfoServiceImpl.java, int, Session, str, _apply_list_filters(), create_supplier_info() (+13 more)
### Community 20 - "Community 20"
Cohesion: 0.37
@@ -230,12 +253,12 @@ Cohesion: 0.34
Nodes (16): Any, int, Session, str, _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept() (+8 more)
### Community 22 - "Community 22"
Cohesion: 0.07
Nodes (48): 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export (+40 more)
Cohesion: 0.18
Nodes (19): str, Any, bool, Session, str, StreamingResponse, CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _yes_no() (+11 more)
### Community 23 - "Community 23"
Cohesion: 0.09
Nodes (23): download_contract_pdf(), export_contracts(), get_contract_detail(), ORM → camelCase(单点真相), ORM → camelCase(单点真相), ORM → camelCase(单点真相), ORM → camelCase(单点真相), ORM → camelCase(单点真相) (+15 more)
Cohesion: 0.13
Nodes (17): CrmContract, contract_row_to_dict(), _parse_letter_oss_ids_from_tier(), Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, ORM → camelCase(单点真相) (+9 more)
### Community 24 - "Community 24"
Cohesion: 0.29
@@ -250,12 +273,12 @@ Cohesion: 0.27
Nodes (11): Any, bool, str, decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。 (+3 more)
### Community 27 - "Community 27"
Cohesion: 0.26
Nodes (20): Any, bool, int, Session, str, create_follow(), _enrich_follow_dict(), _follow_row_to_dict() (+12 more)
Cohesion: 0.16
Nodes (31): Any, bool, int, Session, str, Any, Session, str (+23 more)
### Community 28 - "Community 28"
Cohesion: 0.26
Nodes (19): Any, bool, date, int, Session, str, _contact_row_to_dict(), create_contact() (+11 more)
Cohesion: 0.23
Nodes (34): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, BxUserContext, int, Session, str (+26 more)
### Community 29 - "Community 29"
Cohesion: 0.32
@@ -266,76 +289,76 @@ Cohesion: 0.40
Nodes (3): BaseSettings, Config, Settings
### Community 36 - "Community 36"
Cohesion: 0.17
Nodes (32): Any, int, Session, str, Any, bool, int, Session (+24 more)
Cohesion: 0.27
Nodes (14): Any, bool, bytes, int, str, ApiCryptoError, _b64url_decode(), _b64url_encode() (+6 more)
### Community 37 - "Community 37"
Cohesion: 0.19
Nodes (17): Any, int, str, BusinessException, InvalidStatusTransitionException, log_api_request(), log_delivery_operation(), log_operation() (+9 more)
Cohesion: 0.08
Nodes (46): str, ContractApprovalPageListBody, 全局审批记录分页(approval/index.vue), CustomerFollowByCustomerBody, CustomerFollowDetailBody, BaseModel, AttachmentUploadResponse, ChangePasswordRequest (+38 more)
### Community 38 - "Community 38"
Cohesion: 0.40
Nodes (4): str, Config, format_api_error_message(), 将 HTTP/SQLAlchemy 等异常转为可展示的错误文案(避免 str([]) 空列表)。
Cohesion: 0.26
Nodes (21): approve_task(), _business_type_label(), create_process_def(), delete_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields(), get_designer_data() (+13 more)
### Community 39 - "Community 39"
Cohesion: 0.20
Nodes (26): Any, bool, Decimal, int, Session, str, batch_insert_quote_products(), calculate_subtotal() (+18 more)
### Community 40 - "Community 40"
Cohesion: 0.22
Nodes (33): Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作), date, datetime, int, Session, str (+25 more)
Cohesion: 0.10
Nodes (60): Any, bool, int, Session, str, Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作) (+52 more)
### Community 41 - "Community 41"
Cohesion: 0.11
Nodes (19): 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll (+11 more)
Cohesion: 0.17
Nodes (22): BxUserContext, Session, str, str, bool, BxUserContext, Session, str (+14 more)
### Community 42 - "Community 42"
Cohesion: 0.34
Nodes (16): Any, bool, int, Session, str, create_dict_type(), dict_type_code_exists(), get_dict_type() (+8 more)
### Community 43 - "Community 43"
Cohesion: 0.23
Nodes (16): bool, int, Redis, Request, str, _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client() (+8 more)
Cohesion: 0.30
Nodes (11): bool, BxUserContext, BxWorkflowNode, int, Session, str, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, WorkflowService (+3 more)
### Community 44 - "Community 44"
Cohesion: 0.16
Nodes (21): _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin (+13 more)
Cohesion: 0.22
Nodes (16): Any, bool, bytes, int, Request, str, _claims_from_auth(), _format_body_bytes() (+8 more)
### Community 45 - "Community 45"
Cohesion: 0.08
Nodes (76): register_contract_routes(), bool, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), Session, str, batch_assign_owner(), batch_submit_approval(), _build_customer_address() (+68 more)
Cohesion: 0.29
Nodes (34): AdvanceIssueRequest, BxUserContext, int, ReimburseApplyOut, Session, str, str, BatchOffsetRequest (+26 more)
### Community 46 - "Community 46"
Cohesion: 0.17
Nodes (23): Any, bool, bytes, int, Request, str, bool, int (+15 more)
Cohesion: 0.05
Nodes (83): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), public_quote_share(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线) (+75 more)
### Community 47 - "Community 47"
Cohesion: 0.17
Nodes (26): API 路由包 — 山峡化工生产一体化门户, get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单), approve_task(), _business_type_label(), create_process_def(), delete_process_def(), _enrich_task_list() (+18 more)
Cohesion: 0.20
Nodes (25): Any, bool, BxUserContext, BxWorkflowNode, float, int, ReimburseApplyOut, Session (+17 more)
### Community 48 - "Community 48"
Cohesion: 0.07
Nodes (51): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, CRM 合同子模块路由 — 变更/模板/审批独立页, CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody, ContractApprovalListBody (+43 more)
Nodes (52): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody (+44 more)
### Community 49 - "Community 49"
Cohesion: 0.44
Nodes (11): Any, Session, str, get_follow_statistics(), get_industry_statistics(), get_level_statistics(), get_overview_statistics(), get_owner_statistics() (+3 more)
Cohesion: 0.29
Nodes (29): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, MenuCreateBody (+21 more)
### Community 50 - "Community 50"
Cohesion: 0.19
Nodes (20): operlog_detail(), operlog_list(), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), Session, Any, int, Session, str (+12 more)
Cohesion: 0.14
Nodes (22): _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin (+14 more)
### Community 51 - "Community 51"
Cohesion: 0.16
Nodes (13): float, int, str, 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import WechatMes, 发送订单状态通知 Args: openid: 用户openid order_n, 发送支付成功通知 Args: openid: 用户openid order_n, 发送配送通知 Args: openid: 配送员openid task_no:, 发送配送时效预警 Args: openid: 配送员openid task_n (+5 more)
### Community 52 - "Community 52"
Cohesion: 0.37
Nodes (13): create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave(), update_leave() (+5 more)
Cohesion: 0.33
Nodes (6): int, Config, LeaveCreate, LeaveResponse, LeaveUpdate, Pydantic schemas - 请假申请
### Community 53 - "Community 53"
Cohesion: 0.29
Nodes (7): batch_submit_contract_approval(), Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval
Cohesion: 0.28
Nodes (20): Any, bool, date, int, Session, str, _contact_row_to_dict(), create_contact() (+12 more)
### Community 54 - "Community 54"
Cohesion: 0.34
@@ -351,7 +374,7 @@ Nodes (10): Any, Request, str, str, parse_admin_request_json(), 运营 Web(/we
### Community 57 - "Community 57"
Cohesion: 0.11
Nodes (26): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), public_quote_share(), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。 (+18 more)
Nodes (19): 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll (+11 more)
### Community 59 - "Community 59"
Cohesion: 0.11
@@ -362,8 +385,8 @@ Cohesion: 0.19
Nodes (32): Any, bool, CrmContractProductPriceTier, date, Decimal, int, Session, str (+24 more)
### Community 61 - "Community 61"
Cohesion: 0.17
Nodes (30): register_quote_routes(), bool, int, Session, str, accept_quote(), generate_pdf(), generate_share() (+22 more)
Cohesion: 0.11
Nodes (27): API 路由包 — 山峡化工生产一体化门户, create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave() (+19 more)
### Community 62 - "Community 62"
Cohesion: 0.35
@@ -371,51 +394,51 @@ Nodes (17): Any, bool, int, Session, str, _build_tree(), _cascade_soft_delete(),
### Community 63 - "Community 63"
Cohesion: 0.17
Nodes (29): Any, bool, int, Session, str, Query, approve_quote(), can_user_approve() (+21 more)
Nodes (30): Any, bool, int, Session, str, date, Query, approve_quote() (+22 more)
### Community 65 - "Community 65"
Cohesion: 0.12
Nodes (16): invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价) (+8 more)
### Community 68 - "Community 68"
Cohesion: 0.27
Nodes (18): Any, bool, int, Session, str, create_clue_follow(), delete_clue_follow(), get_clue_follow() (+10 more)
Cohesion: 0.33
Nodes (14): BxUserContext, Session, str, UploadFile, AttachmentOut, bind_biz(), delete_attachment(), download_file() (+6 more)
### Community 69 - "Community 69"
Cohesion: 0.19
Nodes (45): Any, CRM 商机 CRUD(主表 + 阶段/赢输单/统计/看板/漏斗), date, datetime, Decimal, int, Session, str (+37 more)
### Community 70 - "Community 70"
Cohesion: 0.20
Nodes (28): 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), refresh_token(), bool, Redis, Session, str, create_access_token(), create_refresh_token() (+20 more)
Cohesion: 0.33
Nodes (14): Any, int, Session, str, create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options() (+6 more)
### Community 71 - "Community 71"
Cohesion: 0.14
Nodes (17): Any, int, _build_category_tree(), _collect_category_with_ancestors(), list_category_tree(), list_products(), _product_row_to_dict(), 获取产品分类树(嵌套 children,供树形表格/树选择器使用) (+9 more)
Cohesion: 0.40
Nodes (5): _product_row_to_dict(), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名)
### Community 72 - "Community 72"
Cohesion: 0.38
Nodes (15): Any, bool, int, Session, str, create_post(), get_post(), list_posts() (+7 more)
Nodes (15): Any, bool, int, Session, str, create_site(), get_site(), list_sites() (+7 more)
### Community 73 - "Community 73"
Cohesion: 0.24
Nodes (12): Session, str, iter_user_menu_perm_codes(), _map_role_name_to_web(), Web 管理端登录:基于 V0.3 sys_user / sys_user_role / sys_role 的角色与菜单权限解析, 将 sys_role.role_name 映射为 web-admin 使用的 role 字符串。, 根据用户关联角色解析 Web 端 role(admin / super_admin)。, 根据用户关联角色解析 Web 端 role(admin / super_admin)。 (+4 more)
Cohesion: 0.33
Nodes (6): bx_health(), P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。
### Community 74 - "Community 74"
Cohesion: 0.07
Nodes (46): Decimal, _apply_contract_entity_fields(), _apply_contract_scalar_fields(), _build_contract_filters(), _calc_amount_with_tax(), create_contract(), create_from_opportunity(), create_from_quote() (+38 more)
Cohesion: 0.08
Nodes (99): register_contract_routes(), Any, bool, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), Decimal, int, Session, str (+91 more)
### Community 75 - "Community 75"
Cohesion: 0.16
Nodes (21): Any, bool, Session, str, Java: viewByShareCode, Java: viewByShareCode, Java: viewByShareCode, Java: viewByShareCode (+13 more)
### Community 76 - "Community 76"
Cohesion: 0.34
Nodes (18): Any, bool, datetime, int, Session, str, create_opportunity_follow(), get_opportunity_follow() (+10 more)
Cohesion: 0.35
Nodes (19): Any, bool, datetime, int, Session, str, create_opportunity_follow(), get_opportunity_follow() (+11 more)
### Community 78 - "Community 78"
Cohesion: 0.11
Nodes (42): Any, CRM 报价单 CRUD — 对照 CrmQuoteServiceImpl.java(P2 全量), date, datetime, Decimal, _build_quote_filters(), calculate_quote_amount(), create_quote() (+34 more)
Cohesion: 0.42
Nodes (13): Any, bool, Session, str, _clear_default_for_customer(), create_invoice_info(), get_invoice_info(), list_invoice_info_by_customer() (+5 more)
### Community 79 - "Community 79"
Cohesion: 0.22
@@ -426,16 +449,16 @@ Cohesion: 0.22
Nodes (9): generate_contract_no(), Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} (+1 more)
### Community 81 - "Community 81"
Cohesion: 0.60
Nodes (10): Any, Session, str, _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics() (+2 more)
Cohesion: 0.22
Nodes (18): bytes, int, str, download_file_bytes(), file_stream(), get_access_url(), get_download_url(), get_preview_url() (+10 more)
### Community 82 - "Community 82"
Cohesion: 0.33
Nodes (11): SysOss, int, Session, str, SysOss, create_oss(), _oss_row_to_dict(), create_oss() (+3 more)
### Community 83 - "Community 83"
Cohesion: 0.18
Nodes (31): CrmContract, Query, Any, CrmContract, CrmContractProductPriceTier, date, datetime, int (+23 more)
Cohesion: 0.14
Nodes (14): _build_contract_filters(), export_contracts(), Java: queryList for export, Java: queryList for export, Java: queryList for export, Java: queryList for export, Java: buildQueryWrapper, Java: queryList for export (+6 more)
### Community 84 - "Community 84"
Cohesion: 0.33
@@ -446,16 +469,16 @@ Cohesion: 0.11
Nodes (19): 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list (+11 more)
### Community 86 - "Community 86"
Cohesion: 0.20
Nodes (10): 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo (+2 more)
Cohesion: 0.30
Nodes (8): Any, bool, int, Session, str, BxCrudService, 报销模块 Service 基类 — 租户隔离 + UUID + 软删除。, M
### Community 87 - "Community 87"
Cohesion: 0.20
Nodes (10): 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove, 删除供方 — Java: CrmSupplierInfoController.remove (+2 more)
### Community 88 - "Community 88"
Cohesion: 0.33
Nodes (10): Any, int, Session, str, list_ethnics(), 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 民族码表 sys_ethnic(国标 GB/T 3304,全局) (+2 more)
Cohesion: 0.21
Nodes (16): Any, int, str, BusinessException, InvalidStatusTransitionException, log_api_request(), log_delivery_operation(), log_operation() (+8 more)
### Community 89 - "Community 89"
Cohesion: 0.25
@@ -474,71 +497,155 @@ Cohesion: 0.29
Nodes (7): Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, renew_contract()
### Community 93 - "Community 93"
Cohesion: 0.42
Nodes (13): Any, bool, Session, str, _clear_default_for_customer(), create_invoice_info(), get_invoice_info(), list_invoice_info_by_customer() (+5 more)
Cohesion: 0.26
Nodes (22): Any, bool, datetime, int, Session, str, BizSampleCode, batch_generate_sample_codes() (+14 more)
### Community 94 - "Community 94"
Cohesion: 0.17
Nodes (21): Decimal, str, Any, str, derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。 (+13 more)
Cohesion: 0.18
Nodes (20): Decimal, str, Any, str, derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。 (+12 more)
### Community 95 - "Community 95"
Cohesion: 0.13
Nodes (15): generate_auto_product_no(), _get_quote_item_field(), Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: resolveProductIdForQuoteItem(CrmProduct 无 tax_rate,跳过税率匹配), Java: resolveProductIdForQuoteItem(CrmProduct 无 tax_rate,跳过税率匹配), Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: generateAutoProductNo — SXWL + 8位随机大写字符 (+7 more)
### Community 96 - "Community 96"
Cohesion: 0.18
Nodes (19): datetime, int, str, datetime, int, Session, str, BizNoKind (+11 more)
Cohesion: 0.21
Nodes (14): Any, str, bool, BxUserContext, int, Session, str, AttachmentService (+6 more)
### Community 97 - "Community 97"
Cohesion: 0.22
Nodes (9): Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview, Java: statisticsOverview (+1 more)
Cohesion: 0.10
Nodes (61): register_quote_routes(), Any, bool, CRM 报价单 CRUD — 对照 CrmQuoteServiceImpl.java(P2 全量), int, Session, str, accept_quote() (+53 more)
### Community 98 - "Community 98"
Cohesion: 0.22
Nodes (9): Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend, Java: statisticsTrend (+1 more)
Cohesion: 0.26
Nodes (9): Exception, bytes, str, bytes, BytesIO, convert_heic_to_jpeg(), process_image_content(), 将 HEIC/HEIF 格式转换为 JPEG 格式 (+1 more)
### Community 99 - "Community 99"
Cohesion: 0.29
Nodes (7): _parse_letter_oss_ids_from_tier(), Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, Java: validateFrameworkContractForSubmitApproval, validate_framework_contract_for_submit_approval()
Cohesion: 0.20
Nodes (10): 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo (+2 more)
### Community 100 - "Community 100"
Cohesion: 0.40
Nodes (5): bool, str, get_password_hash(), 验证密码(哈希非 bcrypt 或损坏时返回 False,避免抛错), verify_password()
Cohesion: 0.36
Nodes (9): Any, int, Session, str, get_pool_record(), list_pool_records(), _row_to_dict(), total_pages() (+1 more)
### Community 101 - "Community 101"
Cohesion: 0.40
Nodes (5): list_roles_for_designer(), 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID
Cohesion: 0.18
Nodes (15): Any, int, Session, str, Base, list_ethnics(), 民族码表 CRUD(全局 sys_ethnic), _row_to_dict() (+7 more)
### Community 102 - "Community 102"
Cohesion: 0.22
Nodes (9): Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus (+1 more)
### Community 103 - "Community 103"
Cohesion: 0.67
Nodes (3): Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), web_login_rsa_public_key()
Cohesion: 0.22
Nodes (17): bool, int, Redis, Request, str, _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client() (+9 more)
### Community 104 - "Community 104"
Cohesion: 0.14
Nodes (25): Any, bool, BxUserContext, BxWorkflowNode, float, int, Session, str (+17 more)
### Community 105 - "Community 105"
Cohesion: 0.32
Nodes (10): datetime, int, str, 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, format_datetime(), generate_random_password(), get_current_time(), get_file_size_display() (+2 more)
### Community 106 - "Community 106"
Cohesion: 0.29
Nodes (10): datetime, int, Session, str, BizNoKind, allocate_next_biz_no(), _format_biz_no(), _prefix_for() (+2 more)
### Community 107 - "Community 107"
Cohesion: 0.29
Nodes (7): batch_submit_contract_approval(), Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval, Java: batchSubmitApproval
### Community 108 - "Community 108"
Cohesion: 0.18
Nodes (11): batch_assign_owner(), download_contract_pdf(), Java: batchAssignOwner, Java: downloadContractPdf — 返回合同文件 URL/路径, Java: batchAssignOwner, Java: batchAssignOwner, Java: batchAssignOwner, Java: batchAssignOwner (+3 more)
### Community 109 - "Community 109"
Cohesion: 0.40
Nodes (4): str, 附件上传 bizType 与 MinIO 目录映射(对齐原 CRM oss 上传语义), 优先使用显式 folder;否则按 bizType 映射默认目录。, resolve_upload_folder()
Cohesion: 0.33
Nodes (5): str, 附件上传 bizType 与 MinIO 目录映射(对齐原 CRM oss 上传语义), 优先使用显式 folder;否则按 bizType 映射默认目录。, 优先使用显式 folder;否则按 bizType 映射默认目录。, resolve_upload_folder()
### Community 111 - "Community 111"
Cohesion: 0.18
Nodes (5): ContactByCustomerBody, ContactDetailBody, ContactSetPrimaryBody, CRM 联系人 Pydantic Schemas, SoftDeleteModel
### Community 112 - "Community 112"
Cohesion: 0.29
Nodes (7): create_from_opportunity(), _opportunity_products_to_contract_products(), Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id
### Community 113 - "Community 113"
Cohesion: 0.18
Nodes (10): Session, str, _auto_fill_create_dept_id(), get_db(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID, get_user_menu_codes(), 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。 (+2 more)
Cohesion: 0.35
Nodes (9): check_customer_protect_time(), _execute_single_rule(), _get_all_tenant_nos(), pool_recovery_job(), 公海池自动回收任务(由 APScheduler 调度) 遍历所有租户的启用规则,执行自动回收。, 每天凌晨1点:检查保护期到期客户,跟进次数不足则退回公海, 每天早上9点:保护期到期提醒(3天内到期且跟进不足), remind_customer_recycle() (+1 more)
### Community 114 - "Community 114"
Cohesion: 0.29
Nodes (7): Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, withdraw_contract_approval()
### Community 116 - "Community 116"
Cohesion: 0.25
Nodes (8): create_from_quote(), _quote_products_to_contract_products(), Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id
### Community 117 - "Community 117"
Cohesion: 0.33
Nodes (6): datetime, str, _dt_to_str(), PagedQuery, PagedResponse, bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。
### Community 118 - "Community 118"
Cohesion: 0.09
Nodes (27): datetime, Decimal, calculate_quote_amount(), _decimal_value(), _parse_optional_datetime(), _period_key(), Java: statisticsOverview, Java: statisticsOverview (+19 more)
### Community 119 - "Community 119"
Cohesion: 0.29
Nodes (4): int, MinIOStorage, MinIO 预签名下载 URL,携带原始文件名。, 带下载文件名的访问 URL;默认实现为普通访问地址。
### Community 120 - "Community 120"
Cohesion: 0.33
Nodes (6): Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, submit_change_approval()
### Community 121 - "Community 121"
Cohesion: 0.44
Nodes (8): bool, int, Session, str, ApiRequestLogCRUD, SysApiRequestLog, SysApiRequestLogHis, SysApiRequestLog
### Community 122 - "Community 122"
Cohesion: 0.38
Nodes (11): Any, int, Session, str, _business_type_from_path(), get_oper_log(), list_oper_logs(), _oper_status() (+3 more)
### Community 123 - "Community 123"
Cohesion: 0.20
Nodes (10): 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export, 导出供方列表 — Java: CrmSupplierInfoController.export (+2 more)
### Community 124 - "Community 124"
Cohesion: 0.29
Nodes (6): _coerce_optional_id(), WorkflowDefCreate, WorkflowDefOut, WorkflowDefUpdate, WorkflowNodeCreate, WorkflowNodeOut
### Community 125 - "Community 125"
Cohesion: 0.29
Nodes (6): CustomerPoolDetailBody, CustomerPoolListBody, PoolCustomerListBody, PoolRecordDetailBody, PoolRecordListBody, CRM 客户公海池记录 Pydantic Schemas
### Community 127 - "Community 127"
Cohesion: 0.50
Nodes (3): Config, PageResult, ResponseModel
### Community 128 - "Community 128"
Cohesion: 0.67
Nodes (3): str, format_api_error_message(), 将 HTTP/SQLAlchemy 等异常转为可展示的错误文案(避免 str([]) 空列表)。
### Community 129 - "Community 129"
Cohesion: 0.33
Nodes (5): CRM 客户管理 Pydantic Schemas, CustomerAssignPoolBody, CustomerCheckDuplicateBody, CustomerDetailBody, CRM 客户管理 CRUD(客户 + 联系人 + 跟进记录)
## Knowledge Gaps
- **42 isolated node(s):** `bool`, `bool`, `Config`, `bool`, `bool` (+37 more)
- **52 isolated node(s):** `bool`, `bool`, `Config`, `bool`, `bool` (+47 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **3 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **5 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 5`, `Community 6`, `Community 8`, `Community 10`, `Community 14`, `Community 17`, `Community 18`, `Community 36`, `Community 40`, `Community 45`, `Community 47`, `Community 57`, `Community 61`, `Community 63`, `Community 69`, `Community 70`, `Community 74`, `Community 78`, `Community 83`, `Community 96`?**
_High betweenness centrality (0.148) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 0` to `Community 1`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 14`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 27`, `Community 28`, `Community 36`, `Community 39`, `Community 40`, `Community 42`, `Community 45`, `Community 54`, `Community 55`, `Community 57`, `Community 60`, `Community 62`, `Community 63`, `Community 68`, `Community 69`, `Community 70`, `Community 72`, `Community 74`, `Community 75`, `Community 76`, `Community 78`, `Community 82`, `Community 83`, `Community 93`, `Community 95`, `Community 96`?**
_High betweenness centrality (0.113) - this node is a cross-community bridge._
- **Why does `error_response()` connect `Community 11` to `Community 1`, `Community 5`, `Community 6`, `Community 10`, `Community 16`, `Community 22`, `Community 36`, `Community 38`, `Community 41`, `Community 43`, `Community 45`, `Community 47`, `Community 48`, `Community 50`, `Community 52`, `Community 57`, `Community 59`, `Community 61`, `Community 65`, `Community 70`, `Community 85`, `Community 86`, `Community 87`?**
_High betweenness centrality (0.088) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 0` to `Community 4`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 14`, `Community 18`, `Community 19`, `Community 21`, `Community 27`, `Community 39`, `Community 40`, `Community 42`, `Community 43`, `Community 46`, `Community 47`, `Community 53`, `Community 54`, `Community 55`, `Community 60`, `Community 62`, `Community 63`, `Community 69`, `Community 70`, `Community 72`, `Community 74`, `Community 75`, `Community 76`, `Community 78`, `Community 82`, `Community 86`, `Community 93`, `Community 95`, `Community 97`, `Community 100`, `Community 104`, `Community 105`, `Community 106`, `Community 113`?**
_High betweenness centrality (0.111) - this node is a cross-community bridge._
- **Why does `SysUser` connect `Community 101` to `Community 1`, `Community 6`, `Community 8`, `Community 10`, `Community 12`, `Community 14`, `Community 18`, `Community 23`, `Community 38`, `Community 40`, `Community 44`, `Community 46`, `Community 49`, `Community 63`, `Community 69`, `Community 74`, `Community 93`, `Community 97`, `Community 100`, `Community 105`, `Community 118`?**
_High betweenness centrality (0.101) - this node is a cross-community bridge._
- **Why does `BaseModel` connect `Community 37` to `Community 129`, `Community 12`, `Community 13`, `Community 16`, `Community 17`, `Community 28`, `Community 41`, `Community 45`, `Community 47`, `Community 48`, `Community 49`, `Community 52`, `Community 61`, `Community 68`, `Community 101`, `Community 111`, `Community 117`, `Community 124`, `Community 125`, `Community 127`?**
_High betweenness centrality (0.092) - this node is a cross-community bridge._
- **Are the 28 inferred relationships involving `str` (e.g. with `CRUDBase` and `LeaveCreate`) actually correct?**
_`str` has 28 INFERRED edges - model-reasoned connections that need verification._
- **Are the 100 inferred relationships involving `Session` (e.g. with `ClueAssignBody` and `ClueConvertBody`) actually correct?**
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "label": "attachment.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L1"}, {"id": "bx_attachment_attachmentservice", "label": "AttachmentService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L14"}, {"id": "bx_attachment_attachmentservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L16"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L16"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L16"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L16"}, {"id": "bx_attachment_attachmentservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L20"}, {"id": "bxattachment", "label": "BxAttachment", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L20"}, {"id": "bx_attachment_attachmentservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L24"}, {"id": "bx_attachment_attachmentservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L41"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L41"}, {"id": "bx_attachment_attachmentservice_get_by_biz", "label": ".get_by_biz()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L48"}, {"id": "bx_attachment_attachmentservice_bind_biz", "label": ".bind_biz()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L58"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L58"}, {"id": "bx_attachment_attachmentservice_count_invoice_attach_by_biz_ids", "label": ".count_invoice_attach_by_biz_ids()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L89"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_attachment_py", "target": "app_models_bx_models", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py", "sourceLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_coerce_optional_id", "label": "_coerce_optional_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9"}, {"id": "bx_advance_advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_advance_advanceapplycreate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L27"}, {"id": "bx_advance_advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L31"}, {"id": "bx_advance_advanceapplyupdate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L46"}, {"id": "bx_advance_advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L69"}, {"id": "bx_advance_advanceapprovalout", "label": "AdvanceApprovalOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L76"}, {"id": "bx_advance_advancemystatsout", "label": "AdvanceMyStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L89"}, {"id": "bx_advance_advanceapproverpendingstatsout", "label": "AdvanceApproverPendingStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L104"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "bx_advance_coerce_optional_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "bx_advance_advanceapplycreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L16", "weight": 1.0}, {"source": "bx_advance_advanceapplycreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L16", "weight": 1.0}, {"source": "bx_advance_advanceapplycreate", "target": "bx_advance_advanceapplycreate_validate_expense_item_id", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L27", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bx_advance_fill_offset_amounts", "label": "_fill_offset_amounts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "bx_advance_list_advances", "label": "list_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_my_advances", "label": "my_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L77"}, {"id": "bx_advance_my_advance_stats", "label": "my_advance_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L114"}, {"id": "bx_advance_advance_status_counts", "label": "advance_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L124"}, {"id": "bx_advance_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L133"}, {"id": "bx_advance_advance_pending_approval_stats", "label": "advance_pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L159"}, {"id": "bx_advance_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L169"}, {"id": "bx_advance_get_advance", "label": "get_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L195"}, {"id": "bx_advance_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L211"}, {"id": "bx_advance_get_approval_history", "label": "get_approval_history()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L224"}, {"id": "bx_advance_create_advance", "label": "create_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L237"}, {"id": "advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L237"}, {"id": "bx_advance_update_advance", "label": "update_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L270"}, {"id": "advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L270"}, {"id": "bx_advance_submit_advance", "label": "submit_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L291"}, {"id": "bx_advance_approve_advance", "label": "approve_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L305"}, {"id": "advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L305"}, {"id": "bx_advance_reject_advance", "label": "reject_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09Pydantic \u6a21\u578b \u2014 \u54cd\u5e94\u534f\u8bae\u4e0e bx \u539f\u7cfb\u7edf\u4fdd\u6301\u4e00\u81f4", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/__init__.py", "source_location": "L1"}], "edges": [{"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/__init__.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bx_reimburse_build_details_data", "label": "_build_details_data()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L33"}, {"id": "bx_reimburse_get_generated_title", "label": "get_generated_title()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L54"}, {"id": "bx_reimburse_list_reimburses", "label": "list_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "bx_reimburse_my_reimburses", "label": "my_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L101"}, {"id": "bx_reimburse_my_reimburse_stats", "label": "my_reimburse_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L132"}, {"id": "bx_reimburse_reimburse_status_counts", "label": "reimburse_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L142"}, {"id": "bx_reimburse_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L151"}, {"id": "bx_reimburse_pending_approval_stats", "label": "pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L177"}, {"id": "bx_reimburse_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L187"}, {"id": "bx_reimburse_get_reimburse", "label": "get_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L213"}, {"id": "bx_reimburse_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L227"}, {"id": "bx_reimburse_get_approval_history", "label": "get_approval_history()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L240"}, {"id": "bx_reimburse_create_reimburse", "label": "create_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L253"}, {"id": "reimburseapplycreate", "label": "ReimburseApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L253"}, {"id": "bx_reimburse_update_reimburse", "label": "update_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L286"}, {"id": "reimburseapplyupdate", "label": "ReimburseApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L286"}, {"id": "bx_reimburse_submit_reimburse", "label": "submit_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L311"}, {"id": "bx_reimburse_approve_reimburse", "label": "approve_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L326"}, {"id": "reimburseapprovalcreate", "label": "ReimburseApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_locatiLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "label": "workflow.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L1"}, {"id": "bx_workflow_workflowservice", "label": "WorkflowService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L26"}, {"id": "bx_workflow_workflowservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L28"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L28"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L28"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L28"}, {"id": "bx_workflow_workflowservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L32"}, {"id": "bxworkflowdef", "label": "BxWorkflowDef", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L32"}, {"id": "bx_workflow_workflowservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L36"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L36"}, {"id": "bx_workflow_workflowservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L45"}, {"id": "bx_workflow_workflowservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L62"}, {"id": "bx_workflow_workflowservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L80"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L80"}, {"id": "bx_workflow_workflowservice_get_by_biz_type", "label": ".get_by_biz_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L94"}, {"id": "bx_workflow_workflowservice_ensure_by_biz_type", "label": ".ensure_by_biz_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L106"}, {"id": "bx_workflow_workflowservice_save_nodes", "label": ".save_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L137"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L137"}, {"id": "bx_workflow_rationale_112", "label": "\u6309\u4e1a\u52a1\u7c7b\u578b\u83b7\u53d6\u5de5\u4f5c\u6d41\uff1b\u5df2\u77e5\u7c7b\u578b\u4e0d\u5b58\u5728\u65f6\u81ea\u52a8\u521b\u5efa\u7a7a\u5b9a\u4e49\uff08\u65e0\u8282\u70b9\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L112"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_coerce_optional_id", "label": "_coerce_optional_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9"}, {"id": "bx_advance_advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_advance_advanceapplycreate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L27"}, {"id": "bx_advance_advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L31"}, {"id": "bx_advance_advanceapplyupdate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L46"}, {"id": "bx_advance_advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L69"}, {"id": "bx_advance_advanceapprovalout", "label": "AdvanceApprovalOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L76"}, {"id": "bx_advance_advancemystatsout", "label": "AdvanceMyStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L89"}, {"id": "bx_advance_advanceapproverpendingstatsout", "label": "AdvanceApproverPendingStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L104"}, {"id": "bx_advance_advanceissueout", "label": "AdvanceIssueOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L111"}, {"id": "bx_advance_advanceissuerequest", "label": "AdvanceIssueRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L129"}, {"id": "bx_advance_advanceoffsetrecordout", "label": "AdvanceOffsetRecordOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L136"}, {"id": "bx_advance_advancefinanceissuestatsout", "label": "AdvanceFinanceIssueStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L149"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "bx_advance_coerce_optional_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "label": "common.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L1"}, {"id": "bx_common_dt_to_str", "label": "_dt_to_str()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11"}, {"id": "bx_common_pagedquery", "label": "PagedQuery", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L21"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_common_apiresponse", "label": "ApiResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L28"}, {"id": "bx_common_pageddata", "label": "PagedData", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L34"}, {"id": "bx_common_pagedresponse", "label": "PagedResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L43"}, {"id": "bx_common_rationale_1", "label": "bx \u539f\u7cfb\u7edf API \u54cd\u5e94\u5305\u88c5\uff08code=0 / msg / data.rows\uff09\uff0c\u4e0e\u95e8\u6237 success_response \u5206\u79bb\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "bx_common_dt_to_str", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11", "weight": 1.0}, {"source": "bx_common_dt_to_str", "target": "datetime", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11", "weight": 1.0}, {"source": "bx_common_dt_to_str", "target": "str", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L11", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "bx_common_pagedquery", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L21", "weight": 1.0}, {"source": "bx_common_pagedquery", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L21", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_common_py", "target": "bx_common_apiresponse", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py", "source_location": "L28", "weight": 1.0}, {"source": "bx_common_apiresponse", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00cLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_advanceservice", "label": "AdvanceService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L25"}, {"id": "bx_advance_advanceservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "bx_advance_advanceservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L31"}, {"id": "bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L31"}, {"id": "bx_advance_advanceservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "bx_advance_advanceservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L45"}, {"id": "bx_advance_advanceservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L62"}, {"id": "bx_advance_advanceservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L80"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L80"}, {"id": "bx_advance_advanceservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L87"}, {"id": "bx_advance_advanceservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L143"}, {"id": "bx_advance_advanceservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L191"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L191"}, {"id": "bx_advance_advanceservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L204"}, {"id": "bx_advance_advanceservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L221"}, {"id": "bx_advance_advanceservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L266"}, {"id": "bx_advance_advanceservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L303"}, {"id": "bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L303"}, {"id": "bx_advance_advanceservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_locLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_bx_health", "label": "bx_health()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L10"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09API \u8def\u7531\u805a\u5408 \u2014 REST \u98ce\u683c\uff0c\u534f\u8bae\u5bf9\u9f50 bx \u539f\u7cfb\u7edf\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_11", "label": "P0 \u5065\u5eb7\u68c0\u67e5\uff1a\u9a8c\u8bc1 bx \u8def\u7531\u5df2\u6302\u8f7d\u4e14\u54cd\u5e94\u534f\u8bae\u4e3a code=0\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L11"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "bx_init_bx_health", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_init_rationale_11", "target": "bx_init_bx_health", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L11", "weight": 1.0}], "raw_calls": [{"caller_nid": "bx_init_bx_health", "callee": "ApiResponse", "is_member_call": false, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L12"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_bx_health", "label": "bx_health()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L13"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09API \u8def\u7531\u805a\u5408 \u2014 REST \u98ce\u683c\uff0c\u534f\u8bae\u5bf9\u9f50 bx \u539f\u7cfb\u7edf\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_14", "label": "P0 \u5065\u5eb7\u68c0\u67e5\uff1a\u9a8c\u8bc1 bx \u8def\u7531\u5df2\u6302\u8f7d\u4e14\u54cd\u5e94\u534f\u8bae\u4e3a code=0\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L14"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_api_bx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "bx_init_bx_health", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_init_rationale_14", "target": "bx_init_bx_health", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L14", "weight": 1.0}], "raw_calls": [{"caller_nid": "bx_init_bx_health", "callee": "ApiResponse", "is_member_call": false, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L15"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_date_range_py", "label": "bx_date_range.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L1"}, {"id": "utils_bx_date_range_current_month_bounds", "label": "current_month_bounds()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7"}, {"id": "utils_bx_date_range_parse_create_time_day_bounds", "label": "parse_create_time_day_bounds()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19"}, {"id": "utils_bx_date_range_rationale_1", "label": "\u62a5\u9500\u6a21\u5757\u65e5\u671f\u8303\u56f4\u5de5\u5177\uff08\u4ece bx-backend \u8fc1\u79fb\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L1"}, {"id": "utils_bx_date_range_rationale_8", "label": "\u5f53\u524d\u81ea\u7136\u6708 [month_start, next_month_start)\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L8"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_date_range_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_date_range_py", "target": "utils_bx_date_range_current_month_bounds", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7", "weight": 1.0}, {"source": "utils_bx_date_range_current_month_bounds", "target": "datetime", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7", "weight": 1.0}, {"source": "utils_bx_date_range_current_month_bounds", "target": "datetime", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7", "weight": 1.0}, {"source": "utils_bx_date_range_current_month_bounds", "target": "datetime", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_date_range_py", "target": "utils_bx_date_range_parse_create_time_day_bounds", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19", "weight": 1.0}, {"source": "utils_bx_date_range_parse_create_time_day_bounds", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19", "weight": 1.0}, {"source": "utils_bx_date_range_parse_create_time_day_bounds", "target": "datetime", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19", "weight": 1.0}, {"source": "utils_bx_date_range_parse_create_time_day_bounds", "target": "datetime", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L19", "weight": 1.0}, {"source": "utils_bx_date_range_current_month_bounds", "target": "datetime", "relation": "calls", "context": "call", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L13", "weight": 1.0}, {"source": "utils_bx_date_range_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_date_range_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py", "source_location": "L1", "weight": 1.0}, {"source": "utils_bx_date_range_rationale_8", "target": "utils_bx_date_range_current_monthLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_approval_cycle_py", "label": "approval_cycle.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L1"}, {"id": "bx_approval_cycle_filter_current_cycle_approvals", "label": "filter_current_cycle_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10"}, {"id": "t", "label": "T", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10"}, {"id": "bx_approval_cycle_rationale_1", "label": "\u5ba1\u6279\u8f6e\u6b21\u8fc7\u6ee4 \u2014 \u9a73\u56de\u540e\u91cd\u65b0\u63d0\u4ea4\u65f6\uff0c\u8fdb\u5ea6\u63a5\u53e3\u4ec5\u5c55\u793a\u5f53\u524d\u8f6e\u6b21\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L1"}, {"id": "bx_approval_cycle_rationale_17", "label": "\u5ba1\u6279\u8fdb\u5ea6\u4ec5\u5c55\u793a\u5f53\u524d\u8f6e\u6b21\uff1b\u5b8c\u6574\u5386\u53f2\u7531 get_approval_history \u8fd4\u56de\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L17"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_approval_cycle_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_approval_cycle_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_approval_cycle_py", "target": "bx_approval_cycle_filter_current_cycle_approvals", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_filter_current_cycle_approvals", "target": "t", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_filter_current_cycle_approvals", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_filter_current_cycle_approvals", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_filter_current_cycle_approvals", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_filter_current_cycle_approvals", "target": "t", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L10", "weight": 1.0}, {"source": "bx_approval_cycle_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_approval_cycle_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_approval_cycle_rationale_17", "target": "bx_approval_cycle_filter_current_cycle_approvals", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py", "source_location": "L17", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_bx_health", "label": "bx_health()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L17"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09API \u8def\u7531\u805a\u5408 \u2014 REST \u98ce\u683c\uff0c\u534f\u8bae\u5bf9\u9f50 bx \u539f\u7cfb\u7edf\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_18", "label": "P0 \u5065\u5eb7\u68c0\u67e5\uff1a\u9a8c\u8bc1 bx \u8def\u7531\u5df2\u6302\u8f7d\u4e14\u54cd\u5e94\u534f\u8bae\u4e3a code=0\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L18"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_api_bx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "bx_init_bx_health", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L17", "weight": 1.0}, {"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_init_rationale_18", "target": "bx_init_bx_health", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L18", "weight": 1.0}], "raw_calls": [{"caller_nid": "bx_init_bx_health", "callee": "ApiResponse", "is_member_call": false, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L19"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_reimburseservice", "label": "ReimburseService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L30"}, {"id": "bx_reimburse_reimburseservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "bx_reimburse_reimburseservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L36"}, {"id": "bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L36"}, {"id": "bx_reimburse_reimburseservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "bx_reimburse_reimburseservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L50"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L50"}, {"id": "bx_reimburse_reimburseservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L57"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L57"}, {"id": "bx_reimburse_reimburseservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L70"}, {"id": "bx_reimburse_reimburseservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L126"}, {"id": "bx_reimburse_reimburseservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L174"}, {"id": "bx_reimburse_reimburseservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L191"}, {"id": "bx_reimburse_reimburseservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L236"}, {"id": "bx_reimburse_reimburseservice_get_approvals", "label": ".get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L273"}, {"id": "bx_reimburse_reimburseservice_create_with_details", "label": ".create_with_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L325"}, {"id": "bx_reimburse_reimburseservice_update_with_details", "label": ".update_with_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L371"}, {"id": "bx_reimburse_reimburseservice_get_details", "label": ".get_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L424"}, {"id": "bxreimbursedetail", "label": "BxReimburseDLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "label": "bx_auth.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L1"}, {"id": "core_bx_auth_bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L13"}, {"id": "core_bx_auth_bxusercontext_to_tenant_user_dict", "label": ".to_tenant_user_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22"}, {"id": "core_bx_auth_get_bx_current_user", "label": "get_bx_current_user()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L31"}, {"id": "core_bx_auth_rationale_1", "label": "\u62a5\u9500\u6a21\u5757\u8ba4\u8bc1\u9002\u914d\uff1a\u95e8\u6237 JWT \u7528\u6237 \u2192 bx \u4e1a\u52a1\u4e0a\u4e0b\u6587\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L1"}, {"id": "core_bx_auth_rationale_14", "label": "bx Service/Router \u4f7f\u7528\u7684\u5f53\u524d\u7528\u6237\u4e0a\u4e0b\u6587\uff08sys_user.id \u4e3a UUID \u5b57\u7b26\u4e32\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L14"}, {"id": "core_bx_auth_rationale_23", "label": "\u4f9b get_tenant_no \u7b49\u95e8\u6237\u5de5\u5177\u4f7f\u7528\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L23"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "dataclasses", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "app_core_menu_guard", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "core_bx_auth_bxusercontext", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L13", "weight": 1.0}, {"source": "core_bx_auth_bxusercontext", "target": "core_bx_auth_bxusercontext_to_tenant_user_dict", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22", "weight": 1.0}, {"source": "core_bx_auth_bxusercontext_to_tenant_user_dict", "target": "str", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22", "weight": 1.0}, {"source": "core_bx_auth_bxusercontext_to_tenant_user_dict", "target": "any", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L22", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_bx_auth_py", "target": "core_bx_auth_get_bx_current_user", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L31", "weight": 1.0}, {"source": "core_bx_auth_get_bx_current_user", "target": "core_bx_auth_bxusercontext", "relation": "references", "context": "return_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py", "source_location": "L31", "weight": 1.0}, {"source": "core_bx_auth_get_bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L1"}, {"id": "api_init_rationale_1", "label": "API \u8def\u7531\u5305 \u2014 \u5c71\u5ce1\u5316\u5de5\u751f\u4ea7\u4e00\u4f53\u5316\u95e8\u6237", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_public", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_menu", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_admin", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_workflow", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_leave", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_crm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_monitor", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "target": "app_api_bx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L9", "weight": 1.0}, {"source": "api_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_advanceservice", "label": "AdvanceService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L24"}, {"id": "bx_advance_advanceservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bx_advance_advanceservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bx_advance_advanceservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "bx_advance_advanceservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L44"}, {"id": "bx_advance_advanceservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L61"}, {"id": "bx_advance_advanceservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bx_advance_advanceservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L86"}, {"id": "bx_advance_advanceservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L142"}, {"id": "bx_advance_advanceservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bx_advance_advanceservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L203"}, {"id": "bx_advance_advanceservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L220"}, {"id": "bx_advance_advanceservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L265"}, {"id": "bx_advance_advanceservice_get_approvals", "label": ".get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L302"}, {"id": "bx_advance_advanceservice_global_status_counts", "label": ".global_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L354"}, {"id": "bx_advance_advanceservice_my_apply_stats", "label": ".my_apply_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_locaLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_bx_health", "label": "bx_health()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L15"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09API \u8def\u7531\u805a\u5408 \u2014 REST \u98ce\u683c\uff0c\u534f\u8bae\u5bf9\u9f50 bx \u539f\u7cfb\u7edf\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_16", "label": "P0 \u5065\u5eb7\u68c0\u67e5\uff1a\u9a8c\u8bc1 bx \u8def\u7531\u5df2\u6302\u8f7d\u4e14\u54cd\u5e94\u534f\u8bae\u4e3a code=0\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L16"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_api_bx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "bx_init_bx_health", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L15", "weight": 1.0}, {"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_init_rationale_16", "target": "bx_init_bx_health", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L16", "weight": 1.0}], "raw_calls": [{"caller_nid": "bx_init_bx_health", "callee": "ApiResponse", "is_member_call": false, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L17"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_reimburseservice", "label": "ReimburseService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "bx_reimburse_reimburseservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "bx_reimburse_reimburseservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L37"}, {"id": "bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L37"}, {"id": "bx_reimburse_reimburseservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "bx_reimburse_reimburseservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L51"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L51"}, {"id": "bx_reimburse_reimburseservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L58"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L58"}, {"id": "bx_reimburse_reimburseservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L71"}, {"id": "bx_reimburse_reimburseservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L127"}, {"id": "bx_reimburse_reimburseservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L175"}, {"id": "bx_reimburse_reimburseservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L192"}, {"id": "bx_reimburse_reimburseservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L237"}, {"id": "bx_reimburse_reimburseservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L274"}, {"id": "bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L274"}, {"id": "bx_reimburse_reimburseservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L287"}, {"id": "bx_reimburse_reimburseservice_query_approvals", "label": "._query_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L304"}, {"id": "bx_reimburse_reimburseservice_get_approvLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bx_reimburse_build_details_data", "label": "_build_details_data()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L33"}, {"id": "bx_reimburse_get_generated_title", "label": "get_generated_title()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L54"}, {"id": "bx_reimburse_list_reimburses", "label": "list_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "bx_reimburse_my_reimburses", "label": "my_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L101"}, {"id": "bx_reimburse_my_reimburse_stats", "label": "my_reimburse_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L132"}, {"id": "bx_reimburse_reimburse_status_counts", "label": "reimburse_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L142"}, {"id": "bx_reimburse_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L151"}, {"id": "bx_reimburse_pending_approval_stats", "label": "pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L177"}, {"id": "bx_reimburse_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L187"}, {"id": "bx_reimburse_get_reimburse", "label": "get_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L213"}, {"id": "bx_reimburse_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L227"}, {"id": "bx_reimburse_create_reimburse", "label": "create_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L240"}, {"id": "reimburseapplycreate", "label": "ReimburseApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L240"}, {"id": "bx_reimburse_update_reimburse", "label": "update_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L273"}, {"id": "reimburseapplyupdate", "label": "ReimburseApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L273"}, {"id": "bx_reimburse_submit_reimburse", "label": "submit_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L298"}, {"id": "bx_reimburse_approve_reimburse", "label": "approve_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L313"}, {"id": "reimburseapprovalcreate", "label": "ReimburseApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L313"}, {"id": "bx_reimburse_reject_reimburse", "label": "reject_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L3Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "label": "bx_models.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}, {"id": "models_bx_models_bxexpenseitem", "label": "BxExpenseItem", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L9"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_bx_models_bxworkflowdef", "label": "BxWorkflowDef", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L32"}, {"id": "models_bx_models_bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L52"}, {"id": "models_bx_models_bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L74"}, {"id": "models_bx_models_bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L107"}, {"id": "models_bx_models_bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L130"}, {"id": "models_bx_models_bxreimbursedetail", "label": "BxReimburseDetail", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L166"}, {"id": "models_bx_models_bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L187"}, {"id": "models_bx_models_bxreimburseoffset", "label": "BxReimburseOffset", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L210"}, {"id": "models_bx_models_bxattachment", "label": "BxAttachment", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L228"}, {"id": "models_bx_models_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09\u6a21\u5757\u6570\u636e\u6a21\u578b \u2014 \u5bf9\u9f50 docs/sql/20260606_bx_tables.sql", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxexpenseitem", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L9", "weight": 1.0}, {"source": "models_bx_models_bxexpenseitem", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxworkflowdef", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L32", "weight": 1.0}, {"source": "models_bx_models_bxworkflowdef", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L32", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxworkflownode", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L52", "wLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_payment_py", "label": "payment.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L1"}, {"id": "bx_payment_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L39"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L39"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L39"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L39"}, {"id": "bx_payment_wrap_reimburse_rows", "label": "_wrap_reimburse_rows()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L43"}, {"id": "reimburseapplyout", "label": "ReimburseApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L43"}, {"id": "bx_payment_list_pending_payments", "label": "list_pending_payments()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L48"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L48"}, {"id": "bx_payment_payment_finance_stats", "label": "payment_finance_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L81"}, {"id": "bx_payment_list_processed_payments", "label": "list_processed_payments()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L91"}, {"id": "bx_payment_pay", "label": "pay()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L124"}, {"id": "payrequest", "label": "PayRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L124"}, {"id": "bx_payment_offset", "label": "offset()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L144"}, {"id": "offsetrequest", "label": "OffsetRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L144"}, {"id": "bx_payment_batch_offset", "label": "batch_offset()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L167"}, {"id": "batchoffsetrequest", "label": "BatchOffsetRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L167"}, {"id": "bx_payment_list_advance_issues", "label": "list_advance_issues()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L190"}, {"id": "bx_payment_advance_issue_stats", "label": "advance_issue_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L233"}, {"id": "bx_payment_export_advance_issues", "label": "export_advance_issues()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L243"}, {"id": "bx_payment_issue_advance", "label": "issue_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L319"}, {"id": "advanceissuerequest", "label": "AdvanceIssueRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L319"}, {"id": "bx_payment_list_advance_offset_records", "label": "list_advance_offset_records()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L343"}, {"id": "bx_payment_list_reimburse_offset_records", "label": "list_reimburse_offset_records()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L382"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_payment_py", "target": "csv", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py", "source_location": "L1", "weight": 1.0}, {"source": "usLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bx_advance_fill_offset_amounts", "label": "_fill_offset_amounts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "bx_advance_list_advances", "label": "list_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_my_advances", "label": "my_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L77"}, {"id": "bx_advance_my_advance_stats", "label": "my_advance_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L109"}, {"id": "bx_advance_advance_status_counts", "label": "advance_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L119"}, {"id": "bx_advance_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L128"}, {"id": "bx_advance_advance_pending_approval_stats", "label": "advance_pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L154"}, {"id": "bx_advance_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L164"}, {"id": "bx_advance_get_advance", "label": "get_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L190"}, {"id": "bx_advance_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L206"}, {"id": "bx_advance_create_advance", "label": "create_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L219"}, {"id": "advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L219"}, {"id": "bx_advance_update_advance", "label": "update_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L252"}, {"id": "advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L252"}, {"id": "bx_advance_submit_advance", "label": "submit_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L273"}, {"id": "bx_advance_approve_advance", "label": "approve_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L287"}, {"id": "advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L287"}, {"id": "bx_advance_reject_advance", "label": "reject_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L302"}, {"id": "bx_advance_delete_advance", "label": "delete_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/__init__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "label": "main.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L1"}, {"id": "app_main_lifespan", "label": "lifespan()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L30"}, {"id": "fastapi", "label": "FastAPI", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L30"}, {"id": "app_main_error_json", "label": "_error_json()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L77"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L77"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L77"}, {"id": "jsonresponse", "label": "JSONResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L77"}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L90"}, {"id": "request", "label": "Request", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L90"}, {"id": "sqlalchemyerror", "label": "SQLAlchemyError", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L90"}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L101"}, {"id": "exception", "label": "Exception", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L101"}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L121"}, {"id": "app_main_root", "label": "root()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L143"}, {"id": "app_main_health_check", "label": "health_check()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L148"}, {"id": "app_main_rationale_91", "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", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L91"}, {"id": "app_main_rationale_102", "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", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L102"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "contextlib", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "logging", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "fastapi_middleware_cors", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_main_py", "target": "fastapi_responses", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py", "source_location": "L8", "weight": 1.0}, {"sLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "label": "attachment.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L1"}, {"id": "bx_attachment_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L24"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L24"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L24"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L24"}, {"id": "bx_attachment_parse_id_list", "label": "_parse_id_list()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L28"}, {"id": "bx_attachment_upload_file", "label": "upload_file()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L33"}, {"id": "uploadfile", "label": "UploadFile", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L33"}, {"id": "bx_attachment_download_file", "label": "download_file()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L67"}, {"id": "bx_attachment_delete_attachment", "label": "delete_attachment()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L89"}, {"id": "bx_attachment_preview_attachment", "label": "preview_attachment()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L103"}, {"id": "bx_attachment_list_by_biz", "label": "list_by_biz()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L127"}, {"id": "bx_attachment_bind_biz", "label": "bind_biz()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L140"}, {"id": "bx_attachment_rationale_108", "label": "\u8fd4\u56de\u56fe\u7247/\u9644\u4ef6\u9884\u89c8 URL\uff08MinIO \u9884\u7b7e\u540d\u6216\u672c\u5730\u516c\u5f00\u5730\u5740\uff0c\u5bf9\u9f50\u95e8\u6237 storage \u80fd\u529b\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L108"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "mimetypes", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "urllib_parse", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "fastapi_responses", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_attachment_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00codLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "label": "bx_models.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}, {"id": "models_bx_models_bxexpenseitem", "label": "BxExpenseItem", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_bx_models_bxworkflowdef", "label": "BxWorkflowDef", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L33"}, {"id": "models_bx_models_bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L62"}, {"id": "models_bx_models_bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L95"}, {"id": "models_bx_models_bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L128"}, {"id": "models_bx_models_bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L151"}, {"id": "models_bx_models_bxreimbursedetail", "label": "BxReimburseDetail", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L187"}, {"id": "models_bx_models_bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L208"}, {"id": "models_bx_models_bxreimburseoffset", "label": "BxReimburseOffset", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L231"}, {"id": "models_bx_models_bxattachment", "label": "BxAttachment", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L249"}, {"id": "models_bx_models_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09\u6a21\u5757\u6570\u636e\u6a21\u578b \u2014 \u5bf9\u9f50 docs/sql/20260606_bx_tables.sql", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxexpenseitem", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10", "weight": 1.0}, {"source": "models_bx_models_bxexpenseitem", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxworkflowdef", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L33", "weight": 1.0}, {"source": "models_bx_models_bxworkflowdef", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location":Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_coerce_id", "label": "_coerce_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L9"}, {"id": "bx_reimburse_reimbursedetailcreate", "label": "ReimburseDetailCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L15"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_reimburse_reimbursedetailcreate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L24"}, {"id": "bx_reimburse_reimbursedetailout", "label": "ReimburseDetailOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L31"}, {"id": "bx_reimburse_reimburseapplycreate", "label": "ReimburseApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L41"}, {"id": "bx_reimburse_reimburseapplycreate_details_must_not_be_empty", "label": ".details_must_not_be_empty()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L50"}, {"id": "bx_reimburse_reimburseapplyupdate", "label": "ReimburseApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L56"}, {"id": "bx_reimburse_reimburseapplyout", "label": "ReimburseApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L63"}, {"id": "bx_reimburse_reimburseapprovalcreate", "label": "ReimburseApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L89"}, {"id": "bx_reimburse_reimburseapprovalout", "label": "ReimburseApprovalOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L96"}, {"id": "bx_reimburse_reimbursemystatsout", "label": "ReimburseMyStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L109"}, {"id": "bx_reimburse_reimburseapproverpendingstatsout", "label": "ReimburseApproverPendingStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L121"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "bx_reimburse_coerce_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "bx_reimburse_reimbursedetailcreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L15", "weight": 1.0}, {"source": "bx_reimburse_reimbursedetailcreate", "target": "basemodel", "relation": "inherits", "confiLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_advanceservice", "label": "AdvanceService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L25"}, {"id": "bx_advance_advanceservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L27"}, {"id": "bx_advance_advanceservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L31"}, {"id": "bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L31"}, {"id": "bx_advance_advanceservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L35"}, {"id": "bx_advance_advanceservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L45"}, {"id": "bx_advance_advanceservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L62"}, {"id": "bx_advance_advanceservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L80"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L80"}, {"id": "bx_advance_advanceservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L87"}, {"id": "bx_advance_advanceservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L143"}, {"id": "bx_advance_advanceservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L191"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L191"}, {"id": "bx_advance_advanceservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L204"}, {"id": "bx_advance_advanceservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L221"}, {"id": "bx_advance_advanceservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L266"}, {"id": "bx_advance_advanceservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L303"}, {"id": "bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L303"}, {"id": "bx_advance_advanceservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_locLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_coerce_optional_id", "label": "_coerce_optional_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9"}, {"id": "bx_advance_advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_advance_advanceapplycreate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L27"}, {"id": "bx_advance_advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L31"}, {"id": "bx_advance_advanceapplyupdate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L46"}, {"id": "bx_advance_advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L69"}, {"id": "bx_advance_advanceapprovalout", "label": "AdvanceApprovalOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L76"}, {"id": "bx_advance_advancemystatsout", "label": "AdvanceMyStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L89"}, {"id": "bx_advance_advanceapproverpendingstatsout", "label": "AdvanceApproverPendingStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L104"}, {"id": "bx_advance_advanceissueout", "label": "AdvanceIssueOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L111"}, {"id": "bx_advance_advanceissuerequest", "label": "AdvanceIssueRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L130"}, {"id": "bx_advance_advanceoffsetrecordout", "label": "AdvanceOffsetRecordOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L137"}, {"id": "bx_advance_advancefinanceissuestatsout", "label": "AdvanceFinanceIssueStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L150"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_advance_py", "target": "bx_advance_coerce_optional_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_advanceservice", "label": "AdvanceService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L24"}, {"id": "bx_advance_advanceservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bx_advance_advanceservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bx_advance_advanceservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "bx_advance_advanceservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L44"}, {"id": "bx_advance_advanceservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L61"}, {"id": "bx_advance_advanceservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bx_advance_advanceservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L86"}, {"id": "bx_advance_advanceservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L142"}, {"id": "bx_advance_advanceservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bx_advance_advanceservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L203"}, {"id": "bx_advance_advanceservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L220"}, {"id": "bx_advance_advanceservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L265"}, {"id": "bx_advance_advanceservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L302"}, {"id": "bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L302"}, {"id": "bx_advance_advanceservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_locLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "label": "expense_item.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L1"}, {"id": "bx_expense_item_coerce_id", "label": "_coerce_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L7"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L7"}, {"id": "bx_expense_item_expenseitemcreate", "label": "ExpenseItemCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L13"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_expense_item_expenseitemcreate_validate_parent_id", "label": ".validate_parent_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L26"}, {"id": "bx_expense_item_expenseitemupdate", "label": "ExpenseItemUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L30"}, {"id": "bx_expense_item_expenseitemupdate_validate_parent_id", "label": ".validate_parent_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L43"}, {"id": "bx_expense_item_expenseitemout", "label": "ExpenseItemOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L49"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "bx_expense_item_coerce_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L7", "weight": 1.0}, {"source": "bx_expense_item_coerce_id", "target": "str", "relation": "references", "context": "return_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "bx_expense_item_expenseitemcreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_expense_item_expenseitemcreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_expense_item_expenseitemcreate", "target": "bx_expense_item_expenseitemcreate_validate_parent_id", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L26", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_expense_item_py", "target": "bx_expense_item_expenseitemupdate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L30", "weight": 1.0}, {"source": "bx_expense_item_expenseitemupdate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py", "source_location": "L30", "weight": 1.0}, {"source": "bx_expense_item_expenseitemupdate", "target": "bx_expense_item_expenseitemupdate_validate_parent_id", "relation": "method", "confidenceLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "label": "expense_item.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L1"}, {"id": "bx_expense_item_get_expense_item_tree", "label": "get_expense_item_tree()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "bx_expense_item_get_expense_item", "label": "get_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L27"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L27"}, {"id": "bx_expense_item_create_expense_item", "label": "create_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L40"}, {"id": "expenseitemcreate", "label": "ExpenseItemCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L40"}, {"id": "bx_expense_item_update_expense_item", "label": "update_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L56"}, {"id": "expenseitemupdate", "label": "ExpenseItemUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L56"}, {"id": "bx_expense_item_delete_expense_item", "label": "delete_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L78"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_schemas_bx_expense_item", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_services_bx_expense_item", "relation": "imports_from", "context": "import", "confidence": "EXTRACLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L29"}, {"id": "bx_advance_fill_offset_amounts", "label": "_fill_offset_amounts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "advanceapplyout", "label": "AdvanceApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L33"}, {"id": "bx_advance_list_advances", "label": "list_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L42"}, {"id": "bx_advance_my_advances", "label": "my_advances()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L77"}, {"id": "bx_advance_my_advance_stats", "label": "my_advance_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L109"}, {"id": "bx_advance_advance_status_counts", "label": "advance_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L119"}, {"id": "bx_advance_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L128"}, {"id": "bx_advance_advance_pending_approval_stats", "label": "advance_pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L154"}, {"id": "bx_advance_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L164"}, {"id": "bx_advance_get_advance", "label": "get_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L190"}, {"id": "bx_advance_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L206"}, {"id": "bx_advance_get_approval_history", "label": "get_approval_history()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L219"}, {"id": "bx_advance_create_advance", "label": "create_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L232"}, {"id": "advanceapplycreate", "label": "AdvanceApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L232"}, {"id": "bx_advance_update_advance", "label": "update_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L265"}, {"id": "advanceapplyupdate", "label": "AdvanceApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L265"}, {"id": "bx_advance_submit_advance", "label": "submit_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L286"}, {"id": "bx_advance_approve_advance", "label": "approve_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L300"}, {"id": "advanceapprovalcreate", "label": "AdvanceApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py", "source_location": "L300"}, {"id": "bx_advance_reject_advance", "label": "reject_advance()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_reimburseservice", "label": "ReimburseService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L30"}, {"id": "bx_reimburse_reimburseservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L32"}, {"id": "bx_reimburse_reimburseservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L36"}, {"id": "bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L36"}, {"id": "bx_reimburse_reimburseservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L40"}, {"id": "bx_reimburse_reimburseservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L50"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L50"}, {"id": "bx_reimburse_reimburseservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L57"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L57"}, {"id": "bx_reimburse_reimburseservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L70"}, {"id": "bx_reimburse_reimburseservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L126"}, {"id": "bx_reimburse_reimburseservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L174"}, {"id": "bx_reimburse_reimburseservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L191"}, {"id": "bx_reimburse_reimburseservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L236"}, {"id": "bx_reimburse_reimburseservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L273"}, {"id": "bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L273"}, {"id": "bx_reimburse_reimburseservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L286"}, {"id": "bx_reimburse_reimburseservice_query_approvals", "label": "._query_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L303"}, {"id": "bx_reimburse_reimburseservice_get_approvLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "label": "config.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L1"}, {"id": "core_config_settings", "label": "Settings", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/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/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L151"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "pydantic_settings", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "pathlib", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "os", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "core_config_settings", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L7", "weight": 1.0}, {"source": "core_config_settings", "target": "basesettings", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_config_py", "target": "core_config_config", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py", "source_location": "L151", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_base_py", "label": "base.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L1"}, {"id": "bx_base_bxcrudservice", "label": "BxCrudService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L13"}, {"id": "bx_base_bxcrudservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15"}, {"id": "m", "label": "M", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15"}, {"id": "bx_base_bxcrudservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L22"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L22"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L22"}, {"id": "bx_base_bxcrudservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L44"}, {"id": "bx_base_bxcrudservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L64"}, {"id": "bx_base_bxcrudservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L84"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L84"}, {"id": "bx_base_rationale_1", "label": "\u62a5\u9500\u6a21\u5757 Service \u57fa\u7c7b \u2014 \u79df\u6237\u9694\u79bb + UUID + \u8f6f\u5220\u9664\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_base_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_base_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_base_py", "target": "app_core_utils", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_base_py", "target": "bx_base_bxcrudservice", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_base_bxcrudservice", "target": "bx_base_bxcrudservice_get_by_id", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15", "weight": 1.0}, {"source": "bx_base_bxcrudservice_get_by_id", "target": "m", "relation": "references", "context": "generic_arg", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15", "weight": 1.0}, {"source": "bx_base_bxcrudservice_get_by_id", "target": "session", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15", "weight": 1.0}, {"source": "bx_base_bxcrudservice_get_by_id", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py", "source_location": "L15", "weight": 1.0}, {"soLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "label": "expense_item.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L1"}, {"id": "bx_expense_item_expenseitemservice", "label": "ExpenseItemService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L13"}, {"id": "bx_expense_item_expenseitemservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L15"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L15"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L15"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L15"}, {"id": "bx_expense_item_expenseitemservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L19"}, {"id": "bxexpenseitem", "label": "BxExpenseItem", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L19"}, {"id": "bx_expense_item_expenseitemservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L23"}, {"id": "bx_expense_item_expenseitemservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L51"}, {"id": "bx_expense_item_expenseitemservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L79"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L79"}, {"id": "bx_expense_item_expenseitemservice_get_item_tree", "label": ".get_item_tree()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L86"}, {"id": "bx_expense_item_expenseitemservice_build_tree", "label": "._build_tree()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L96"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "app_models_bx_models", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_item_py", "target": "app_services_bx_base", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_expense_itLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_storage_py", "label": "storage.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L1"}, {"id": "bx_storage_normalize_storage_path", "label": "normalize_storage_path()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L12"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L12"}, {"id": "bx_storage_upload_bx_file", "label": "upload_bx_file()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L32"}, {"id": "bytes", "label": "bytes", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L32"}, {"id": "bx_storage_get_access_url", "label": "get_access_url()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L45"}, {"id": "bx_storage_get_preview_url", "label": "get_preview_url()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L54"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L54"}, {"id": "bx_storage_get_download_url", "label": "get_download_url()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L69"}, {"id": "bx_storage_download_file_bytes", "label": "download_file_bytes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L76"}, {"id": "bx_storage_file_stream", "label": "file_stream()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L102"}, {"id": "bytesio", "label": "BytesIO", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L102"}, {"id": "bx_storage_rationale_1", "label": "\u62a5\u9500\u9644\u4ef6\u5b58\u50a8\u9002\u914d \u2014 \u590d\u7528\u95e8\u6237 app.core.storage\uff0c\u5bf9\u5916\u4fdd\u6301 bx \u534f\u8bae\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L1"}, {"id": "bx_storage_rationale_13", "label": "\u5c06 upload \u8fd4\u56de\u7684\u5b8c\u6574 URL \u5f52\u4e00\u5316\u4e3a\u5b58\u50a8\u76f8\u5bf9\u8def\u5f84\uff08\u4e0e admin \u9644\u4ef6 upload \u4e00\u81f4\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L13"}, {"id": "bx_storage_rationale_33", "label": "\u4e0a\u4f20\u6587\u4ef6\u5230\u5bf9\u8c61\u5b58\u50a8\u3002 \u8fd4\u56de (relative_path, access_url)\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L33"}, {"id": "bx_storage_rationale_46", "label": "\u83b7\u53d6\u53ef\u8bbf\u95ee URL\uff08\u672c\u5730/MinIO \u516c\u5f00\u5730\u5740\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L46"}, {"id": "bx_storage_rationale_55", "label": "\u56fe\u7247/\u9644\u4ef6\u9884\u89c8 URL\uff1aMinIO \u9884\u7b7e\u540d inline\uff0c\u672c\u5730/\u5176\u4ed6\u5b58\u50a8\u7528 get_url\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L55"}, {"id": "bx_storage_rationale_70", "label": "\u5e26\u6587\u4ef6\u540d\u7684\u4e0b\u8f7d URL\uff08\u5bf9\u9f50\u95e8\u6237 attachment/download-url\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L70"}, {"id": "bx_storage_rationale_77", "label": "\u8bfb\u53d6\u6587\u4ef6\u4e8c\u8fdb\u5236\uff0c\u4f9b StreamingResponse \u4e0b\u8f7d\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L77"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_storage_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_storage_py", "target": "io", "relation": "imports_from", "contexLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_core_attachment_biz_py", "label": "attachment_biz.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L1"}, {"id": "core_attachment_biz_resolve_upload_folder", "label": "resolve_upload_folder()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19"}, {"id": "core_attachment_biz_rationale_1", "label": "\u9644\u4ef6\u4e0a\u4f20 bizType \u4e0e MinIO \u76ee\u5f55\u6620\u5c04\uff08\u5bf9\u9f50\u539f CRM oss \u4e0a\u4f20\u8bed\u4e49\uff09", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L1"}, {"id": "core_attachment_biz_rationale_20", "label": "\u4f18\u5148\u4f7f\u7528\u663e\u5f0f folder\uff1b\u5426\u5219\u6309 bizType \u6620\u5c04\u9ed8\u8ba4\u76ee\u5f55\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L20"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_core_attachment_biz_py", "target": "core_attachment_biz_resolve_upload_folder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19", "weight": 1.0}, {"source": "core_attachment_biz_resolve_upload_folder", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19", "weight": 1.0}, {"source": "core_attachment_biz_resolve_upload_folder", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19", "weight": 1.0}, {"source": "core_attachment_biz_resolve_upload_folder", "target": "str", "relation": "references", "context": "return_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L19", "weight": 1.0}, {"source": "core_attachment_biz_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_core_attachment_biz_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L1", "weight": 1.0}, {"source": "core_attachment_biz_rationale_20", "target": "core_attachment_biz_resolve_upload_folder", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L20", "weight": 1.0}], "raw_calls": [{"caller_nid": "core_attachment_biz_resolve_upload_folder", "callee": "strip", "is_member_call": true, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L21"}, {"caller_nid": "core_attachment_biz_resolve_upload_folder", "callee": "get", "is_member_call": true, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L24"}, {"caller_nid": "core_attachment_biz_resolve_upload_folder", "callee": "strip", "is_member_call": true, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py", "source_location": "L24"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_bx_health", "label": "bx_health()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L16"}, {"id": "bx_init_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09API \u8def\u7531\u805a\u5408 \u2014 REST \u98ce\u683c\uff0c\u534f\u8bae\u5bf9\u9f50 bx \u539f\u7cfb\u7edf\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1"}, {"id": "bx_init_rationale_17", "label": "P0 \u5065\u5eb7\u68c0\u67e5\uff1a\u9a8c\u8bc1 bx \u8def\u7531\u5df2\u6302\u8f7d\u4e14\u54cd\u5e94\u534f\u8bae\u4e3a code=0\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L17"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_api_bx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "target": "bx_init_bx_health", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L16", "weight": 1.0}, {"source": "bx_init_rationale_1", "target": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_init_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "bx_init_rationale_17", "target": "bx_init_bx_health", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L17", "weight": 1.0}], "raw_calls": [{"caller_nid": "bx_init_bx_health", "callee": "ApiResponse", "is_member_call": false, "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py", "source_location": "L18"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "label": "workflow.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L1"}, {"id": "bx_workflow_list_workflows", "label": "list_workflows()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "bx_workflow_get_workflow_by_biz_type", "label": "get_workflow_by_biz_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L36"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L36"}, {"id": "bx_workflow_get_workflow", "label": "get_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L49"}, {"id": "bx_workflow_create_workflow", "label": "create_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L62"}, {"id": "workflowdefcreate", "label": "WorkflowDefCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L62"}, {"id": "bx_workflow_update_workflow", "label": "update_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L78"}, {"id": "workflowdefupdate", "label": "WorkflowDefUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L78"}, {"id": "bx_workflow_save_workflow_nodes", "label": "save_workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L98"}, {"id": "workflownodecreate", "label": "WorkflowNodeCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L98"}, {"id": "bx_workflow_delete_workflow", "label": "delete_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L120"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shaLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "label": "workflow.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L1"}, {"id": "bx_workflow_coerce_optional_id", "label": "_coerce_optional_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L7"}, {"id": "bx_workflow_workflownodecreate", "label": "WorkflowNodeCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L13"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_workflow_workflownodecreate_validate_approver_id", "label": ".validate_approver_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L23"}, {"id": "bx_workflow_workflownodeout", "label": "WorkflowNodeOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L27"}, {"id": "bx_workflow_workflowdefcreate", "label": "WorkflowDefCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L37"}, {"id": "bx_workflow_workflowdefupdate", "label": "WorkflowDefUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L46"}, {"id": "bx_workflow_workflowdefout", "label": "WorkflowDefOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L55"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "bx_workflow_coerce_optional_id", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "bx_workflow_workflownodecreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_workflow_workflownodecreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L13", "weight": 1.0}, {"source": "bx_workflow_workflownodecreate", "target": "bx_workflow_workflownodecreate_validate_approver_id", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L23", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "bx_workflow_workflownodeout", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L27", "weight": 1.0}, {"source": "bx_workflow_workflownodeout", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L27", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_workflow_py", "target": "bx_workflow_workflowdefcreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "source_location": "L37", "weight": 1.0}, {"source": "bx_workflow_workflowdefcreate", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py", "sourLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_reimburseservice", "label": "ReimburseService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "bx_reimburse_reimburseservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L33"}, {"id": "bx_reimburse_reimburseservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L37"}, {"id": "bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L37"}, {"id": "bx_reimburse_reimburseservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L41"}, {"id": "bx_reimburse_reimburseservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L51"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L51"}, {"id": "bx_reimburse_reimburseservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L58"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L58"}, {"id": "bx_reimburse_reimburseservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L71"}, {"id": "bx_reimburse_reimburseservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L127"}, {"id": "bx_reimburse_reimburseservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L175"}, {"id": "bx_reimburse_reimburseservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L192"}, {"id": "bx_reimburse_reimburseservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L237"}, {"id": "bx_reimburse_reimburseservice_approval_record_dict", "label": "._approval_record_dict()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L274"}, {"id": "bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L274"}, {"id": "bx_reimburse_reimburseservice_latest_approval_per_node", "label": "._latest_approval_per_node()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L287"}, {"id": "bx_reimburse_reimburseservice_query_approvals", "label": "._query_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L304"}, {"id": "bx_reimburse_reimburseservice_get_approvLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "label": "bx_codegen.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L1"}, {"id": "utils_bx_codegen_generate_apply_code", "label": "generate_apply_code()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8"}, {"id": "utils_bx_codegen_generate_title", "label": "generate_title()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L33"}, {"id": "utils_bx_codegen_rationale_1", "label": "\u62a5\u9500\u5355\u53f7/\u6807\u9898\u751f\u6210\uff08\u4ece bx-backend \u8fc1\u79fb\uff0c\u589e\u52a0 tenant_no \u9694\u79bb\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L1"}, {"id": "utils_bx_codegen_rationale_9", "label": "\u751f\u6210\u987a\u5e8f\u5355\u53f7\uff1a{prefix}{YYYYMMDD}{3\u4f4d\u5e8f\u53f7}\u3002 prefix \u793a\u4f8b\uff1aBYJ\uff08\u5907\u7528\u91d1\uff09\u3001BX\uff08\u62a5\u9500\uff09\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L9"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "target": "utils_bx_codegen_generate_apply_code", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8", "weight": 1.0}, {"source": "utils_bx_codegen_generate_apply_code", "target": "session", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8", "weight": 1.0}, {"source": "utils_bx_codegen_generate_apply_code", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8", "weight": 1.0}, {"source": "utils_bx_codegen_generate_apply_code", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8", "weight": 1.0}, {"source": "utils_bx_codegen_generate_apply_code", "target": "str", "relation": "references", "context": "return_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_utils_bx_codegen_py", "target": "utils_bx_codegen_generate_title", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L33", "weight": 1.0}, {"source": "utils_bx_codegen_generate_title", "target": "session", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L33", "weight": 1.0}, {"source": "utils_bx_codegen_generate_title", "target": "str", "relation": "references", "context": "parameter_type", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py", "source_location": "L33", "weight": 1.0}, {"source": "utils_bx_codegen_generate_title", "target": "str", "relation": "references", "context": "parameLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_coerce_id", "label": "_coerce_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L9"}, {"id": "bx_reimburse_reimbursedetailcreate", "label": "ReimburseDetailCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L15"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "bx_reimburse_reimbursedetailcreate_validate_expense_item_id", "label": ".validate_expense_item_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L24"}, {"id": "bx_reimburse_reimbursedetailout", "label": "ReimburseDetailOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L31"}, {"id": "bx_reimburse_reimburseapplycreate", "label": "ReimburseApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L41"}, {"id": "bx_reimburse_reimburseapplycreate_details_must_not_be_empty", "label": ".details_must_not_be_empty()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L50"}, {"id": "bx_reimburse_reimburseapplyupdate", "label": "ReimburseApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L56"}, {"id": "bx_reimburse_reimburseapplyout", "label": "ReimburseApplyOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L63"}, {"id": "bx_reimburse_reimburseapprovalcreate", "label": "ReimburseApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L89"}, {"id": "bx_reimburse_reimburseapprovalout", "label": "ReimburseApprovalOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L96"}, {"id": "bx_reimburse_reimbursemystatsout", "label": "ReimburseMyStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L109"}, {"id": "bx_reimburse_reimburseapproverpendingstatsout", "label": "ReimburseApproverPendingStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L121"}, {"id": "bx_reimburse_payrequest", "label": "PayRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L128"}, {"id": "bx_reimburse_offsetrequest", "label": "OffsetRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L135"}, {"id": "bx_reimburse_offsetrequest_validate_advance_id", "label": ".validate_advance_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L144"}, {"id": "bx_reimburse_offsetitem", "label": "OffsetItem", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L151"}, {"id": "bx_reimburse_offsetitem_validate_advance_id", "label": ".validate_advance_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L159"}, {"id": "bx_reimburse_batchoffsetrequest", "label": "BatchOffsetRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L166"}, {"id": "bx_reimburse_reimburseoffsetrecordout", "label": "ReimburseOffsetRecordOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L174"}, {"id": "bx_reimburse_paymentfinancestatsout", "label": "PaymentFinanceStatsOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L187"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_reimburse_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py", "source_location": "L1"Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_tenant", "label": "_tenant()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L29"}, {"id": "bx_reimburse_build_details_data", "label": "_build_details_data()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L33"}, {"id": "bx_reimburse_get_generated_title", "label": "get_generated_title()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L54"}, {"id": "bx_reimburse_list_reimburses", "label": "list_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L64"}, {"id": "bx_reimburse_my_reimburses", "label": "my_reimburses()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L101"}, {"id": "bx_reimburse_my_reimburse_stats", "label": "my_reimburse_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L137"}, {"id": "bx_reimburse_reimburse_status_counts", "label": "reimburse_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L147"}, {"id": "bx_reimburse_pending_approval", "label": "pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L156"}, {"id": "bx_reimburse_pending_approval_stats", "label": "pending_approval_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L182"}, {"id": "bx_reimburse_approved_by_me", "label": "approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L192"}, {"id": "bx_reimburse_get_reimburse", "label": "get_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L218"}, {"id": "bx_reimburse_get_approvals", "label": "get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L232"}, {"id": "bx_reimburse_get_approval_history", "label": "get_approval_history()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L245"}, {"id": "bx_reimburse_create_reimburse", "label": "create_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L258"}, {"id": "reimburseapplycreate", "label": "ReimburseApplyCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L258"}, {"id": "bx_reimburse_update_reimburse", "label": "update_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L291"}, {"id": "reimburseapplyupdate", "label": "ReimburseApplyUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L291"}, {"id": "bx_reimburse_submit_reimburse", "label": "submit_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L316"}, {"id": "bx_reimburse_approve_reimburse", "label": "approve_reimburse()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_location": "L331"}, {"id": "reimburseapprovalcreate", "label": "ReimburseApprovalCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py", "source_locatiLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "label": "workflow.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L1"}, {"id": "bx_workflow_list_workflows", "label": "list_workflows()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L22"}, {"id": "bx_workflow_get_workflow_by_biz_type", "label": "get_workflow_by_biz_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L36"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L36"}, {"id": "bx_workflow_get_workflow", "label": "get_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L48"}, {"id": "bx_workflow_create_workflow", "label": "create_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L61"}, {"id": "workflowdefcreate", "label": "WorkflowDefCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L61"}, {"id": "bx_workflow_update_workflow", "label": "update_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L77"}, {"id": "workflowdefupdate", "label": "WorkflowDefUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L77"}, {"id": "bx_workflow_save_workflow_nodes", "label": "save_workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L97"}, {"id": "workflownodecreate", "label": "WorkflowNodeCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L97"}, {"id": "bx_workflow_delete_workflow", "label": "delete_workflow()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L119"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_workflow_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shaLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_reimburse_py", "label": "reimburse.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L1"}, {"id": "bx_reimburse_reimburseservice", "label": "ReimburseService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L29"}, {"id": "bx_reimburse_reimburseservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L31"}, {"id": "bx_reimburse_reimburseservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L35"}, {"id": "bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L35"}, {"id": "bx_reimburse_reimburseservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L39"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L39"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L39"}, {"id": "bx_reimburse_reimburseservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L49"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L49"}, {"id": "bx_reimburse_reimburseservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L56"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L56"}, {"id": "bx_reimburse_reimburseservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L69"}, {"id": "bx_reimburse_reimburseservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L125"}, {"id": "bx_reimburse_reimburseservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L173"}, {"id": "bx_reimburse_reimburseservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L190"}, {"id": "bx_reimburse_reimburseservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L235"}, {"id": "bx_reimburse_reimburseservice_get_approvals", "label": ".get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L272"}, {"id": "bx_reimburse_reimburseservice_create_with_details", "label": ".create_with_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L324"}, {"id": "bx_reimburse_reimburseservice_update_with_details", "label": ".update_with_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L370"}, {"id": "bx_reimburse_reimburseservice_get_details", "label": ".get_details()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py", "source_location": "L423"}, {"id": "bxreimbursedetail", "label": "BxReimburseDLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "label": "workflow.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L1"}, {"id": "bx_workflow_workflowservice", "label": "WorkflowService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L14"}, {"id": "bx_workflow_workflowservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L16"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L16"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L16"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L16"}, {"id": "bx_workflow_workflowservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L20"}, {"id": "bxworkflowdef", "label": "BxWorkflowDef", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L20"}, {"id": "bx_workflow_workflowservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L24"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L24"}, {"id": "bx_workflow_workflowservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L33"}, {"id": "bx_workflow_workflowservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L50"}, {"id": "bx_workflow_workflowservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L68"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L68"}, {"id": "bx_workflow_workflowservice_get_by_biz_type", "label": ".get_by_biz_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L82"}, {"id": "bx_workflow_workflowservice_save_nodes", "label": ".save_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L94"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L94"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_workflow_py", "target": "app_core_utils", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py", "source_location": "L9", "weight": 1.0}, {"source": "uLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_bx_advance_py", "label": "advance.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L1"}, {"id": "bx_advance_advanceservice", "label": "AdvanceService", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L24"}, {"id": "bx_advance_advanceservice_tenant_no", "label": "._tenant_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L26"}, {"id": "bx_advance_advanceservice_get_by_id", "label": ".get_by_id()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L30"}, {"id": "bx_advance_advanceservice_get_paged", "label": ".get_paged()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L34"}, {"id": "bx_advance_advanceservice_create", "label": ".create()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L44"}, {"id": "bx_advance_advanceservice_update", "label": ".update()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L61"}, {"id": "bx_advance_advanceservice_soft_delete", "label": ".soft_delete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L79"}, {"id": "bx_advance_advanceservice_pending_approval", "label": ".pending_approval()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L86"}, {"id": "bx_advance_advanceservice_approved_by_me", "label": ".approved_by_me()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L142"}, {"id": "bx_advance_advanceservice_workflow_nodes", "label": "._workflow_nodes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L190"}, {"id": "bx_advance_advanceservice_submit", "label": ".submit()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L203"}, {"id": "bx_advance_advanceservice_approve", "label": ".approve()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L220"}, {"id": "bx_advance_advanceservice_reject", "label": ".reject()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L265"}, {"id": "bx_advance_advanceservice_get_approvals", "label": ".get_approvals()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L302"}, {"id": "bx_advance_advanceservice_global_status_counts", "label": ".global_status_counts()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_location": "L354"}, {"id": "bx_advance_advanceservice_my_apply_stats", "label": ".my_apply_stats()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py", "source_locaLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "label": "expense_item.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L1"}, {"id": "bx_expense_item_get_expense_item_tree", "label": "get_expense_item_tree()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "bxusercontext", "label": "BxUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L17"}, {"id": "bx_expense_item_get_expense_item", "label": "get_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L27"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L27"}, {"id": "bx_expense_item_create_expense_item", "label": "create_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L40"}, {"id": "expenseitemcreate", "label": "ExpenseItemCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L40"}, {"id": "bx_expense_item_update_expense_item", "label": "update_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L56"}, {"id": "expenseitemupdate", "label": "ExpenseItemUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L56"}, {"id": "bx_expense_item_delete_expense_item", "label": "delete_expense_item()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L77"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_bx_auth", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_schemas_bx_expense_item", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_bx_expense_item_py", "target": "app_services_bx_expense_item", "relation": "imports_from", "context": "import", "confidence": "EXTRACLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "label": "attachment.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L1"}, {"id": "bx_attachment_attachmentout", "label": "AttachmentOut", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L9"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L1", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "target": "pydantic", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L3", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "target": "pydantic_alias_generators", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "target": "app_schemas_bx_common", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_bx_attachment_py", "target": "bx_attachment_attachmentout", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L9", "weight": 1.0}, {"source": "bx_attachment_attachmentout", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py", "source_location": "L9", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "label": "bx_models.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}, {"id": "models_bx_models_bxexpenseitem", "label": "BxExpenseItem", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_bx_models_bxworkflowdef", "label": "BxWorkflowDef", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L33"}, {"id": "models_bx_models_bxworkflownode", "label": "BxWorkflowNode", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L61"}, {"id": "models_bx_models_bxadvanceapply", "label": "BxAdvanceApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L85"}, {"id": "models_bx_models_bxadvanceapproval", "label": "BxAdvanceApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L118"}, {"id": "models_bx_models_bxreimburseapply", "label": "BxReimburseApply", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L141"}, {"id": "models_bx_models_bxreimbursedetail", "label": "BxReimburseDetail", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L177"}, {"id": "models_bx_models_bxreimburseapproval", "label": "BxReimburseApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L198"}, {"id": "models_bx_models_bxreimburseoffset", "label": "BxReimburseOffset", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L221"}, {"id": "models_bx_models_bxattachment", "label": "BxAttachment", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L239"}, {"id": "models_bx_models_rationale_1", "label": "\u62a5\u9500\uff08bx\uff09\u6a21\u5757\u6570\u636e\u6a21\u578b \u2014 \u5bf9\u9f50 docs/sql/20260606_bx_tables.sql", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L2", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxexpenseitem", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10", "weight": 1.0}, {"source": "models_bx_models_bxexpenseitem", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_models_bx_models_py", "target": "models_bx_models_bxworkflowdef", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location": "L33", "weight": 1.0}, {"source": "models_bx_models_bxworkflowdef", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py", "source_location":Line truncated
+1 -1
View File
@@ -1 +1 @@
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":5362,"mtime_ns":1780108930820143914,"hash":"712114b9a96e370660a7d8455b0cb014b6387a352e25c114bb396bc9815d48e6"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":4532,"mtime_ns":1780201410113131199,"hash":"276f225cb323016de117038efef175f4e91a9474bced64b43dc0ea4bd976b057"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":1634,"mtime_ns":1780201410115316494,"hash":"9ee508b67da0044015aff95ebddaf52a5ba551ccfee772bf134ca88e2d482e72"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":8744,"mtime_ns":1780201410112435240,"hash":"df84858ae3cea547e5ae0174a2ee76cd4df8835f4b75827843369e74ea91098b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":5505,"mtime_ns":1780131767561871048,"hash":"bd8940c34f0feef909d268e6af9fee67705b0a95eae4906ca82e712097e20748"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":8978,"mtime_ns":1780108930724617124,"hash":"198e0861bb9933a7cee510524c4802001543468727a45e779bc4684d0214ffe2"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1173,"mtime_ns":1780108930724965209,"hash":"8c45b5a56110164f2488c9c8db27db62fe77f0997cf22c53991c0bd1dde2043a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":13209,"mtime_ns":1780108930725541087,"hash":"c3138253eb34f65b09f4436e037fb3179d947f84d3b5fef007ed9a8e3bb135ee"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":5949,"mtime_ns":1780108930724890709,"hash":"c92a7dbcfe3d7440bfafc83a76e9ed431e2b054ddd95bc4bfac52b241635ab17"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":11243,"mtime_ns":1780108930724537332,"hash":"0c2045d182138ec8e333356d36d418004a53948e2eda494cd419a36365ebbfae"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4eLine truncated
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":5362,"mtime_ns":1780108930820143914,"hash":"712114b9a96e370660a7d8455b0cb014b6387a352e25c114bb396bc9815d48e6"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":4650,"mtime_ns":1780750556882139523,"hash":"5906745ed3a9a4a62ccfec436a8fac62e8c6cb5c5a61715bb1979a7e5bef804a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":1634,"mtime_ns":1780201410115316494,"hash":"9ee508b67da0044015aff95ebddaf52a5ba551ccfee772bf134ca88e2d482e72"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":8744,"mtime_ns":1780201410112435240,"hash":"df84858ae3cea547e5ae0174a2ee76cd4df8835f4b75827843369e74ea91098b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":5692,"mtime_ns":1780750556882308648,"hash":"a1404064f42d27efcdf287062da76d4cc863112aa1f2ace1bf86c2f72e8faef9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":8978,"mtime_ns":1780108930724617124,"hash":"198e0861bb9933a7cee510524c4802001543468727a45e779bc4684d0214ffe2"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1173,"mtime_ns":1780108930724965209,"hash":"8c45b5a56110164f2488c9c8db27db62fe77f0997cf22c53991c0bd1dde2043a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":13209,"mtime_ns":1780108930725541087,"hash":"c3138253eb34f65b09f4436e037fb3179d947f84d3b5fef007ed9a8e3bb135ee"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":5949,"mtime_ns":1780108930724890709,"hash":"c92a7dbcfe3d7440bfafc83a76e9ed431e2b054ddd95bc4bfac52b241635ab17"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":11243,"mtime_ns":1780108930724537332,"hash":"0c2045d182138ec8e333356d36d418004a53948e2eda494cd419a36365ebbfae"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4eLine truncated
+4 -4
View File
@@ -63,12 +63,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">2643 nodes &middot; 18676 edges &middot; 106 communities</div>
<div id="stats">3047 nodes &middot; 20600 edges &middot; 130 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "start_prod_sh", "label": "start-prod.sh", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.9, "font": {"size": 0, "color": "#ffffff"}, "title": "start-prod.sh", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 13}, {"id": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "label": "start-prod.sh script", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "start-prod.sh script", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 7}, {"id": "backend_start_prod_log_info", "label": "log_info()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_info()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_log_warn", "label": "log_warn()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_warn()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_log_error", "label": "log_error()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_error()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_show_help", "label": "show_help()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "show_help()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 2}, {"id": "backend_start_prod_ensure_dirs", "label": "ensure_dirs()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "ensure_dirs()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 3}, {"id": "backend_start_prod_check_python", "label": "check_python()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "check_python()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_ensure_venv_and_runtime", "label": "ensure_venv_and_runtime()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "ensure_venv_and_runtime()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_is_running", "label": "is_running()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "is_running()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_start_server", "label": "start_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.6, "font": {"size": 0, "color": "#ffffff"}, "title": "start_server()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 9}, {"id": "backend_start_prod_stop_server", "label": "stop_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "stop_server()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_status_server", "label": "status_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "status_server()", "community": 25, "community_name": "Community 25", "source_fiLine truncated
const RAW_EDGES = [{"from": "start_prod_sh", "to": "backend_start_prod_check_python", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_ensure_dirs", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_ensure_venv_and_runtime", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_is_running", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_error", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_info", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_warn", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_show_help", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_show_logs", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_start_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_status_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_stop_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_log_error", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_show_help", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_show_logs", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_start_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_status_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_stop_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_check_python", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_start_server", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_stop_server", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_ensure_venv_and_runtime", "to": "backend_start_prod_log_warn", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "conLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 44}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 103}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 24}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 35}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 30}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 67}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 95}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 9}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 36}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 16}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 57}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 169}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 85}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 222}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 40}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 26}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 33}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 23}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 37}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 11}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 18}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 54}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 23}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 15}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 14}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 12}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 21}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 20}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 8}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 5}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 2}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 1}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 1}, {"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": 34}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 25}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 5}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 27}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 34}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 19}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 17}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 18}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 24}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 77}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 29}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 29}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 54}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 12}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 23}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 23}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 14}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 7}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 22}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 17}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 12}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 26}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 3}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 18}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 33}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 30}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 18}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 30}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 16}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 1}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 1}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 19}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 46}, {"cid": 70, "color": "#4E79A7", "label": "CoLine truncated
const RAW_NODES = [{"id": "start_prod_sh", "label": "start-prod.sh", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.9, "font": {"size": 0, "color": "#ffffff"}, "title": "start-prod.sh", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 13}, {"id": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "label": "start-prod.sh script", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "start-prod.sh script", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 7}, {"id": "backend_start_prod_log_info", "label": "log_info()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_info()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_log_warn", "label": "log_warn()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_warn()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_log_error", "label": "log_error()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "log_error()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_show_help", "label": "show_help()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "show_help()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 2}, {"id": "backend_start_prod_ensure_dirs", "label": "ensure_dirs()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "ensure_dirs()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 3}, {"id": "backend_start_prod_check_python", "label": "check_python()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "check_python()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_ensure_venv_and_runtime", "label": "ensure_venv_and_runtime()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "ensure_venv_and_runtime()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_is_running", "label": "is_running()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "is_running()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 4}, {"id": "backend_start_prod_start_server", "label": "start_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.6, "font": {"size": 0, "color": "#ffffff"}, "title": "start_server()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 9}, {"id": "backend_start_prod_stop_server", "label": "stop_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "stop_server()", "community": 25, "community_name": "Community 25", "source_file": "start-prod.sh", "file_type": "code", "degree": 5}, {"id": "backend_start_prod_status_server", "label": "status_server()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "status_server()", "community": 25, "community_name": "Community 25", "source_fiLine truncated
const RAW_EDGES = [{"from": "start_prod_sh", "to": "backend_start_prod_check_python", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_ensure_dirs", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_ensure_venv_and_runtime", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_is_running", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_error", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_info", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_log_warn", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_show_help", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_show_logs", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_start_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_status_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "backend_start_prod_stop_server", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "start_prod_sh", "to": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_log_error", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_show_help", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_show_logs", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_start_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_status_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "users_zlwu_work_00code_shanxia2_0_backend_start_prod_sh__entry", "to": "backend_start_prod_stop_server", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_check_python", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_start_server", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_stop_server", "to": "backend_start_prod_log_info", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "backend_start_prod_ensure_venv_and_runtime", "to": "backend_start_prod_log_warn", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "conLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 28}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 43}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 23}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 35}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 42}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 18}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 73}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 9}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 53}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 16}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 70}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 168}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 53}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 222}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 46}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 26}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 33}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 67}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 37}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 22}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 18}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 24}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 17}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 15}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 14}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 12}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 33}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 38}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 8}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 5}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 2}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 1}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 1}, {"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": 15}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 55}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 22}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 27}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 63}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 34}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 17}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 21}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 18}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 35}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 89}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 55}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 55}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 29}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 25}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 23}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 7}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 21}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 22}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 17}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 12}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 19}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 3}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 18}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 33}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 33}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 18}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 31}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 16}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 1}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 1}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 16}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 46}, {"cid": 70, "color": "#4E79A7", "label": "CoLine truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
+24926 -1068
View File
File diff suppressed because it is too large. Load diff
+526 -113
View File
@@ -1,334 +1,747 @@
{
"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py": {
"mtime": 1780113982.281974,
"hash": "9c26ed050e94de5ab7c16bf27c0c42ef"
"ast_hash": "9c26ed050e94de5ab7c16bf27c0c42ef",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py": {
"mtime": 1780129938.8065724,
"hash": "7b32b6d042a2b3afa012cd3d8fe9cfac"
"mtime": 1780750556.8821394,
"ast_hash": "9d2dc014c76287a8e8a4a7f4c4c633cc",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/customer_pool.py": {
"mtime": 1780148500.3765707,
"hash": "6aa9af295015a82fdb6f54e50a8f6a2f"
"mtime": 1780202350.1478229,
"ast_hash": "1fcafe4082c11b33ab705292cf07b20f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py": {
"mtime": 1780148514.5971222,
"hash": "753c8681539418af3ddf8c57880b6442"
"mtime": 1780201410.1153164,
"ast_hash": "334dffe3a8b19bc6b49008d6f7265a11",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/tenant_context.py": {
"mtime": 1780136000.1439688,
"hash": "b18133acb1c4bd1ab0709c786f70e133"
"mtime": 1780201410.2101986,
"ast_hash": "b18133acb1c4bd1ab0709c786f70e133",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py": {
"mtime": 1780136008.2288265,
"hash": "66e0e3cc285a14d0f224f3bd3ee24db7"
"mtime": 1780201410.1124353,
"ast_hash": "66e0e3cc285a14d0f224f3bd3ee24db7",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py": {
"mtime": 1780108930.7244444,
"hash": "fe51609d806a6f1ccb81ddce26e79320"
"ast_hash": "fe51609d806a6f1ccb81ddce26e79320",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py": {
"mtime": 1780108930.7251556,
"hash": "3f7807dffdde603ff65eeeed5883f115"
"ast_hash": "3f7807dffdde603ff65eeeed5883f115",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py": {
"mtime": 1780131767.561871,
"hash": "650ee00a9cad587e2ca66be4bc583aff"
"mtime": 1780750556.8823087,
"ast_hash": "b7a28e6af80b6160097dfd7dbdfa5ff6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py": {
"mtime": 1780108930.725768,
"hash": "7b125f92286034b9c80b7015ea7eac0e"
"ast_hash": "7b125f92286034b9c80b7015ea7eac0e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py": {
"mtime": 1780108930.7246172,
"hash": "c6c3d77e419c40a3315a4d8cb9c86198"
"ast_hash": "c6c3d77e419c40a3315a4d8cb9c86198",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py": {
"mtime": 1780108930.724965,
"hash": "8679f18f61d636cf8c9888af4583f140"
"ast_hash": "8679f18f61d636cf8c9888af4583f140",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py": {
"mtime": 1780108930.72547,
"hash": "d4b24a2d7249c3e3fb15d5e7456fbed8"
"ast_hash": "d4b24a2d7249c3e3fb15d5e7456fbed8",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py": {
"mtime": 1780108930.7253509,
"hash": "f683e5ef0867a02da2c06d8461359096"
"ast_hash": "f683e5ef0867a02da2c06d8461359096",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py": {
"mtime": 1780108930.7243123,
"hash": "04e3e5031c7ca38b0fc3c69272f0b4da"
"ast_hash": "04e3e5031c7ca38b0fc3c69272f0b4da",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py": {
"mtime": 1780108930.7254124,
"hash": "bec8efa5e032e42a16cafeff26bf98af"
"ast_hash": "bec8efa5e032e42a16cafeff26bf98af",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py": {
"mtime": 1780108930.725223,
"hash": "c63dc7c451f9b8b826e88135bbbc667e"
"ast_hash": "c63dc7c451f9b8b826e88135bbbc667e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py": {
"mtime": 1780108930.725025,
"hash": "2d581fb593646b13bba707de9e64368f"
"ast_hash": "2d581fb593646b13bba707de9e64368f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py": {
"mtime": 1780108930.7248244,
"hash": "4a9ca81161363408fe2ac0397cde2a1c"
"ast_hash": "4a9ca81161363408fe2ac0397cde2a1c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py": {
"mtime": 1780108930.7256074,
"hash": "88470f8073435a02b371a8a95c7d54a1"
"ast_hash": "88470f8073435a02b371a8a95c7d54a1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py": {
"mtime": 1780108930.725541,
"hash": "81f48c571d43280f09fd8dd0eda348cc"
"ast_hash": "81f48c571d43280f09fd8dd0eda348cc",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py": {
"mtime": 1780108930.7252846,
"hash": "bd9a75bf01f391271943d8ade0f96558"
"ast_hash": "bd9a75bf01f391271943d8ade0f96558",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py": {
"mtime": 1780108930.7250843,
"hash": "6b750c71e0cccaa33199ab7dd9784d47"
"ast_hash": "6b750c71e0cccaa33199ab7dd9784d47",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py": {
"mtime": 1780108930.7248907,
"hash": "6a28c0d184a61ebf4426e9475c9caddf"
"ast_hash": "6a28c0d184a61ebf4426e9475c9caddf",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py": {
"mtime": 1780108930.7243788,
"hash": "8ca43f66333d7a0219028e53f488d3e6"
"ast_hash": "8ca43f66333d7a0219028e53f488d3e6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py": {
"mtime": 1780108930.7245374,
"hash": "ccd86a39a77b19b1a3641dc854d7ddb7"
"ast_hash": "ccd86a39a77b19b1a3641dc854d7ddb7",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py": {
"mtime": 1780108930.7256842,
"hash": "bccf04281267702872c44091979972e1"
"ast_hash": "bccf04281267702872c44091979972e1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/models.py": {
"mtime": 1780131847.0058825,
"hash": "2412d5b473634e2e582d07b3ac2f8d0f"
"mtime": 1780201410.1145027,
"ast_hash": "2412d5b473634e2e582d07b3ac2f8d0f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/__init__.py": {
"mtime": 1780112936.52883,
"hash": "c7b352109a86ebf6a84e98cade8c0fc0"
"ast_hash": "c7b352109a86ebf6a84e98cade8c0fc0",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/crm_models.py": {
"mtime": 1780150221.3008816,
"hash": "8e9c2517e004387f5e7276250c1f1096"
"mtime": 1780743830.2210455,
"ast_hash": "e219acfafb17fc75b435021e04fc892a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/base.py": {
"mtime": 1780108930.807299,
"hash": "bd94a0e35e05556b29b23e2fdbe108ee"
"ast_hash": "bd94a0e35e05556b29b23e2fdbe108ee",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/auth.py": {
"mtime": 1780108930.8081977,
"hash": "10bd8e938e2e2069dd06b69c1a261db5"
"ast_hash": "10bd8e938e2e2069dd06b69c1a261db5",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/user.py": {
"mtime": 1780108930.8101847,
"hash": "7c9449203e3f490cf6c7925c53830bb9"
"ast_hash": "7c9449203e3f490cf6c7925c53830bb9",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/__init__.py": {
"mtime": 1780112973.062398,
"hash": "6928db58baf42e0b4c181fb52ab50c4a"
"ast_hash": "6928db58baf42e0b4c181fb52ab50c4a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/menu.py": {
"mtime": 1780108930.808682,
"hash": "4a96aebf6a475372053a632212422204"
"ast_hash": "4a96aebf6a475372053a632212422204",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/attachment.py": {
"mtime": 1780108930.8081448,
"hash": "a2a9aa2a9ec2ad982bfc0925bd60ff47"
"ast_hash": "a2a9aa2a9ec2ad982bfc0925bd60ff47",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/common.py": {
"mtime": 1780108930.8084266,
"hash": "49c0726bb57476e86f16f3b67dfb89e3"
"ast_hash": "49c0726bb57476e86f16f3b67dfb89e3",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/workflow.py": {
"mtime": 1780108930.810246,
"hash": "c8f5dda38b78a3b7b08e26994d82bb99"
"ast_hash": "c8f5dda38b78a3b7b08e26994d82bb99",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/leave.py": {
"mtime": 1780108930.808614,
"hash": "5466d56c6c00865aac2400bc5ed0bfde"
"ast_hash": "5466d56c6c00865aac2400bc5ed0bfde",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/pool_rule.py": {
"mtime": 1780146664.2302885,
"hash": "de74af98ef8b3f4cea1515b45221d000"
"mtime": 1780202212.7342336,
"ast_hash": "75feeee85a38a29ff9d54872ced67b52",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/customer_pool.py": {
"mtime": 1780146654.2520175,
"hash": "41f825d4a540b3b21773664726aa5925"
"mtime": 1780202198.6982312,
"ast_hash": "a741112d51c7b9429d0b34845674253c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/customer_follow.py": {
"mtime": 1780146651.429359,
"hash": "e0dc2a6ccc7643de2fc02c321a238f07"
"mtime": 1780201410.2139344,
"ast_hash": "d59a60bd1ff1fdd175d98bd39aa0b248",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/__init__.py": {
"mtime": 1780129331.8594196,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
"mtime": 1780201410.213315,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/product.py": {
"mtime": 1780135028.7992775,
"hash": "93a8897e3a2b6380268b4fb241cd1fde"
"mtime": 1780743830.2208753,
"ast_hash": "56b53f2ca2ffccab8186a6cb61e7c94e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/customer.py": {
"mtime": 1780146617.7360954,
"hash": "42d1c32291de54a365f6802bd064ebc0"
"mtime": 1780713560.8434699,
"ast_hash": "639b5ea6917acab50233b93149d96f3b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/contact.py": {
"mtime": 1780146642.8435316,
"hash": "9d145bfd34f8106be403e7058c144111"
"mtime": 1780742840.9759488,
"ast_hash": "b8b1b11592045acba091761fe3a193aa",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/sample_code.py": {
"mtime": 1780114002.126415,
"hash": "b7daeb88aeae6bb90b6db066c5a9dc32"
"ast_hash": "b7daeb88aeae6bb90b6db066c5a9dc32",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/post.py": {
"mtime": 1780114001.6183877,
"hash": "fb8194bc855fd9928b51ad2923481be3"
"ast_hash": "fb8194bc855fd9928b51ad2923481be3",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/user.py": {
"mtime": 1780132195.6693316,
"hash": "18d14c927fd17385d3c5727c32192087"
"mtime": 1780201410.1127322,
"ast_hash": "18d14c927fd17385d3c5727c32192087",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/dict_type.py": {
"mtime": 1780108930.7273602,
"hash": "1078f742bd94ea4910f6b0455e7b6907"
"ast_hash": "1078f742bd94ea4910f6b0455e7b6907",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/tenant.py": {
"mtime": 1780108930.7319968,
"hash": "c06904189c34747b76d2156eec371510"
"ast_hash": "c06904189c34747b76d2156eec371510",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/biz_no_generator.py": {
"mtime": 1780108930.726091,
"hash": "7fbe59bac864b3b6a1ce8c98d3433806"
"ast_hash": "7fbe59bac864b3b6a1ce8c98d3433806",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/__init__.py": {
"mtime": 1780108930.725878,
"hash": "187d5328ae0c945daec2698273de8e3f"
"ast_hash": "187d5328ae0c945daec2698273de8e3f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/role.py": {
"mtime": 1780114000.0273001,
"hash": "2cd44770735b6a837e6b6e962baee4c1"
"ast_hash": "2cd44770735b6a837e6b6e962baee4c1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/menu.py": {
"mtime": 1780113999.2640977,
"hash": "f1beeee9f545a3c3be738d41db5bce92"
"ast_hash": "f1beeee9f545a3c3be738d41db5bce92",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/site.py": {
"mtime": 1780113998.4629104,
"hash": "15931bf4ec37b46d0c69787857570f8f"
"ast_hash": "15931bf4ec37b46d0c69787857570f8f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/api_request_log.py": {
"mtime": 1780108930.725946,
"hash": "7720097399d092082a8f3dfe85e7cf17"
"ast_hash": "7720097399d092082a8f3dfe85e7cf17",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/dict_data.py": {
"mtime": 1780112650.1176188,
"hash": "73db684fd213bcafa513c4688011e3e9"
"ast_hash": "73db684fd213bcafa513c4688011e3e9",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/ethnic.py": {
"mtime": 1780108930.727449,
"hash": "00eda51c8ed1364f97c402a7473845a7"
"ast_hash": "00eda51c8ed1364f97c402a7473845a7",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/workflow.py": {
"mtime": 1780113998.1283996,
"hash": "0f633df841ec3b1add87d19c4dd915f7"
"ast_hash": "0f633df841ec3b1add87d19c4dd915f7",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/dept.py": {
"mtime": 1780113996.9075956,
"hash": "be372df2f78a90c68ab8b719a7aa7690"
"ast_hash": "be372df2f78a90c68ab8b719a7aa7690",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/base.py": {
"mtime": 1780108930.7260103,
"hash": "06a4dbc841251f19cc2c2c394529f94c"
"ast_hash": "06a4dbc841251f19cc2c2c394529f94c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/leave.py": {
"mtime": 1780113996.1154153,
"hash": "1632690f656222fb876eb136ca2035db"
"ast_hash": "1632690f656222fb876eb136ca2035db",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/pool_rule.py": {
"mtime": 1780148540.1242862,
"hash": "e975f0167db96a1895bac63c792b6c97"
"mtime": 1780202270.0656207,
"ast_hash": "e1692096d32c0fe8c742744725d1a28e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/customer_follow.py": {
"mtime": 1780147793.2627335,
"hash": "8196ca6a811adcfe69f7d4399c0d164e"
"mtime": 1780730768.3131928,
"ast_hash": "a9c334b9b2df04b5ac130a58d3c6e21b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/__init__.py": {
"mtime": 1780129578.933081,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
"mtime": 1780201410.2102547,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/product.py": {
"mtime": 1780129637.8935082,
"hash": "434a739ff235bc09da6a80ae363b05f2"
"mtime": 1780743830.2505348,
"ast_hash": "fc7384d219c7405aec4121235966bae0",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/customer.py": {
"mtime": 1780148039.453028,
"hash": "492328d53b41893fabb02f2c03389c19"
"mtime": 1780713573.6127172,
"ast_hash": "550c56779b2d3d6a4ad79c792e84eea2",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contact.py": {
"mtime": 1780147415.5234418,
"hash": "504e27b49e3cca101eba696695702947"
"mtime": 1780742842.0334857,
"ast_hash": "e6f05d53c32fc8a564af616b7f71ce4e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/public.py": {
"mtime": 1780132204.7861738,
"hash": "b8dbbad0cee1fcfbca1b29fce2709ac3"
"mtime": 1780242127.112912,
"ast_hash": "535c23e75e73d357bec70077940a8f58",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm.py": {
"mtime": 1780150691.0089288,
"hash": "0e5d9937b200cf143d7b1f5e7ca39df5"
"mtime": 1780743830.2466538,
"ast_hash": "2653a10ba9dc92333bb62a12dd58ea4d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py": {
"mtime": 1780129936.4530525,
"hash": "8a72471f9488247ccc6483544e3dd015"
"mtime": 1780750556.8831582,
"ast_hash": "f0ed76e504b60330b436160d96753b61",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/menu.py": {
"mtime": 1780150457.723112,
"hash": "f88d23c27bb4142f77c42741ea90b9cd"
"mtime": 1780211169.1086953,
"ast_hash": "f88d23c27bb4142f77c42741ea90b9cd",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/admin.py": {
"mtime": 1780113995.6094534,
"hash": "fb95c76c1927b1140aa1baaa10dfec13"
"mtime": 1780736311.9087148,
"ast_hash": "b04ea9596095a1f88fd7e4b9ba050144",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/workflow.py": {
"mtime": 1780113993.7604175,
"hash": "d1a662b8282b35634082fa2a96fba3f1"
"ast_hash": "d1a662b8282b35634082fa2a96fba3f1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/leave.py": {
"mtime": 1780108930.7226436,
"hash": "ee4d128a94fe28cb7c34ff3db71699a1"
"ast_hash": "ee4d128a94fe28cb7c34ff3db71699a1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/workflow_callback.py": {
"mtime": 1780108930.810395,
"hash": "6e6a664c5edb9d5f294caef41f5fc477"
"ast_hash": "6e6a664c5edb9d5f294caef41f5fc477",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/workflow_engine.py": {
"mtime": 1780113992.9149039,
"hash": "fa0d8d7290b264cc636268b3191c4ea7"
"ast_hash": "fa0d8d7290b264cc636268b3191c4ea7",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/web_auth_roles.py": {
"mtime": 1780132204.0484734,
"hash": "76817c400c5eb1caf47ebe6aa4ab01a4"
"mtime": 1780201410.114958,
"ast_hash": "76817c400c5eb1caf47ebe6aa4ab01a4",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/ws/__init__.py": {
"mtime": 1780108930.810617,
"hash": "fe3e36317dd396513ca21b57101ee48f"
"ast_hash": "fe3e36317dd396513ca21b57101ee48f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/scripts/generate_dict_reimport_sql.py": {
"mtime": 1780134700.0788686,
"hash": "c6aae5828051c66f51b0da5561b399f8"
"mtime": 1780201410.2450788,
"ast_hash": "c6aae5828051c66f51b0da5561b399f8",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/requirements.txt": {
"mtime": 1780129939.3284025,
"hash": "b253ed31d8d9baf7b62ec4819c9901ef"
"mtime": 1780201410.1338527,
"ast_hash": "b253ed31d8d9baf7b62ec4819c9901ef",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/README.md": {
"mtime": 1780114581.2918417,
"hash": "bc7b9cd033927a3f580cd89c5deb6494"
"ast_hash": "bc7b9cd033927a3f580cd89c5deb6494",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh": {
"mtime": 1780108930.820144,
"ast_hash": "7e463abdbe55313b4e9959312fd91460",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh": {
"mtime": 1780114661.4469833,
"ast_hash": "214c10e3fb95b8d7f17bcd5c6dd96126",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py": {
"mtime": 1780750904.0571795,
"ast_hash": "31445f82b9838f6ddcbf08ecedaea351",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py": {
"mtime": 1780750556.882335,
"ast_hash": "992aca5bcca63b250109f23d36031bdb",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py": {
"mtime": 1780750556.881769,
"ast_hash": "c40b659e0b1d8a549e97923a9a536719",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/crm_export_columns.py": {
"mtime": 1780743830.2552605,
"ast_hash": "b24d3dcc0dadcfcc7b6db5171873a852",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/__init__.py": {
"mtime": 1780242127.0433192,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/excel_export.py": {
"mtime": 1780242127.113234,
"ast_hash": "3a3590bf5730f7eb196963e76d5baa21",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py": {
"mtime": 1780750556.8829591,
"ast_hash": "ca25a3c561c4a1bfc566168d85b5f4de",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py": {
"mtime": 1780750591.4901433,
"ast_hash": "e8acfeef225d507ed02a31a5e64ba3f1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/monitor/__init__.py": {
"mtime": 1780242127.108406,
"ast_hash": "68b329da9893e34099c7d8ad5cb9c940",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/monitor/operlog.py": {
"mtime": 1780201410.2162259,
"ast_hash": "47b4532502b115b79d489386f8470fbf",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/__init__.py": {
"mtime": 1780750556.8817346,
"ast_hash": "2cea3812d44c57f24aa7f00648a14412",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py": {
"mtime": 1780750556.882896,
"ast_hash": "e97cb788e93cfac0688c0aa4cbd2cf93",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/quote.py": {
"mtime": 1780745856.749184,
"ast_hash": "d3d1b49b13fecb1c34fbb695ca6cafe9",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/supplier_info.py": {
"mtime": 1780242127.1122696,
"ast_hash": "60446fd83e876332a414fecefc3216ee",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/opportunity.py": {
"mtime": 1780242127.112297,
"ast_hash": "6af8aec9a012bb026f75feebab2f41f3",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/clue.py": {
"mtime": 1780215022.1890757,
"ast_hash": "fce08aacb7699a63010fcd3f29f8a965",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/statistics.py": {
"mtime": 1780201410.2159066,
"ast_hash": "6b5620e22a44ce3555597fe45d968813",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/contract.py": {
"mtime": 1780750556.8821254,
"ast_hash": "6553f54ca1fd19086c170095f402a628",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/invoice_info.py": {
"mtime": 1780406386.885208,
"ast_hash": "910b8037a62a9af50f5803f8a243e3ae",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/oss.py": {
"mtime": 1780201410.2129664,
"ast_hash": "8ee3bcd0df19a921a7530e52c89e4866",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/monitor/__init__.py": {
"mtime": 1780242127.1123073,
"ast_hash": "68b329da9893e34099c7d8ad5cb9c940",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/monitor/oper_log.py": {
"mtime": 1780201410.2128606,
"ast_hash": "4cfec230d90a820a2350201da6e940bf",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract_product.py": {
"mtime": 1780750556.8823483,
"ast_hash": "d2e1538af2d36ecc9daf28a3b135bd0f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/customer_pool.py": {
"mtime": 1780202237.3421319,
"ast_hash": "a54f2cb33f4466814ac86f043d844b98",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract_approval.py": {
"mtime": 1780750556.8831775,
"ast_hash": "c9c338510247afdcd733412c10e0ded3",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/opportunity_follow.py": {
"mtime": 1780242127.1121516,
"ast_hash": "6fe6db9ed2c4f2a7b703464c83961fdf",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/quote.py": {
"mtime": 1780745858.4985561,
"ast_hash": "a70dc5bf1f9cf76746aa2c0718041690",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/clue_follow.py": {
"mtime": 1780215022.1935425,
"ast_hash": "5ac9d3d0e7c35d761bdfda85a19b3178",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/quote_product.py": {
"mtime": 1780242127.0685878,
"ast_hash": "99a00e493072478bdd3e91e6436efd99",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/supplier_info.py": {
"mtime": 1780242127.1134887,
"ast_hash": "7d09a0306a02be994c6cb455c7238e3a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/quote_share_log.py": {
"mtime": 1780242127.1124375,
"ast_hash": "a07b92a54b8b0306dfc06845f038a52d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/quote_approval.py": {
"mtime": 1780736832.004071,
"ast_hash": "7af79c7ce3f3d5b8a42260b68f9613d6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/opportunity_product.py": {
"mtime": 1780242127.0218031,
"ast_hash": "715eeca2e6270b4527ceec40c4007e11",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/opportunity.py": {
"mtime": 1780242127.1123164,
"ast_hash": "fca0ee3a3a9d131cf586c8e25f67e40b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/clue.py": {
"mtime": 1780215022.193645,
"ast_hash": "6a233d23691eee9749ebb7848b54e650",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/clue_statistics.py": {
"mtime": 1780750556.8829157,
"ast_hash": "9a026c628764467e4d1398900105dc78",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/statistics.py": {
"mtime": 1780201410.2125814,
"ast_hash": "a881a548c85953fc7da786343d69b9f8",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract.py": {
"mtime": 1780750556.8728096,
"ast_hash": "3cd90455687d8cc24d4b664d1fd5eaa2",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/invoice_info.py": {
"mtime": 1780406390.4614334,
"ast_hash": "c64a80cd0c5825f111e30fb6d745a40f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/monitor.py": {
"mtime": 1780201410.2100992,
"ast_hash": "438a2c9feb094343269bc83f7d1e884e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_sub_routes.py": {
"mtime": 1780750556.8831708,
"ast_hash": "c05e2faf142cb664a73975112d26d658",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_quote_routes.py": {
"mtime": 1780736833.57648,
"ast_hash": "1c648042be53d61883b62f966aa900ca",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py": {
"mtime": 1780750556.8818061,
"ast_hash": "e12302586789d2e8cf85469226be2899",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py": {
"mtime": 1780751360.1800659,
"ast_hash": "683657a15bc1923bff7a7d60f1dd965d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/crm_quote_pdf.py": {
"mtime": 1780242127.1123822,
"ast_hash": "c6247a99b20d9541dbabd49791104918",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py": {
"mtime": 1780750595.66133,
"ast_hash": "2dcb1f8935122ecc1129d47f41329e2d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py": {
"mtime": 1780750597.0023274,
"ast_hash": "84a4177765ae7baf25f55b8a7858cffe",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py": {
"mtime": 1780750631.6409054,
"ast_hash": "ec3143dd9c80ed89ccbdada33af3b048",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py": {
"mtime": 1780750609.568306,
"ast_hash": "254bddbaabdcab96560f20fe41924675",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py": {
"mtime": 1780750601.2191534,
"ast_hash": "a3f9e3f9e03b2b81d048be2fbb995a53",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/__init__.py": {
"mtime": 1780750594.045302,
"ast_hash": "782178a68c005e9671eeaef56ce8979f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py": {
"mtime": 1780750604.01886,
"ast_hash": "28fdbea9fada1d94d7de516d3979682d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py": {
"mtime": 1780750593.8290622,
"ast_hash": "1e6a5197afea4b037af84cccf34a884b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py": {
"mtime": 1780750871.4800005,
"ast_hash": "97ee54c0f5e7e6944a6d5edba9959b7b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py": {
"mtime": 1780751332.9608216,
"ast_hash": "cfef88a2406327f06f40623d0449b0f1",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py": {
"mtime": 1780750903.379992,
"ast_hash": "c3d0da835eff5f4d08d174741c7faa83",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py": {
"mtime": 1780750899.1853952,
"ast_hash": "9c83a5bfd6e2e780c216585ea3df6884",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py": {
"mtime": 1780750887.666369,
"ast_hash": "1534ad3911e5b3da36e09fec5efbb9fc",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py": {
"mtime": 1780751337.5985446,
"ast_hash": "f37820e31e876d2d9c3e7aecd61c64f6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py": {
"mtime": 1780750869.826924,
"ast_hash": "a2549a79ff52c07c21bcdce831ca28f5",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py": {
"mtime": 1780751335.1849163,
"ast_hash": "4d040ab00d9288b03a0e5448e54340cd",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py": {
"mtime": 1780751015.345417,
"ast_hash": "c0024c52ab77393141773302deaa44e5",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py": {
"mtime": 1780751365.9291217,
"ast_hash": "cd82cdcd1fecce13dd5c078782e128a5",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py": {
"mtime": 1780751365.9410522,
"ast_hash": "f9e1133888b3bf36e702a6e303623a34",
"semantic_hash": ""
}
}
+469
View File
@@ -0,0 +1,469 @@
# 报销系统(bx)移植实施方案
> **编写日期**:2026-06-06
> **状态**:方案已定稿,待按阶段实施
> **源系统**:`shanxia/bx/bx-backend` + `shanxia/bx/bx-web`
> **目标系统**:`shanxia2.0/backend` + `shanxia2.0/web-admin`
> **DDL 基线**:`docs/sql/20260606_bx_tables.sql`(已在 shanxia2.0 库执行)
---
## 1. 迁移目标
将原报销系统业务功能迁入山峡一体化门户(shanxia2.0),实现:
1. **业务等价**:备用金、报销、工作流、支付/冲抵、视图中心等能力与 bx 原系统一致;
2. **协议兼容**:移植 API 保持 bx 原 **REST 风格**(GET/PUT/DELETE/POST)、**入参位置**、**响应 envelope**(`code:0`、`msg`、`data.rows`),**不**统一为 CRM 的 POST Body 风格;
3. **体系融合**:用户/角色/菜单/认证使用门户 `sys_*`,业务表使用 `bx_*` + 门户公共字段;
4. **UI 融合**:Web 端挂「报销管理」一级菜单,页面适配 Fresh Mint 规范(第二阶段)。
**不在本次范围**:`bx-mobile` 移动端、`bx_user/bx_role/bx_menu` 等独立权限表、历史业务数据导入(空库启动)。
---
## 2. 范围边界
### 2.1 数据库(10 张业务表,已完成 DDL)
| 表 | 用途 | DDL 状态 |
|----|------|---------|
| `bx_expense_item` | 费用项目(二级树) | ✅ |
| `bx_workflow_def` | 工作流定义 | ✅ |
| `bx_workflow_node` | 工作流节点 | ✅ |
| `bx_advance_apply` | 备用金申请 | ✅ |
| `bx_advance_approval` | 备用金审批记录 | ✅ |
| `bx_reimburse_apply` | 报销申请 | ✅ |
| `bx_reimburse_detail` | 报销明细 | ✅ |
| `bx_reimburse_approval` | 报销审批记录 | ✅ |
| `bx_reimburse_offset` | 冲抵关联 | ✅ |
| `bx_attachment` | 附件 | ✅ |
**不建表**:`bx_user`、`bx_role`、`bx_role_user`、`bx_menu`、`bx_menu_role`。
### 2.2 后端 API(约 60 个端点,待移植)
| 模块 | 源文件 | 端点数 | 优先级 |
|------|--------|--------|--------|
| 费用项目 | `routers/expense_item.py` | 5 | P1 |
| 工作流 | `routers/workflow.py` | 8 | P1 |
| 备用金 | `routers/advance.py` | 14 | P2 |
| 附件 | `routers/attachment.py` | 6 | P2 |
| 报销 | `routers/reimburse.py` | 15 | P3 |
| 支付/发放 | `routers/payment.py` | 12 | P4 |
**不移植**:`auth.py`、`user.py`、`role.py`、`menu.py`。
### 2.3 前端页面(13 页,第二阶段)
| 分组 | 页面 | 源文件 |
|------|------|--------|
| 基础配置 | 费用项目管理、工作流(备用金)、工作流(报销) | `ExpenseProjectPage`、`WfAdvancePage`、`WfReimbursePage` |
| 备用金 | 申请、审批、发放管理 | `AdvanceApplyPage`、`AdvanceApprovePage`、`AdvanceIssuePage` |
| 报销 | 申请、审批、支付管理 | `ReimburseApplyPage`、`ReimburseApprovePage`、`PaymentPage` |
| 视图中心 | 我的申请、待我审批、全部申请单 | `MinePage`、`TodoPage`、`AllPage` |
**不移植**:用户/角色/菜单管理、首页 Dashboard、Demo 页。
---
## 3. 核心原则(与 CRM 移植的差异)
| 原则 | 说明 |
|------|------|
| **API 协议保持 bx 原样** | 列表用 GET + Query;响应 `{ code: 0, msg, data }`;分页 `data.rows`;**禁止**改为门户 `code:200` / `message` / `data.list` |
| **Router 近原样复制** | 从 `bx-backend/app/routers/` 复制到 `backend/app/api/bx/`,仅改 Depends、import、前缀、租户注入 |
| **Service 选择性迁移** | `services/*.py` 业务算法可复用;必须加 `tenant_no`、UUID 主键、`sys_user` 用户 ID |
| **不走 CRM crud 分层** | bx 模块内部保持 `router → service → model`,不强制拆 `crud/bx/`(除非后续维护需要) |
| **最小改动** | 不重命名 URL、不改 HTTP 方法、不改前端 camelCase 字段名 |
| **门户能力注入** | 认证、租户、DB Session、对象存储用 shanxia2.0 现有能力 |
---
## 4. 源系统与目标系统对照
### 4.1 技术栈(同源,利于移植)
| 项 | bx-backend | shanxia2.0 backend |
|----|-----------|-------------------|
| 语言 | Python 3 | Python 3.9 |
| 框架 | FastAPI | FastAPI |
| ORM | SQLAlchemy | SQLAlchemy |
| 校验 | Pydantic v2 | Pydantic v2 |
### 4.2 API 协议对照
| 维度 | bx 原系统 | shanxia2.0 CRM(勿用于 bx) | bx 移植目标 |
|------|----------|---------------------------|------------|
| 成功码 | `0` | `200` | **保持 `0`** |
| 消息字段 | `msg` | `message` | **保持 `msg`** |
| 时间戳 | 无 | `timestamp` | **不加** |
| 分页字段 | `rows, total, page, pageSize` | `list, total, page, pageSize` | **保持 `rows`** |
| 列表请求 | GET + query | POST + body | **保持 GET** |
| 路径示例 | `/api/advances/` | `/api/web/crm/customer/list` | **保持 `/api/advances/`** |
### 4.3 数据模型对照
| 维度 | bx 原 sx.sql | shanxia2.0 已执行 DDL |
|------|-------------|----------------------|
| 主键 | `bigint AUTO_INCREMENT` | `varchar(64) UUID` |
| 外键 ID | `bigint` | `varchar(64)` |
| 租户 | 无 | `tenant_no DEFAULT 'shanxia2.0'` |
| 用户引用 | `bx_user.id` | `sys_user.id` |
---
## 5. 目标架构
### 5.1 后端目录结构
```
backend/app/
├── models/bx_models.py # 10 张表 ORM(对齐 20260606 DDL)
├── schemas/bx/
│ ├── common.py # ApiResponse / PagedData(从 bx 复制)
│ ├── expense_item.py
│ ├── workflow.py
│ ├── advance.py
│ ├── reimburse.py
│ ├── payment.py
│ └── attachment.py # id: int → str
├── services/bx/ # 从 bx-backend/services 迁移
│ ├── base.py # 可选:通用分页(改 UUID)
│ ├── expense_item.py
│ ├── workflow.py
│ ├── advance.py
│ ├── reimburse.py
│ ├── payment.py
│ └── attachment.py
├── core/
│ └── bx_auth.py # 门户 user → BxUserContext
└── api/bx/
├── __init__.py # 聚合 bx_router
├── expense_item.py # prefix="/expense-items"
├── workflow.py # prefix="/workflows"
├── advance.py # prefix="/advances"
├── reimburse.py # prefix="/reimburses"
├── payment.py # prefix="/payments"
└── attachment.py # prefix="/attachments"
```
### 5.2 路由挂载(避免 `/api/api/...`)
bx 原 router 前缀为 `/api/advances`;门户 `main.py` 已挂 `prefix="/api"`。
**规则**:bx router 的 `APIRouter(prefix=...)` **去掉 `/api`**,例如:
```python
# bx 原:prefix="/api/advances"
# 门户:prefix="/advances"
router = APIRouter(prefix="/advances", tags=["备用金管理"])
```
```python
# main.py
from app.api.bx import bx_router
app.include_router(bx_router, prefix=settings.API_V1_PREFIX) # → /api/advances/...
```
### 5.3 响应包装(独立体系)
bx 模块 **禁止** 使用 `app.core.response.success_response()`。
统一使用 `schemas/bx/common.py`:
```python
class ApiResponse(BaseModel, Generic[T]):
code: int = 0
msg: str = "success"
data: Optional[T] = None
class PagedData(BaseModel, Generic[T]):
rows: List[T]
total: int
page: int
page_size: int # JSON 序列化为 pageSize(alias_generator=to_camel)
```
---
## 6. 门户适配层
### 6.1 认证(`core/bx_auth.py`)
bx 原:`get_current_user` → ORM `User`,`user.id: int`
门户:`require_admin_or_super_admin` → `dict`,`user_id: str`
```python
@dataclass
class BxUserContext:
id: str # sys_user.id
name: str # nick_name 冗余写入 applicant_name 等
dept_id: str # create_dept_id
def get_bx_current_user(user: dict = Depends(require_admin_or_super_admin)) -> BxUserContext:
...
```
Router 中:`Depends(get_current_user)` → `Depends(get_bx_current_user)`。
### 6.2 租户隔离
所有业务查询/写入通过 `get_tenant_no(db, user_dict)` 获取 `tenant_no`,在 Service 层追加:
```python
query.filter(Model.tenant_no == tenant_no, Model.del_flag == 0)
```
创建记录时:
```python
obj_in["id"] = generate_uuid()
obj_in["tenant_no"] = tenant_no
obj_in["create_by"] = user.id
obj_in["create_dept_id"] = user.dept_id or ""
```
### 6.3 API 加密中间件
门户默认对 `/api/*` 启用 `ApiCryptoMiddleware`;bx 原系统无加密。
**开发阶段**:将 bx 业务路径加入 `API_ENCRYPT_EXCLUDE_PATHS`(`backend/app/core/config.py`):
```
/api/expense-items,
/api/workflows,
/api/advances,
/api/reimburses,
/api/payments,
/api/attachments
```
**前端接入 web-admin 后**:评估是否改为走门户加密(需 bx 页 axios 适配),或长期保持 exclude。
### 6.4 附件 / MinIO
不直接拷贝 `bx-backend/app/services/minio.py`。实现 `services/bx/storage.py` 适配器,内部调用门户已有 OSS/MinIO 上传能力,对外仍暴露:
- `POST /api/attachments/upload`(multipart)
- `GET /api/attachments/download/{id}`(StreamingResponse)
- `GET /api/attachments/preview/{id}`
---
## 7. 分阶段实施计划
### 总览
```
P0 基础设施 → P1 基础配置 → P2 备用金+附件 → P3 报销 → P4 支付 → P5 菜单 → P6 前端
│ │ │ │ │ │ │
ORM/Auth expense+wf advance reimburse payment sys_menu web-admin
```
### P0:基础设施(后端骨架)
| 步骤 | 任务 | 产出 | 完成标准 |
|------|------|------|---------|
| P0-1 | 创建 `models/bx_models.py` | 10 表 ORM | 字段顺序与 DDL 一致 |
| P0-2 | 创建 `schemas/bx/common.py` | ApiResponse / PagedData | 与 bx 响应一致 |
| P0-3 | 创建 `core/bx_auth.py` | BxUserContext + Depends | 能解析门户 JWT 用户 |
| P0-4 | 创建 `api/bx/__init__.py` | 空 bx_router | main.py 可注册 |
| P0-5 | 配置加密排除路径 | config 更新 | bx 路径不加密 |
| P0-6 | 迁移 `utils/codegen.py`、`date_range.py` | 单号生成等 | 单元可调用 |
**验收**:服务启动无报错;`GET /api/health` 正常;bx_router 已挂载。
---
### P1:基础配置(费用项目 + 工作流)
| 步骤 | 任务 | 源参考 |
|------|------|--------|
| P1-1 | `schemas/bx/expense_item.py` | `bx-backend/app/schemas/expense_item.py` |
| P1-2 | `services/bx/expense_item.py` | 加 tenant + UUID |
| P1-3 | `api/bx/expense_item.py` | 5 端点原样 |
| P1-4 | `schemas/bx/workflow.py` + service + router | 8 端点 |
**API 清单**:
| 方法 | 路径 |
|------|------|
| GET | `/api/expense-items/tree` |
| GET/POST/PUT/DELETE | `/api/expense-items/`、`/api/expense-items/{item_id}` |
| GET | `/api/workflows/`、`/api/workflows/biz/{biz_type}`、`/api/workflows/{workflow_id}` |
| POST/PUT/DELETE | `/api/workflows/`、`/api/workflows/{workflow_id}` |
| PUT | `/api/workflows/{workflow_id}/nodes` |
**验收**:Swagger 可调通 CRUD;费用树、工作流节点保存与原 bx 行为一致。
---
### P2:备用金 + 附件
| 步骤 | 任务 | 说明 |
|------|------|------|
| P2-1 | advance schemas / service / router | 含 submit/approve/reject 状态机 |
| P2-2 | attachment storage 适配 + router | upload/download/preview/bind-biz |
| P2-3 | 联调:申请 → 上传附件 → 提交审批 | 端到端 |
**API 清单(advance,14 端点)**:
| 方法 | 路径 |
|------|------|
| GET | `/api/advances/`、`/my`、`/my/stats`、`/stats/status-counts` |
| GET | `/api/advances/pending-approval`、`/pending-approval/stats`、`/approved-by-me` |
| GET | `/api/advances/{advance_id}`、`/{advance_id}/approvals` |
| POST | `/api/advances/`、`/{advance_id}/submit`、`/approve`、`/reject` |
| PUT/DELETE | `/api/advances/{advance_id}` |
**验收**:完整备用金审批链路;附件 upload/list/download 可用。
---
### P3:报销
| 步骤 | 任务 |
|------|------|
| P3-1 | reimburse schemas(含 detail) |
| P3-2 | reimburse service(明细、标题生成) |
| P3-3 | reimburse router(15 端点) |
**验收**:报销申请 CRUD + 明细 + 审批流与 bx 一致。
---
### P4:支付 / 发放 / 冲抵
| 步骤 | 任务 |
|------|------|
| P4-1 | payment service(最复杂:pay/offset/issue/batch-offset) |
| P4-2 | payment router(含 CSV export StreamingResponse) |
**验收**:报销支付、备用金发放、冲抵关联、offset-records 查询正确。
---
### P5:菜单与权限(门户 sys_menu)
在 `sys_menu` 新增一级 **「报销管理」**(`parent_id='2'`,Web 端根),子结构:
```
报销管理
├── 基础配置
│ ├── 费用项目管理
│ ├── 工作流配置(备用金)
│ └── 工作流配置(报销单)
├── 备用金
│ ├── 备用金申请
│ ├── 备用金审批
│ └── 备用金发放管理
├── 报销
│ ├── 报销申请
│ ├── 报销审批
│ └── 支付管理(发放/冲抵)
└── 视图中心
├── 我的申请单
├── 待我审批
└── 全部申请单(总经理)
```
权限标识(`perms`)建议前缀 `bx:`,与 CRM `crm:` 并列。角色-菜单通过 `sys_role_menu` 配置(可参考 bx 原 `bx-menu-role-init.sql` 角色语义:员工/财务/总经理)。
**验收**:admin 登录可见菜单;无权限用户 403。
---
### P6:前端移植(web-admin)
| 步骤 | 任务 | 参考 |
|------|------|------|
| P6-1 | 拷贝 `bx-web/src/views/pages/*.vue` → `web-admin/src/views/bx/` | 排除 User/Role/Menu/Home/Demo |
| P6-2 | 拷贝/适配 `bx-web/src/api/` | baseURL 指向门户 `/api`,协议已是 bx 风格 |
| P6-3 | Fresh Mint UI 适配 | 参考 `docs/crm-migration/step-03-frontend-adapt.md` |
| P6-4 | 认证改为门户登录态 | 不再调 `/api/auth/login` |
| P6-5 | 用户下拉改用 `sys_user` API | 替代 `userApi.dropdown` |
| P6-6 | 路由与 component 路径对齐 sys_menu | hash 路由 |
**UI 规范**:`docs/web-admin-ui-spec-fresh-mint.md`;列表参考 `/#/demo/order`,树形参考 `/#/demo/category`。
**验收**:各页面功能与 bx-web 对照通过;UI 符合 Fresh Mint。
---
## 8. 单模块后端实施 SOP(每个模块重复)
1. **读源**:`bx-backend/app/routers/{module}.py` + `services/{module}.py`,中文简述业务逻辑
2. **Schema**:复制到 `schemas/bx/`,`id` 及外键改 `str`,保留 camelCase alias
3. **Service**:复制到 `services/bx/`,追加 `tenant_no` 过滤、UUID 生成、用户字段映射
4. **Router**:复制到 `api/bx/`,改 Depends / import / prefix(去 `/api`)
5. **注册**:在 `api/bx/__init__.py` include_router
6. **验证**:Swagger 或 Postman 对照 bx 原接口逐项测试
7. **更新**:[module-status.md](./module-status.md)
---
## 9. 完整 API 路径索引(移植后保持不变)
详见 [01-api-inventory.md](./01-api-inventory.md)。
---
## 10. 验证清单
### 10.1 后端协议
- [ ] 成功响应 `code === 0`,字段名为 `msg`(非 `message`)
- [ ] 分页 `data.rows`(非 `data.list`)
- [ ] 列表接口为 GET + query 参数
- [ ] 路径与 bx-web `api/index.ts` 一致
- [ ] 错误时 HTTP 4xx/5xx + 合理 detail(与 bx 行为接近)
### 10.2 业务
- [ ] 费用项目二级树 CRUD
- [ ] 工作流定义 + 节点保存(advance / reimburse 各一条)
- [ ] 备用金:草稿 → 提交 → 多级审批 → 驳回/通过 → 发放
- [ ] 报销:申请 + 明细 + 审批 + 支付/冲抵
- [ ] 附件:upload → bind-biz → download/preview
- [ ] 视图:我的 / 待办 / 全部(总经理)
### 10.3 门户集成
- [ ] 租户隔离:不同 tenant 数据不可见
- [ ] 门户 JWT 可访问 bx 接口
- [ ] 菜单权限控制生效
---
## 11. 风险与对策
| 风险 | 对策 |
|------|------|
| ID 类型 int→str,前端 bx 页仍用 number | Schema 用 str;前端 P6 逐步改类型;JSON 字符串 ID 可兼容 |
| API 加密与 bx GET 冲突 | 开发期 exclude;联调后再定是否前端加密 |
| 门户与 bx 响应双标准并存 | bx 模块独立 common.py,禁止混用 success_response |
| payment 业务复杂 | 单独 P4,充分对照 bx service 单测/手测 |
| 审批人取自 workflow_node.approver_id | 配置工作流时选 sys_user,与原 bx 选 bx_user 操作一致 |
---
## 12. 文档索引
| 文档 | 用途 |
|------|------|
| [00-migration-plan.md](./00-migration-plan.md) | 本文:总实施方案 |
| [module-status.md](./module-status.md) | 模块进度跟踪 |
| [01-api-inventory.md](./01-api-inventory.md) | 全量 API 路径与方法 |
| [ref-backend-adapt.md](./ref-backend-adapt.md) | 后端适配细则(认证/租户/Service 改造清单) |
| `docs/sql/20260606_bx_tables.sql` | DDL 权威来源 |
| `docs/sql/sx.sql` | bx 原表业务字段对照 |
| `docs/crm-migration/ref-lessons-learned.md` | 前端/UI 踩坑(P6 参考) |
| `docs/web-admin-ui-spec-fresh-mint.md` | Web UI 规范(P6) |
---
## 13. 下一步行动
**当前建议从 P0 开始**:创建 `bx_models.py`、`schemas/bx/common.py`、`core/bx_auth.py` 并挂载空 `bx_router`。
每完成一个阶段,更新 [module-status.md](./module-status.md) 并在 PR/提交说明中标注阶段编号(如 `bx P1 expense-item`)。
+141
View File
@@ -0,0 +1,141 @@
# 报销系统 API 全量清单(移植后路径保持不变)
> 源:`shanxia/bx/bx-backend/app/routers/` + `shanxia/bx/bx-web/src/api/index.ts`
> 挂载:门户 `API_V1_PREFIX=/api` + router prefix(**不含** `/api` 前缀)
---
## 费用项目 `/api/expense-items`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/expense-items/tree` | 费用项目树 |
| GET | `/api/expense-items/{item_id}` | 详情 |
| POST | `/api/expense-items/` | 新增 |
| PUT | `/api/expense-items/{item_id}` | 更新 |
| DELETE | `/api/expense-items/{item_id}` | 软删除 |
---
## 工作流 `/api/workflows`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/workflows/` | 分页列表(page, pageSize) |
| GET | `/api/workflows/biz/{biz_type}` | 按业务类型(advance / reimburse) |
| GET | `/api/workflows/{workflow_id}` | 详情(含节点) |
| POST | `/api/workflows/` | 新增(body 可含 nodes) |
| PUT | `/api/workflows/{workflow_id}` | 更新 |
| PUT | `/api/workflows/{workflow_id}/nodes` | 保存节点列表 |
| DELETE | `/api/workflows/{workflow_id}` | 软删除 |
---
## 备用金 `/api/advances`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/advances/` | 列表(status, keyword, applicantId, applyDate) |
| GET | `/api/advances/my` | 我的申请 |
| GET | `/api/advances/my/stats` | 我的统计 |
| GET | `/api/advances/stats/status-counts` | 状态计数 |
| GET | `/api/advances/pending-approval` | 待我审批 |
| GET | `/api/advances/pending-approval/stats` | 待审批统计 |
| GET | `/api/advances/approved-by-me` | 我已审批 |
| GET | `/api/advances/{advance_id}` | 详情 |
| GET | `/api/advances/{advance_id}/approvals` | 审批记录 |
| POST | `/api/advances/` | 新增 |
| PUT | `/api/advances/{advance_id}` | 更新 |
| POST | `/api/advances/{advance_id}/submit` | 提交 |
| POST | `/api/advances/{advance_id}/approve` | 审批通过 |
| POST | `/api/advances/{advance_id}/reject` | 审批驳回 |
| DELETE | `/api/advances/{advance_id}` | 软删除 |
---
## 报销 `/api/reimburses`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/reimburses/generate-title` | 自动生成标题 |
| GET | `/api/reimburses/` | 列表 |
| GET | `/api/reimburses/my` | 我的申请 |
| GET | `/api/reimburses/my/stats` | 我的统计 |
| GET | `/api/reimburses/stats/status-counts` | 状态计数 |
| GET | `/api/reimburses/pending-approval` | 待我审批 |
| GET | `/api/reimburses/pending-approval/stats` | 待审批统计 |
| GET | `/api/reimburses/approved-by-me` | 我已审批 |
| GET | `/api/reimburses/{reimburse_id}` | 详情 |
| GET | `/api/reimburses/{reimburse_id}/approvals` | 审批记录 |
| POST | `/api/reimburses/` | 新增(含明细) |
| PUT | `/api/reimburses/{reimburse_id}` | 更新 |
| POST | `/api/reimburses/{reimburse_id}/submit` | 提交 |
| POST | `/api/reimburses/{reimburse_id}/approve` | 审批通过 |
| POST | `/api/reimburses/{reimburse_id}/reject` | 审批驳回 |
| DELETE | `/api/reimburses/{reimburse_id}` | 软删除 |
---
## 支付 / 发放 `/api/payments`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/payments/pending` | 待支付报销列表 |
| GET | `/api/payments/stats` | 财务统计 |
| GET | `/api/payments/processed` | 已处理列表 |
| POST | `/api/payments/{id}/pay` | 报销发放 |
| POST | `/api/payments/{id}/offset` | 单笔冲抵 |
| POST | `/api/payments/{id}/batch-offset` | 批量冲抵 |
| GET | `/api/payments/advance-issues` | 备用金发放列表 |
| GET | `/api/payments/advance-issues/stats` | 发放统计 |
| GET | `/api/payments/advance-issues/export` | 导出 CSV(StreamingResponse) |
| POST | `/api/payments/advance/{advance_id}/issue` | 备用金发放 |
| GET | `/api/payments/advance/{advance_id}/offset-records` | 备用金被冲抵记录 |
| GET | `/api/payments/{id}/offset-records` | 报销冲抵记录 |
---
## 附件 `/api/attachments`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/attachments/upload` | 上传(multipart: file, biz_type, biz_id) |
| GET | `/api/attachments/download/{attachment_id}` | 下载文件流 |
| GET | `/api/attachments/preview/{attachment_id}` | 预览 URL |
| GET | `/api/attachments/list` | 按 biz_type、biz_id 列表 |
| PUT | `/api/attachments/bind-biz` | 绑定业务单(query: ids, biz_type, biz_id) |
| DELETE | `/api/attachments/{attachment_id}` | 软删除 |
---
## 响应格式(所有 JSON 接口)
```json
{
"code": 0,
"msg": "success",
"data": { }
}
```
分页 `data` 结构:
```json
{
"rows": [],
"total": 0,
"page": 1,
"pageSize": 10
}
```
---
## 不移植的 API
| 前缀 | 说明 |
|------|------|
| `/api/auth/*` | 门户登录 |
| `/api/users/*` | sys_user |
| `/api/roles/*` | sys_role |
| `/api/menus/*` | sys_menu(门户菜单接口) |
+43
View File
@@ -0,0 +1,43 @@
# 报销(bx)模块移植状态跟踪表
> 实施过程中更新此文档。方案见 [00-migration-plan.md](./00-migration-plan.md)。
---
## 阶段总览
| 阶段 | 内容 | 状态 | 日期 |
|------|------|------|------|
| DDL | 10 张 `bx_*` 业务表 | ✅ 已完成 | 2026-06-06 |
| P0 | 基础设施(ORM/Auth/Router 骨架) | ✅ 已完成 | 2026-06-06 |
| P1 | 费用项目 + 工作流 | ✅ 已完成 | 2026-06-06 |
| P2 | 备用金 + 附件 | ✅ 已完成 | 2026-06-06 |
| P3 | 报销 | ✅ 已完成 | 2026-06-06 |
| P4 | 支付/发放/冲抵 | ✅ 已完成 | 2026-06-06 |
| P5 | sys_menu 菜单与权限 | ✅ 已完成 | 2026-06-06 |
| P6 | web-admin 前端(拷贝 + 门户适配) | 🟡 进行中 | 2026-06-06 |
| P6-FM | Fresh Mint UI 深度对齐 | ⬜ 未开始 | — |
---
## 后端模块明细
| 模块 | 表 | 后端 | 前端 | 验证 | 备注 |
|------|-----|------|------|------|------|
| 费用项目 | `bx_expense_item` | ✅ | 🟡 | — | P6 页面已拷贝 |
| 工作流 | `bx_workflow_def`、`bx_workflow_node` | ✅ | 🟡 | — | P6 页面已拷贝 |
| 备用金 | `bx_advance_apply`、`bx_advance_approval` | ✅ | 🟡 | — | P6 页面已拷贝 |
| 附件 | `bx_attachment` | ✅ | 🟡 | — | 组件内上传已拷贝 |
| 报销 | `bx_reimburse_apply`、`bx_reimburse_detail`、`bx_reimburse_approval` | ✅ | 🟡 | — | P6 页面已拷贝 |
| 支付 | `bx_reimburse_offset` + payment 逻辑 | ✅ | 🟡 | — | P6 页面已拷贝 |
| 视图中心 | —(聚合 API) | ✅ | 🟡 | — | 我的/待办/全部页已拷贝 |
---
## 不移植项
| 项 | 替代 |
|----|------|
| `bx_user` / `bx_role` / `bx_menu` | `sys_user` / `sys_role` / `sys_menu` |
| `bx-backend/routers/auth.py` | 门户 `/api/web/auth/login` |
| bx-web User/Role/Menu 页 | 门户系统管理 |
+147
View File
@@ -0,0 +1,147 @@
# 报销后端移植适配细则
> 实施 P0–P4 时加载。与 [00-migration-plan.md](./00-migration-plan.md) 配套。
---
## 1. 文件复制对照表
| bx-backend 源 | shanxia2.0 目标 | 改造程度 |
|---------------|----------------|---------|
| `schemas/common.py` | `schemas/bx/common.py` | 原样 |
| `schemas/expense_item.py` 等 | `schemas/bx/*.py` | id 改 str |
| `services/*.py` | `services/bx/*.py` | tenant + uuid + user |
| `routers/*.py` | `api/bx/*.py` | Depends + prefix + import |
| `models/*.py` | `models/bx_models.py` | 按 DDL 重写 |
| `utils/codegen.py` | `utils/bx_codegen.py` 或 `utils/codegen.py` | 租户隔离可选 |
| `core/deps.py` | **不复制** | 用 `core/bx_auth.py` |
| `services/minio.py` | **不复制** | 门户 storage 适配 |
---
## 2. Router 改造检查清单
复制每个 router 后逐项核对:
- [ ] `APIRouter(prefix="/api/xxx")` → `prefix="/xxx"`(去掉 `/api`)
- [ ] `from app.database import get_db` → `from app.core.database import get_db`
- [ ] `Depends(get_current_user)` → `Depends(get_bx_current_user)`
- [ ] `User` 类型 → `BxUserContext`
- [ ] `require_roles("ADMIN")` → `Depends(get_bx_current_user)` 或 `require_menu_code("bx:...")`(P5 后)
- [ ] 路径参数 `{advance_id}: int` → `str`
- [ ] `response_model=ApiResponse[...]` 保持不变
- [ ] **禁止** `return success_response(...)`
---
## 3. Service 改造检查清单
- [ ] 所有 `get_by_id(model, db, id: int)` → `id: str`
- [ ] 查询追加 `Model.tenant_no == tenant_no`
- [ ] 创建:`generate_uuid()`、`tenant_no`、`create_by`、`create_dept_id`
- [ ] `applicant_id` / `approver_id` 使用 `sys_user.id`(str)
- [ ] 冗余姓名字段:从 `SysUser.nick_name` 写入 `applicant_name` 等
- [ ] 单号生成:`generate_apply_code` 加租户维度(同日重置逻辑保留)
- [ ] 软删除:`del_flag=1`,查询默认 `del_flag=0`
---
## 4. Schema 改造检查清单
- [ ] `AdvanceApplyOut.id: int` → `str`
- [ ] `expense_item_id: Optional[int]` → `Optional[str]`
- [ ] 保留 `model_config`:`populate_by_name=True`, `alias_generator=to_camel`
- [ ] 保留 `DatetimeToStr` / `apply_time` 等字段 alias
- [ ] Create/Update body 与 bx 原字段一致
---
## 5. bx_auth 参考实现
```python
# backend/app/core/bx_auth.py(实施 P0 时落地)
from dataclasses import dataclass
from fastapi import Depends
from app.core.menu_guard import require_admin_or_super_admin
@dataclass
class BxUserContext:
id: str
name: str
dept_id: str
def get_bx_current_user(
user: dict = Depends(require_admin_or_super_admin),
) -> BxUserContext:
return BxUserContext(
id=user.get("user_id") or "",
name=user.get("nick_name") or user.get("username") or "",
dept_id=user.get("dept_id") or "",
)
```
Service 中需要 tenant 时:
```python
from app.core.tenant_context import get_tenant_no
tenant_no = get_tenant_no(db, {"user_id": user.id, "role": ...})
```
---
## 6. main.py 注册
```python
from app.api.bx import bx_router
app.include_router(bx_router, prefix=settings.API_V1_PREFIX)
```
`api/bx/__init__.py`:
```python
from fastapi import APIRouter
from app.api.bx import expense_item, workflow, advance, reimburse, payment, attachment
bx_router = APIRouter()
bx_router.include_router(expense_item.router)
bx_router.include_router(workflow.router)
# ...
```
`app/api/__init__.py` 增加 `bx_router` 导出(可选,或直接 main 引用)。
---
## 7. 加密排除(config.py)
在 `API_ENCRYPT_EXCLUDE_PATHS` 追加(逗号分隔):
```
/api/expense-items,
/api/workflows,
/api/advances,
/api/reimburses,
/api/payments,
/api/attachments
```
---
## 8. 字段一致性(可选)
后端 CRUD 落地后,若 WriteBody 字段较多,可扩展 `scripts/check_field_consistency.py` 支持 `bx_*` 表,规则同 CRM。
---
## 9. 测试建议
| 阶段 | 方式 |
|------|------|
| P1 | Swagger `/docs` 手动测 expense-items / workflows |
| P2 | 备用金完整流程 + 附件 upload |
| P3–P4 | 对照 bx 原环境相同操作序列 |
| 协议 | 断言响应 `code===0`、`data.rows` 存在 |
原 bx 环境(8011)可与门户(8000)并行,便于 diff 响应结构。
+298
View File
@@ -0,0 +1,298 @@
/*
报销系统(bx_*)表结构迁移 — shanxia2.0 门户标准字段适配
来源:docs/sql/sx.sql 中 bx_* 业务表(排除 bx_user / bx_role / bx_role_user / bx_menu / bx_menu_role)
适配:主键 UUID(varchar64)、tenant_no、order_num、create_dept_id、del_flag smallint 等公共字段
说明:仅建表,不导入数据
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for bx_expense_item
-- ----------------------------
DROP TABLE IF EXISTS `bx_expense_item`;
CREATE TABLE `bx_expense_item` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`parent_id` varchar(64) NOT NULL DEFAULT '0' COMMENT '父级项目ID(0为一级项目)',
`item_name` varchar(128) NOT NULL COMMENT '项目名称',
`item_code` varchar(64) DEFAULT NULL COMMENT '项目编码',
`level` tinyint(4) NOT NULL DEFAULT '1' COMMENT '层级:1-一级 2-二级',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-启用 0-禁用',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_parent_id` (`parent_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-费用项目表';
-- ----------------------------
-- Table structure for bx_workflow_def
-- ----------------------------
DROP TABLE IF EXISTS `bx_workflow_def`;
CREATE TABLE `bx_workflow_def` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`biz_type` varchar(32) NOT NULL COMMENT '业务类型:advance-备用金 reimburse-报销单',
`wf_name` varchar(128) NOT NULL COMMENT '工作流名称',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-启用 0-禁用',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_biz_type` (`biz_type`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-工作流定义表';
-- ----------------------------
-- Table structure for bx_workflow_node
-- ----------------------------
DROP TABLE IF EXISTS `bx_workflow_node`;
CREATE TABLE `bx_workflow_node` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`wf_id` varchar(64) NOT NULL COMMENT '工作流定义ID',
`node_name` varchar(128) NOT NULL COMMENT '节点名称(如:主管审批、财务复核)',
`node_order` int(11) NOT NULL DEFAULT '0' COMMENT '节点顺序(从小到大执行)',
`approver_id` varchar(64) DEFAULT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) DEFAULT NULL COMMENT '审批人姓名(冗余)',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_wf_id` (`wf_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-工作流节点表';
-- ----------------------------
-- Table structure for bx_advance_apply
-- ----------------------------
DROP TABLE IF EXISTS `bx_advance_apply`;
CREATE TABLE `bx_advance_apply` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_code` varchar(32) NOT NULL COMMENT '申请单号(如 BYJ20260424001)',
`applicant_id` varchar(64) NOT NULL COMMENT '申请人用户ID',
`applicant_name` varchar(64) NOT NULL COMMENT '申请人姓名(冗余)',
`reason` varchar(1024) NOT NULL COMMENT '申请原因',
`purpose` varchar(1024) DEFAULT NULL COMMENT '申请目的',
`expense_item_id` varchar(64) DEFAULT NULL COMMENT '费用项目ID',
`amount` decimal(14,2) NOT NULL COMMENT '申请金额',
`status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT '状态:draft-草稿 pending-待审批 approved-审批通过 rejected-审批驳回 issued-已发放',
`current_node` varchar(128) DEFAULT NULL COMMENT '当前审批节点名称',
`wf_id` varchar(64) DEFAULT NULL COMMENT '关联工作流定义ID',
`issue_status` varchar(32) DEFAULT NULL COMMENT '发放状态:unissued-未发放 issued-已发放',
`issue_time` datetime DEFAULT NULL COMMENT '发放时间',
`issue_user_id` varchar(64) DEFAULT NULL COMMENT '发放操作人ID',
`issue_user_name` varchar(64) DEFAULT NULL COMMENT '发放操作人姓名',
`issue_remark` varchar(512) DEFAULT NULL COMMENT '发放备注',
`actual_amount` decimal(14,2) DEFAULT NULL COMMENT '实际发放金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_apply_code` (`apply_code`) USING BTREE,
KEY `idx_applicant_id` (`applicant_id`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-备用金申请单';
-- ----------------------------
-- Table structure for bx_advance_approval
-- ----------------------------
DROP TABLE IF EXISTS `bx_advance_approval`;
CREATE TABLE `bx_advance_approval` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_id` varchar(64) NOT NULL COMMENT '备用金申请单ID',
`node_order` int(11) NOT NULL COMMENT '节点顺序',
`node_name` varchar(128) NOT NULL COMMENT '节点名称',
`approver_id` varchar(64) NOT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) NOT NULL COMMENT '审批人姓名',
`approval_status` varchar(32) NOT NULL COMMENT '审批结果:approved-通过 rejected-驳回',
`approval_opinion` varchar(1024) DEFAULT NULL COMMENT '审批意见',
`approval_time` datetime NOT NULL COMMENT '审批时间',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_apply_id` (`apply_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-备用金审批记录表';
-- ----------------------------
-- Table structure for bx_reimburse_apply
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_apply`;
CREATE TABLE `bx_reimburse_apply` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_code` varchar(32) NOT NULL COMMENT '报销单号(如 BX20260424001)',
`title` varchar(256) NOT NULL COMMENT '报销标题',
`applicant_id` varchar(64) NOT NULL COMMENT '申请人用户ID',
`applicant_name` varchar(64) NOT NULL COMMENT '申请人姓名(冗余)',
`expense_id` varchar(64) NOT NULL COMMENT '费用项目ID(二级项目)',
`expense_name` varchar(128) NOT NULL COMMENT '费用项目名称(冗余)',
`amount` decimal(14,2) NOT NULL COMMENT '报销金额',
`invoice_count` int(11) NOT NULL DEFAULT '0' COMMENT '发票数量',
`attach_count` int(11) NOT NULL DEFAULT '0' COMMENT '附件数量',
`status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT '状态:draft-草稿 pending-待审批 approved-审批通过 rejected-审批驳回 paid-已发放 offset-已冲抵',
`current_node` varchar(128) DEFAULT NULL COMMENT '当前审批节点名称',
`wf_id` varchar(64) DEFAULT NULL COMMENT '关联工作流定义ID',
`pay_status` varchar(32) DEFAULT NULL COMMENT '支付状态:unpaid-未支付 paid-已发放 offset-已冲抵',
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
`pay_user_id` varchar(64) DEFAULT NULL COMMENT '支付操作人ID',
`pay_user_name` varchar(64) DEFAULT NULL COMMENT '支付操作人姓名',
`actual_amount` decimal(14,2) DEFAULT NULL COMMENT '实际发放金额',
`offset_advance_id` varchar(64) DEFAULT NULL COMMENT '冲抵关联的备用金单ID',
`offset_amount` decimal(14,2) DEFAULT NULL COMMENT '冲抵金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注说明',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_apply_code` (`apply_code`) USING BTREE,
KEY `idx_applicant_id` (`applicant_id`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE,
KEY `idx_expense_id` (`expense_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销申请单';
-- ----------------------------
-- Table structure for bx_reimburse_detail
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_detail`;
CREATE TABLE `bx_reimburse_detail` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`reimburse_id` varchar(64) NOT NULL COMMENT '报销单ID',
`expense_id` varchar(64) NOT NULL COMMENT '费用项目ID',
`expense_name` varchar(128) NOT NULL COMMENT '费用项目名称',
`amount` decimal(14,2) NOT NULL COMMENT '报销金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_reimburse_id` (`reimburse_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销明细';
-- ----------------------------
-- Table structure for bx_reimburse_approval
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_approval`;
CREATE TABLE `bx_reimburse_approval` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_id` varchar(64) NOT NULL COMMENT '报销申请单ID',
`node_order` int(11) NOT NULL COMMENT '节点顺序',
`node_name` varchar(128) NOT NULL COMMENT '节点名称',
`approver_id` varchar(64) NOT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) NOT NULL COMMENT '审批人姓名',
`approval_status` varchar(32) NOT NULL COMMENT '审批结果:approved-通过 rejected-驳回',
`approval_opinion` varchar(1024) DEFAULT NULL COMMENT '审批意见',
`approval_time` datetime NOT NULL COMMENT '审批时间',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_apply_id` (`apply_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销审批记录表';
-- ----------------------------
-- Table structure for bx_reimburse_offset
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_offset`;
CREATE TABLE `bx_reimburse_offset` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`reimburse_id` varchar(64) NOT NULL COMMENT '报销申请单ID',
`advance_id` varchar(64) NOT NULL COMMENT '备用金申请单ID',
`offset_amount` decimal(14,2) NOT NULL COMMENT '冲抵金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_reimburse_id` (`reimburse_id`) USING BTREE,
KEY `idx_advance_id` (`advance_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销单备用金冲抵关联表';
-- ----------------------------
-- Table structure for bx_attachment
-- ----------------------------
DROP TABLE IF EXISTS `bx_attachment`;
CREATE TABLE `bx_attachment` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`biz_type` varchar(32) NOT NULL COMMENT '业务类型:advance-备用金 reimburse-报销 advance_issue-备用金发放',
`biz_id` varchar(64) NOT NULL COMMENT '业务单据ID',
`file_name` varchar(256) NOT NULL COMMENT '文件名',
`file_path` varchar(512) NOT NULL COMMENT '文件存储路径',
`file_type` varchar(128) DEFAULT NULL COMMENT '文件MIME类型',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)',
`attach_type` varchar(32) DEFAULT 'attachment' COMMENT '附件类型:invoice-发票 attachment-普通附件 issue_proof-发放截图',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_biz` (`biz_type`,`biz_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-附件表';
SET FOREIGN_KEY_CHECKS = 1;
+346
View File
@@ -51,6 +51,352 @@ CREATE TABLE `biz_leave_request` (
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_advance_apply
-- ----------------------------
DROP TABLE IF EXISTS `bx_advance_apply`;
CREATE TABLE `bx_advance_apply` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_code` varchar(32) NOT NULL COMMENT '申请单号(如 BYJ20260424001)',
`applicant_id` varchar(64) NOT NULL COMMENT '申请人用户ID',
`applicant_name` varchar(64) NOT NULL COMMENT '申请人姓名(冗余)',
`reason` varchar(1024) NOT NULL COMMENT '申请原因',
`purpose` varchar(1024) DEFAULT NULL COMMENT '申请目的',
`expense_item_id` varchar(64) DEFAULT NULL COMMENT '费用项目ID',
`amount` decimal(14,2) NOT NULL COMMENT '申请金额',
`status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT '状态:draft-草稿 pending-待审批 approved-审批通过 rejected-审批驳回 issued-已发放',
`current_node` varchar(128) DEFAULT NULL COMMENT '当前审批节点名称',
`wf_id` varchar(64) DEFAULT NULL COMMENT '关联工作流定义ID',
`issue_status` varchar(32) DEFAULT NULL COMMENT '发放状态:unissued-未发放 issued-已发放',
`issue_time` datetime DEFAULT NULL COMMENT '发放时间',
`issue_user_id` varchar(64) DEFAULT NULL COMMENT '发放操作人ID',
`issue_user_name` varchar(64) DEFAULT NULL COMMENT '发放操作人姓名',
`issue_remark` varchar(512) DEFAULT NULL COMMENT '发放备注',
`actual_amount` decimal(14,2) DEFAULT NULL COMMENT '实际发放金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_apply_code` (`apply_code`) USING BTREE,
KEY `idx_applicant_id` (`applicant_id`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-备用金申请单';
-- ----------------------------
-- Records of bx_advance_apply
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_advance_approval
-- ----------------------------
DROP TABLE IF EXISTS `bx_advance_approval`;
CREATE TABLE `bx_advance_approval` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_id` varchar(64) NOT NULL COMMENT '备用金申请单ID',
`node_order` int(11) NOT NULL COMMENT '节点顺序',
`node_name` varchar(128) NOT NULL COMMENT '节点名称',
`approver_id` varchar(64) NOT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) NOT NULL COMMENT '审批人姓名',
`approval_status` varchar(32) NOT NULL COMMENT '审批结果:approved-通过 rejected-驳回',
`approval_opinion` varchar(1024) DEFAULT NULL COMMENT '审批意见',
`approval_time` datetime NOT NULL COMMENT '审批时间',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_apply_id` (`apply_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-备用金审批记录表';
-- ----------------------------
-- Records of bx_advance_approval
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_attachment
-- ----------------------------
DROP TABLE IF EXISTS `bx_attachment`;
CREATE TABLE `bx_attachment` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`biz_type` varchar(32) NOT NULL COMMENT '业务类型:advance-备用金 reimburse-报销 advance_issue-备用金发放',
`biz_id` varchar(64) NOT NULL COMMENT '业务单据ID',
`file_name` varchar(256) NOT NULL COMMENT '文件名',
`file_path` varchar(512) NOT NULL COMMENT '文件存储路径',
`file_type` varchar(128) DEFAULT NULL COMMENT '文件MIME类型',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)',
`attach_type` varchar(32) DEFAULT 'attachment' COMMENT '附件类型:invoice-发票 attachment-普通附件 issue_proof-发放截图',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_biz` (`biz_type`,`biz_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-附件表';
-- ----------------------------
-- Records of bx_attachment
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_expense_item
-- ----------------------------
DROP TABLE IF EXISTS `bx_expense_item`;
CREATE TABLE `bx_expense_item` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`parent_id` varchar(64) NOT NULL DEFAULT '0' COMMENT '父级项目ID(0为一级项目)',
`item_name` varchar(128) NOT NULL COMMENT '项目名称',
`item_code` varchar(64) DEFAULT NULL COMMENT '项目编码',
`level` tinyint(4) NOT NULL DEFAULT '1' COMMENT '层级:1-一级 2-二级',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-启用 0-禁用',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_parent_id` (`parent_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-费用项目表';
-- ----------------------------
-- Records of bx_expense_item
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_reimburse_apply
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_apply`;
CREATE TABLE `bx_reimburse_apply` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_code` varchar(32) NOT NULL COMMENT '报销单号(如 BX20260424001)',
`title` varchar(256) NOT NULL COMMENT '报销标题',
`applicant_id` varchar(64) NOT NULL COMMENT '申请人用户ID',
`applicant_name` varchar(64) NOT NULL COMMENT '申请人姓名(冗余)',
`expense_id` varchar(64) NOT NULL COMMENT '费用项目ID(二级项目)',
`expense_name` varchar(128) NOT NULL COMMENT '费用项目名称(冗余)',
`amount` decimal(14,2) NOT NULL COMMENT '报销金额',
`invoice_count` int(11) NOT NULL DEFAULT '0' COMMENT '发票数量',
`attach_count` int(11) NOT NULL DEFAULT '0' COMMENT '附件数量',
`status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT '状态:draft-草稿 pending-待审批 approved-审批通过 rejected-审批驳回 paid-已发放 offset-已冲抵',
`current_node` varchar(128) DEFAULT NULL COMMENT '当前审批节点名称',
`wf_id` varchar(64) DEFAULT NULL COMMENT '关联工作流定义ID',
`pay_status` varchar(32) DEFAULT NULL COMMENT '支付状态:unpaid-未支付 paid-已发放 offset-已冲抵',
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
`pay_user_id` varchar(64) DEFAULT NULL COMMENT '支付操作人ID',
`pay_user_name` varchar(64) DEFAULT NULL COMMENT '支付操作人姓名',
`actual_amount` decimal(14,2) DEFAULT NULL COMMENT '实际发放金额',
`offset_advance_id` varchar(64) DEFAULT NULL COMMENT '冲抵关联的备用金单ID',
`offset_amount` decimal(14,2) DEFAULT NULL COMMENT '冲抵金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注说明',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_apply_code` (`apply_code`) USING BTREE,
KEY `idx_applicant_id` (`applicant_id`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE,
KEY `idx_expense_id` (`expense_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销申请单';
-- ----------------------------
-- Records of bx_reimburse_apply
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_reimburse_approval
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_approval`;
CREATE TABLE `bx_reimburse_approval` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`apply_id` varchar(64) NOT NULL COMMENT '报销申请单ID',
`node_order` int(11) NOT NULL COMMENT '节点顺序',
`node_name` varchar(128) NOT NULL COMMENT '节点名称',
`approver_id` varchar(64) NOT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) NOT NULL COMMENT '审批人姓名',
`approval_status` varchar(32) NOT NULL COMMENT '审批结果:approved-通过 rejected-驳回',
`approval_opinion` varchar(1024) DEFAULT NULL COMMENT '审批意见',
`approval_time` datetime NOT NULL COMMENT '审批时间',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_apply_id` (`apply_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销审批记录表';
-- ----------------------------
-- Records of bx_reimburse_approval
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_reimburse_detail
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_detail`;
CREATE TABLE `bx_reimburse_detail` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`reimburse_id` varchar(64) NOT NULL COMMENT '报销单ID',
`expense_id` varchar(64) NOT NULL COMMENT '费用项目ID',
`expense_name` varchar(128) NOT NULL COMMENT '费用项目名称',
`amount` decimal(14,2) NOT NULL COMMENT '报销金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_reimburse_id` (`reimburse_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销明细';
-- ----------------------------
-- Records of bx_reimburse_detail
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_reimburse_offset
-- ----------------------------
DROP TABLE IF EXISTS `bx_reimburse_offset`;
CREATE TABLE `bx_reimburse_offset` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`reimburse_id` varchar(64) NOT NULL COMMENT '报销申请单ID',
`advance_id` varchar(64) NOT NULL COMMENT '备用金申请单ID',
`offset_amount` decimal(14,2) NOT NULL COMMENT '冲抵金额',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_reimburse_id` (`reimburse_id`) USING BTREE,
KEY `idx_advance_id` (`advance_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-报销单备用金冲抵关联表';
-- ----------------------------
-- Records of bx_reimburse_offset
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_workflow_def
-- ----------------------------
DROP TABLE IF EXISTS `bx_workflow_def`;
CREATE TABLE `bx_workflow_def` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`biz_type` varchar(32) NOT NULL COMMENT '业务类型:advance-备用金 reimburse-报销单',
`wf_name` varchar(128) NOT NULL COMMENT '工作流名称',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-启用 0-禁用',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_biz_type` (`biz_type`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-工作流定义表';
-- ----------------------------
-- Records of bx_workflow_def
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for bx_workflow_node
-- ----------------------------
DROP TABLE IF EXISTS `bx_workflow_node`;
CREATE TABLE `bx_workflow_node` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`wf_id` varchar(64) NOT NULL COMMENT '工作流定义ID',
`node_name` varchar(128) NOT NULL COMMENT '节点名称(如:主管审批、财务复核)',
`node_order` int(11) NOT NULL DEFAULT '0' COMMENT '节点顺序(从小到大执行)',
`approver_id` varchar(64) DEFAULT NULL COMMENT '审批人用户ID',
`approver_name` varchar(64) DEFAULT NULL COMMENT '审批人姓名(冗余)',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号(公共)',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建部门',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_wf_id` (`wf_id`) USING BTREE,
KEY `idx_tenant_no` (`tenant_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报销-工作流节点表';
-- ----------------------------
-- Records of bx_workflow_node
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for crm_clue
-- ----------------------------
+362 -22
View File
@@ -5,8 +5,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py": {
"mtime": 1780201410.1131313,
"ast_hash": "14dee80cbc1a9e3a5a0fc7b5b7f3820c",
"mtime": 1780750556.8821394,
"ast_hash": "9d2dc014c76287a8e8a4a7f4c4c633cc",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/customer_pool.py": {
@@ -40,8 +40,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py": {
"mtime": 1780131767.561871,
"ast_hash": "650ee00a9cad587e2ca66be4bc583aff",
"mtime": 1780750556.8823087,
"ast_hash": "b7a28e6af80b6160097dfd7dbdfa5ff6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py": {
@@ -90,8 +90,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/attachment_biz.py": {
"mtime": 1780360597.164981,
"ast_hash": "69b67b2e72706b20233fdeda283c4576",
"mtime": 1780750904.0571795,
"ast_hash": "31445f82b9838f6ddcbf08ecedaea351",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py": {
@@ -280,7 +280,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/contract.py": {
"mtime": 1780743830.220982,
"mtime": 1780758461.4052296,
"ast_hash": "6553f54ca1fd19086c170095f402a628",
"semantic_hash": ""
},
@@ -395,7 +395,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract_product.py": {
"mtime": 1780743830.2274046,
"mtime": 1780758461.405257,
"ast_hash": "d2e1538af2d36ecc9daf28a3b135bd0f",
"semantic_hash": ""
},
@@ -410,7 +410,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract_approval.py": {
"mtime": 1780743830.2537448,
"mtime": 1780758461.4052966,
"ast_hash": "c9c338510247afdcd733412c10e0ded3",
"semantic_hash": ""
},
@@ -475,7 +475,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/clue_statistics.py": {
"mtime": 1780743830.2298,
"mtime": 1780758461.405326,
"ast_hash": "9a026c628764467e4d1398900105dc78",
"semantic_hash": ""
},
@@ -495,7 +495,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/crud/crm/contract.py": {
"mtime": 1780743830.2209544,
"mtime": 1780758461.4050884,
"ast_hash": "3cd90455687d8cc24d4b664d1fd5eaa2",
"semantic_hash": ""
},
@@ -520,7 +520,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_sub_routes.py": {
"mtime": 1780743830.2502718,
"mtime": 1780758461.4060414,
"ast_hash": "c05e2faf142cb664a73975112d26d658",
"semantic_hash": ""
},
@@ -530,8 +530,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/__init__.py": {
"mtime": 1780201410.1104653,
"ast_hash": "32a1c56ed1b24308513c1ab4f44c72a2",
"mtime": 1780750556.8831582,
"ast_hash": "f0ed76e504b60330b436160d96753b61",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/menu.py": {
@@ -550,7 +550,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py": {
"mtime": 1780743830.220907,
"mtime": 1780758461.4051092,
"ast_hash": "e12302586789d2e8cf85469226be2899",
"semantic_hash": ""
},
@@ -2475,8 +2475,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue": {
"mtime": 1780201410.1612706,
"ast_hash": "e2137fd84ca3a00d18cb546d29df04ca",
"mtime": 1780753478.4552467,
"ast_hash": "ba265e7f6a3b6b3586d274e7c79ce140",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue": {
@@ -2910,7 +2910,7 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/clue/statistics/index.vue": {
"mtime": 1780743830.2537415,
"mtime": 1780763324.1037285,
"ast_hash": "83247d6f2a740c829b7a4d0a296fdf83",
"semantic_hash": ""
},
@@ -3420,8 +3420,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/router/modules/sqxp.ts": {
"mtime": 1780242127.0352678,
"ast_hash": "145b83a631711ccb3f59d78d5fe34758",
"mtime": 1780753257.1643171,
"ast_hash": "7752e455e4c11ef368bda42ff9e9f92b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/router/modules/index.ts": {
@@ -5225,8 +5225,8 @@
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/package.json": {
"mtime": 1780219620.50469,
"ast_hash": "7d7dd80d11cdf382ad2364128145b29c",
"mtime": 1780752670.7718465,
"ast_hash": "e93877b494638c840d003c67c3f6401a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/tsconfig.json": {
@@ -5283,5 +5283,345 @@
"mtime": 1780742010.017625,
"ast_hash": "c0df54dff6daad2a091db842b3e0c803",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/bx/expenseItemCode.ts": {
"mtime": 1780752613.9072092,
"ast_hash": "65d0715cb8d40d23b4df1552f07ca136",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/bx/format.ts": {
"mtime": 1780756590.4817746,
"ast_hash": "b96c952649dd029d16f36b6fcb8e4a76",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/http/bx.ts": {
"mtime": 1780752599.942857,
"ast_hash": "eb352e21db69cd209062459ec2ba6584",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/template/ListToolbar.vue": {
"mtime": 1780752613.8986316,
"ast_hash": "8184200c37281fa8875a3ab8286d6d1f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/template/FilterBlock.vue": {
"mtime": 1780752613.893088,
"ast_hash": "7a521e6b019db562d13a8f5209bac19c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/template/DataTablePanel.vue": {
"mtime": 1780752613.8926337,
"ast_hash": "21d810c4e33ab5c643d6dae8db27cdf9",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/template/ListPageScaffold.vue": {
"mtime": 1780752613.89413,
"ast_hash": "9281a4b23ca4a6016377a1eeaca1130f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseOpButton.vue": {
"mtime": 1780752613.8844402,
"ast_hash": "7c721fcfad0a270eaaf5303b057a057f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseSelect.vue": {
"mtime": 1780752613.8852646,
"ast_hash": "99711a65c38e8d1fff9091af84770da0",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseTag.vue": {
"mtime": 1780752613.8868194,
"ast_hash": "30e0e8e89087099cb4bd0a13a3a0ae4c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseButton.vue": {
"mtime": 1780752613.872528,
"ast_hash": "81bd26a126797cef57f3d2b4961801f2",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseDialog.vue": {
"mtime": 1780752613.8741815,
"ast_hash": "1c052608fcb18e3cc9d4376a559d2966",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseTable.vue": {
"mtime": 1780752613.88601,
"ast_hash": "32a8d89915b1fb7ba183a00a036450f6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseDetailTableSection.vue": {
"mtime": 1780760191.3960986,
"ast_hash": "3dd2c900e22a2f38438cbe30ba615331",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseInput.vue": {
"mtime": 1780752613.8769777,
"ast_hash": "73fa5e005cf32338ca6c46b0d7880683",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BaseClampText.vue": {
"mtime": 1780752613.873146,
"ast_hash": "280acf00d1bbfc6b11eee7e5212f421b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/ApprovalFlowView.vue": {
"mtime": 1780758206.8546696,
"ast_hash": "d2dde9134386fd15e21a45e8fa1f5882",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/WorkflowNodeCell.vue": {
"mtime": 1780752653.179815,
"ast_hash": "d5c3e9e4a5cefd43e73f7ffc37608feb",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/hooks/bx/useApproveMenuBadge.ts": {
"mtime": 1780752647.2106802,
"ast_hash": "924a161edbd72b13e17227292d057401",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/hooks/bx/useApplicantOptions.ts": {
"mtime": 1780752646.1810515,
"ast_hash": "b9e2c4cb03ec6299dafceea4a47752e0",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/hooks/bx/useExpenseItemTree.ts": {
"mtime": 1780760526.6676378,
"ast_hash": "ef7ab8a134a297d77282009416706a74",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/hooks/bx/usePagedTable.ts": {
"mtime": 1780752613.9877086,
"ast_hash": "d455d26313c07aeeacbda18d2e23434b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/hooks/bx/useServerPaged.ts": {
"mtime": 1780752613.990241,
"ast_hash": "66708069c354455b78556c58d5f94771",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/api/bx/index.ts": {
"mtime": 1780757619.4888484,
"ast_hash": "6c911937604a39d3f0153eb5def19ae8",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/payment/index.vue": {
"mtime": 1780763490.7587097,
"ast_hash": "5b5dd01ef0f3561130c293149aa3c8dd",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/advance/apply/index.vue": {
"mtime": 1780762665.5609775,
"ast_hash": "f61237436c91367499d5f32df088c76c",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/advance/approve/index.vue": {
"mtime": 1780757454.7531683,
"ast_hash": "447dd94608d177c8f637de6e7f34b36f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/advance/issue/index.vue": {
"mtime": 1780763653.0696034,
"ast_hash": "554e505ffb23107026fdfd65957e4fda",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/reimburse/apply/index.vue": {
"mtime": 1780763416.0074606,
"ast_hash": "d8372ca9957c1b6a552b7e2ee074b434",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/reimburse/approve/index.vue": {
"mtime": 1780763428.446719,
"ast_hash": "025d09b210a1f6239f46df60dd0c4a08",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/workflow/advance/index.vue": {
"mtime": 1780756601.8436413,
"ast_hash": "e33c4ad30db3e418d345e96b96f9e814",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/workflow/reimburse/index.vue": {
"mtime": 1780756601.8592331,
"ast_hash": "bba9ee238816707eba5bfdf60e694dc2",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/view/all/index.vue": {
"mtime": 1780760362.8554158,
"ast_hash": "2c3af567ddf2fd568441454bef07f745",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/view/todo/index.vue": {
"mtime": 1780761403.9424925,
"ast_hash": "f29ef9f6eeccc5742671475081b558b4",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/view/mine/index.vue": {
"mtime": 1780760362.6476135,
"ast_hash": "cf0d49d7921a16a4ef65cda5ad88446e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/bx/expense-item/index.vue": {
"mtime": 1780756601.7798786,
"ast_hash": "dbb41e8e7c6b51c0bcf372e09f1e1fc6",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/bx_auth.py": {
"mtime": 1780750556.882335,
"ast_hash": "992aca5bcca63b250109f23d36031bdb",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_date_range.py": {
"mtime": 1780750556.881769,
"ast_hash": "c40b659e0b1d8a549e97923a9a536719",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/utils/bx_codegen.py": {
"mtime": 1780750556.8829591,
"ast_hash": "ca25a3c561c4a1bfc566168d85b5f4de",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/bx_models.py": {
"mtime": 1780752873.936801,
"ast_hash": "53f1d2880a144b02074b7f734f12d92a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/reimburse.py": {
"mtime": 1780751335.1849163,
"ast_hash": "4d040ab00d9288b03a0e5448e54340cd",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/expense_item.py": {
"mtime": 1780750595.66133,
"ast_hash": "2dcb1f8935122ecc1129d47f41329e2d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/__init__.py": {
"mtime": 1780750556.8817346,
"ast_hash": "2cea3812d44c57f24aa7f00648a14412",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/attachment.py": {
"mtime": 1780750871.4800005,
"ast_hash": "97ee54c0f5e7e6944a6d5edba9959b7b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/advance.py": {
"mtime": 1780757815.2697868,
"ast_hash": "d2eeaba820accc122366f244b3e50ef9",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/common.py": {
"mtime": 1780750556.882896,
"ast_hash": "e97cb788e93cfac0688c0aa4cbd2cf93",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/bx/workflow.py": {
"mtime": 1780750597.0023274,
"ast_hash": "84a4177765ae7baf25f55b8a7858cffe",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/reimburse.py": {
"mtime": 1780758906.744178,
"ast_hash": "e6f722a54dcb0a1017d54334e59c731e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py": {
"mtime": 1780750631.6409054,
"ast_hash": "ec3143dd9c80ed89ccbdada33af3b048",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/__init__.py": {
"mtime": 1780751360.1800659,
"ast_hash": "683657a15bc1923bff7a7d60f1dd965d",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/attachment.py": {
"mtime": 1780750903.379992,
"ast_hash": "c3d0da835eff5f4d08d174741c7faa83",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/advance.py": {
"mtime": 1780758906.5070424,
"ast_hash": "fd125c07b86418ad454f7b2d9f090aec",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/workflow.py": {
"mtime": 1780756089.7304258,
"ast_hash": "e13af66a1b3ac92225315a4df38db13f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/bx/payment.py": {
"mtime": 1780751365.9410522,
"ast_hash": "f9e1133888b3bf36e702a6e303623a34",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/reimburse.py": {
"mtime": 1780758908.405342,
"ast_hash": "2e8ac33bc523284cfea413db1aaeaf6b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/expense_item.py": {
"mtime": 1780750601.2191534,
"ast_hash": "a3f9e3f9e03b2b81d048be2fbb995a53",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/__init__.py": {
"mtime": 1780750594.045302,
"ast_hash": "782178a68c005e9671eeaef56ce8979f",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/attachment.py": {
"mtime": 1780750887.666369,
"ast_hash": "1534ad3911e5b3da36e09fec5efbb9fc",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/advance.py": {
"mtime": 1780758906.8892431,
"ast_hash": "b1271ff02969bff61d4190fb50c0d55a",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/storage.py": {
"mtime": 1780750869.826924,
"ast_hash": "a2549a79ff52c07c21bcdce831ca28f5",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/workflow.py": {
"mtime": 1780756089.1444035,
"ast_hash": "a58293c795a169f40656222738a22630",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/base.py": {
"mtime": 1780750593.8290622,
"ast_hash": "1e6a5197afea4b037af84cccf34a884b",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/index/route-parent.vue": {
"mtime": 1780753053.641752,
"ast_hash": "42ecef008755f2cece85bc0c376ff4e8",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/ApprovalHistoryView.vue": {
"mtime": 1780758211.63801,
"ast_hash": "d8426be908f01e26aa7b3e7f0c1d2418",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/bx/approval_cycle.py": {
"mtime": 1780758200.9048047,
"ast_hash": "2147b45ce5131338bdefbf473777e751",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/bx/attachment.ts": {
"mtime": 1780763324.103673,
"ast_hash": "acea2f7b9bc88bcf68b6a7d85306a62e",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BxAttachmentReadonly.vue": {
"mtime": 1780763385.7825282,
"ast_hash": "7928eeef33f1e2df7525bd8b86bb3a07",
"semantic_hash": ""
},
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/bx/base/BxAttachmentUpload.vue": {
"mtime": 1780763324.103737,
"ast_hash": "1b9cd42b0a1e3b7ff74821185d65b432",
"semantic_hash": ""
}
}
Loaded 100 of 155 files, more files were not shown because too many files have changed in this diff. Show more