|
|
@@ -0,0 +1,361 @@
|
|
|
+package com.ylx.massage.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.ylx.common.config.WechatAccountConfig;
|
|
|
+import com.ylx.massage.domain.TOrder;
|
|
|
+import com.ylx.massage.domain.TXiangmu;
|
|
|
+import com.ylx.massage.service.OrderNotificationService;
|
|
|
+import com.ylx.massage.service.TJsService;
|
|
|
+import com.ylx.massage.utils.DateTimeUtils;
|
|
|
+import com.ylx.massage.utils.WeChatUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 订单消息通知服务实现类
|
|
|
+ * <p>
|
|
|
+ * 负责处理订单生命周期中各阶段的消息通知功能。
|
|
|
+ * 所有通知通过微信公众号模板消息推送给用户。
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @author ylx
|
|
|
+ * @version 1.0
|
|
|
+ * @since 2024
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class OrderNotificationServiceImpl implements OrderNotificationService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private WeChatUtil weChatUtil;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private WechatAccountConfig wxPayProperties;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private TJsService jsService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单待接单提醒(用户侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:template1
|
|
|
+ * 包含字段:订单号、项目名称、预约时间、用户电话、服务地址
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendPendingRemindNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ cn.hutool.json.JSONObject param = buildPendingRemindParams(order);
|
|
|
+ weChatUtil.notification(order.getcOpenId(), wxPayProperties.getTemplate1(), param);
|
|
|
+ log.info("订单待接单提醒发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单待接单提醒发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单已接单通知(用户侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:template1
|
|
|
+ * 包含字段:订单号、项目名称、预约时间、用户电话、服务地址
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendReceivedNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ cn.hutool.json.JSONObject param = buildReceivedNotificationParams(order);
|
|
|
+ weChatUtil.notification(order.getcOpenId(), wxPayProperties.getTemplate1(), param);
|
|
|
+ log.info("订单已接单通知发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单已接单通知发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单完成通知(用户侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:template1
|
|
|
+ * 包含字段:订单号、项目名称、完成时间
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendCompletedNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ cn.hutool.json.JSONObject param = buildCompletedNotificationParams(order);
|
|
|
+ weChatUtil.notification(order.getcOpenId(), wxPayProperties.getTemplate1(), param);
|
|
|
+ log.info("订单完成通知发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单完成通知发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单取消通知(用户侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:template1
|
|
|
+ * 包含字段:订单号、取消原因、项目名称
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendCancelledNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ cn.hutool.json.JSONObject param = buildCancelledNotificationParams(order);
|
|
|
+ weChatUtil.notification(order.getcOpenId(), wxPayProperties.getTemplate1(), param);
|
|
|
+ log.info("订单取消通知发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单取消通知发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建待接单提醒消息参数
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildPendingRemindParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 项目名称
|
|
|
+ String projectNames = getProjectNames(order);
|
|
|
+ param.set("thing10", JSONUtil.createObj().set("value", projectNames));
|
|
|
+ // 预约时间
|
|
|
+ LocalDateTime startTime = order.getStartTime();
|
|
|
+ Date date = Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+ param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm")));
|
|
|
+ // 用户电话
|
|
|
+ param.set("phone_number3", JSONUtil.createObj().set("value", order.getcPhone()));
|
|
|
+ // 服务地址
|
|
|
+ param.set("thing13", JSONUtil.createObj().set("value", order.getAddress()));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建已接单通知消息参数
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildReceivedNotificationParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string1", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 项目名称
|
|
|
+ String projectNames = getProjectNames(order);
|
|
|
+ param.set("thing4", JSONUtil.createObj().set("value", projectNames));
|
|
|
+ // 预约时间
|
|
|
+ LocalDateTime startTime = order.getStartTime();
|
|
|
+ Date date = Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+ param.set("time3", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm")));
|
|
|
+ // 用户电话
|
|
|
+ param.set("phone_number5", JSONUtil.createObj().set("value", order.getcPhone()));
|
|
|
+ // 服务地址
|
|
|
+ param.set("thing14", JSONUtil.createObj().set("value", order.getAddress()));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建完成通知消息参数
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildCompletedNotificationParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string8", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 项目名称
|
|
|
+ String projectNames = getProjectNames(order);
|
|
|
+ param.set("thing19", JSONUtil.createObj().set("value", projectNames));
|
|
|
+ // 完成时间
|
|
|
+ LocalDateTime completeTime = order.getEndTime();
|
|
|
+ Date date = Date.from(completeTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+ param.set("time18", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm")));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建取消通知消息参数
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildCancelledNotificationParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string2", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 取消原因
|
|
|
+ param.set("thing5", JSONUtil.createObj().set("value", "用户个人原因"));
|
|
|
+ // 项目名称
|
|
|
+ String projectNames = getProjectNames(order);
|
|
|
+ param.set("thing25", JSONUtil.createObj().set("value", projectNames));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从订单中提取项目名称
|
|
|
+ * <p>
|
|
|
+ * 解析订单的商品列表,将多个项目名称用逗号连接
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 项目名称字符串,多个项目用逗号分隔
|
|
|
+ */
|
|
|
+ private String getProjectNames(TOrder order) {
|
|
|
+ List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (TXiangmu xiangmu : list) {
|
|
|
+ sb.append(xiangmu.getcTitle()).append(",");
|
|
|
+ }
|
|
|
+ // 移除最后一个逗号
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.setLength(sb.length() - 1);
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单已接单通知(技师侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:templateId1
|
|
|
+ * 包含字段:订单号、用户姓名、联系电话、服务地址、下单时间、项目名称
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendTechnicianReceivedNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ // 获取技师信息
|
|
|
+ String jsOpenId = getTechnicianOpenId(order);
|
|
|
+ if (jsOpenId == null) {
|
|
|
+ log.warn("技师信息不存在,无法发送已接单通知,订单号:{}", order.getOrderNo());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ cn.hutool.json.JSONObject param = buildTechnicianReceivedNotificationParams(order);
|
|
|
+ weChatUtil.notification(jsOpenId, wxPayProperties.getTemplateId1(), param);
|
|
|
+ log.info("技师侧已接单通知发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("技师侧已接单通知发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单取消通知(技师侧)
|
|
|
+ * <p>
|
|
|
+ * 消息模板ID:templateId1
|
|
|
+ * 包含字段:订单号、取消原因、项目名称
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void sendTechnicianCancelledNotification(TOrder order) {
|
|
|
+ try {
|
|
|
+ // 获取技师信息
|
|
|
+ String jsOpenId = getTechnicianOpenId(order);
|
|
|
+ if (jsOpenId == null) {
|
|
|
+ log.warn("技师信息不存在,无法发送取消通知,订单号:{}", order.getOrderNo());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ cn.hutool.json.JSONObject param = buildTechnicianCancelledNotificationParams(order);
|
|
|
+ weChatUtil.notification(jsOpenId, wxPayProperties.getTemplateId1(), param);
|
|
|
+ log.info("技师侧取消订单通知发送成功,订单号:{}", order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("技师侧取消订单通知发送失败,订单号:{}", order.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建技师侧已接单通知消息参数
|
|
|
+ * <p>
|
|
|
+ * 参考原有的 newOrderNotification 方法实现
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildTechnicianReceivedNotificationParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 用户电话
|
|
|
+ param.set("phone_number14", JSONUtil.createObj().set("value", order.getcPhone()));
|
|
|
+ // 用户姓名
|
|
|
+ param.set("thing18", JSONUtil.createObj().set("value", order.getcName()));
|
|
|
+ // 下单时间
|
|
|
+ param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
|
|
|
+ // 服务地址
|
|
|
+ param.set("thing27", JSONUtil.createObj().set("value", order.getName()));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建技师侧取消通知消息参数
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 消息参数
|
|
|
+ */
|
|
|
+ private cn.hutool.json.JSONObject buildTechnicianCancelledNotificationParams(TOrder order) {
|
|
|
+ cn.hutool.json.JSONObject param = JSONUtil.createObj();
|
|
|
+ // 订单号
|
|
|
+ param.set("character_string2", JSONUtil.createObj().set("value", order.getOrderNo()));
|
|
|
+ // 取消原因
|
|
|
+ param.set("thing5", JSONUtil.createObj().set("value", getCancelReason(order)));
|
|
|
+ // 项目名称
|
|
|
+ String projectNames = getProjectNames(order);
|
|
|
+ param.set("thing25", JSONUtil.createObj().set("value", projectNames));
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取技师的 OpenId
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 技师 OpenId,如果技师不存在则返回 null
|
|
|
+ */
|
|
|
+ private String getTechnicianOpenId(TOrder order) {
|
|
|
+ if (order.getcJsId() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return jsService.getById(order.getcJsId()).getcOpenId();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("获取技师信息失败,技师ID:{}", order.getcJsId(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取订单取消原因
|
|
|
+ *
|
|
|
+ * @param order 订单信息
|
|
|
+ * @return 取消原因描述
|
|
|
+ */
|
|
|
+ private String getCancelReason(TOrder order) {
|
|
|
+ // 如果订单中有具体的取消原因,使用订单中的原因
|
|
|
+ if (order.getReasonRefusal() != null && !order.getReasonRefusal().trim().isEmpty()) {
|
|
|
+ return order.getReasonRefusal();
|
|
|
+ }
|
|
|
+ // 默认取消原因
|
|
|
+ return "用户取消订单";
|
|
|
+ }
|
|
|
+}
|