第五节:路由使用及用户模型设计(仿若依)

7 阅读4分钟

路由配置与用户模型设计指南

一、路由统一配置与使用

1. API根路径配置

为了便于API版本管理和统一前缀配置,在配置文件中添加API根路径:

// config/index.js
module.exports = {
  DBHOST: '127.0.0.1',                // 数据库主机
  DBPORT: 27017,                      // 数据库端口
  DBNAME: 'node-ruoyi',               // 数据库名称
  API_ROOT: '/trserver',              // 接口统一根路径
  COUNTERS_COLLECTION: 'auto_increment_counters', // 自增ID计数集合
  // ... 其他配置
}

2. Express应用路由配置

在应用入口文件中使用统一的路由配置:

// app.js
const createError = require('http-errors')
const express = require('express')
const path = require('path')
const cookieParser = require('cookie-parser')
const logger = require('morgan')
const { API_ROOT } = require('./config')  // 引入配置
const routes = require('./routes')        // 统一路由入口

const app = express()

// 视图引擎配置
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')

// 中间件配置
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))

// 使用统一API前缀注册路由
app.use(API_ROOT, routes)

// 404错误处理
app.use(function(req, res, next) {
  next(createError(404))
})

// 全局错误处理
app.use(function(err, req, res, next) {
  res.locals.message = err.message
  res.locals.error = req.app.get('env') === 'development' ? err : {}
  
  res.status(err.status || 500)
  res.render('error')
})

module.exports = app

配置说明:

  • API_ROOT:为所有接口提供统一前缀,便于API版本管理和路由分组
  • 模块化路由:将所有路由集中管理,提高代码可维护性
  • 中间件链:确保中间件按正确顺序执行

二、Mongoose Sequence插件安装

安装mongoose-sequence插件,实现业务ID自增功能:

npm install mongoose-sequence --save

三、用户数据模型详细设计

1. 用户模型文件结构

// models/system/user.js
const mongoose = require('mongoose')
const { AtModel } = require('./../base')                // 基础时间戳模型
const utils = require('./../../utils')                  // 工具函数
const { SEX, STATUS } = require('./../../enums/user')   // 枚举定义
const mongooseSequence = require('mongoose-sequence')(mongoose) // 自增插件
const { COUNTERS_COLLECTION } = require('./../../config/index')
const { AUTO_INCREMENT_USER_ID } = require('./../../utils/constant')

// 用户模式定义
const userSchema = new mongoose.Schema({
  ...AtModel,  // 继承创建时间和更新时间字段
  
  // 基础信息
  avatar: {
    type: String,
    default: '',
    description: '用户头像URL'
  },
  
  nickName: {
    type: String,
    default: '',
    required: [true, '昵称不能为空'],
    description: '用户昵称'
  },
  
  userName: {
    type: String,
    default: '',
    unique: [true, '用户名已存在'],
    trim: true,
    description: '用户名',
    required: [true, '用户名不能为空'],
    index: true  // 添加索引提升查询性能
  },
  
  // 联系信息
  email: {
    type: String,
    default: '',
    description: '邮箱地址',
    trim: true,
    validate: {
      validator: val => (val ? utils.isEmail(val) : true),
      message: '邮箱格式错误'
    }
  },
  
  phonenumber: {
    type: String,
    default: '',
    description: '手机号码',
    required: [true, '手机号不能为空'],
    trim: true,
    validate: {
      validator: val => utils.isPhone(val),
      message: '手机号格式错误'
    },
    index: true  // 添加索引提升查询性能
  },
  
  // 安全信息
  password: {
    type: String,
    default: '123456',
    select: false,  // 查询时不返回密码字段
    description: '登录密码',
    required: [true, '密码不能为空'],
    minlength: [6, '密码长度不能小于6位'],
    maxlength: [20, '密码长度不能大于20位'],
    trim: true,
    validate: {
      validator: val => /^[^<>"'|\\]+$/.test(val),
      message: '密码不能包含非法字符:< > " \' \\ |'
    }
  },
  
  // 状态信息
  sex: {
    type: String,
    enum: Object.values(SEX),
    default: SEX.MAN,
    description: '性别:0-男,1-女,2-未知'
  },
  
  status: {
    type: String,
    enum: Object.values(STATUS),
    default: STATUS.NORMAL,
    description: '状态:0-正常,1-停用'
  },
  
  // 登录信息
  loginDate: {
    type: Date,
    default: null,
    description: '最后登录时间'
  },
  
  loginIp: {
    type: String,
    default: '',
    description: '最后登录IP'
  },
  
  // 权限信息
  permissions: {
    type: [String],  // 使用数组简写语法
    default: []
  },
  
  roleIds: {
    type: [Number],  // 使用数组简写语法
    default: [],
    description: '角色ID列表',
    required: [true, '角色ID列表不能为空']
  },
  
  // 组织信息
  deptId: {
    type: Number,
    default: null,
    description: '部门ID'
  },
  
  tenantId: {
    type: String,
    default: '',
    description: '租户ID',
    required: [true, '租户ID不能为空']
  },
  
  // 自增业务ID
  userId: {
    type: Number,
    description: '用户业务ID',
    unique: [true, '用户ID已存在'],
    index: true  // 添加索引提升查询性能
  },
  
  userType: {
    type: String,
    default: 'sys_user',
    description: '用户类型'
  },
  
  // 岗位信息
  postIds: {
    type: [Number],  // 使用数组简写语法
    default: []
  },
  
  // 备注
  remark: {
    type: String,
    default: '',
    description: '备注信息'
  }
})

// 应用自增插件
userSchema.plugin(mongooseSequence, {
  inc_field: AUTO_INCREMENT_USER_ID,     // 自增字段名
  start_seq: 1,                          // 起始序列号
  id: `${AUTO_INCREMENT_USER_ID}_seq`,   // 序列ID
  collection_name: COUNTERS_COLLECTION   // 计数器集合名称
})

// 创建模型
module.exports = mongoose.model('Users', userSchema)

2. 支持文件说明

枚举定义文件 (enums/user.js):

// enums/user.js
const SEX = {
  MAN: '0',      // 男
  WOMAN: '1',    // 女
  UNKNOWN: '2'   // 未知
}

const STATUS = {
  NORMAL: '0',   // 正常
  DISABLE: '1'   // 停用
}

module.exports = {
  SEX,
  STATUS
}

常量定义文件 (utils/constant.js):

// utils/constant.js
const AUTO_INCREMENT_USER_ID = 'userId'

module.exports = {
  AUTO_INCREMENT_USER_ID
}

四、管理员用户初始化脚本

1. 初始化脚本实现

// scripts/initAdminUser.js
const {
  SystemModel: { UsersModel }
} = require('../models')
const db = require('../db/db')

/**
 * 初始化管理员用户
 * 创建默认的admin用户,用于系统初始登录
 */
const initAdminUser = async () => {
  try {
    // 检查是否已存在admin用户
    const existingAdmin = await UsersModel.findOne({ userName: 'admin' })
    
    if (existingAdmin) {
      console.log('⚠️ admin用户已存在,跳过初始化')
      return Promise.resolve({ skipped: true, user: existingAdmin })
    }

    // 创建管理员用户
    const adminData = {
      avatar: 'https://i.pravatar.cc/100',
      email: 'crazyLionLi@163.com',
      loginDate: null,
      loginIp: '::ffff:127.0.0.1',
      nickName: '疯狂的狮子Li',
      password: 'admin123',        // 注意:实际应用中密码应加密存储
      phonenumber: '15888888888',
      remark: '系统管理员',
      sex: '1',                    // 使用枚举值SEX.MAN
      status: '0',                 // 使用枚举值STATUS.NORMAL
      permissions: ['*:*:*'],      // 拥有所有权限
      deptId: 6,                   // 默认部门ID
      tenantId: '000000',          // 默认租户ID
      userType: 'sys_user',        // 系统用户类型
      userName: 'admin',
      roleIds: [1],                // 管理员角色ID
      postIds: []                  // 无关联岗位
    }

    const createdUser = await UsersModel.create(adminData)
    
    console.log('✅ admin用户初始化成功')
    return Promise.resolve({ 
      success: true, 
      user: {
        userId: createdUser.userId,
        userName: createdUser.userName,
        nickName: createdUser.nickName
      }
    })
    
  } catch (error) {
    console.error('❌ admin用户初始化失败:', error.message)
    return Promise.reject(error)
  }
}

// 执行初始化
db(() => {
  console.log('🚀 开始初始化admin用户...')
  
  initAdminUser()
    .then(result => {
      if (result.skipped) {
        console.log('📋 admin用户已存在,详细信息:', result.user)
      } else {
        console.log('🎉 初始化完成,创建的用户信息:', result.user)
      }
      process.exit(0)  // 正常退出
    })
    .catch(error => {
      console.error('💥 初始化过程中出现错误:', error)
      process.exit(1)  // 异常退出
    })
})

2. 执行初始化脚本

在项目根目录下执行:

node scripts/initAdminUser.js

3. 执行结果说明

成功情况:

🚀 开始初始化admin用户...
✅ admin用户初始化成功
🎉 初始化完成,创建的用户信息: { userId: 1, userName: 'admin', nickName: '疯狂的狮子Li' }

已存在情况:

🚀 开始初始化admin用户...
⚠️ admin用户已存在,跳过初始化
📋 admin用户已存在,详细信息: { userId: 1, userName: 'admin', ... }

失败情况:

🚀 开始初始化admin用户...
❌ admin用户初始化失败: [错误信息]
💥 初始化过程中出现错误: [错误详情]