【Trae+AI】和Trae学习搭建App_01(附加可略过):测试Trae的后端功能

📚前言

这一章是临时测试,测试完成后,会恢复成测试前的状态,大家可以自己选择是否进行测试。

在前一章代码的基础上进行:

和Trae学习搭建App_01:项目架构设计与环境搭建

目标:

测试Trae如何实际进行后端开发。

Trae后端开发实战演示

1. 创建Express服务器主文件(backend\src\app.js)

复制代码
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3000;

// 中间件配置
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 数据库连接
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
})
.then(() => console.log('MongoDB连接成功'))
.catch(err => console.error('MongoDB连接失败:', err));

// 路由导入
const authRoutes = require('./routes/auth');
const taskRoutes = require('./routes/tasks');

// 使用路由
app.use('/api/auth', authRoutes);
app.use('/api/tasks', taskRoutes);

// 健康检查端点
app.get('/health', (req, res) => {
  res.status(200).json({ 
    status: 'OK', 
    timestamp: new Date().toISOString(),
    service: 'Todo App Backend'
  });
});

// 404处理
app.use('*', (req, res) => {
  res.status(404).json({ error: '接口不存在' });
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: '服务器内部错误' });
});

app.listen(PORT, () => {
  console.log(`服务器运行在端口 ${PORT}`);
  console.log(`环境: ${process.env.NODE_ENV}`);
});

module.exports = app;

2. 创建用户认证路由(backend\src\routes\auth.js)

复制代码
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

const router = express.Router();

// 用户注册
router.post('/register', async (req, res) => {
  try {
    const { username, email, password } = req.body;

    // 验证输入
    if (!username || !email || !password) {
      return res.status(400).json({ error: '请填写所有必填字段' });
    }

    // 检查用户是否已存在
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      return res.status(400).json({ error: '该邮箱已被注册' });
    }

    // 加密密码
    const saltRounds = 10;
    const hashedPassword = await bcrypt.hash(password, saltRounds);

    // 创建新用户
    const user = new User({
      username,
      email,
      password: hashedPassword,
    });

    await user.save();

    // 生成JWT token
    const token = jwt.sign(
      { userId: user._id }, 
      process.env.JWT_SECRET, 
      { expiresIn: '24h' }
    );

    res.status(201).json({
      message: '用户注册成功',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });

  } catch (error) {
    console.error('注册错误:', error);
    res.status(500).json({ error: '注册失败,请稍后重试' });
  }
});

// 用户登录
router.post('/login', async (req, res) => {
  try {
    const { email, password } = req.body;

    if (!email || !password) {
      return res.status(400).json({ error: '请提供邮箱和密码' });
    }

    // 查找用户
    const user = await User.findOne({ email });
    if (!user) {
      return res.status(401).json({ error: '邮箱或密码错误' });
    }

    // 验证密码
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      return res.status(401).json({ error: '邮箱或密码错误' });
    }

    // 生成JWT token
    const token = jwt.sign(
      { userId: user._id },
      process.env.JWT_SECRET,
      { expiresIn: '24h' }
    );

    res.json({
      message: '登录成功',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });

  } catch (error) {
    console.error('登录错误:', error);
    res.status(500).json({ error: '登录失败,请稍后重试' });
  }
});

module.exports = router;

3. 创建用户数据模型(backend\src\models\User.js)

复制代码
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: [true, '用户名是必填项'],
    trim: true,
    minlength: [2, '用户名至少2个字符'],
    maxlength: [30, '用户名不能超过30个字符']
  },
  email: {
    type: String,
    required: [true, '邮箱是必填项'],
    unique: true,
    lowercase: true,
    match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, '请输入有效的邮箱地址']
  },
  password: {
    type: String,
    required: [true, '密码是必填项'],
    minlength: [6, '密码至少6个字符']
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  }
});

// 更新时自动设置updatedAt
userSchema.pre('save', function(next) {
  this.updatedAt = Date.now();
  next();
});

// 移除密码字段的JSON输出
userSchema.methods.toJSON = function() {
  const user = this.toObject();
  delete user.password;
  return user;
};

module.exports = mongoose.model('User', userSchema);

4. 创建任务路由文件(backend\src\routes\tasks.js)

复制代码
const express = require('express');
const Task = require('../models/Task');
const auth = require('../middleware/auth');

const router = express.Router();

// 获取所有任务(需要认证)
router.get('/', auth, async (req, res) => {
  try {
    const tasks = await Task.find({ user: req.userId })
      .sort({ createdAt: -1 });
    
    res.json(tasks);
  } catch (error) {
    console.error('获取任务列表错误:', error);
    res.status(500).json({ error: '获取任务失败' });
  }
});

// 创建新任务(需要认证)
router.post('/', auth, async (req, res) => {
  try {
    const { title, description, category, dueDate, priority } = req.body;

    if (!title) {
      return res.status(400).json({ error: '任务标题是必填项' });
    }

    const task = new Task({
      title,
      description: description || '',
      category: category || '默认',
      dueDate: dueDate || null,
      priority: priority || '中等',
      user: req.userId,
      completed: false
    });

    await task.save();
    res.status(201).json(task);
  } catch (error) {
    console.error('创建任务错误:', error);
    res.status(500).json({ error: '创建任务失败' });
  }
});

// 更新任务(需要认证)
router.put('/:id', auth, async (req, res) => {
  try {
    const { title, description, category, dueDate, priority, completed } = req.body;
    
    const task = await Task.findOne({ _id: req.params.id, user: req.userId });
    
    if (!task) {
      return res.status(404).json({ error: '任务不存在' });
    }

    // 更新字段
    if (title !== undefined) task.title = title;
    if (description !== undefined) task.description = description;
    if (category !== undefined) task.category = category;
    if (dueDate !== undefined) task.dueDate = dueDate;
    if (priority !== undefined) task.priority = priority;
    if (completed !== undefined) task.completed = completed;
    
    task.updatedAt = new Date();
    await task.save();

    res.json(task);
  } catch (error) {
    console.error('更新任务错误:', error);
    res.status(500).json({ error: '更新任务失败' });
  }
});

// 删除任务(需要认证)
router.delete('/:id', auth, async (req, res) => {
  try {
    const task = await Task.findOneAndDelete({ 
      _id: req.params.id, 
      user: req.userId 
    });
    
    if (!task) {
      return res.status(404).json({ error: '任务不存在' });
    }

    res.json({ message: '任务删除成功' });
  } catch (error) {
    console.error('删除任务错误:', error);
    res.status(500).json({ error: '删除任务失败' });
  }
});

// 标记任务完成/未完成(需要认证)
router.patch('/:id/toggle', auth, async (req, res) => {
  try {
    const task = await Task.findOne({ _id: req.params.id, user: req.userId });
    
    if (!task) {
      return res.status(404).json({ error: '任务不存在' });
    }

    task.completed = !task.completed;
    task.updatedAt = new Date();
    await task.save();

    res.json(task);
  } catch (error) {
    console.error('切换任务状态错误:', error);
    res.status(500).json({ error: '切换任务状态失败' });
  }
});

module.exports = router;

5. 创建任务数据模型(backend\src\models\Task.js)

复制代码
const mongoose = require('mongoose');

const taskSchema = new mongoose.Schema({
  title: {
    type: String,
    required: [true, '任务标题是必填项'],
    trim: true,
    minlength: [1, '任务标题不能为空'],
    maxlength: [200, '任务标题不能超过200个字符']
  },
  description: {
    type: String,
    maxlength: [1000, '任务描述不能超过1000个字符'],
    default: ''
  },
  category: {
    type: String,
    default: '默认',
    trim: true
  },
  priority: {
    type: String,
    enum: ['低', '中等', '高'],
    default: '中等'
  },
  completed: {
    type: Boolean,
    default: false
  },
  dueDate: {
    type: Date,
    validate: {
      validator: function(value) {
        return !value || value > new Date();
      },
      message: '截止日期必须是将来时间'
    }
  },
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  }
});

// 更新时自动设置updatedAt
taskSchema.pre('save', function(next) {
  this.updatedAt = new Date();
  next();
});

// 创建索引以提高查询性能
taskSchema.index({ user: 1, createdAt: -1 });
taskSchema.index({ user: 1, completed: 1 });
taskSchema.index({ user: 1, category: 1 });

module.exports = mongoose.model('Task', taskSchema);

6. 创建认证中间件(backend\src\middleware\auth.js)

复制代码
const jwt = require('jsonwebtoken');
const User = require('../models/User');

const auth = async (req, res, next) => {
  try {
    // 从请求头获取token
    const token = req.header('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return res.status(401).json({ error: '访问被拒绝,请提供有效的token' });
    }

    // 验证token
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    
    // 查找用户
    const user = await User.findById(decoded.userId);
    if (!user) {
      return res.status(401).json({ error: 'token无效,用户不存在' });
    }

    // 将用户信息添加到请求对象中
    req.userId = user._id;
    req.user = user;
    
    next();
  } catch (error) {
    console.error('认证中间件错误:', error);
    
    if (error.name === 'JsonWebTokenError') {
      return res.status(401).json({ error: '无效的token' });
    }
    
    if (error.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'token已过期' });
    }
    
    res.status(500).json({ error: '认证失败' });
  }
};

module.exports = auth;

创建文件总结

✅ 已创建的文件:

  1. `tasks.js` - 任务管理API路由

  2. `Task.js` - 任务数据模型

  3. `auth.js` - 认证中间件
    ✅ 已存在的文件:

  • `auth.js` - 用户认证路由

  • `User.js` - 用户数据模型

  • `app.js` - 主应用文件

启动服务器

在终端运行命令:

复制代码
# 进入后端目录
cd e:\99mydata\traeplace\app\backend

# 安装依赖
npm install

# 启动开发服务器
npm run dev

执行界面参考:

✍️说明:没有连上数据库是正常的,因为还没有配置

web测试:

在浏览器上,输入检查地址:localhost:3000/health

测试恢复

恢复后,测试创建的文件被删除了,文件夹还保留了下来,如下图:

相关推荐
Haha_bj4 天前
Flutter——状态管理 Provider 详解
flutter·app
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
QING6186 天前
使用ADB分析CPU性能 —— 基础指南
android·前端·app
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习