feat: 添加 mongoose 数据库模型 JS 规范文档,并更新基础规范中的常量命名检查说明
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
## 常量名使用大写加下划线命名(UPPER_CASE),单词间以下划线分隔 `[JsTs.Base.ConstUpperCase]`
|
## 常量名使用大写加下划线命名(UPPER_CASE),单词间以下划线分隔 `[JsTs.Base.ConstUpperCase]`
|
||||||
|
|
||||||
|
- 不检查 nodejs 的导包定义,比如 `const fs = require("fs")`
|
||||||
|
|
||||||
### Good
|
### Good
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|||||||
322
references/js.models.md
Normal file
322
references/js.models.md
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
# mongoose 数据库模型 JS 规范 `[Js.Model]`
|
||||||
|
|
||||||
|
> - includes `models/*.js`
|
||||||
|
|
||||||
|
## 模型文件命名规范 `[Js.Model.FileName]`
|
||||||
|
|
||||||
|
- 文件名使用小写加下划线命名(如 `user_extends.js`),或者使用小写加横线命名(如 `user-extends.js`)
|
||||||
|
- 文件名应与模型名称对应
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```txt
|
||||||
|
user.js
|
||||||
|
user_extends.js
|
||||||
|
user-profile.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bad
|
||||||
|
|
||||||
|
```txt
|
||||||
|
User.js
|
||||||
|
userExtends.js
|
||||||
|
UserProfile.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schema 字段命名规范 `[Js.Model.FieldName]`
|
||||||
|
|
||||||
|
- 字段名使用小写加下划线命名(snake_case)
|
||||||
|
- 避免使用驼峰命名
|
||||||
|
- 要补充字段注释
|
||||||
|
- 要补充字段类型
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
user_name: { type: String },
|
||||||
|
created_at: { type: Date },
|
||||||
|
is_active: { type: Boolean },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bad
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
userName: { type: String },
|
||||||
|
createdAt: { type: Date },
|
||||||
|
isActive: { type: Boolean },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schema 定义规范 `[Js.Model.SchemaDefinition]`
|
||||||
|
|
||||||
|
- 必须为每个字段指定类型
|
||||||
|
- 必要时添加 `required`、`default` 等属性
|
||||||
|
- 复杂类型应使用嵌套 Schema 或引用
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
user_name: { type: String, required: true },
|
||||||
|
email: { type: String, required: true, unique: true },
|
||||||
|
age: { type: Number, default: 0 },
|
||||||
|
status: { type: String, enum: ["active", "inactive"], default: "active" },
|
||||||
|
created_at: { type: Date, default: Date.now },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bad
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
user_name: String,
|
||||||
|
email: String,
|
||||||
|
age: Number,
|
||||||
|
status: String,
|
||||||
|
created_at: Date,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 索引定义规范 `[Js.Model.IndexDefinition]`
|
||||||
|
|
||||||
|
- 需要添加注释说明索引用途
|
||||||
|
- 不能在 Schema 定义中直接添加索引,添加到索引要注释掉
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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 定义后声明
|
||||||
|
- 需要添加注释说明虚拟字段用途
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
first_name: { type: String },
|
||||||
|
last_name: { type: String },
|
||||||
|
full_name: { type: String }, // 不应该存储可计算的字段
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 中间件/钩子规范 `[Js.Model.Middleware]`
|
||||||
|
|
||||||
|
- 中间件应在 Schema 定义后、模型导出前声明
|
||||||
|
- 需要添加注释说明中间件用途
|
||||||
|
- 避免在中间件中执行耗时操作
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
password: { type: String },
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.pre("save", function (next) {
|
||||||
|
// 没有注释说明用途
|
||||||
|
this.password = bcrypt.hashSync(this.password, 10); // 使用同步方法阻塞
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 静态方法规范 `[Js.Model.StaticMethod]`
|
||||||
|
|
||||||
|
- 静态方法命名使用小驼峰
|
||||||
|
- 静态方法应在 Schema 定义后声明
|
||||||
|
- 需要添加注释说明方法用途
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
email: { type: String },
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.statics.find_by_email = function (email) {
|
||||||
|
// 方法名不应使用下划线
|
||||||
|
return this.findOne({ email: email });
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例方法规范 `[Js.Model.InstanceMethod]`
|
||||||
|
|
||||||
|
- 实例方法命名使用小驼峰
|
||||||
|
- 实例方法应在 Schema 定义后声明
|
||||||
|
- 需要添加注释说明方法用途
|
||||||
|
- 实例方法内部使用 `this` 访问文档属性
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
password: { type: String },
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.methods.compare_password = function (candidatePassword) {
|
||||||
|
// 方法名不应使用下划线
|
||||||
|
return bcrypt.compareSync(candidatePassword, this.password);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关联引用规范 `[Js.Model.Reference]`
|
||||||
|
|
||||||
|
- 引用字段使用 `_id` 后缀命名
|
||||||
|
- 必须指定 `ref` 属性
|
||||||
|
- 需要添加注释说明关联关系
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const orderSchema = new Schema({
|
||||||
|
// 关联用户
|
||||||
|
user_id: { type: Schema.Types.ObjectId, ref: "User", required: true },
|
||||||
|
// 关联商品列表
|
||||||
|
product_ids: [{ type: Schema.Types.ObjectId, ref: "Product" }],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bad
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const orderSchema = new Schema({
|
||||||
|
user: { type: Schema.Types.ObjectId }, // 缺少 ref,命名不规范
|
||||||
|
products: [{ type: String }], // 应使用 ObjectId 引用
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 必须写明字段的类型 `[Js.Model.FieldType]`
|
||||||
|
|
||||||
|
- 字段定义必须使用对象形式,明确指定 `type` 属性
|
||||||
|
- 禁止使用简写形式(如 `field: String`)
|
||||||
|
- 不允许使用 Object 等模糊类型
|
||||||
|
|
||||||
|
### Good
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const userSchema = new Schema({
|
||||||
|
user_name: String, // 缺少类型对象包装
|
||||||
|
age: Number,
|
||||||
|
is_active: Boolean,
|
||||||
|
created_at: Date,
|
||||||
|
ext: { type: Object, default: {} },
|
||||||
|
ext2: {},
|
||||||
|
});
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user