当前位置: 首页 > news >正文

Next.js项目MindAI教程 - 第三章:数据库集成

1. MySQL安装和配置

1.1 安装MySQL

# Windows用户可以从官网下载MySQL安装包
# https://dev.mysql.com/downloads/installer/

# 验证安装
mysql --version

# 启动MySQL服务
# Windows: 
net start mysql
# Linux/Mac: 
sudo service mysql start

1.2 创建数据库

CREATE DATABASE mindai_db;

2. Prisma设置

2.1 安装Prisma

npm install prisma --save-dev
npm install @prisma/client

# 初始化Prisma
npx prisma init

2.2 配置数据库连接

# .env
DATABASE_URL="mysql://用户名:密码@localhost:3306/mindai_db"

3. 数据模型设计

3.1 创建Prisma模型

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

// 用户模型
model User {
  id            String         @id @default(cuid())
  email         String         @unique
  name          String?
  password      String
  role          Role          @default(USER)
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
  profile       Profile?
  assessments   Assessment[]
  emotions      Emotion[]
  consultations Consultation[]
  posts         Post[]
  comments      Comment[]
}

// 用户角色枚举
enum Role {
  USER
  COUNSELOR
  ADMIN
}

// 用户档案
model Profile {
  id        String   @id @default(cuid())
  userId    String   @unique
  age       Int?
  gender    String?
  avatar    String?
  bio       String?  @db.Text
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id])
}

// 心理测评
model Assessment {
  id        String   @id @default(cuid())
  userId    String
  type      String
  answers   Json
  score     Int
  result    Json
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id])
}

// 情绪记录
model Emotion {
  id        String   @id @default(cuid())
  userId    String
  content   String   @db.Text
  type      String
  intensity Float
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id])
}

// 咨询会话
model Consultation {
  id          String   @id @default(cuid())
  userId      String
  counselorId String?
  status      String   @default("PENDING")
  topic       String
  description String   @db.Text
  startTime   DateTime?
  endTime     DateTime?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  user        User     @relation(fields: [userId], references: [id])
  messages    Message[]
}

// 咨询消息
model Message {
  id             String       @id @default(cuid())
  consultationId String
  senderId       String
  content        String       @db.Text
  createdAt      DateTime     @default(now())
  consultation   Consultation @relation(fields: [consultationId], references: [id])
}

// 社区帖子
model Post {
  id        String    @id @default(cuid())
  userId    String
  title     String
  content   String    @db.Text
  tags      String?
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  user      User      @relation(fields: [userId], references: [id])
  comments  Comment[]
}

// 评论
model Comment {
  id        String   @id @default(cuid())
  postId    String
  userId    String
  content   String   @db.Text
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  post      Post     @relation(fields: [postId], references: [id])
  user      User     @relation(fields: [userId], references: [id])
}

4. 数据库操作封装

4.1 创建Prisma客户端实例

// src/lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma = globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

4.2 创建数据库服务类

// src/services/database/user.service.ts
import { prisma } from '@/lib/prisma'
import { hash } from 'bcryptjs'
import type { User } from '@prisma/client'

export class UserService {
  static async create(data: {
    email: string
    password: string
    name?: string
  }) {
    const hashedPassword = await hash(data.password, 12)
    
    return prisma.user.create({
      data: {
        ...data,
        password: hashedPassword,
      },
    })
  }

  static async findByEmail(email: string) {
    return prisma.user.findUnique({
      where: { email },
      include: { profile: true },
    })
  }

  static async updateProfile(userId: string, data: {
    age?: number
    gender?: string
    avatar?: string
    bio?: string
  }) {
    return prisma.profile.upsert({
      where: { userId },
      update: data,
      create: {
        userId,
        ...data,
      },
    })
  }
}

// src/services/database/assessment.service.ts
export class AssessmentService {
  static async create(data: {
    userId: string
    type: string
    answers: any
    score: number
    result: any
  }) {
    return prisma.assessment.create({
      data,
    })
  }

  static async findByUser(userId: string) {
    return prisma.assessment.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
    })
  }
}

// 其他服务类似...

5. 数据迁移与验证

5.1 创建数据库迁移

# 生成迁移文件
npx prisma migrate dev --name init

# 应用迁移
npx prisma migrate deploy

5.2 验证数据模型

// src/scripts/test-db.ts
import { prisma } from '@/lib/prisma'

async function main() {
  // 创建测试用户
  const user = await prisma.user.create({
    data: {
      email: 'test@example.com',
      password: 'hashed_password',
      name: '测试用户',
      profile: {
        create: {
          age: 25,
          gender: '男',
        },
      },
    },
  })

  console.log('Created user:', user)

  // 创建测试评估
  const assessment = await prisma.assessment.create({
    data: {
      userId: user.id,
      type: 'personality',
      answers: {},
      score: 85,
      result: {},
    },
  })

  console.log('Created assessment:', assessment)
}

main()
  .catch(console.error)
  .finally(() => prisma.$disconnect())

6. API路由实现

6.1 用户API

// src/app/api/users/route.ts
import { NextResponse } from 'next/server'
import { UserService } from '@/services/database/user.service'

export async function POST(req: Request) {
  try {
    const body = await req.json()
    const user = await UserService.create(body)
    return NextResponse.json(user)
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to create user' },
      { status: 500 }
    )
  }
}

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url)
  const email = searchParams.get('email')

  if (!email) {
    return NextResponse.json(
      { error: 'Email is required' },
      { status: 400 }
    )
  }

  try {
    const user = await UserService.findByEmail(email)
    return NextResponse.json(user)
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch user' },
      { status: 500 }
    )
  }
}

6.2 评估API

// src/app/api/assessments/route.ts
import { NextResponse } from 'next/server'
import { AssessmentService } from '@/services/database/assessment.service'

export async function POST(req: Request) {
  try {
    const body = await req.json()
    const assessment = await AssessmentService.create(body)
    return NextResponse.json(assessment)
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to create assessment' },
      { status: 500 }
    )
  }
}

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url)
  const userId = searchParams.get('userId')

  if (!userId) {
    return NextResponse.json(
      { error: 'User ID is required' },
      { status: 400 }
    )
  }

  try {
    const assessments = await AssessmentService.findByUser(userId)
    return NextResponse.json(assessments)
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch assessments' },
      { status: 500 }
    )
  }
}

7. 下一步计划

  • 实现用户认证系统
  • 集成OpenAI API
  • 开发测评功能
  • 实现情绪检测

相关文章:

  • CAN通讯不同类型帧C语言示例
  • 使用tiptap快速搭建markdown-富文本编辑器
  • Linux:利用System V系列的-共享内存,消息队列实现进程间通信
  • Java数据结构第二十三期:Map与Set的高效应用之道(二)
  • Python基础入门掌握(二)
  • 蓝桥杯:山
  • R语言高效数据处理-自定义EXCEL数据排版
  • 顺序表和链表的对比(一)
  • 基于Uniapp开发tab选项卡/标签栏前端组件
  • 医院手术麻醉信息系统是如何为医院提质增效的?
  • 【Kubernets】Deployment 和 StatefulSet 有什么区别?什么时候用 StatefulSet?
  • WIN10隐藏文件夹怎么显示?
  • pip install和conda install的区别
  • Vue秘籍:如何动态修改页面 Title(浏览器页签名称)?
  • Shell 脚本实现内存和磁盘监控
  • C语言踩坑题:int8_t类型数据的移位或运算
  • (五)Dart 数据类型
  • 嵌入式人工智能应用- 第十章街景分类
  • 现在有分段、句子数量可能不一致的中英文文本,如何用python实现中英文对照翻译(即每行英文对应相应的中文)
  • C# 建造者模式(Builder Pattern)详细讲解
  • 陕西省副省长窦敬丽已任宁夏回族自治区党委常委、统战部部长
  • 马上评丨别让“免费领养”套路坑消费者又坑宠物
  • 今年一季度全国城镇新增就业308万人,就业形势保持总体稳定
  • 电话费被私改成48元套餐长达数年,投诉后移动公司退补600元话费
  • 4月人文社科联合书单|天文学家的椅子
  • 赛力斯拟赴港上市:去年扭亏为盈净利59亿元,三年内实现百万销量目标