上一篇我们介绍了评价算法的设计,本篇我们来实现一下用户评价的功能。用户评价的时候要留存评价数据,需要创建一个评价的数据模型,对于相关表需要扩充字段来支持评分的展现。
1 创建数据源
打开我们的数据模型,创建评价表
修改服务表,增加评分字段
修改工人表,增加评分对应字段
2 创建API
表创建好了之后,就需要编写API来实现评价算法,在我们的订单管理里增加评价方法
输入如下代码
/**
* ErrorCode 定义
* 统一管理系统可能返回的错误码和消息。
*/
const ErrorCode = {
SUCCESS: 0,
PARAM_ERROR: 1001, // 参数缺失或无效
NOT_FOUND: 1002, // 订单或派工记录未找到
SYSTEM_ERROR: 1003, // 通用系统错误
INVALID_ORDER_STATUS: 1004, // 订单当前主状态不允许评价
// 更多错误码可以按需添加
};
/**
* 订单在 `jz_orders` (订单主表) 中可能的主状态枚举。
*/
const ORDER_STATUS_ENUM = {
PENDING_PAYMENT: '1',
PENDING_ACCEPTANCE: '2', // 待接单
ACCEPTED: '3', // 已接单
IN_SERVICE: '4', // 服务中
PENDING_REVIEW: '5', // 待评价 (服务已完成,等待用户评价)
COMPLETED: '6', // 已完成 (用户已评价或已过评价期)
CANCELED: '7',
REFUNDED: '8',
};
/**
* 订单主表 `jz_orders` 中 `dispatchStatus` 字段的枚举。
*/
const DISPATCH_STATUS_ENUM = {
PENDING_DISPATCH: '1',
DISPATCHED_PENDING_ACCEPTANCE: '2',
DISPATCHED_ACCEPTED: '3',
DISPATCHED_REJECTED: '4',
DISPATCHED_COMPLETED: '5', // 派单已完成 (工人已完成服务)
};
/**
* 提交评价函数
* 用户通过此函数提交对已完成服务的评价。
*
* @param {object} params - 入参
* @param {string} params.orderId - 订单ID (主键 _id)
* @param {string} params.userId - 提交评价的用户ID (当前登录用户)
* @param {number} params.overallRating - 整体服务评分 (1-5星)
* @param {number} params.serviceQualityRating - 服务质量评分 (1-5星)
* @param {number} params.serviceAttitudeRating - 服务态度评分 (1-5星)
* @param {number} params.professionalismRating - 专业程度评分 (1-5星)
* @param {number} params.punctualityRating - 准时程度评分 (1-5星)
* @param {string} [params.reviewContent] - 评价内容 (可选)
* @param {Array<string>} [params.reviewPhotos] - 评价照片的URL数组 (可选)
* @param {boolean} [params.isAnonymous=false] - 是否匿名评价 (可选,默认为false)
* @param {object} context - 微搭云函数上下文对象
* @returns {Promise<object>} 评价结果,包含 code, message, data (更新后的订单信息)
*/
module.exports = async function (params, context) {
const {
orderId,
userId,
overallRating,
serviceQualityRating,
serviceAttitudeRating,
professionalismRating,
punctualityRating,
reviewContent = '',
reviewPhotos = [],
isAnonymous = false
} = params;
// 1. 参数验证
if (!orderId || !userId ||
overallRating === undefined ||
serviceQualityRating === undefined ||
serviceAttitudeRating === undefined ||
professionalismRating === undefined ||
punctualityRating === undefined
) {
return { code: ErrorCode.PARAM_ERROR, message: '必填参数缺失或无效:orderId, userId, 完整评分数据' };
}
// 验证评分是否在有效范围内 (1-5)
const ratings = [overallRating, serviceQualityRating, serviceAttitudeRating, professionalismRating, punctualityRating];
if (ratings.some(r => r < 1 || r > 5)) {
return { code: ErrorCode.PARAM_ERROR, message: '评分必须在1到5之间' };
}
let order = null;
let service = null;
let provider = null;
try {
// 2. 查找订单的当前状态,并获取 service_id 和 assignedWorkerId
order = await context.callModel({
dataSourceName: "jz_orders", // 订单表模型标识
methodName: "wedaGetItemV2",
params: {
filter: {
where: {
_id: { $eq: orderId }
}
},
select: {
"$master": true,
"serviceId": true, // 获取关联的服务ID
"assignedWorkerId": true // 获取被指派的工人ID
}
}
});
if (!order || Object.keys(order).length === 0) {
return { code: ErrorCode.NOT_FOUND, message: '订单未找到' };
}
// 验证订单是否处于“待评价”状态
if (order.status !== ORDER_STATUS_ENUM.PENDING_REVIEW) {
return {
code: ErrorCode.INVALID_ORDER_STATUS,
message: `订单当前状态不允许评价。订单必须处于“待评价”状态。当前状态码: ${order.status}`
};
}
const serviceId = order.serviceId && order.serviceId._id; // 假设 serviceId 是一个引用类型,需要取 _id
const providerId = order.assignedWorkerId && order.assignedWorkerId._id; // 假设 assignedWorkerId 是一个引用类型,需要取 _id
if (!serviceId || !providerId) {
return { code: ErrorCode.SYSTEM_ERROR, message: '订单关联的服务或工人信息不完整' };
}
// 3. 查找服务信息 (用于后续更新服务的平均分)
service = await context.callModel({
dataSourceName: "jz_services", // 服务表模型标识
methodName: "wedaGetItemV2",
params: {
filter: {
where: {
_id: { $eq: serviceId }
}
},
select: { "_id": true, "avg_rating": true, "total_reviews": true }
}
});
// 4. 查找服务提供者信息 (用于后续更新工人的平均分)
provider = await context.callModel({
dataSourceName: "jz_fwssq", // 服务提供者表模型标识
methodName: "wedaGetItemV2",
params: {
filter: {
where: {
_id: { $eq: providerId }
}
},
select: { "_id": true, "avg_rating": true, "total_reviews": true }
}
});
if (!service || !provider || Object.keys(service).length === 0 || Object.keys(provider).length === 0) {
return { code: ErrorCode.NOT_FOUND, message: '关联的服务或服务提供者未找到' };
}
// 5. 插入新的评价记录到 `Reviews` 表
const newReviewData = {
orderId: { _id: orderId }, // 关联订单
userId: { _id: userId }, // 关联用户
serviceId: { _id: serviceId }, // 关联服务
providerId: { _id: providerId }, // 关联服务提供者
overall_rating: overallRating,
service_quality_rating: serviceQualityRating,
service_attitude_rating: serviceAttitudeRating,
professionalism_rating: professionalismRating,
punctuality_rating: punctualityRating,
review_content: reviewContent,
review_photos: reviewPhotos,
is_anonymous: isAnonymous,
review_date: Date.now()
};
await context.callModel({
dataSourceName: "Reviews", // 评价表模型标识
methodName: "wedaCreateV2",
params: {
data: newReviewData
}
});
// 6. 更新 `ServiceProviders` 表中的评分和评价数
// 重新计算该工人的平均评分和总评价数
const providerReviews = await context.callModel({
dataSourceName: "Reviews", // 数据模型标识,对应你之前设计的 Reviews 表
methodName: "wedaGetRecordsV2", // 获取所有相关评价记录的方法
params: {
filter: {
relateWhere: {
providerId: {
where: {
_id: {
$eq: providerId
}
}
}
}
},
select: {
"overall_rating": true // 只选择评分字段,减少数据传输量
},
getCount: true // 获取总条数,wedaGetRecordsV2 返回的数据结构中会包含 total 字段
// 如果数据量巨大,不建议一次性获取所有记录。
// 可以考虑分批获取或利用数据库的聚合功能(如果微搭的callModel支持)
// 或者在 Reviews 表中维护 providerId 相关的冗余统计字段,通过触发器或定期任务更新
}
});
let newProviderAvgRating = 0;
let newProviderTotalReviews = providerReviews.total; // total属性表示总条数
if (newProviderTotalReviews > 0) {
const sumProviderRatings = providerReviews.records.reduce((sum, r) => sum + r.overall_rating, 0);
newProviderAvgRating = parseFloat((sumProviderRatings / newProviderTotalReviews).toFixed(1)); // 保留一位小数
}
await context.callModel({
dataSourceName: "jz_fwssq",
methodName: "wedaUpdateV2",
params: {
data: {
avg_rating: newProviderAvgRating,
total_reviews: newProviderTotalReviews
},
filter: {
where: { _id: { $eq: providerId } }
}
}
});
// 7. 更新 `Services` 表中的评分和评价数
// 重新计算该服务的平均评分和总评价数
const serviceReviews = await context.callModel({
dataSourceName: "Reviews",
methodName: "wedaGetRecordsV2", // 获取所有相关评价
params: {
filter: {
relateWhere: {
serviceId: {
where: {
_id: {
$eq: serviceId
}
}
}
}
},
select: { "overall_rating": true },
getCount:true
}
});
let newServiceAvgRating = 0;
let newServiceTotalReviews = serviceReviews.total;
if (newServiceTotalReviews > 0) {
const sumServiceRatings = serviceReviews.records.reduce((sum, r) => sum + r.overall_rating, 0);
newServiceAvgRating = parseFloat((sumServiceRatings / newServiceTotalReviews).toFixed(1)); // 保留一位小数
}
await context.callModel({
dataSourceName: "jz_services",
methodName: "wedaUpdateV2",
params: {
data: {
avg_rating: newServiceAvgRating,
total_reviews: newServiceTotalReviews
},
filter: {
where: { _id: { $eq: serviceId } }
}
}
});
// 8. 更新 `jz_orders` (订单主表) 的状态为“已完成”
const updateOrderParams = {
status: ORDER_STATUS_ENUM.COMPLETED, // 订单主状态更新为 '6' (已完成)
dispatchStatus: DISPATCH_STATUS_ENUM.DISPATCHED_COMPLETED, // 派单状态也更新为已完成
completionTime: Date.now(), // 订单完成时间
};
await context.callModel({
dataSourceName: "jz_orders",
methodName: "wedaUpdateV2",
params: {
data: updateOrderParams,
filter: {
where: { _id: { $eq: orderId } }
}
}
});
// 9. 在 `order_status_logs` (订单状态日志表) 插入日志记录
const logDescription = `用户 (ID: ${userId}) 已提交订单 (ID: ${orderId}) 评价。订单主状态由 "${order.status}" 变为 "${ORDER_STATUS_ENUM.COMPLETED}"。`;
await context.callModel({
dataSourceName: "order_status_logs", // 订单状态日志表模型标识
methodName: "wedaCreateV2",
params: {
data: {
orderId: { _id: orderId },
eventType: '10', // 事件类型
eventDescription: logDescription,
operatorRole: '用户', // 操作角色为用户
operatorId: userId, // 操作人ID为用户ID
fssj: Date.now(), // 发生时间
}
}
});
// 10. 返回评价结果
const updatedOrder = await context.callModel({
dataSourceName: "jz_orders",
methodName: "wedaGetItemV2",
params: {
filter: {
where: { _id: { $eq: orderId } }
},
select: { "$master": true, "status": true, "dispatchStatus": true }
}
});
return {
code: ErrorCode.SUCCESS,
message: '订单评价成功',
data: updatedOrder // 返回更新后的订单信息
};
} catch (error) {
console.error("submitReview error:", error);
return {
code: ErrorCode.SYSTEM_ERROR,
message: `系统错误: ${error.message}`
};
}
};
添加入参
点击方法测试,测试我们的API,测试通过后点击出参自动映射完成API的编写
3 搭建评价页面
打开我们的小程序,点击新建页面
输入页面的名称
添加表单容器,数据表选择评价表
选中页面组件,添加一个URL参数
选择关联订单ID,选中值绑定为我们的URL参数
选择评价用户ID,选中值绑定为我们全局变量的用户信息的数据标识
选中表单容器,将表单的提交方法改为我们的API的方法
表单提交之后增加一个返回上一页的方法
切换到我的订单页面,增加一个评价按钮
给按钮设置点击事件,打开我们的评价页面,并且传入订单的数据标识
总结
本篇我们介绍了用户评价的一个实现过程,创建了评价的数据源,增加了API实现了评价的业务逻辑,搭建了前端界面完成了交互。后续我们就继续完善我们用户端的功能,实现一下售后的功能。