274 lines
7.9 KiB
TypeScript
274 lines
7.9 KiB
TypeScript
|
|
import { NextResponse } from "next/server"
|
|||
|
|
import type { TrafficUser } from "@/types/traffic"
|
|||
|
|
|
|||
|
|
// 中文名字生成器数据
|
|||
|
|
const familyNames = [
|
|||
|
|
"张",
|
|||
|
|
"王",
|
|||
|
|
"李",
|
|||
|
|
"赵",
|
|||
|
|
"陈",
|
|||
|
|
"刘",
|
|||
|
|
"杨",
|
|||
|
|
"黄",
|
|||
|
|
"周",
|
|||
|
|
"吴",
|
|||
|
|
"朱",
|
|||
|
|
"孙",
|
|||
|
|
"马",
|
|||
|
|
"胡",
|
|||
|
|
"郭",
|
|||
|
|
"林",
|
|||
|
|
"何",
|
|||
|
|
"高",
|
|||
|
|
"梁",
|
|||
|
|
"郑",
|
|||
|
|
"罗",
|
|||
|
|
"宋",
|
|||
|
|
"谢",
|
|||
|
|
"唐",
|
|||
|
|
"韩",
|
|||
|
|
"曹",
|
|||
|
|
"许",
|
|||
|
|
"邓",
|
|||
|
|
"萧",
|
|||
|
|
"冯",
|
|||
|
|
]
|
|||
|
|
const givenNames1 = [
|
|||
|
|
"志",
|
|||
|
|
"建",
|
|||
|
|
"文",
|
|||
|
|
"明",
|
|||
|
|
"永",
|
|||
|
|
"春",
|
|||
|
|
"秀",
|
|||
|
|
"金",
|
|||
|
|
"水",
|
|||
|
|
"玉",
|
|||
|
|
"国",
|
|||
|
|
"立",
|
|||
|
|
"德",
|
|||
|
|
"海",
|
|||
|
|
"和",
|
|||
|
|
"荣",
|
|||
|
|
"伟",
|
|||
|
|
"新",
|
|||
|
|
"英",
|
|||
|
|
"佳",
|
|||
|
|
]
|
|||
|
|
const givenNames2 = [
|
|||
|
|
"华",
|
|||
|
|
"平",
|
|||
|
|
"军",
|
|||
|
|
"强",
|
|||
|
|
"辉",
|
|||
|
|
"敏",
|
|||
|
|
"峰",
|
|||
|
|
"磊",
|
|||
|
|
"超",
|
|||
|
|
"艳",
|
|||
|
|
"娜",
|
|||
|
|
"霞",
|
|||
|
|
"燕",
|
|||
|
|
"娟",
|
|||
|
|
"静",
|
|||
|
|
"丽",
|
|||
|
|
"涛",
|
|||
|
|
"洋",
|
|||
|
|
"勇",
|
|||
|
|
"龙",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
// 生成固定的用户数据池
|
|||
|
|
const userPool: TrafficUser[] = Array.from({ length: 1610 }, (_, i) => {
|
|||
|
|
const familyName = familyNames[Math.floor(Math.random() * familyNames.length)]
|
|||
|
|
const givenName1 = givenNames1[Math.floor(Math.random() * givenNames1.length)]
|
|||
|
|
const givenName2 = givenNames2[Math.floor(Math.random() * givenNames2.length)]
|
|||
|
|
const fullName = Math.random() > 0.5 ? familyName + givenName1 + givenName2 : familyName + givenName1
|
|||
|
|
|
|||
|
|
// 生成随机时间(在过去7天内)
|
|||
|
|
const date = new Date()
|
|||
|
|
date.setDate(date.getDate() - Math.floor(Math.random() * 7))
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
id: `${Date.now()}-${i}`,
|
|||
|
|
avatar: `/placeholder.svg?height=40&width=40&text=${fullName[0]}`,
|
|||
|
|
nickname: fullName,
|
|||
|
|
wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`,
|
|||
|
|
phone: `1${["3", "5", "7", "8", "9"][Math.floor(Math.random() * 5)]}${Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)).join("")}`,
|
|||
|
|
region: [
|
|||
|
|
"广东深圳",
|
|||
|
|
"浙江杭州",
|
|||
|
|
"江苏苏州",
|
|||
|
|
"北京",
|
|||
|
|
"上海",
|
|||
|
|
"四川成都",
|
|||
|
|
"湖北武汉",
|
|||
|
|
"福建厦门",
|
|||
|
|
"山东青岛",
|
|||
|
|
"河南郑州",
|
|||
|
|
][Math.floor(Math.random() * 10)],
|
|||
|
|
note: [
|
|||
|
|
"咨询产品价格",
|
|||
|
|
"对产品很感兴趣",
|
|||
|
|
"准备购买",
|
|||
|
|
"需要更多信息",
|
|||
|
|
"想了解优惠活动",
|
|||
|
|
"询问产品规格",
|
|||
|
|
"要求产品demo",
|
|||
|
|
"索要产品目录",
|
|||
|
|
"询问售后服务",
|
|||
|
|
"要求上门演示",
|
|||
|
|
][Math.floor(Math.random() * 10)],
|
|||
|
|
status: ["pending", "added", "failed"][Math.floor(Math.random() * 3)] as TrafficUser["status"],
|
|||
|
|
addTime: date.toISOString(),
|
|||
|
|
source: ["抖音直播", "小红书", "微信朋友圈", "视频号", "公众号", "个人主页"][Math.floor(Math.random() * 6)],
|
|||
|
|
assignedTo: "",
|
|||
|
|
category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"],
|
|||
|
|
tags: [],
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 计算今日新增数量
|
|||
|
|
const todayStart = new Date()
|
|||
|
|
todayStart.setHours(0, 0, 0, 0)
|
|||
|
|
const todayUsers = userPool.filter((user) => new Date(user.addTime) >= todayStart)
|
|||
|
|
|
|||
|
|
// 生成微信好友数据池
|
|||
|
|
const generateWechatFriends = (wechatId: string, count: number) => {
|
|||
|
|
return Array.from({ length: count }, (_, i) => {
|
|||
|
|
const familyName = familyNames[Math.floor(Math.random() * familyNames.length)]
|
|||
|
|
const givenName1 = givenNames1[Math.floor(Math.random() * givenNames1.length)]
|
|||
|
|
const givenName2 = givenNames2[Math.floor(Math.random() * givenNames2.length)]
|
|||
|
|
const fullName = Math.random() > 0.5 ? familyName + givenName1 + givenName2 : familyName + givenName1
|
|||
|
|
|
|||
|
|
// 生成随机时间(在过去30天内)
|
|||
|
|
const date = new Date()
|
|||
|
|
date.setDate(date.getDate() - Math.floor(Math.random() * 30))
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
id: `wechat-${wechatId}-${i}`,
|
|||
|
|
avatar: `/placeholder.svg?height=40&width=40&text=${fullName[0]}`,
|
|||
|
|
nickname: fullName,
|
|||
|
|
wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`,
|
|||
|
|
phone: `1${["3", "5", "7", "8", "9"][Math.floor(Math.random() * 5)]}${Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)).join("")}`,
|
|||
|
|
region: [
|
|||
|
|
"广东深圳",
|
|||
|
|
"浙江杭州",
|
|||
|
|
"江苏苏州",
|
|||
|
|
"北京",
|
|||
|
|
"上海",
|
|||
|
|
"四川成都",
|
|||
|
|
"湖北武汉",
|
|||
|
|
"福建厦门",
|
|||
|
|
"山东青岛",
|
|||
|
|
"河南郑州",
|
|||
|
|
][Math.floor(Math.random() * 10)],
|
|||
|
|
note: [
|
|||
|
|
"咨询产品价格",
|
|||
|
|
"对产品很感兴趣",
|
|||
|
|
"准备购买",
|
|||
|
|
"需要更多信息",
|
|||
|
|
"想了解优惠活动",
|
|||
|
|
"询问产品规格",
|
|||
|
|
"要求产品demo",
|
|||
|
|
"索要产品目录",
|
|||
|
|
"询问售后服务",
|
|||
|
|
"要求上门演示",
|
|||
|
|
][Math.floor(Math.random() * 10)],
|
|||
|
|
status: ["pending", "added", "failed"][Math.floor(Math.random() * 3)] as TrafficUser["status"],
|
|||
|
|
addTime: date.toISOString(),
|
|||
|
|
source: ["抖音直播", "小红书", "微信朋友圈", "视频号", "公众号", "个人主页", "微信好友"][
|
|||
|
|
Math.floor(Math.random() * 7)
|
|||
|
|
],
|
|||
|
|
assignedTo: "",
|
|||
|
|
category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"],
|
|||
|
|
tags: [],
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 微信好友数据缓存
|
|||
|
|
const wechatFriendsCache = new Map<string, TrafficUser[]>()
|
|||
|
|
|
|||
|
|
export async function GET(request: Request) {
|
|||
|
|
const { searchParams } = new URL(request.url)
|
|||
|
|
const page = Number.parseInt(searchParams.get("page") || "1")
|
|||
|
|
const pageSize = Number.parseInt(searchParams.get("pageSize") || "10")
|
|||
|
|
const search = searchParams.get("search") || ""
|
|||
|
|
const category = searchParams.get("category") || "all"
|
|||
|
|
const source = searchParams.get("source") || "all"
|
|||
|
|
const status = searchParams.get("status") || "all"
|
|||
|
|
const startDate = searchParams.get("startDate")
|
|||
|
|
const endDate = searchParams.get("endDate")
|
|||
|
|
const wechatSource = searchParams.get("wechatSource") || ""
|
|||
|
|
|
|||
|
|
let filteredUsers = [...userPool]
|
|||
|
|
|
|||
|
|
// 如果有微信来源参数,生成或获取微信好友数据
|
|||
|
|
if (wechatSource) {
|
|||
|
|
if (!wechatFriendsCache.has(wechatSource)) {
|
|||
|
|
// 生成150-300个随机好友
|
|||
|
|
const friendCount = Math.floor(Math.random() * (300 - 150)) + 150
|
|||
|
|
wechatFriendsCache.set(wechatSource, generateWechatFriends(wechatSource, friendCount))
|
|||
|
|
}
|
|||
|
|
filteredUsers = wechatFriendsCache.get(wechatSource) || []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用过滤条件
|
|||
|
|
filteredUsers = filteredUsers.filter((user) => {
|
|||
|
|
const matchesSearch = search
|
|||
|
|
? user.nickname.toLowerCase().includes(search.toLowerCase()) ||
|
|||
|
|
user.wechatId.toLowerCase().includes(search.toLowerCase()) ||
|
|||
|
|
user.phone.includes(search)
|
|||
|
|
: true
|
|||
|
|
|
|||
|
|
const matchesCategory = category === "all" ? true : user.category === category
|
|||
|
|
const matchesSource = source === "all" ? true : user.source === source
|
|||
|
|
const matchesStatus = status === "all" ? true : user.status === status
|
|||
|
|
|
|||
|
|
const matchesDate =
|
|||
|
|
startDate && endDate
|
|||
|
|
? new Date(user.addTime) >= new Date(startDate) && new Date(user.addTime) <= new Date(endDate)
|
|||
|
|
: true
|
|||
|
|
|
|||
|
|
return matchesSearch && matchesCategory && matchesSource && matchesStatus && matchesDate
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 按添加时间倒序排序
|
|||
|
|
filteredUsers.sort((a, b) => new Date(b.addTime).getTime() - new Date(a.addTime).getTime())
|
|||
|
|
|
|||
|
|
// 计算分页
|
|||
|
|
const total = filteredUsers.length
|
|||
|
|
const totalPages = Math.ceil(total / pageSize)
|
|||
|
|
const start = (page - 1) * pageSize
|
|||
|
|
const end = start + pageSize
|
|||
|
|
const users = filteredUsers.slice(start, end)
|
|||
|
|
|
|||
|
|
// 计算分类统计
|
|||
|
|
const categoryStats = {
|
|||
|
|
potential: userPool.filter((user) => user.category === "potential").length,
|
|||
|
|
customer: userPool.filter((user) => user.category === "customer").length,
|
|||
|
|
lost: userPool.filter((user) => user.category === "lost").length,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
|||
|
|
|
|||
|
|
return NextResponse.json({
|
|||
|
|
users,
|
|||
|
|
pagination: {
|
|||
|
|
total,
|
|||
|
|
totalPages,
|
|||
|
|
currentPage: page,
|
|||
|
|
pageSize,
|
|||
|
|
},
|
|||
|
|
stats: {
|
|||
|
|
total: wechatSource ? filteredUsers.length : userPool.length,
|
|||
|
|
todayNew: wechatSource ? Math.floor(filteredUsers.length * 0.1) : todayUsers.length,
|
|||
|
|
categoryStats,
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|