import { randomUUID } from 'crypto' export type UserStatus = '活跃' | '沉睡' | '流失风险' export interface User { id: string name: string email: string phone: string avatar: string tags: string[] status: UserStatus rfmScore: number createdAt: string lastActiveAt: string } export interface MockUser { id: string name: string nickname?: string avatar?: string tags: string[] lastActive: string city?: string valueScore?: number } export const MOCK_USERS: MockUser[] = [ { id: "u_1001", name: "张三", nickname: "技术控", avatar: "/user-avatar-zhangsan.png", tags: ["技术爱好者", "夜猫子"], lastActive: "2025-08-01T10:20:00Z", city: "深圳", valueScore: 86, }, { id: "u_1002", name: "李四", nickname: "内容创作者", avatar: "/user-avatar-lisi.png", tags: ["短视频", "自动化工具"], lastActive: "2025-08-06T14:30:00Z", city: "广州", valueScore: 72, }, { id: "u_1003", name: "王雷", avatar: "/avatar-wanglei.png", tags: ["效率提升"], lastActive: "2025-08-07T08:05:00Z", city: "成都", valueScore: 64, }, ] const familyNames = ['张','李','王','赵','刘','陈','杨','黄','周','吴','徐','孙','胡','朱','高','林','何','郭','马','罗'] const givenNames = ['伟','芳','娜','敏','静','秀英','丽','强','磊','军','洋','艳','勇','杰','娟','涛','明','超','霞','平','俊','凯','佳','鑫','鹏','晨','倩','颖','梅','慧','雪','宇','涵','宁','璐','龙','震','航','璟','钰'] const tagPool = ['高价值','近7日活跃','新客','回流','社群达人','潜在复购','高互动','低客单','私域粉','公众号粉'] const statusPool: UserStatus[] = ['活跃','沉睡','流失风险'] const avatars = [ '/user-avatar-zhangsan.png', '/user-avatar-lisi.png', '/avatar-wanglei.png', '/generic-user-avatar.png', '/wechat-avatar-1.png', '/wechat-avatar-2.png', '/wechat-avatar-3.png', ] const rand = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min const pick = (arr: T[]) => arr[rand(0, arr.length - 1)] function toPinyinLike(name: string) { const map: Record = { '张':'zhang','李':'li','王':'wang','赵':'zhao','刘':'liu','陈':'chen','杨':'yang','黄':'huang','周':'zhou','吴':'wu','徐':'xu','孙':'sun','胡':'hu','朱':'zhu','高':'gao','林':'lin','何':'he','郭':'guo','马':'ma','罗':'luo' } return name.split('').map(c => map[c] ?? 'u').join('') } function randomPhone() { const prefixes = ['139','138','137','136','135','188','187','186','185','184','183','182','159','158','157','156','155'] return `${pick(prefixes)}${rand(1000,9999)}${rand(1000,9999)}` } function randomTags() { const count = rand(2,4) const s = new Set() while (s.size < count) s.add(pick(tagPool)) return Array.from(s) } function timeNearNow(daysSpan = 90) { const now = Date.now() const offset = rand(0, daysSpan * 86400000) return new Date(now - offset).toISOString() } let cache: User[] | null = null function seed(n = 120) { const list: User[] = [] for (let i = 0; i < n; i++) { const name = `${pick(familyNames)}${pick(givenNames)}${Math.random() < 0.2 ? pick(givenNames) : ''}` const email = `${toPinyinLike(name)}${rand(1,99)}@example.com` const phone = randomPhone() list.push({ id: randomUUID(), name, email, phone, avatar: avatars[i % avatars.length], tags: randomTags(), status: pick(statusPool), rfmScore: rand(15, 95), createdAt: timeNearNow(180), lastActiveAt: timeNearNow(15), }) } return list } export function getUsersStore() { if (!cache) cache = seed() return cache } export interface QueryParams { q?: string tags?: string[] status?: UserStatus[] rfmMin?: number rfmMax?: number page?: number pageSize?: number } export function queryUsers(params: QueryParams) { const { q, tags, status, rfmMin = 0, rfmMax = 100, page = 1, pageSize = 20 } = params let list = getUsersStore() if (q && q.trim()) { const s = q.trim().toLowerCase() list = list.filter(u => u.name.toLowerCase().includes(s) || u.email.toLowerCase().includes(s) || u.phone.includes(s) || u.tags.some(t => t.toLowerCase().includes(s)), ) } if (tags?.length) { list = list.filter(u => tags.every(t => u.tags.includes(t))) } if (status?.length) { const st = new Set(status) list = list.filter(u => st.has(u.status)) } list = list.filter(u => u.rfmScore >= rfmMin && u.rfmScore <= rfmMax) const total = list.length const start = (page - 1) * pageSize const end = start + pageSize const data = list.slice(start, end) // 列表行仅返回必要字段 const thin = data.map(u => ({ id: u.id, name: u.name, email: u.email, phone: u.phone, rfmScore: u.rfmScore, lastActiveAt: u.lastActiveAt, tags: u.tags, })) return { data: thin, pagination: { page, pageSize, total, totalPages: Math.max(1, Math.ceil(total / pageSize)) } } } export function addUser(input: Partial) { const list = getUsersStore() const now = new Date().toISOString() const name = input.name ?? `${pick(familyNames)}${pick(givenNames)}` const email = input.email ?? `${toPinyinLike(name)}@example.com` const phone = input.phone ?? randomPhone() const u: User = { id: randomUUID(), name, email, phone, avatar: input.avatar ?? avatars[rand(0, avatars.length - 1)], tags: input.tags ?? randomTags(), status: input.status ?? pick(statusPool), rfmScore: input.rfmScore ?? rand(20, 80), createdAt: now, lastActiveAt: now, } list.unshift(u) return u } export function getUserById(id: string) { return getUsersStore().find(u => u.id === id) ?? null }