feat: AI智能学习助手分析结果缓存到Redis
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:
yuliang_guo
2026-02-05 12:06:12 +08:00
parent 3a238f810c
commit b2815ef3cb
3 changed files with 71 additions and 11 deletions

View File

@@ -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. 缓存结果到Redis4小时有效期
要求:
- 用户必须已绑定手机号(用于匹配言迹数据)
参数:
- 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']}, "

View File

@@ -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}`)
}

View File

@@ -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)
}
/**