修改了employee相关

This commit is contained in:
lik
2026-10-06 21:44:42 +08:00
parent 0e81341bff
commit 7fc91b75f7
7 changed files with 309 additions and 9 deletions
+3 -1
View File
@@ -5,6 +5,7 @@ import { EscortRecordSchema } from "./schema/escort_record.js"
import { HealthProfileSchema } from "./schema/health_profile.js"
import { OrganizationSchema } from "./schema/organization.js"
import { EmployeeSchema } from "./schema/employee.js"
import { EmployeeReviewSchema } from "./schema/employee_review.js"
import config from '../config.js';
import logger from '../utils/logger.js';
@@ -16,6 +17,7 @@ class MongoDBSchema {
this.Organization = null;
this.Employee = null;
this.HealthProfile = null;
this.EmployeeReview = null;
}
init() {
@@ -45,9 +47,9 @@ class MongoDBSchema {
this.HealthProfile = this.dbConnection.model('health_profile', HealthProfileSchema)
this.Organization = this.dbConnection.model('organization', OrganizationSchema)
this.Employee = this.dbConnection.model('employee', EmployeeSchema)
this.EmployeeReview = this.dbConnection.model('employee_review', EmployeeReviewSchema)
}
}
const DBModel = new MongoDBSchema()
export { DBModel };
+36 -2
View File
@@ -3,6 +3,21 @@
import mongoose from "mongoose";
import { pinyin } from "pinyin-pro";
/**
* 培训记录子文档
*
* 国标 GB/T 47801—2026 4.2.1.5 把「培训记录」列为陪同人员信息公示项,
* 故按一条一次培训存成数组,便于逐条核对;不用 _id,这些条目只跟着员工走
*/
const TrainingSchema = mongoose.Schema(
{
title: { type: String, default: "", comment: "培训名称/课程" },
org: { type: String, default: "", comment: "培训机构" },
date: { type: Date, comment: "结业日期" },
},
{ _id: false }
);
/**
* Employee Schema
* 员工/人员信息,支持陪诊员、护士、医生、管理员等角色
@@ -56,6 +71,19 @@ const EmployeeSchema = mongoose.Schema(
entryDate: { type: Date, comment: "入职日期" },
workYears: { type: Number, default: 0, comment: "工作年限" },
qualification: { type: String, default: "", comment: "资质证书编号" },
// 健康证明:国标 4.2.1.5 公示的是「健康证明有效期」。
// 公示接口只输出"是否在有效期内"+ 到期日;number 属资质原文,
// 与 qualification 一样只作内部留存,绝不随公示接口下发
healthCertificate: {
number: { type: String, default: "", comment: "健康证明编号(不对外公示)" },
org: { type: String, default: "", comment: "发证机构" },
issueDate: { type: Date, comment: "签发日期" },
expireDate: { type: Date, comment: "有效期至" },
},
// 培训记录:同为 4.2.1.5 公示项
trainings: { type: [TrainingSchema], default: [], comment: "培训记录列表" },
},
// 陪诊员特有信息
@@ -63,7 +91,10 @@ const EmployeeSchema = mongoose.Schema(
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)" },
// 注意:本字段全项目没有任何写入点(恒为默认 5.0),不可作为评级来源。
// 对外评级一律由 employee_review 真实评价聚合得出(employee_review.js),
// 达阈值才展示,详见 .trae/documents/陪诊师公示与指定陪诊师.md
rating: { type: Number, default: 5.0, min: 1, max: 5, comment: "评分(无写入点,勿用于公示)" },
orderCount: { type: Number, default: 0, comment: "接单数量" },
isOnline: { type: Boolean, default: false, comment: "是否在线" },
},
@@ -197,6 +228,9 @@ EmployeeSchema.statics.updateEmployee = async function (id, update) {
/**
* 查询在线陪诊员列表
*
* 排序口径与 handler/employee.js 的公示列表保持一致:服务年限优先、累计服务次数次之。
* 曾按 attendantInfo.rating 排,但该字段没有任何写入点(恒为 5.0),等于没排序
*/
EmployeeSchema.statics.findOnlineAttendants = async function (options = {}) {
const { page = 1, pageSize = 20 } = options;
@@ -206,7 +240,7 @@ EmployeeSchema.statics.findOnlineAttendants = async function (options = {}) {
status: "active",
"attendantInfo.isOnline": true,
})
.sort({ "attendantInfo.rating": -1 })
.sort({ "work.workYears": -1, "attendantInfo.orderCount": -1 })
.skip(skip)
.limit(pageSize)
.exec();
+166
View File
@@ -0,0 +1,166 @@
"use strict";
import mongoose from "mongoose";
/**
* 员工评价 Schema
*
* 用途:为「员工信息公示」提供唯一可信的评级来源。当前只有陪诊师(role: attendant)
* 有对外的评价公示场景,故本表目前只被 handler/employee.js 的陪诊师详情页消费;
* 但表本身按「员工」建,不绑死陪诊师——后期护士 / 医生等角色接评价时直接复用,
* 不必再建一张平行的表(这也是它叫 employee_review 而不是 attendant_review 的原因)。
*
* 设计前提(见 .trae/documents/陪诊师公示与指定陪诊师.md):
* - 一次服务最多产生一条评价,故 recordId 建唯一索引,从数据层断掉刷分
* - 评级不由 employee.attendantInfo.rating 提供——那个字段没有任何写入点
* - 只有 status 为 published 的评价计入评分,运营隐藏掉的不参与
* - 是否对外展示分数由阈值决定(阈值在 handler/employee.js),本表只负责算平均分
*/
const EmployeeReviewSchema = mongoose.Schema(
{
/**
* 关联的服务记录ID(一次服务一条评价)
*
* 当前评价只发生在陪诊服务上,故指向 escort_record。
* 若未来非陪诊类评价(如问诊)接入,此处需补一个来源标识字段来区分记录所属集合——
* ObjectId 全局唯一,唯一索引可以照旧沿用,但读取时要先知道去哪个集合取原文。
* 该字段现在不加:不想再造一个没有写入点、等不到用处的空字段。
*
* @type {ObjectId}
* @ref escort_record
*/
recordId: {
type: mongoose.Schema.Types.ObjectId,
ref: "escort_record",
required: true,
comment: "关联服务记录ID",
},
/**
* 被评价的员工ID
*
* 名字用 employeeId 而非 attendantId:面向 employee 集合,不预设角色。
* 角色本身不冗余在这里,需要时由 employee 文档的 role 提供,避免两处口径打架。
*
* @type {ObjectId}
* @ref employee
*/
employeeId: {
type: mongoose.Schema.Types.ObjectId,
ref: "employee",
index: true,
required: true,
comment: "被评价员工ID",
},
/**
* 评价人用户ID
* @type {ObjectId}
* @ref user
*/
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
index: true,
comment: "评价人用户ID",
},
/**
* 评分(1-5 星)
*/
score: {
type: Number,
min: 1,
max: 5,
required: true,
comment: "评分(1-5)",
},
/**
* 评价正文
*/
content: { type: String, default: "", comment: "评价正文" },
/**
* 状态
* - published: 已发布 - 计入评分,可对外展示
* - hidden: 已隐藏 - 运营判定为不当内容,不计入评分
*/
status: {
type: String,
enum: ["published", "hidden"],
default: "published",
comment: "状态:已发布、已隐藏",
},
/**
* 元数据
*/
meta: {
createtime: { type: Date, default: Date.now, comment: "创建时间" },
updatetime: { type: Date, default: Date.now, comment: "更新时间" },
},
},
{
minimize: false,
strict: false,
collection: "employee_review",
timestamps: false,
}
);
/**
* 统计某位员工的公开评价数与平均分
*
* 只聚合 published,隐藏评价不参与;平均分保留一位小数,
* 公示页只展示一位,多出来的小数位没有意义。
* 是否对外展示由调用方按阈值决定,本方法不做门槛判断
*
* @param {ObjectId|string} employeeId - 员工ID
* @returns {Promise<{count: number, score: number|null}>} 评价条数与平均分(无评价时 score 为 null)
*/
EmployeeReviewSchema.statics.statsByEmployee = async function (employeeId) {
// 非法 ID 直接返回空统计,不抛 CastError
if (!employeeId || !mongoose.Types.ObjectId.isValid(employeeId)) {
return { count: 0, score: null };
}
const rows = await this.aggregate([
{
$match: {
employeeId: new mongoose.Types.ObjectId(employeeId),
status: "published",
},
},
{
$group: {
_id: null,
count: { $sum: 1 },
score: { $avg: "$score" },
},
},
]);
if (!rows.length) {
return { count: 0, score: null };
}
return {
count: rows[0].count,
score: Math.round(rows[0].score * 10) / 10,
};
};
// ==================== 索引定义 ====================
/**
* 服务记录唯一索引 - 一次服务只允许一条评价,从数据层堵住刷分
*/
EmployeeReviewSchema.index({ recordId: 1 }, { unique: true });
/**
* 员工 + 状态索引 - 用于按员工聚合公开评价
*/
EmployeeReviewSchema.index({ employeeId: 1, status: 1 });
export { EmployeeReviewSchema };