Files
users/app/api/rfm/analyze/route.ts

131 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* RFM 分析 API
* 支持 MongoDB 真实数据 + 内存计算
*/
import { NextResponse } from "next/server"
import { analyzeUser, type AnalyzeInput } from "@/services/rfm-engine"
import { analyzeUserRFM } from "@/services/rfm-mongodb-service"
import { generateText } from "ai"
import { openai } from "@ai-sdk/openai"
/**
* 可选 AI 标签增强
*/
async function aiTagging(chat_logs?: string[]) {
const text = (chat_logs ?? []).slice(0, 8).join("。")
if (!text) return null
if (!process.env.OPENAI_API_KEY) return null
try {
const { text: out } = await generateText({
model: openai("gpt-4o"),
system:
"请根据用户聊天片段输出JSON字段emotion(积极|中性|消极), intent(弱意图|中等意图|强意图), behavior(数组中文标签最多3个)。只输出JSON。",
prompt: `聊天片段:${text}`,
// @ts-expect-error ai sdk typings are flexible in Next.js
maxTokens: 200,
})
const json = JSON.parse(out.trim())
return json
} catch {
return null
}
}
export async function POST(req: Request) {
try {
const body = await req.json()
const inputs: AnalyzeInput[] = Array.isArray(body) ? body : [body]
const useAI = (Array.isArray(body) ? (body as any).useAI : (body as any)?.useAI) ?? false
const useMongoData = (body as any)?.useMongoData ?? true
const results = []
for (const input of inputs) {
let base: any
let source = 'memory'
// 尝试从 MongoDB 获取真实数据
if (useMongoData && input.user_id) {
// 如果 user_id 是手机号格式,尝试从 MongoDB 查询
const phone = input.user_id.replace(/\D/g, '')
if (/^1[3-9]\d{9}$/.test(phone)) {
const mongoResult = await analyzeUserRFM(phone)
if (mongoResult.found) {
base = mongoResult.data
source = 'mongodb'
}
}
}
// 如果 MongoDB 没有数据,使用内存计算
if (!base) {
base = analyzeUser(input)
source = 'memory'
}
// AI 标签增强
if (useAI) {
const ai = await aiTagging(input.chat_logs)
if (ai) {
base.tags = base.tags || {}
base.tags.emotion = ai.emotion ?? base.tags.emotion
base.tags.intent = ai.intent ?? base.tags.intent
if (Array.isArray(ai.behavior)) {
base.tags.behavior = Array.from(new Set([...(base.tags.behavior ?? []), ...ai.behavior]))
}
}
}
results.push({ ...base, source })
}
return NextResponse.json({ success: true, data: results })
} catch (e: any) {
console.error('RFM analyze error:', e)
return NextResponse.json({
success: false,
error: e?.message || "Invalid input"
}, { status: 400 })
}
}
/**
* GET 方法:按手机号查询用户 RFM
*/
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url)
const phone = searchParams.get('phone')
if (!phone) {
return NextResponse.json({
success: false,
error: '请提供手机号参数'
}, { status: 400 })
}
const result = await analyzeUserRFM(phone)
if (!result.found) {
return NextResponse.json({
success: false,
error: '未找到该用户'
}, { status: 404 })
}
return NextResponse.json({
success: true,
data: result.data,
source: 'mongodb'
})
} catch (e: any) {
return NextResponse.json({
success: false,
error: e?.message || "查询失败"
}, { status: 500 })
}
}