fix: 修复多个关键问题

- 修复前端路由守卫:未登录时不显示提示,直接跳转登录页
- 修复API拦截器:401错误不显示提示,直接跳转
- 增强验证码显示:图片尺寸从120x40增加到200x80
- 增大验证码字体:从28号增加到48号
- 优化验证码字符:排除易混淆的0和1
- 减少干扰线:从5条减少到3条,添加背景色优化
- 增强登录API日志:添加详细的调试日志
- 增强验证码生成和验证日志
- 优化异常处理和错误追踪

影响文件:
- src/router/index.ts
- src/api/request.ts
- app/services/auth_service.py
- app/api/v1/auth.py
- app/schemas/user.py

测试状态:
- 前端构建通过
- 后端语法检查通过
- 验证码显示效果优化完成

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-01-25 00:26:21 +08:00
commit e71181f0a3
150 changed files with 39549 additions and 0 deletions

240
app/api/v1/organizations.py Normal file
View File

@@ -0,0 +1,240 @@
"""
机构网点API路由
"""
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from app.core.deps import get_db, get_current_user
from app.schemas.organization import (
OrganizationCreate,
OrganizationUpdate,
OrganizationResponse,
OrganizationTreeNode,
OrganizationWithParent
)
from app.services.organization_service import organization_service
from app.utils.redis_client import redis_client
router = APIRouter()
# 异步缓存包装器
@redis_client.cached_async("organizations:list", expire=1800) # 缓存30分钟
async def _cached_get_organizations(
skip: int,
limit: int,
org_type: Optional[str],
status: Optional[str],
keyword: Optional[str],
db: Session
):
"""获取机构列表的缓存包装器"""
items, total = organization_service.get_organizations(
db=db,
skip=skip,
limit=limit,
org_type=org_type,
status=status,
keyword=keyword
)
return items
@redis_client.cached_async("organizations:tree", expire=1800) # 缓存30分钟
async def _cached_get_organization_tree(
status: Optional[str],
db: Session
):
"""获取机构树的缓存包装器"""
return organization_service.get_organization_tree(db, status)
@router.get("/", response_model=List[OrganizationResponse])
async def get_organizations(
skip: int = Query(0, ge=0, description="跳过条数"),
limit: int = Query(20, ge=1, le=100, description="返回条数"),
org_type: Optional[str] = Query(None, description="机构类型"),
status: Optional[str] = Query(None, description="状态"),
keyword: Optional[str] = Query(None, description="搜索关键词"),
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
获取机构列表已启用缓存30分钟
- **skip**: 跳过条数
- **limit**: 返回条数最大100
- **org_type**: 机构类型筛选province/city/outlet
- **status**: 状态筛选active/inactive
- **keyword**: 搜索关键词(代码或名称)
"""
return await _cached_get_organizations(
skip=skip,
limit=limit,
org_type=org_type,
status=status,
keyword=keyword,
db=db
)
@router.get("/tree", response_model=List[OrganizationTreeNode])
async def get_organization_tree(
status: Optional[str] = Query(None, description="状态筛选"),
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
获取机构树已启用缓存30分钟
- **status**: 状态筛选active/inactive
返回树形结构的机构列表
"""
return await _cached_get_organization_tree(status, db)
@router.get("/{org_id}", response_model=OrganizationWithParent)
def get_organization(
org_id: int,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
获取机构详情
- **org_id**: 机构ID
返回机构详情及其父机构信息
"""
org = organization_service.get_organization(db, org_id)
# 加载父机构信息
if org.parent_id:
from app.crud.organization import organization as organization_crud
parent = organization_crud.get(db, org.parent_id)
org.parent = parent
return org
@router.get("/{org_id}/children", response_model=List[OrganizationResponse])
def get_organization_children(
org_id: int,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
获取直接子机构
- **org_id**: 父机构ID0表示根节点
返回指定机构的直接子机构列表
"""
return organization_service.get_organization_children(db, org_id)
@router.get("/{org_id}/all-children", response_model=List[OrganizationResponse])
def get_all_organization_children(
org_id: int,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
递归获取所有子机构
- **org_id**: 父机构ID
返回指定机构的所有子机构(包括子节点的子节点)
"""
return organization_service.get_all_children(db, org_id)
@router.get("/{org_id}/parents", response_model=List[OrganizationResponse])
def get_organization_parents(
org_id: int,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
递归获取所有父机构
- **org_id**: 子机构ID
返回从根到直接父节点的所有父机构列表
"""
return organization_service.get_parents(db, org_id)
@router.post("/", response_model=OrganizationResponse, status_code=status.HTTP_201_CREATED)
def create_organization(
obj_in: OrganizationCreate,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
创建机构
- **org_code**: 机构代码(唯一)
- **org_name**: 机构名称
- **org_type**: 机构类型province/city/outlet
- **parent_id**: 父机构ID可选
- **address**: 地址
- **contact_person**: 联系人
- **contact_phone**: 联系电话
- **sort_order**: 排序
"""
return organization_service.create_organization(
db=db,
obj_in=obj_in,
creator_id=current_user.id
)
@router.put("/{org_id}", response_model=OrganizationResponse)
def update_organization(
org_id: int,
obj_in: OrganizationUpdate,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
更新机构
- **org_id**: 机构ID
- **org_name**: 机构名称
- **org_type**: 机构类型
- **parent_id**: 父机构ID
- **address**: 地址
- **contact_person**: 联系人
- **contact_phone**: 联系电话
- **status**: 状态
- **sort_order**: 排序
"""
return organization_service.update_organization(
db=db,
org_id=org_id,
obj_in=obj_in,
updater_id=current_user.id
)
@router.delete("/{org_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_organization(
org_id: int,
db: Session = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
删除机构
- **org_id**: 机构ID
软删除机构(如果机构下存在子机构则无法删除)
"""
organization_service.delete_organization(
db=db,
org_id=org_id,
deleter_id=current_user.id
)
return None