- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
312 lines
9.3 KiB
Python
312 lines
9.3 KiB
Python
"""
|
||
SCRM 系统对接 API 路由
|
||
|
||
提供给 SCRM 系统调用的数据查询接口
|
||
认证方式:Bearer Token (SCRM_API_KEY)
|
||
"""
|
||
|
||
import logging
|
||
from typing import Optional
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.core.deps import get_db, verify_scrm_api_key
|
||
from app.services.scrm_service import SCRMService
|
||
from app.schemas.scrm import (
|
||
EmployeePositionResponse,
|
||
EmployeePositionData,
|
||
PositionCoursesResponse,
|
||
PositionCoursesData,
|
||
KnowledgePointSearchRequest,
|
||
KnowledgePointSearchResponse,
|
||
KnowledgePointSearchData,
|
||
KnowledgePointDetailResponse,
|
||
KnowledgePointDetailData,
|
||
SCRMErrorResponse,
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter(prefix="/scrm", tags=["scrm"])
|
||
|
||
|
||
# ==================== 1. 获取员工岗位 ====================
|
||
|
||
@router.get(
|
||
"/employees/{userid}/position",
|
||
response_model=EmployeePositionResponse,
|
||
summary="获取员工岗位(通过userid)",
|
||
description="根据企微 userid 查询员工在考陪练系统中的岗位信息",
|
||
responses={
|
||
200: {"model": EmployeePositionResponse, "description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
404: {"model": SCRMErrorResponse, "description": "员工不存在"},
|
||
}
|
||
)
|
||
async def get_employee_position_by_userid(
|
||
userid: str,
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取员工岗位(通过企微userid)
|
||
|
||
- **userid**: 企微员工 userid
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.get_employee_position(userid=userid)
|
||
|
||
if result is None:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail={
|
||
"code": 404,
|
||
"message": "员工不存在",
|
||
"data": None
|
||
}
|
||
)
|
||
|
||
# 检查是否有多个匹配结果
|
||
if result.get("multiple_matches"):
|
||
return {
|
||
"code": 0,
|
||
"message": f"找到 {result['count']} 个匹配的员工,请确认",
|
||
"data": result
|
||
}
|
||
|
||
return EmployeePositionResponse(
|
||
code=0,
|
||
message="success",
|
||
data=EmployeePositionData(**result)
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/employees/search/by-name",
|
||
summary="获取员工岗位(通过姓名搜索)",
|
||
description="根据员工姓名查询员工在考陪练系统中的岗位信息,支持精确匹配和模糊匹配",
|
||
responses={
|
||
200: {"description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
404: {"model": SCRMErrorResponse, "description": "员工不存在"},
|
||
}
|
||
)
|
||
async def get_employee_position_by_name(
|
||
name: str = Query(..., description="员工姓名,支持精确匹配和模糊匹配"),
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取员工岗位(通过姓名搜索)
|
||
|
||
- **name**: 员工姓名(必填),优先精确匹配,无结果时模糊匹配
|
||
|
||
注意:如果有多个同名员工,会返回员工列表供确认
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.get_employee_position(name=name)
|
||
|
||
if result is None:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail={
|
||
"code": 404,
|
||
"message": f"未找到姓名包含 '{name}' 的员工",
|
||
"data": None
|
||
}
|
||
)
|
||
|
||
# 检查是否有多个匹配结果
|
||
if result.get("multiple_matches"):
|
||
return {
|
||
"code": 0,
|
||
"message": f"找到 {result['count']} 个匹配的员工,请确认后使用 employee_id 精确查询",
|
||
"data": result
|
||
}
|
||
|
||
return EmployeePositionResponse(
|
||
code=0,
|
||
message="success",
|
||
data=EmployeePositionData(**result)
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/employees/by-id/{employee_id}/position",
|
||
response_model=EmployeePositionResponse,
|
||
summary="获取员工岗位(通过员工ID)",
|
||
description="根据员工ID精确查询员工岗位信息,用于多个同名员工时的精确查询",
|
||
responses={
|
||
200: {"model": EmployeePositionResponse, "description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
404: {"model": SCRMErrorResponse, "description": "员工不存在"},
|
||
}
|
||
)
|
||
async def get_employee_position_by_id(
|
||
employee_id: int,
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取员工岗位(通过员工ID精确查询)
|
||
|
||
- **employee_id**: 员工ID(考陪练系统用户ID)
|
||
|
||
适用场景:通过姓名搜索返回多个匹配结果后,使用此接口精确查询
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.get_employee_position_by_id(employee_id)
|
||
|
||
if result is None:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail={
|
||
"code": 404,
|
||
"message": "员工不存在",
|
||
"data": None
|
||
}
|
||
)
|
||
|
||
return EmployeePositionResponse(
|
||
code=0,
|
||
message="success",
|
||
data=EmployeePositionData(**result)
|
||
)
|
||
|
||
|
||
# ==================== 2. 获取岗位课程列表 ====================
|
||
|
||
@router.get(
|
||
"/positions/{position_id}/courses",
|
||
response_model=PositionCoursesResponse,
|
||
summary="获取岗位课程列表",
|
||
description="获取指定岗位的必修/选修课程列表",
|
||
responses={
|
||
200: {"model": PositionCoursesResponse, "description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
404: {"model": SCRMErrorResponse, "description": "岗位不存在"},
|
||
}
|
||
)
|
||
async def get_position_courses(
|
||
position_id: int,
|
||
course_type: Optional[str] = Query(
|
||
default="all",
|
||
description="课程类型:required/optional/all",
|
||
regex="^(required|optional|all)$"
|
||
),
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取岗位课程列表
|
||
|
||
- **position_id**: 岗位ID
|
||
- **course_type**: 课程类型筛选(required/optional/all,默认 all)
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.get_position_courses(position_id, course_type)
|
||
|
||
if result is None:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail={
|
||
"code": 40002,
|
||
"message": "position_id 不存在",
|
||
"data": None
|
||
}
|
||
)
|
||
|
||
return PositionCoursesResponse(
|
||
code=0,
|
||
message="success",
|
||
data=PositionCoursesData(**result)
|
||
)
|
||
|
||
|
||
# ==================== 3. 搜索知识点 ====================
|
||
|
||
@router.post(
|
||
"/knowledge-points/search",
|
||
response_model=KnowledgePointSearchResponse,
|
||
summary="搜索知识点",
|
||
description="根据关键词和岗位搜索匹配的知识点",
|
||
responses={
|
||
200: {"model": KnowledgePointSearchResponse, "description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
400: {"model": SCRMErrorResponse, "description": "请求参数错误"},
|
||
}
|
||
)
|
||
async def search_knowledge_points(
|
||
request: KnowledgePointSearchRequest,
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
搜索知识点
|
||
|
||
- **keywords**: 搜索关键词列表(必填)
|
||
- **position_id**: 岗位ID(用于优先排序,可选)
|
||
- **course_ids**: 限定课程范围(可选)
|
||
- **knowledge_type**: 知识点类型筛选(可选)
|
||
- **limit**: 返回数量,默认10,最大100
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.search_knowledge_points(
|
||
keywords=request.keywords,
|
||
position_id=request.position_id,
|
||
course_ids=request.course_ids,
|
||
knowledge_type=request.knowledge_type,
|
||
limit=request.limit
|
||
)
|
||
|
||
return KnowledgePointSearchResponse(
|
||
code=0,
|
||
message="success",
|
||
data=KnowledgePointSearchData(**result)
|
||
)
|
||
|
||
|
||
# ==================== 4. 获取知识点详情 ====================
|
||
|
||
@router.get(
|
||
"/knowledge-points/{knowledge_point_id}",
|
||
response_model=KnowledgePointDetailResponse,
|
||
summary="获取知识点详情",
|
||
description="获取知识点的完整信息",
|
||
responses={
|
||
200: {"model": KnowledgePointDetailResponse, "description": "成功"},
|
||
401: {"model": SCRMErrorResponse, "description": "认证失败"},
|
||
404: {"model": SCRMErrorResponse, "description": "知识点不存在"},
|
||
}
|
||
)
|
||
async def get_knowledge_point_detail(
|
||
knowledge_point_id: int,
|
||
_: bool = Depends(verify_scrm_api_key),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取知识点详情
|
||
|
||
- **knowledge_point_id**: 知识点ID
|
||
"""
|
||
service = SCRMService(db)
|
||
result = await service.get_knowledge_point_detail(knowledge_point_id)
|
||
|
||
if result is None:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail={
|
||
"code": 40003,
|
||
"message": "knowledge_point_id 不存在",
|
||
"data": None
|
||
}
|
||
)
|
||
|
||
return KnowledgePointDetailResponse(
|
||
code=0,
|
||
message="success",
|
||
data=KnowledgePointDetailData(**result)
|
||
)
|
||
|