const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); const userSchema = new mongoose.Schema({ userId: { type: String, required: true, unique: true, index: true }, email: { type: String, required: true, unique: true, lowercase: true, trim: true, match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, '请输入有效的邮箱地址'] }, username: { type: String, required: true, trim: true, minlength: 2, maxlength: 50 }, password: { type: String, required: true, minlength: 6 }, settings: { defaultOutputFormat: { type: String, enum: ['docx', 'html', 'txt', 'png', 'jpg'], default: 'docx' }, imageQuality: { type: String, enum: ['low', 'medium', 'high'], default: 'medium' }, autoDownload: { type: Boolean, default: true }, language: { type: String, enum: ['zh-CN', 'en-US'], default: 'zh-CN' }, autoDelete: { type: Boolean, default: true }, deleteDelay: { type: Number, default: 24, // 小时 min: 1, max: 168 // 7天 }, maxConcurrentTasks: { type: Number, default: 3, min: 1, max: 10 }, conversionTimeout: { type: Number, default: 10, // 分钟 min: 5, max: 60 } }, profile: { avatar: String, bio: String, company: String, website: String }, statistics: { totalConversions: { type: Number, default: 0 }, successfulConversions: { type: Number, default: 0 }, failedConversions: { type: Number, default: 0 }, totalFileSize: { type: Number, default: 0 // 字节 } }, lastLoginAt: { type: Date, default: null }, lastActiveAt: { type: Date, default: Date.now }, isActive: { type: Boolean, default: true }, isEmailVerified: { type: Boolean, default: false }, emailVerificationToken: String, passwordResetToken: String, passwordResetExpires: Date }, { timestamps: true, toJSON: { transform: function(doc, ret) { delete ret.password; delete ret.emailVerificationToken; delete ret.passwordResetToken; delete ret.passwordResetExpires; delete ret.__v; return ret; } } }); // 索引 userSchema.index({ email: 1 }); userSchema.index({ username: 1 }); userSchema.index({ createdAt: -1 }); userSchema.index({ lastActiveAt: -1 }); // 密码加密中间件 userSchema.pre('save', async function(next) { if (!this.isModified('password')) return next(); try { const salt = await bcrypt.genSalt(12); this.password = await bcrypt.hash(this.password, salt); next(); } catch (error) { next(error); } }); // 实例方法:验证密码 userSchema.methods.comparePassword = async function(candidatePassword) { try { return await bcrypt.compare(candidatePassword, this.password); } catch (error) { throw error; } }; // 实例方法:更新最后活动时间 userSchema.methods.updateLastActive = function() { this.lastActiveAt = new Date(); return this.save(); }; // 实例方法:更新统计信息 userSchema.methods.updateStatistics = function(update) { Object.assign(this.statistics, update); return this.save(); }; // 静态方法:根据邮箱查找用户 userSchema.statics.findByEmail = function(email) { return this.findOne({ email: email.toLowerCase() }); }; // 静态方法:获取活跃用户 userSchema.statics.getActiveUsers = function(days = 30) { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - days); return this.find({ lastActiveAt: { $gte: cutoffDate }, isActive: true }); }; // 静态方法:获取用户统计 userSchema.statics.getUserStats = async function() { const pipeline = [ { $group: { _id: null, totalUsers: { $sum: 1 }, activeUsers: { $sum: { $cond: [{ $eq: ['$isActive', true] }, 1, 0] } }, verifiedUsers: { $sum: { $cond: [{ $eq: ['$isEmailVerified', true] }, 1, 0] } }, totalConversions: { $sum: '$statistics.totalConversions' }, totalFileSize: { $sum: '$statistics.totalFileSize' } } } ]; const result = await this.aggregate(pipeline); return result[0] || { totalUsers: 0, activeUsers: 0, verifiedUsers: 0, totalConversions: 0, totalFileSize: 0 }; }; // 虚拟字段:转换成功率 userSchema.virtual('conversionSuccessRate').get(function() { if (this.statistics.totalConversions === 0) return 0; return (this.statistics.successfulConversions / this.statistics.totalConversions * 100).toFixed(1); }); // 虚拟字段:格式化文件大小 userSchema.virtual('formattedFileSize').get(function() { const sizes = ['Bytes', 'KB', 'MB', 'GB']; if (this.statistics.totalFileSize === 0) return '0 Bytes'; const i = Math.floor(Math.log(this.statistics.totalFileSize) / Math.log(1024)); return (this.statistics.totalFileSize / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]; }); // 中间件:删除前清理关联数据 userSchema.pre('remove', async function(next) { try { // 这里可以添加删除用户相关文件和转换记录的逻辑 // await FileModel.deleteMany({ userId: this.userId }); // await ConversionTaskModel.deleteMany({ userId: this.userId }); next(); } catch (error) { next(error); } }); const User = mongoose.model('User', userSchema); module.exports = User;