Files
code-spec/references/js.models.md
T

7.8 KiB
Raw Blame History

mongoose 数据库模型 JS 规范 [Js.Model]

  • includes */models/*.js
  • override [JsTs.Base]

模型文件命名规范 [Js.Model.FileName]

  • override [JsTs.FileName]
  • 文件名使用小写加下划线命名(如 user_extends.js,或者使用小写加横线命名(如 user-extends.js
  • 文件名应与模型名称对应

Example: 模型文件命名

Good: 使用小写加下划线或横线命名

user.js
user_extends.js
user-profile.js

Bad: 使用大驼峰命名

User.js
userExtends.js
UserProfile.js

Schema 字段命名规范 [Js.Model.FieldName]

  • 字段名使用小写加下划线命名(snake_case)
  • 避免使用驼峰命名
  • 要补充字段注释
  • 要补充字段类型

Example: Schema 字段命名

Good: 字段名使用 snake_case

const userSchema = new Schema({
  user_name: { type: String },
  created_at: { type: Date },
  is_active: { type: Boolean },
});

Bad: 字段名使用驼峰命名

const userSchema = new Schema({
  userName: { type: String },
  createdAt: { type: Date },
  isActive: { type: Boolean },
});

Schema 定义规范 [Js.Model.SchemaDefinition]

  • 必须为每个字段指定类型
  • 必要时添加 requireddefault 等属性
  • 复杂类型应使用嵌套 Schema 或引用

Example: Schema 定义

Good: 完整的字段定义

const userSchema = new Schema({
  user_name: { type: String, required: true },
  email: { type: String, required: true },
  age: { type: Number, default: 0 },
  status: { type: String, enum: ["active", "inactive"], default: "active" },
  created_at: { type: Date, default: Date.now },
});

Bad: 缺少 required/default 等属性

const userSchema = new Schema({
  user_name: String,
  email: String,
  age: Number,
  status: String,
  created_at: Date,
});

索引定义规范 [Js.Model.IndexDefinition]

  • 需要添加注释说明索引用途
  • 不能在 Schema 定义中直接添加索引,添加到索引要注释掉

Example: 索引定义

Good: 索引注释掉并添加用途说明

const orderSchema = new Schema({
  user_id: { type: Schema.Types.ObjectId, ref: "User" },
  order_no: { type: String },
  created_at: { type: Date },
});

// 用户订单查询索引
// orderSchema.index({ user_id: 1, created_at: -1 });
// 订单号唯一索引
// orderSchema.index({ order_no: 1 }, { unique: true });

Bad: 在 Schema 定义中直接添加索引

const orderSchema = new Schema({
  user_id: { type: Schema.Types.ObjectId, ref: "User", index: true },
  order_no: { type: String, index: true },
  created_at: { type: Date, index: true },
});

虚拟字段规范 [Js.Model.VirtualField]

  • 虚拟字段命名使用小写加下划线
  • 虚拟字段应在 Schema 定义后声明
  • 需要添加注释说明虚拟字段用途

Example: 虚拟字段

Good: 使用 virtual 定义可计算字段

const userSchema = new Schema({
  first_name: { type: String },
  last_name: { type: String },
});

// 完整姓名虚拟字段
userSchema.virtual("full_name").get(function () {
  return this.first_name + " " + this.last_name;
});

Bad: 存储可计算的字段

const userSchema = new Schema({
  first_name: { type: String },
  last_name: { type: String },
  full_name: { type: String }, // 不应该存储可计算的字段
});

中间件/钩子规范 [Js.Model.Middleware]

  • 中间件应在 Schema 定义后、模型导出前声明
  • 需要添加注释说明中间件用途
  • 避免在中间件中执行耗时操作

Example: 中间件/钩子

Good: 中间件添加注释说明用途

const userSchema = new Schema({
  password: { type: String },
  updated_at: { type: Date },
});

// 保存前更新时间戳
userSchema.pre("save", function (next) {
  this.updated_at = new Date();
  next();
});

// 保存前加密密码
userSchema.pre("save", async function (next) {
  if (this.isModified("password")) {
    this.password = await bcrypt.hash(this.password, 10);
  }
  next();
});

Bad: 中间件缺少注释且使用同步方法

const userSchema = new Schema({
  password: { type: String },
});

userSchema.pre("save", function (next) {
  // 没有注释说明用途
  this.password = bcrypt.hashSync(this.password, 10); // 使用同步方法阻塞
  next();
});

静态方法规范 [Js.Model.StaticMethod]

  • 静态方法命名使用小驼峰
  • 静态方法应在 Schema 定义后声明
  • 需要添加注释说明方法用途

Example: 静态方法

Good: 静态方法使用小驼峰命名并添加注释

const userSchema = new Schema({
  email: { type: String },
  status: { type: String },
});

// 根据邮箱查找用户
userSchema.statics.findByEmail = function (email) {
  return this.findOne({ email: email });
};

// 查找所有活跃用户
userSchema.statics.findActiveUsers = function () {
  return this.find({ status: "active" });
};

Bad: 静态方法使用下划线命名且缺少注释

const userSchema = new Schema({
  email: { type: String },
});

userSchema.statics.find_by_email = function (email) {
  // 方法名不应使用下划线
  return this.findOne({ email: email });
};

实例方法规范 [Js.Model.InstanceMethod]

  • 实例方法命名使用小驼峰
  • 实例方法应在 Schema 定义后声明
  • 需要添加注释说明方法用途
  • 实例方法内部使用 this 访问文档属性

Example: 实例方法

Good: 实例方法使用小驼峰命名并添加注释

const userSchema = new Schema({
  password: { type: String },
  login_count: { type: Number, default: 0 },
});

// 验证密码
userSchema.methods.comparePassword = async function (candidatePassword) {
  return bcrypt.compare(candidatePassword, this.password);
};

// 增加登录次数
userSchema.methods.incrementLoginCount = function () {
  this.login_count += 1;
  return this.save();
};

Bad: 实例方法使用下划线命名且缺少注释

const userSchema = new Schema({
  password: { type: String },
});

userSchema.methods.compare_password = function (candidatePassword) {
  // 方法名不应使用下划线
  return bcrypt.compareSync(candidatePassword, this.password);
};

关联引用规范 [Js.Model.Reference]

  • 引用字段使用 _id 后缀命名
  • 必须指定 ref 属性
  • 需要添加注释说明关联关系

Example: 关联引用

Good: 引用字段使用 _id 后缀并指定 ref

const orderSchema = new Schema({
  // 关联用户
  user_id: { type: Schema.Types.ObjectId, ref: "User", required: true },
  // 关联商品列表
  product_ids: [{ type: Schema.Types.ObjectId, ref: "Product" }],
});

Bad: 引用字段缺少 ref 且命名不规范

const orderSchema = new Schema({
  user: { type: Schema.Types.ObjectId }, // 缺少 ref,命名不规范
  products: [{ type: String }], // 应使用 ObjectId 引用
});

必须写明字段的类型 [Js.Model.FieldType]

  • 字段定义必须使用对象形式,明确指定 type 属性
  • 禁止使用简写形式(如 field: String
  • 不允许使用 Object 等模糊类型

Example: 字段类型定义

Good: 字段定义使用对象形式明确指定 type

const userSchema = new Schema({
  // 用户名
  user_name: { type: String, required: true },
  // 年龄
  age: { type: Number, default: 0 },
  // 是否激活
  is_active: { type: Boolean, default: true },
  // 创建时间
  created_at: { type: Date, default: Date.now },
});

Bad: 使用简写形式或模糊类型

const userSchema = new Schema({
  user_name: String, // 缺少类型对象包装
  age: Number,
  is_active: Boolean,
  created_at: Date,
  ext: { type: Object, default: {} },
  ext2: {},
});