【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

测试恢复

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

相关推荐
桃子不吃李子5 小时前
axios的二次封装
前端·学习·axios
明明真系叻5 小时前
最优传输理论学习(1)+PINN文献阅读
深度学习·学习
笨鸟笃行6 小时前
百日挑战之单词篇(第三天)
学习
im_AMBER7 小时前
Leetcode 31
学习·算法·leetcode
Miqiuha7 小时前
观察者模式学习
学习·观察者模式
百锦再7 小时前
破茧成蝶:全方位解析Java学习难点与征服之路
java·python·学习·struts·kafka·maven·intellij-idea
雨奔7 小时前
Django 学习路线图
学习·django·sqlite
冷崖8 小时前
MySQL-TrinityCore异步连接池的学习(七)
学习·mysql
pen-ai8 小时前
标签噪声学习:理论与方法详解
学习