Files
shensheshou/app/api/users/route.ts

187 lines
5.3 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.

/**
* 用户 API 路由
* 对接神射手 MongoDB 数据库 - KR.用户估值
*/
import { NextResponse, NextRequest } from "next/server"
import {
queryUserList,
queryUserByPhone,
queryFullProfile,
UserValuationDoc
} from "@/lib/mongodb"
/**
* 脱敏手机号
*/
function maskPhone(phone: string | undefined): string {
if (!phone) return ''
if (phone.length !== 11) return phone
return `${phone.slice(0, 3)}****${phone.slice(-4)}`
}
/**
* 转换用户数据格式(适配前端)
*/
function transformUser(doc: UserValuationDoc, index: number = 0): any {
const name = doc.name || '未知用户'
return {
id: doc._id?.toString() || `user-${index}`,
avatar: `/placeholder.svg?height=40&width=40&text=${name[0] || 'U'}`,
nickname: name,
wechatId: doc.phone ? `wxid_${doc.phone.slice(-8)}` : '',
phone: doc.phone || '',
phone_masked: doc.phone_masked || maskPhone(doc.phone),
region: doc.province && doc.city ? `${doc.province}${doc.city}` : (doc.province || '未知'),
note: '',
status: 'added' as const,
addTime: doc.created_at?.toISOString() || new Date().toISOString(),
source: (doc.source_channels && doc.source_channels[0]) || '神射手',
assignedTo: '',
category: 'customer' as const,
tags: doc.tags || [],
// RFM 数据
userLevel: doc.user_level || 'D',
rfmScore: doc.rfm_composite_score || 0,
rfmR: doc.rfm_r_score,
rfmF: doc.rfm_f_score,
rfmM: doc.rfm_m_score,
totalAmount: doc.total_amount || 0,
orderCount: doc.order_count || 0,
// 额外信息
email: doc.email,
address: doc.address,
province: doc.province,
city: doc.city,
}
}
/**
* GET /api/users
* 查询用户列表或单个用户详情
*/
export async function GET(req: NextRequest) {
try {
const { searchParams } = new URL(req.url)
// 单用户详情查询按ID或手机号
const id = searchParams.get('id')
const phone = searchParams.get('phone')
if (id || phone) {
// 如果是手机号格式,按手机号查询
const queryPhone = phone || (id && /^1[3-9]\d{9}$/.test(id) ? id : null)
if (queryPhone) {
// 完整画像查询(跨库)
const profile = await queryFullProfile(queryPhone)
if (profile.valuation) {
const user = transformUser(profile.valuation)
// 补充 QQ 信息
if (profile.qq) {
user.qq = profile.qq.qq
user.qqScore = profile.qq.QQ号评分
user.phoneScore = profile.qq.
}
// 补充存客宝信息
if (profile.ckb) {
user.wechat = profile.ckb.social_accounts?.wechat
user.trafficPool = profile.ckb.traffic_pool?.pool_name
}
return NextResponse.json({
data: user,
sources: {
valuation: !!profile.valuation,
qq: !!profile.qq,
ckb: !!profile.ckb
}
}, { headers: { 'Cache-Control': 'no-store' } })
}
return NextResponse.json({
data: null,
error: '未找到该用户'
}, { status: 404 })
}
return NextResponse.json({
data: null,
error: '无效的查询参数'
}, { status: 400 })
}
// 列表查询
const q = searchParams.get('q') || undefined
const tagsStr = searchParams.get('tags') || ''
const userLevel = searchParams.get('userLevel') || searchParams.get('status') || undefined
const rfmMin = searchParams.get('rfmMin') ? Number(searchParams.get('rfmMin')) : undefined
const rfmMax = searchParams.get('rfmMax') ? Number(searchParams.get('rfmMax')) : undefined
const page = Number(searchParams.get('page') ?? 1)
const pageSize = Number(searchParams.get('pageSize') ?? 20)
const tags = tagsStr ? tagsStr.split(',').filter(Boolean) : undefined
const result = await queryUserList({
page,
pageSize,
userLevel,
minRfm: rfmMin,
maxRfm: rfmMax,
search: q,
tags
})
const transformedData = result.data.map((doc, i) => transformUser(doc, i))
return NextResponse.json({
data: transformedData,
total: result.total,
page,
pageSize,
totalPages: Math.ceil(result.total / pageSize)
}, { headers: { 'Cache-Control': 'no-store' } })
} catch (error) {
console.error('Users API error:', error)
// 数据库连接失败时返回模拟数据
return NextResponse.json({
data: [],
total: 0,
page: 1,
pageSize: 20,
totalPages: 0,
error: error instanceof Error ? error.message : '查询失败',
fallback: true
}, {
status: 500,
headers: { 'Cache-Control': 'no-store' }
})
}
}
/**
* POST /api/users
* 创建用户(预留接口)
*/
export async function POST(req: NextRequest) {
try {
const body = await req.json().catch(() => ({}))
// TODO: 实现用户创建逻辑
return NextResponse.json({
success: false,
error: '用户创建功能暂未开放'
}, { status: 501 })
} catch (error) {
return NextResponse.json({
error: error instanceof Error ? error.message : '创建失败'
}, { status: 500 })
}
}