diff --git a/backend/app/api/v1/admin_positions_backup.py b/backend/app/api/v1/admin_positions_backup.py deleted file mode 100644 index c8710b7..0000000 --- a/backend/app/api/v1/admin_positions_backup.py +++ /dev/null @@ -1,158 +0,0 @@ -# 此文件备份了admin.py中的positions相关路由代码 -# 这些路由已移至positions.py,为避免冲突,从admin.py中移除 - -@router.get("/positions") -async def list_positions( - keyword: Optional[str] = Query(None, description="关键词"), - page: int = Query(1, ge=1), - pageSize: int = Query(20, ge=1, le=100), - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - """ - 获取岗位列表(stub 数据) - - 返回结构兼容前端:data.list/total/page/pageSize - """ - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - try: - items = _sample_positions() - if keyword: - kw = keyword.lower() - items = [ - p for p in items if kw in (p.get("name", "") + p.get("description", "")).lower() - ] - - total = len(items) - start = (page - 1) * pageSize - end = start + pageSize - page_items = items[start:end] - - return ResponseModel( - code=200, - message="获取岗位列表成功", - data={ - "list": page_items, - "total": total, - "page": page, - "pageSize": pageSize, - }, - ) - except Exception as exc: - # 记录错误堆栈由全局异常中间件处理;此处返回统一结构 - return ResponseModel(code=500, message=f"服务器错误:{exc}") - - -@router.get("/positions/tree") -async def get_position_tree( - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - """ - 获取岗位树(stub 数据) - """ - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - try: - items = _sample_positions() - id_to_node: Dict[int, Dict[str, Any]] = {} - for p in items: - node = {**p, "children": []} - id_to_node[p["id"]] = node - - roots: List[Dict[str, Any]] = [] - for p in items: - parent_id = p.get("parentId") - if parent_id and parent_id in id_to_node: - id_to_node[parent_id]["children"].append(id_to_node[p["id"]]) - else: - roots.append(id_to_node[p["id"]]) - - return ResponseModel(code=200, message="获取岗位树成功", data=roots) - except Exception as exc: - return ResponseModel(code=500, message=f"服务器错误:{exc}") - - -@router.get("/positions/{position_id}") -async def get_position_detail( - position_id: int, - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - items = _sample_positions() - for p in items: - if p["id"] == position_id: - return ResponseModel(code=200, message="获取岗位详情成功", data=p) - return ResponseModel(code=404, message="岗位不存在") - - -@router.get("/positions/{position_id}/check-delete") -async def check_position_delete( - position_id: int, - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - # stub:允许删除非根岗位 - deletable = position_id != 1 - reason = "根岗位不允许删除" if not deletable else "" - return ResponseModel(code=200, message="检查成功", data={"deletable": deletable, "reason": reason}) - - -@router.post("/positions") -async def create_position( - payload: Dict[str, Any], - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - # stub:直接回显并附带一个伪ID - payload = dict(payload) - payload.setdefault("id", 999) - payload.setdefault("createTime", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - return ResponseModel(code=200, message="创建岗位成功", data=payload) - - -@router.put("/positions/{position_id}") -async def update_position( - position_id: int, - payload: Dict[str, Any], - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - # stub:直接回显 - updated = {"id": position_id, **payload} - return ResponseModel(code=200, message="更新岗位成功", data=updated) - - -@router.delete("/positions/{position_id}") -async def delete_position( - position_id: int, - current_user: User = Depends(get_current_user), - _db: AsyncSession = Depends(get_db), -) -> ResponseModel: - not_admin = _ensure_admin(current_user) - if not_admin: - return not_admin - - # stub:直接返回成功 - return ResponseModel(code=200, message="删除岗位成功", data={"id": position_id}) diff --git a/backend/app/api/v1/courses.py b/backend/app/api/v1/courses.py index 4cfa94b..c5e9ac0 100644 --- a/backend/app/api/v1/courses.py +++ b/backend/app/api/v1/courses.py @@ -4,6 +4,7 @@ from typing import List, Optional from fastapi import APIRouter, Depends, Query, status, BackgroundTasks, Request +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db, get_current_user, require_admin, require_admin_or_manager, User diff --git a/backend/app/api/v1/coze_gateway.py b/backend/app/api/v1/coze_gateway.py index e38ae3c..608b6a6 100644 --- a/backend/app/api/v1/coze_gateway.py +++ b/backend/app/api/v1/coze_gateway.py @@ -203,25 +203,29 @@ async def send_message(request: SendMessageRequest, user=Depends(get_current_use }, } - except CozeException as e: - logger.error(f"发送消息失败: {e}") + except CozeException as coze_err: + logger.error(f"发送消息失败: {coze_err}") if request.stream: - # 流式响应的错误处理 + # 流式响应的错误处理 - 捕获异常信息避免闭包问题 + err_code = coze_err.code + err_message = coze_err.message + err_details = coze_err.details + async def error_generator(): yield { "event": "error", "data": { - "code": e.code, - "message": e.message, - "details": e.details, + "code": err_code, + "message": err_message, + "details": err_details, }, } return EventSourceResponse(error_generator()) else: raise HTTPException( - status_code=e.status_code or 500, - detail={"code": e.code, "message": e.message, "details": e.details}, + status_code=coze_err.status_code or 500, + detail={"code": err_code, "message": err_message, "details": err_details}, ) except Exception as e: logger.error(f"未知错误: {e}", exc_info=True) diff --git a/backend/app/models/course.py b/backend/app/models/course.py index 570d0e2..883d528 100644 --- a/backend/app/models/course.py +++ b/backend/app/models/course.py @@ -2,9 +2,12 @@ 课程相关数据库模型 """ from enum import Enum -from typing import List, Optional +from typing import List, Optional, TYPE_CHECKING from datetime import datetime +if TYPE_CHECKING: + from app.models.growth_path import GrowthPathNode + from sqlalchemy import ( String, Text, diff --git a/backend/app/models/growth_path.py b/backend/app/models/growth_path.py index bab1161..6ddbeac 100644 --- a/backend/app/models/growth_path.py +++ b/backend/app/models/growth_path.py @@ -2,10 +2,14 @@ 成长路径相关数据库模型 """ from enum import Enum -from typing import List, Optional +from typing import List, Optional, TYPE_CHECKING from datetime import datetime from decimal import Decimal +if TYPE_CHECKING: + from app.models.course import Course + from app.models.user import User + from sqlalchemy import ( String, Text, @@ -84,7 +88,7 @@ class GrowthPathNode(BaseModel, SoftDeleteMixin): ) # 关联关系 - growth_path: Mapped["GrowthPath"] = relationship( + growth_path: Mapped["GrowthPath"] = relationship( # noqa: F821 "GrowthPath", back_populates="nodes" ) course: Mapped["Course"] = relationship("Course") @@ -146,7 +150,7 @@ class UserGrowthPathProgress(BaseModel): # 关联关系 user: Mapped["User"] = relationship("User") - growth_path: Mapped["GrowthPath"] = relationship("GrowthPath") + growth_path: Mapped["GrowthPath"] = relationship("GrowthPath") # noqa: F821 class UserNodeCompletion(BaseModel): @@ -203,4 +207,4 @@ class UserNodeCompletion(BaseModel): node: Mapped["GrowthPathNode"] = relationship( "GrowthPathNode", back_populates="user_completions" ) - growth_path: Mapped["GrowthPath"] = relationship("GrowthPath") + growth_path: Mapped["GrowthPath"] = relationship("GrowthPath") # noqa: F821 diff --git a/backend/app/services/scheduler_service.py b/backend/app/services/scheduler_service.py index 9ed027d..0e1416c 100644 --- a/backend/app/services/scheduler_service.py +++ b/backend/app/services/scheduler_service.py @@ -261,8 +261,6 @@ def start_scheduler(): def stop_scheduler(): """停止调度器""" - global scheduler - if scheduler and scheduler.running: scheduler.shutdown() logger.info("定时任务调度器已停止")