后端-Day 02 - 数据库设计与模型

2 阅读2分钟

Day 02 - 数据库设计与模型

日期: 第2天
目标: 设计 MongoDB Schema 和 Mongoose 模型

📋 任务清单

  • MongoDB 连接配置
  • 设计用户模型 (User)
  • 设计代币模型 (Coin)
  • 设计交易模型 (Trade)
  • 设计消息/评论模型

💻 核心代码

1. 数据库连接 (src/db/dbConnection.ts)

import mongoose from 'mongoose';

export const init = async () => {
  const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/geng';
  
  try {
    await mongoose.connect(mongoUri);
    console.log('✅ MongoDB connected successfully');
  } catch (error) {
    console.error('❌ MongoDB connection error:', error);
    process.exit(1);
  }
};

mongoose.connection.on('disconnected', () => {
  console.log('MongoDB disconnected');
});

mongoose.connection.on('error', (err) => {
  console.error('MongoDB error:', err);
});

2. 用户模型 (src/models/User.ts)

import mongoose, { Schema, Document } from 'mongoose';

export interface IUser extends Document {
  wallet: string;
  name: string;
  avatar?: string;
  bio?: string;
  isLedger: boolean;
  following: mongoose.Types.ObjectId[];
  followers: mongoose.Types.ObjectId[];
  createdAt: Date;
  updatedAt: Date;
}

const UserSchema = new Schema<IUser>(
  {
    wallet: {
      type: String,
      required: true,
      unique: true,
      index: true,
    },
    name: {
      type: String,
      required: true,
      trim: true,
    },
    avatar: {
      type: String,
      default: null,
    },
    bio: {
      type: String,
      maxlength: 200,
    },
    isLedger: {
      type: Boolean,
      default: false,
    },
    following: [
      {
        type: Schema.Types.ObjectId,
        ref: 'User',
      },
    ],
    followers: [
      {
        type: Schema.Types.ObjectId,
        ref: 'User',
      },
    ],
  },
  {
    timestamps: true,
  }
);

// 索引
UserSchema.index({ wallet: 1 });
UserSchema.index({ createdAt: -1 });

export default mongoose.model<IUser>('User', UserSchema);

3. 代币模型 (src/models/Coin.ts)

import mongoose, { Schema, Document } from 'mongoose';

export interface ICoin extends Document {
  token: string;
  name: string;
  symbol: string;
  description?: string;
  image?: string;
  twitter?: string;
  telegram?: string;
  website?: string;
  creator: mongoose.Types.ObjectId;
  marketCap: number;
  supply: number;
  virtualSolReserves: number;
  virtualTokenReserves: number;
  isGraduated: boolean;
  createdAt: Date;
  updatedAt: Date;
}

const CoinSchema = new Schema<ICoin>(
  {
    token: {
      type: String,
      required: true,
      unique: true,
      index: true,
    },
    name: {
      type: String,
      required: true,
      trim: true,
    },
    symbol: {
      type: String,
      required: true,
      uppercase: true,
      trim: true,
    },
    description: {
      type: String,
      maxlength: 500,
    },
    image: String,
    twitter: String,
    telegram: String,
    website: String,
    creator: {
      type: Schema.Types.ObjectId,
      ref: 'User',
      required: true,
    },
    marketCap: {
      type: Number,
      default: 0,
      min: 0,
    },
    supply: {
      type: Number,
      required: true,
      default: 0,
    },
    virtualSolReserves: {
      type: Number,
      default: 0,
    },
    virtualTokenReserves: {
      type: Number,
      default: 0,
    },
    isGraduated: {
      type: Boolean,
      default: false,
      index: true,
    },
  },
  {
    timestamps: true,
  }
);

// 索引优化
CoinSchema.index({ token: 1 });
CoinSchema.index({ creator: 1, createdAt: -1 });
CoinSchema.index({ marketCap: -1 });
CoinSchema.index({ isGraduated: 1, createdAt: -1 });

export default mongoose.model<ICoin>('Coin', CoinSchema);

4. 交易模型 (src/models/Trade.ts)

import mongoose, { Schema, Document } from 'mongoose';

export interface ITrade extends Document {
  user: mongoose.Types.ObjectId;
  coin: mongoose.Types.ObjectId;
  type: 'buy' | 'sell';
  amount: number;
  price: number;
  solAmount: number;
  signature: string;
  createdAt: Date;
}

const TradeSchema = new Schema<ITrade>(
  {
    user: {
      type: Schema.Types.ObjectId,
      ref: 'User',
      required: true,
      index: true,
    },
    coin: {
      type: Schema.Types.ObjectId,
      ref: 'Coin',
      required: true,
      index: true,
    },
    type: {
      type: String,
      enum: ['buy', 'sell'],
      required: true,
    },
    amount: {
      type: Number,
      required: true,
      min: 0,
    },
    price: {
      type: Number,
      required: true,
      min: 0,
    },
    solAmount: {
      type: Number,
      required: true,
      min: 0,
    },
    signature: {
      type: String,
      required: true,
      unique: true,
      index: true,
    },
  },
  {
    timestamps: true,
  }
);

// 复合索引
TradeSchema.index({ coin: 1, createdAt: -1 });
TradeSchema.index({ user: 1, coin: 1 });

export default mongoose.model<ITrade>('Trade', TradeSchema);

⚠️ 常见问题

问题1:MongoDB 连接超时

现象: MongooseServerSelectionError: connect ECONNREFUSED

解决方案: 检查 MongoDB 服务和连接字符串

# 启动 MongoDB
mongod --dbpath /usr/local/var/mongodb

# 或使用 Docker
docker run -d -p 27017:27017 --name mongodb mongo:latest

# 检查连接字符串
echo $MONGODB_URI

问题2:索引创建失败

现象: 数据库中索引未生效

解决方案: 手动创建索引或使用 syncIndexes

// 在应用启动时同步索引
import User from './models/User';
import Coin from './models/Coin';

async function syncIndexes() {
  await User.syncIndexes();
  await Coin.syncIndexes();
  console.log('✅ Indexes synced');
}

syncIndexes();

📝 总结

✅ 完成数据库设计和模型创建


时间估计: 3-4 小时 | 难度: ⭐⭐ 中等 | 关键词: MongoDB、Mongoose、Schema设计