feat: AI智能学习助手分析结果缓存到Redis
Some checks are pending
continuous-integration/drone/push Build is running
Some checks are pending
continuous-integration/drone/push Build is running
- 后端API添加Redis缓存,有效期4小时 - 支持force_refresh参数强制刷新缓存 - 前端自动加载使用缓存,手动刷新强制更新 - 返回from_cache字段标识数据来源 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
能力评估API接口
|
||||
用于智能工牌数据分析、能力评估报告生成等
|
||||
"""
|
||||
import json
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from app.core.deps import get_current_user, get_db
|
||||
from app.core.redis import get_redis_client
|
||||
from app.models.user import User
|
||||
from app.schemas.base import ResponseModel
|
||||
from app.schemas.ability import AbilityAssessmentResponse, AbilityAssessmentHistory
|
||||
@@ -18,32 +20,42 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
# Redis 缓存配置
|
||||
ABILITY_CACHE_KEY_PREFIX = "ability:assessment:"
|
||||
ABILITY_CACHE_TTL = 4 * 60 * 60 # 4小时缓存
|
||||
|
||||
|
||||
@router.post("/analyze-yanji", response_model=ResponseModel)
|
||||
async def analyze_yanji_badge_data(
|
||||
force_refresh: bool = Query(False, description="强制刷新缓存"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
分析智能工牌数据生成能力评估和课程推荐
|
||||
|
||||
使用 Python 原生 AI 服务实现。
|
||||
使用 Python 原生 AI 服务实现,结果缓存4小时。
|
||||
|
||||
功能说明:
|
||||
1. 从言迹智能工牌获取员工的最近10条录音记录
|
||||
2. 分析对话数据,进行能力评估(6个维度)
|
||||
3. 基于能力短板生成课程推荐(3-5门)
|
||||
4. 保存评估记录到数据库
|
||||
5. 缓存结果到Redis(4小时有效期)
|
||||
|
||||
要求:
|
||||
- 用户必须已绑定手机号(用于匹配言迹数据)
|
||||
|
||||
参数:
|
||||
- force_refresh: 是否强制刷新缓存(默认False)
|
||||
|
||||
返回:
|
||||
- assessment_id: 评估记录ID
|
||||
- total_score: 综合评分(0-100)
|
||||
- dimensions: 能力维度列表(6个维度)
|
||||
- recommended_courses: 推荐课程列表(3-5门)
|
||||
- conversation_count: 分析的对话数量
|
||||
- from_cache: 是否来自缓存
|
||||
"""
|
||||
# 检查用户是否绑定手机号
|
||||
if not current_user.phone:
|
||||
@@ -53,6 +65,29 @@ async def analyze_yanji_badge_data(
|
||||
detail="用户未绑定手机号,无法匹配言迹数据"
|
||||
)
|
||||
|
||||
# Redis 缓存键
|
||||
cache_key = f"{ABILITY_CACHE_KEY_PREFIX}{current_user.id}"
|
||||
|
||||
# 尝试从缓存获取(除非强制刷新)
|
||||
if not force_refresh:
|
||||
try:
|
||||
redis = get_redis_client()
|
||||
cached_data = await redis.get(cache_key)
|
||||
if cached_data:
|
||||
result = json.loads(cached_data)
|
||||
result['from_cache'] = True
|
||||
logger.info(
|
||||
f"从缓存返回能力评估结果: user_id={current_user.id}, "
|
||||
f"assessment_id={result.get('assessment_id')}"
|
||||
)
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="智能工牌数据分析完成(缓存)",
|
||||
data=result
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"读取缓存失败,将重新分析: {e}")
|
||||
|
||||
# 获取服务实例
|
||||
yanji_service = YanjiService()
|
||||
assessment_service = get_ability_assessment_service()
|
||||
@@ -60,7 +95,7 @@ async def analyze_yanji_badge_data(
|
||||
try:
|
||||
logger.info(
|
||||
f"开始分析智能工牌数据: user_id={current_user.id}, "
|
||||
f"phone={current_user.phone}"
|
||||
f"phone={current_user.phone}, force_refresh={force_refresh}"
|
||||
)
|
||||
|
||||
# 调用能力评估服务(使用 Python 原生实现)
|
||||
@@ -72,6 +107,26 @@ async def analyze_yanji_badge_data(
|
||||
engine="v2" # 固定使用 V2
|
||||
)
|
||||
|
||||
# 缓存结果到 Redis
|
||||
try:
|
||||
redis = get_redis_client()
|
||||
# 序列化时处理 datetime 对象
|
||||
cache_data = {
|
||||
"assessment_id": result["assessment_id"],
|
||||
"total_score": result["total_score"],
|
||||
"dimensions": result["dimensions"],
|
||||
"recommended_courses": result["recommended_courses"],
|
||||
"conversation_count": result["conversation_count"],
|
||||
"analyzed_at": result["analyzed_at"].isoformat() if result.get("analyzed_at") else None,
|
||||
"engine": result.get("engine"),
|
||||
}
|
||||
await redis.setex(cache_key, ABILITY_CACHE_TTL, json.dumps(cache_data, ensure_ascii=False))
|
||||
logger.info(f"能力评估结果已缓存: user_id={current_user.id}, ttl={ABILITY_CACHE_TTL}s")
|
||||
except Exception as e:
|
||||
logger.warning(f"缓存结果失败: {e}")
|
||||
|
||||
result['from_cache'] = False
|
||||
|
||||
logger.info(
|
||||
f"智能工牌数据分析完成: user_id={current_user.id}, "
|
||||
f"assessment_id={result['assessment_id']}, "
|
||||
|
||||
@@ -490,8 +490,10 @@ export const sendCourseMessage = (sessionId: string, content: string) => {
|
||||
/**
|
||||
* 分析智能工牌数据
|
||||
* 调用后端API分析言迹智能工牌的录音数据,生成能力评估报告和课程推荐
|
||||
*
|
||||
* @param forceRefresh - 是否强制刷新缓存(默认false,使用4小时缓存)
|
||||
*/
|
||||
export const analyzeYanjiBadge = () => {
|
||||
export const analyzeYanjiBadge = (forceRefresh: boolean = false) => {
|
||||
return request.post<{
|
||||
assessment_id: number
|
||||
total_score: number
|
||||
@@ -509,5 +511,6 @@ export const analyzeYanjiBadge = () => {
|
||||
}>
|
||||
conversation_count: number
|
||||
analyzed_at: string
|
||||
}>('/api/v1/ability/analyze-yanji')
|
||||
from_cache?: boolean
|
||||
}>(`/api/v1/ability/analyze-yanji?force_refresh=${forceRefresh}`)
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@
|
||||
<p class="empty-description">
|
||||
{{ analyzing ? '正在分析您的智能工牌数据,为您推荐最适合的课程' : '暂无智能工牌数据,请先使用智能工牌记录对话' }}
|
||||
</p>
|
||||
<el-button v-if="!analyzing && userInfo.role === 'trainee'" type="primary" @click="analyzeSmartBadgeData">
|
||||
<el-button v-if="!analyzing && userInfo.role === 'trainee'" type="primary" @click="analyzeSmartBadgeData(true)">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重新分析
|
||||
</el-button>
|
||||
@@ -593,8 +593,9 @@ const initRadarChart = () => {
|
||||
|
||||
/**
|
||||
* AI 分析智能工牌数据
|
||||
* @param forceRefresh - 是否强制刷新缓存
|
||||
*/
|
||||
const analyzeSmartBadgeData = async () => {
|
||||
const analyzeSmartBadgeData = async (forceRefresh: boolean = false) => {
|
||||
// 前置检查:只有学员用户且已绑定手机号才能分析
|
||||
if (userInfo.value.role !== 'trainee') {
|
||||
ElMessage.warning('该功能仅对学员开放')
|
||||
@@ -609,8 +610,8 @@ const analyzeSmartBadgeData = async () => {
|
||||
analyzing.value = true
|
||||
|
||||
try {
|
||||
// 调用后端API分析智能工牌数据
|
||||
const response = await analyzeYanjiBadge()
|
||||
// 调用后端API分析智能工牌数据(支持缓存)
|
||||
const response = await analyzeYanjiBadge(forceRefresh)
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const { dimensions, recommended_courses, total_score, conversation_count } = response.data
|
||||
@@ -708,10 +709,11 @@ const analyzeSmartBadgeData = async () => {
|
||||
/**
|
||||
* 刷新AI推荐课程
|
||||
* 重新调用AI分析接口,获取最新的课程推荐
|
||||
* 强制刷新缓存以获取最新数据
|
||||
*/
|
||||
const refreshRecommendations = async () => {
|
||||
// 直接调用AI分析智能工牌数据的方法,复用已有逻辑
|
||||
await analyzeSmartBadgeData()
|
||||
// 强制刷新,不使用缓存
|
||||
await analyzeSmartBadgeData(true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user