实现了AI助手
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export interface ComponentQueryParams {
|
||||
type?: string
|
||||
typeId?: string // 直接使用typeId
|
||||
brand?: string
|
||||
minPrice?: number
|
||||
maxPrice?: number
|
||||
search?: string
|
||||
page?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface ComponentQueryResult {
|
||||
components: Array<{
|
||||
id: string
|
||||
name: string
|
||||
brand: string
|
||||
model: string
|
||||
price: number
|
||||
description: string | null
|
||||
stock: number
|
||||
type: string
|
||||
specifications: string | null
|
||||
imageUrl: string | null
|
||||
}>
|
||||
pagination: {
|
||||
page: number
|
||||
limit: number
|
||||
total: number
|
||||
totalPages: number
|
||||
hasNext: boolean
|
||||
hasPrev: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export class ComponentService {
|
||||
/**
|
||||
* 查询组件列表
|
||||
*/
|
||||
static async queryComponents(params: ComponentQueryParams): Promise<ComponentQueryResult> {
|
||||
const {
|
||||
type,
|
||||
typeId,
|
||||
brand,
|
||||
minPrice,
|
||||
maxPrice,
|
||||
search,
|
||||
page = 1,
|
||||
limit = 12
|
||||
} = params
|
||||
|
||||
const where: any = {}
|
||||
|
||||
// 优先使用typeId,如果没有但有type,则查找typeId
|
||||
if (typeId) {
|
||||
where.componentTypeId = typeId
|
||||
} else if (type) {
|
||||
const componentType = await prisma.componentType.findFirst({
|
||||
where: {
|
||||
name: {
|
||||
contains: type,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
})
|
||||
if (componentType) {
|
||||
where.componentTypeId = componentType.id
|
||||
}
|
||||
}
|
||||
|
||||
if (brand) {
|
||||
where.brand = {
|
||||
contains: brand,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
|
||||
if (minPrice || maxPrice) {
|
||||
where.price = {}
|
||||
if (minPrice) where.price.gte = minPrice
|
||||
if (maxPrice) where.price.lte = maxPrice
|
||||
}
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ brand: { contains: search, mode: 'insensitive' } },
|
||||
{ model: { contains: search, mode: 'insensitive' } },
|
||||
{ description: { contains: search, mode: 'insensitive' } }
|
||||
]
|
||||
}
|
||||
|
||||
const [components, total] = await Promise.all([
|
||||
prisma.component.findMany({
|
||||
where,
|
||||
include: {
|
||||
componentType: true
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
skip: (page - 1) * limit,
|
||||
take: limit
|
||||
}),
|
||||
prisma.component.count({ where })
|
||||
])
|
||||
|
||||
const totalPages = Math.ceil(total / limit)
|
||||
|
||||
return {
|
||||
components: components.map(comp => ({
|
||||
id: comp.id,
|
||||
name: comp.name,
|
||||
brand: comp.brand,
|
||||
model: comp.model,
|
||||
price: comp.price,
|
||||
description: comp.description,
|
||||
stock: comp.stock,
|
||||
type: comp.componentType.name,
|
||||
specifications: comp.specifications,
|
||||
imageUrl: comp.imageUrl
|
||||
})),
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取单个组件
|
||||
*/
|
||||
static async getComponentById(id: string) {
|
||||
return await prisma.component.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
}
|
||||
static async getComponentsByIds(componentIds: string[]) {
|
||||
return await Promise.all(componentIds.map(id => ComponentService.getComponentById(id)))
|
||||
}
|
||||
/**
|
||||
* 获取所有组件类型
|
||||
*/
|
||||
static async getComponentTypes() {
|
||||
return await prisma.componentType.findMany({
|
||||
orderBy: {
|
||||
name: 'asc'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import type { MessageParam } from '@anthropic-ai/sdk/resources'
|
||||
|
||||
export interface ConversationData {
|
||||
id: string
|
||||
title: string | null
|
||||
messages: MessageParam[]
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export class ConversationService {
|
||||
/**
|
||||
* 创建新对话
|
||||
*/
|
||||
static async createConversation(userId: string, initialMessage: string): Promise<string> {
|
||||
// 生成对话标题(取用户消息的前30个字符)
|
||||
const title = initialMessage.length > 30
|
||||
? initialMessage.substring(0, 30) + '...'
|
||||
: initialMessage
|
||||
|
||||
const systemMessage: MessageParam = {
|
||||
role: "user",
|
||||
content: `你是PC DIY商店的专业AI助手。你的主要职责是:
|
||||
|
||||
1. 帮助用户查找和推荐PC配件
|
||||
2. 提供配件的价格、性能和兼容性信息
|
||||
3. 根据用户预算和需求推荐最适合的配件
|
||||
4. 解答关于PC装机的技术问题`
|
||||
}
|
||||
|
||||
const assistantGreeting: MessageParam = {
|
||||
role: "assistant",
|
||||
content: "您好!我是PC DIY商店的AI助手,很高兴为您服务。请告诉我您需要什么帮助?"
|
||||
}
|
||||
|
||||
const userMessage: MessageParam = {
|
||||
role: "user",
|
||||
content: initialMessage
|
||||
}
|
||||
|
||||
const initialMessages = [systemMessage, assistantGreeting, userMessage]
|
||||
|
||||
const conversation = await prisma.conversation.create({
|
||||
data: {
|
||||
userId,
|
||||
title,
|
||||
messages: JSON.stringify(initialMessages)
|
||||
}
|
||||
})
|
||||
|
||||
return conversation.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话
|
||||
*/
|
||||
static async getConversation(conversationId: string, userId: string): Promise<ConversationData | null> {
|
||||
const conversation = await prisma.conversation.findFirst({
|
||||
where: {
|
||||
id: conversationId,
|
||||
userId
|
||||
}
|
||||
})
|
||||
|
||||
if (!conversation) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: conversation.id,
|
||||
title: conversation.title,
|
||||
messages: JSON.parse(conversation.messages) as MessageParam[],
|
||||
createdAt: conversation.createdAt,
|
||||
updatedAt: conversation.updatedAt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话消息
|
||||
*/
|
||||
static async updateConversationMessages(
|
||||
conversationId: string,
|
||||
userId: string,
|
||||
messages: MessageParam[]
|
||||
): Promise<void> {
|
||||
await prisma.conversation.updateMany({
|
||||
where: {
|
||||
id: conversationId,
|
||||
userId
|
||||
},
|
||||
data: {
|
||||
messages: JSON.stringify(messages),
|
||||
updatedAt: new Date()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的对话列表
|
||||
*/
|
||||
static async getUserConversations(userId: string, page = 1, limit = 20) {
|
||||
const skip = (page - 1) * limit
|
||||
|
||||
const [conversations, total] = await Promise.all([
|
||||
prisma.conversation.findMany({
|
||||
where: { userId },
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
createdAt: true,
|
||||
updatedAt: true
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
skip,
|
||||
take: limit
|
||||
}),
|
||||
prisma.conversation.count({
|
||||
where: { userId }
|
||||
})
|
||||
])
|
||||
|
||||
return {
|
||||
conversations,
|
||||
total,
|
||||
page,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对话
|
||||
*/
|
||||
static async deleteConversation(conversationId: string, userId: string): Promise<boolean> {
|
||||
const result = await prisma.conversation.deleteMany({
|
||||
where: {
|
||||
id: conversationId,
|
||||
userId
|
||||
}
|
||||
})
|
||||
|
||||
return result.count > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话标题
|
||||
*/
|
||||
static async updateConversationTitle(
|
||||
conversationId: string,
|
||||
userId: string,
|
||||
title: string
|
||||
): Promise<boolean> {
|
||||
const result = await prisma.conversation.updateMany({
|
||||
where: {
|
||||
id: conversationId,
|
||||
userId
|
||||
},
|
||||
data: { title }
|
||||
})
|
||||
|
||||
return result.count > 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user