修改了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
+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 };