Files
api_health/models/schema/employee.js
T
2026-09-09 17:36:45 +08:00

226 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
import mongoose from "mongoose";
import { pinyin } from "pinyin-pro";
/**
* Employee Schema
* 员工/人员信息,支持陪诊员、护士、医生、管理员等角色
*/
const EmployeeSchema = mongoose.Schema(
{
// 工号(业务雇佣标识)
employeeNo: { type: String, default: "", unique: true, sparse: true, comment: "工号" },
// 登录账号(关联用户体系)
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
index: true,
comment: "关联用户ID",
},
// 所属机构
orgId: {
type: mongoose.Schema.Types.ObjectId,
ref: "organization",
index: true,
comment: "所属机构ID",
},
// 角色/岗位
role: {
type: String,
enum: ["attendant", "nurse", "doctor", "admin", "operator", "other"],
default: "attendant",
comment: "角色:陪诊员、护士、医生、管理员、运营、其他",
},
// 个人信息
profile: {
name: { type: String, required: true, comment: "姓名" },
pinyin: { type: String, default: "", index: true, comment: "姓名的拼音,用于搜索" },
pinyinFL: { type: String, default: "", index: true, comment: "姓名拼音的首字母,用于搜索" },
sex: { type: String, enum: ["male", "female", "other", ""], default: "", comment: "性别" },
birthday: { type: Date, comment: "出生日期" },
idNumber: { type: String, default: "", comment: "身份证号" },
phone: { type: String, default: "", comment: "手机号" },
email: { type: String, default: "", comment: "邮箱" },
avatar: { type: String, default: "", comment: "头像URL" },
},
// 工作信息
work: {
department: { type: String, default: "", comment: "所属科室/部门" },
title: { type: String, default: "", comment: "职称/头衔" },
entryDate: { type: Date, comment: "入职日期" },
workYears: { type: Number, default: 0, comment: "工作年限" },
qualification: { type: String, default: "", comment: "资质证书编号" },
},
// 陪诊员特有信息
attendantInfo: {
serviceAreas: { type: [String], default: [], comment: "服务区域/城市列表" },
specialties: { type: [String], default: [], comment: "擅长科室/领域" },
languages: { type: [String], default: ["普通话"], comment: "掌握语言" },
rating: { type: Number, default: 5.0, min: 1, max: 5, comment: "评分(1-5" },
orderCount: { type: Number, default: 0, comment: "接单数量" },
isOnline: { type: Boolean, default: false, comment: "是否在线" },
},
// 紧急联系人
emergencyContact: {
name: { type: String, default: "", comment: "紧急联系人姓名" },
phone: { type: String, default: "", comment: "紧急联系人电话" },
relation: { type: String, default: "", comment: "关系" },
},
// 状态
status: {
type: String,
enum: ["active", "inactive", "resigned", "suspended"],
default: "active",
comment: "状态:在职、停用、离职、暂停",
},
// 简介/备注
introduction: { type: String, default: "", comment: "个人简介" },
remark: { type: String, default: "", comment: "备注" },
// 元数据
meta: {
createtime: { type: Date, default: Date.now, comment: "创建时间" },
updatetime: { type: Date, default: Date.now, comment: "更新时间" },
},
},
{
minimize: false,
strict: false,
collection: "employee",
timestamps: false,
}
);
// ==================== 拼音生成 ====================
/**
* 姓名 → 全拼(小写、无分隔,如 "张三" → "zhangsan"
*/
const genPinyin = (name) =>
pinyin(String(name), { toneType: "none", nonZh: "consecutive" }).toLowerCase().replace(/[^a-z0-9]/g, "");
/**
* 姓名 → 拼音首字母(如 "张三" → "zs"
*/
const genPinyinFL = (name) =>
pinyin(String(name), { pattern: "first", toneType: "none", nonZh: "consecutive" }).toLowerCase().replace(/[^a-z0-9]/g, "");
// 保存时自动生成/同步拼音字段
EmployeeSchema.pre("save", function (next) {
const name = this.profile?.name;
if (!name) return next();
if (this.isModified("profile.name") || !this.profile.pinyin || !this.profile.pinyinFL) {
this.profile.pinyin = genPinyin(name);
this.profile.pinyinFL = genPinyinFL(name);
}
next();
});
// ==================== 静态方法 ====================
/**
* 根据机构ID查询员工列表
*/
EmployeeSchema.statics.findByOrg = async function (orgId, options = {}) {
const { page = 1, pageSize = 20, role, status } = options;
const filter = { orgId };
if (role) filter.role = role;
if (status) filter.status = status;
const skip = (page - 1) * pageSize;
return await this.find(filter)
.skip(skip)
.limit(pageSize)
.exec();
};
/**
* 根据角色查询员工
*/
EmployeeSchema.statics.findByRole = async function (role, options = {}) {
const { page = 1, pageSize = 20, status = "active" } = options;
const filter = { role };
if (status) filter.status = status;
const skip = (page - 1) * pageSize;
return await this.find(filter)
.skip(skip)
.limit(pageSize)
.exec();
};
/**
* 根据用户ID查找员工
*/
EmployeeSchema.statics.findByUserId = async function (userId) {
return await this.findOne({ userId }).exec();
};
/**
* 根据ID查找员工
*/
EmployeeSchema.statics.findByEmployeeId = async function (id) {
return await this.findById(id).exec();
};
/**
* 创建员工
*/
EmployeeSchema.statics.createEmployee = async function (data) {
data.meta = { createtime: Date.now(), updatetime: Date.now() };
const doc = new this(data);
return await doc.save();
};
/**
* 更新员工
*/
EmployeeSchema.statics.updateEmployee = async function (id, update) {
const newName = update.name || update["profile.name"];
if (newName) {
update["profile.name"] = newName;
delete update.name;
update["profile.pinyin"] = genPinyin(newName);
update["profile.pinyinFL"] = genPinyinFL(newName);
}
update["meta.updatetime"] = Date.now();
return await this.findByIdAndUpdate(id, { $set: update }, { new: true }).exec();
};
/**
* 查询在线陪诊员列表
*/
EmployeeSchema.statics.findOnlineAttendants = async function (options = {}) {
const { page = 1, pageSize = 20 } = options;
const skip = (page - 1) * pageSize;
return await this.find({
role: "attendant",
status: "active",
"attendantInfo.isOnline": true,
})
.sort({ "attendantInfo.rating": -1 })
.skip(skip)
.limit(pageSize)
.exec();
};
// ==================== 索引定义 ====================
EmployeeSchema.index({ "profile.name": 1 });
EmployeeSchema.index({ employeeNo: 1 });
EmployeeSchema.index({ userId: 1 });
EmployeeSchema.index({ orgId: 1, role: 1, status: 1 });
EmployeeSchema.index({ role: 1, status: 1 });
EmployeeSchema.index({ "profile.phone": 1 });
EmployeeSchema.index({ status: 1 });
export { EmployeeSchema };