修改了employee相关
This commit is contained in:
+3
-1
@@ -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 };
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 };
|
||||
Reference in New Issue
Block a user