refactor: restructure project into 5 core modules

Organize project by 5 core modules based on requirement docs.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2026-01-31 04:18:24 +00:00
parent b4aa620c93
commit 22e725887a
74 changed files with 4219 additions and 9234 deletions

View File

@@ -1,16 +1,12 @@
/**
* 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 标签增强
* 可选 AI 标签增强
* - 使用 AI SDK (generateText + openai("gpt-4o")),符合统一标准 [^1]
* - 无 OPENAI_API_KEY 时自动跳过,保持稳定
*/
async function aiTagging(chat_logs?: string[]) {
const text = (chat_logs ?? []).slice(0, 8).join("。")
@@ -37,38 +33,13 @@ export async function POST(req: Request) {
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 标签增强
const base = analyzeUser(input)
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)) {
@@ -76,55 +47,10 @@ export async function POST(req: Request) {
}
}
}
results.push({ ...base, source })
results.push(base)
}
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 })
return NextResponse.json({ success: false, error: e?.message || "Invalid input" }, { status: 400 })
}
}

View File

@@ -1,51 +1,12 @@
/**
* RFM 标签获取 API
* 对接 MongoDB 真实数据
*/
import { NextResponse } from "next/server"
import { getRFMTagsDistribution } from "@/services/rfm-mongodb-service"
import { getUserTags } from "@/services/rfm-engine"
/**
* GET /api/rfm/get_tags
* 获取标签分布或指定用户的标签
*/
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url)
const userId = searchParams.get('user_id')
// 如果指定用户,返回用户标签
if (userId) {
const userTags = getUserTags(userId)
if (userTags) {
return NextResponse.json({
success: true,
data: userTags.tags,
source: 'memory'
})
}
return NextResponse.json({
success: false,
error: '未找到该用户标签'
}, { status: 404 })
}
// 否则返回标签分布
const distribution = await getRFMTagsDistribution()
return NextResponse.json({
success: true,
data: distribution,
source: 'mongodb'
})
} catch (error) {
console.error('Get tags error:', error)
return NextResponse.json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
}, { status: 500 })
const url = new URL(req.url)
const userId = url.searchParams.get("user_id")
if (!userId) {
return NextResponse.json({ success: false, error: "missing user_id" }, { status: 400 })
}
const data = getUserTags(userId)
return NextResponse.json({ success: true, data })
}

View File

@@ -1,54 +1,7 @@
/**
* RFM 分组统计 API
* 对接 MongoDB 真实数据
*/
import { NextResponse } from "next/server"
import { getMongoRFMGroupSummary } from "@/services/rfm-mongodb-service"
import { getGroupSummary } from "@/services/rfm-engine"
export async function GET() {
try {
// 尝试从 MongoDB 获取真实数据
const mongoData = await getMongoRFMGroupSummary()
if (mongoData.totalUsers > 0) {
return NextResponse.json({
success: true,
data: {
gradeCount: mongoData.gradeCount,
valueCount: mongoData.valueCount,
lifecycleCount: {}, // MongoDB 暂无此字段
totalUsers: mongoData.totalUsers,
avgScore: mongoData.avgScore
},
source: 'mongodb'
})
}
// 回退到内存数据
const memData = getGroupSummary()
return NextResponse.json({
success: true,
data: memData,
source: 'memory'
})
} catch (error) {
console.error('RFM group summary error:', error)
// 返回默认数据
return NextResponse.json({
success: true,
data: {
gradeCount: { S: 0, A: 0, B: 0, C: 0, D: 0 },
valueCount: { '高': 0, '中': 0, '低': 0 },
lifecycleCount: {},
totalUsers: 0,
avgScore: 0
},
source: 'fallback',
error: error instanceof Error ? error.message : 'Unknown error'
})
}
const data = getGroupSummary()
return NextResponse.json({ success: true, data })
}