From a62cffd2d300bff5885e983c4fc68d555c1e4157 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期四, 09 七月 2026 15:16:26 +0800
Subject: [PATCH] 微信订阅消息推送
---
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java | 4
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeMessageServiceImpl.java | 188 +++++++++++++++++++++++++++++++++++++
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java | 26 +++++
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/WxSubscribeMessageService.java | 19 +++
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeTemplate.java | 25 +++++
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java | 37 +++++++
6 files changed, 299 insertions(+), 0 deletions(-)
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java
index e33f0e5..27c1c87 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.chargingPile.api.feignClient.SiteClient;
+import com.ruoyi.chargingPile.api.model.Site;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.log.annotation.Log;
@@ -10,6 +12,7 @@
import com.ruoyi.common.security.annotation.Logical;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.service.TokenService;
+import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.model.TOrderEvaluateTag;
import com.ruoyi.order.api.query.TOrderEvaluateQuery;
@@ -18,16 +21,21 @@
import com.ruoyi.order.dto.GetOrderEvaluatePageList;
import com.ruoyi.order.dto.GetOrderEvaluatePageListDTO;
import com.ruoyi.order.dto.OrderEvaluateVo;
+import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.order.service.TOrderEvaluateTagService;
+import com.ruoyi.order.service.WxSubscribeMessageService;
import com.ruoyi.order.vo.ReplyEvaluationVO;
import com.ruoyi.other.api.vo.TEvaluationTagVO;
import io.swagger.annotations.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDateTime;
+import java.util.Collections;
import java.util.List;
/**
@@ -43,6 +51,8 @@
@RequestMapping("/t-order-evaluate")
public class TOrderEvaluateController {
+ private static final Logger log = LoggerFactory.getLogger(TOrderEvaluateController.class);
+
@Autowired
private TOrderEvaluateService orderEvaluateService;
@@ -51,6 +61,15 @@
@Resource
private TokenService tokenService;
+
+ @Resource
+ private TChargingOrderService chargingOrderService;
+
+ @Resource
+ private SiteClient siteClient;
+
+ @Resource
+ private WxSubscribeMessageService wxSubscribeMessageService;
@@ -109,8 +128,26 @@
orderEvaluate.setEvaluationResponse(reply);
orderEvaluate.setResponseTime(LocalDateTime.now());
orderEvaluateService.updateById(orderEvaluate);
+ sendEvaluationReplyMessage(orderEvaluate);
return AjaxResult.success();
}
+
+ private void sendEvaluationReplyMessage(TOrderEvaluate orderEvaluate) {
+ try {
+ if (orderEvaluate == null || !Integer.valueOf(1).equals(orderEvaluate.getOrderType())) {
+ return;
+ }
+ TChargingOrder order = chargingOrderService.getById(orderEvaluate.getOrderId());
+ Site site = null;
+ if (order != null && order.getSiteId() != null) {
+ List<Site> sites = siteClient.getSiteByIds(Collections.singletonList(order.getSiteId())).getData();
+ site = sites == null || sites.isEmpty() ? null : sites.get(0);
+ }
+ wxSubscribeMessageService.sendEvaluationReply(orderEvaluate, order, site);
+ } catch (Exception e) {
+ log.warn("评价回复订阅消息发送异常,evaluateId={}", orderEvaluate == null ? null : orderEvaluate.getId(), e);
+ }
+ }
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/WxSubscribeMessageService.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/WxSubscribeMessageService.java
new file mode 100644
index 0000000..b85bba1
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/WxSubscribeMessageService.java
@@ -0,0 +1,19 @@
+package com.ruoyi.order.service;
+
+import com.ruoyi.chargingPile.api.model.Site;
+import com.ruoyi.chargingPile.api.model.TChargingGun;
+import com.ruoyi.order.api.model.TChargingOrder;
+import com.ruoyi.order.api.model.TOrderEvaluate;
+
+import java.math.BigDecimal;
+
+public interface WxSubscribeMessageService {
+
+ void sendChargeStart(TChargingOrder order, Site site, TChargingGun chargingGun);
+
+ void sendChargeEnd(TChargingOrder order, Site site, TChargingGun chargingGun);
+
+ void sendEvaluationReply(TOrderEvaluate orderEvaluate, TChargingOrder order, Site site);
+
+ void sendPriceDrop(Long appUserId, Site site, BigDecimal price);
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index ee68dae..6500d39 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -169,6 +169,8 @@
@Resource
private SendMessageClient sendMessageClient;
+ @Resource
+ private WxSubscribeMessageService wxSubscribeMessageService;
@Resource
private TOrderEvaluateService orderEvaluateService;
@@ -1256,6 +1258,7 @@
//推送状态给三方平台
tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 4);
}
+ wxSubscribeMessageService.sendChargeStart(order, site, chargingGun);
}
redisService.setCacheObject("AQJC_" + order.getChargingGunId(), preChargeCheck1, 24L, TimeUnit.HOURS);
@@ -3481,6 +3484,7 @@
if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) {
tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 2);
}
+ wxSubscribeMessageService.sendChargeEnd(chargingOrder, site, chargingGun);
//添加积分
if (1 == chargingOrder.getOrderSource()) {
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java
index 3419cb9..cf9f85b 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java
@@ -36,6 +36,7 @@
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TEvaluationAutoReplyService;
import com.ruoyi.order.service.TOrderEvaluateService;
+import com.ruoyi.order.service.WxSubscribeMessageService;
import com.ruoyi.other.api.domain.TEvaluationTag;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.api.domain.TVip;
@@ -45,6 +46,8 @@
import com.ruoyi.system.api.feignClient.SysUserClient;
import com.ruoyi.system.api.feignClient.SysUserRoleClient;
import com.ruoyi.system.api.model.SysUserRoleVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@@ -65,6 +68,8 @@
*/
@Service
public class TOrderEvaluateServiceImpl extends ServiceImpl<TOrderEvaluateMapper, TOrderEvaluate> implements TOrderEvaluateService {
+
+ private static final Logger log = LoggerFactory.getLogger(TOrderEvaluateServiceImpl.class);
@Resource
private TEvaluationTagClient evaluationTagClient;
@@ -112,6 +117,9 @@
@Resource
private TEvaluationAutoReplyService evaluationAutoReplyService;
+
+ @Resource
+ private WxSubscribeMessageService wxSubscribeMessageService;
@@ -409,6 +417,24 @@
orderEvaluate.setEvaluationResponse(rule.getReplyContent());
orderEvaluate.setResponseTime(LocalDateTime.now());
this.updateById(orderEvaluate);
+ sendAutoEvaluationReplyMessage(orderEvaluate);
+ }
+
+ private void sendAutoEvaluationReplyMessage(TOrderEvaluate orderEvaluate) {
+ try {
+ if (orderEvaluate == null || !Integer.valueOf(1).equals(orderEvaluate.getOrderType())) {
+ return;
+ }
+ TChargingOrder order = chargingOrderService.getById(orderEvaluate.getOrderId());
+ Site site = null;
+ if (order != null && order.getSiteId() != null) {
+ List<Site> sites = siteClient.getSiteByIds(Collections.singletonList(order.getSiteId())).getData();
+ site = sites == null || sites.isEmpty() ? null : sites.get(0);
+ }
+ wxSubscribeMessageService.sendEvaluationReply(orderEvaluate, order, site);
+ } catch (Exception e) {
+ log.warn("评价自动回复订阅消息发送异常,evaluateId={}", orderEvaluate == null ? null : orderEvaluate.getId(), e);
+ }
}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeMessageServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeMessageServiceImpl.java
new file mode 100644
index 0000000..f59df6a
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeMessageServiceImpl.java
@@ -0,0 +1,188 @@
+package com.ruoyi.order.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.account.api.feignClient.AppUserClient;
+import com.ruoyi.account.api.model.TAppUser;
+import com.ruoyi.chargingPile.api.model.Site;
+import com.ruoyi.chargingPile.api.model.TChargingGun;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.order.api.model.TChargingOrder;
+import com.ruoyi.order.api.model.TOrderEvaluate;
+import com.ruoyi.order.service.WxSubscribeMessageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Service
+public class WxSubscribeMessageServiceImpl implements WxSubscribeMessageService {
+
+ private static final Logger log = LoggerFactory.getLogger(WxSubscribeMessageServiceImpl.class);
+ private static final String SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={accessToken}";
+
+ @Resource
+ private AppUserClient appUserClient;
+ @Resource
+ private RestTemplate restTemplate;
+
+ @Override
+ public void sendChargeStart(TChargingOrder order, Site site, TChargingGun chargingGun) {
+ if (order == null) {
+ return;
+ }
+ Map<String, String> data = new LinkedHashMap<>();
+ data.put("thing16", limit(siteName(site), 20));
+ data.put("thing17", limit(gunCode(chargingGun), 20));
+ data.put("amount10", amount(order.getRechargeAmount()));
+ data.put("thing21", limit(defaultText(order.getPlateNum(), "未填写"), 20));
+ send(WxSubscribeTemplate.CHARGE_START, order.getAppUserId(), page(WxSubscribeTemplate.CHARGE_START, order), data);
+ }
+
+ @Override
+ public void sendChargeEnd(TChargingOrder order, Site site, TChargingGun chargingGun) {
+ if (order == null) {
+ return;
+ }
+ Map<String, String> data = new LinkedHashMap<>();
+ data.put("thing6", limit(siteName(site), 20));
+ data.put("character_string15", limit(gunCode(chargingGun), 32));
+ data.put("amount33", amount(order.getPaymentAmount() == null ? order.getOrderAmount() : order.getPaymentAmount()));
+ data.put("amount18", amount(order.getRefundAmount()));
+ data.put("thing3", limit(endReason(order.getEndMode()), 20));
+ send(WxSubscribeTemplate.CHARGE_END, order.getAppUserId(), page(WxSubscribeTemplate.CHARGE_END, order), data);
+ }
+
+ @Override
+ public void sendEvaluationReply(TOrderEvaluate orderEvaluate, TChargingOrder order, Site site) {
+ if (orderEvaluate == null) {
+ return;
+ }
+ Map<String, String> data = new LinkedHashMap<>();
+ data.put("thing1", limit(siteName(site), 20));
+ data.put("thing3", limit(defaultText(orderEvaluate.getEvaluationResponse(), "平台已回复您的评价"), 20));
+ send(WxSubscribeTemplate.SERVICE_EVALUATION, orderEvaluate.getAppUserId(), WxSubscribeTemplate.SERVICE_EVALUATION.getDefaultPage(), data);
+ }
+
+ @Override
+ public void sendPriceDrop(Long appUserId, Site site, BigDecimal price) {
+ Map<String, String> data = new LinkedHashMap<>();
+ data.put("thing1", limit(siteName(site), 20));
+ data.put("amount4", amount(price));
+ send(WxSubscribeTemplate.PRICE_DROP, appUserId, priceDropPage(site), data);
+ }
+
+ private void send(WxSubscribeTemplate template, Long appUserId, String page, Map<String, String> data) {
+ try {
+ if (appUserId == null) {
+ return;
+ }
+ R<TAppUser> userResult = appUserClient.getUserById(appUserId);
+ TAppUser appUser = userResult == null ? null : userResult.getData();
+ if (appUser == null || !StringUtils.hasText(appUser.getWxOpenid())) {
+ log.info("微信订阅消息未发送,用户openid为空,template={}, appUserId={}", template.name(), appUserId);
+ return;
+ }
+ R<String> tokenResult = appUserClient.getWXToken();
+ String accessToken = tokenResult == null ? null : tokenResult.getData();
+ if (!StringUtils.hasText(accessToken)) {
+ log.warn("微信订阅消息未发送,access_token为空,template={}, appUserId={}", template.name(), appUserId);
+ return;
+ }
+ JSONObject payload = buildPayload(appUser.getWxOpenid(), template, page, data);
+ JSONObject response = restTemplate.postForObject(SEND_URL, payload, JSONObject.class, accessToken);
+ if (response != null && response.getIntValue("errcode") != 0) {
+ log.warn("微信订阅消息发送失败,template={}, appUserId={}, response={}", template.name(), appUserId, response);
+ }
+ } catch (Exception e) {
+ log.warn("微信订阅消息发送异常,template={}, appUserId={}", template.name(), appUserId, e);
+ }
+ }
+
+ static JSONObject buildPayload(String openid, WxSubscribeTemplate template, String page, Map<String, String> data) {
+ JSONObject payload = new JSONObject();
+ payload.put("touser", openid);
+ payload.put("template_id", template.getTemplateId());
+ if (StringUtils.hasText(page)) {
+ payload.put("page", page);
+ }
+ JSONObject dataJson = new JSONObject();
+ for (Map.Entry<String, String> entry : data.entrySet()) {
+ JSONObject value = new JSONObject();
+ value.put("value", entry.getValue());
+ dataJson.put(entry.getKey(), value);
+ }
+ payload.put("data", dataJson);
+ return payload;
+ }
+
+ private static String page(WxSubscribeTemplate template, TChargingOrder order) {
+ if (order == null || order.getChargingGunId() == null) {
+ return template.getDefaultPage();
+ }
+ return template.getDefaultPage() + "?gunId=" + order.getChargingGunId();
+ }
+
+ private static String priceDropPage(Site site) {
+ if (site == null || site.getId() == null) {
+ return WxSubscribeTemplate.PRICE_DROP.getDefaultPage();
+ }
+ return WxSubscribeTemplate.PRICE_DROP.getDefaultPage() + "?id=" + site.getId();
+ }
+
+ private static String siteName(Site site) {
+ return site == null ? "充电站" : defaultText(site.getName(), "充电站");
+ }
+
+ private static String gunCode(TChargingGun chargingGun) {
+ if (chargingGun == null) {
+ return "充电桩";
+ }
+ if (StringUtils.hasText(chargingGun.getFullNumber())) {
+ return chargingGun.getFullNumber();
+ }
+ if (StringUtils.hasText(chargingGun.getCode())) {
+ return chargingGun.getCode();
+ }
+ return defaultText(chargingGun.getName(), "充电桩");
+ }
+
+ private static String amount(BigDecimal value) {
+ return (value == null ? BigDecimal.ZERO : value).setScale(2, RoundingMode.HALF_UP).toPlainString() + "元";
+ }
+
+ private static String endReason(Integer endMode) {
+ if (endMode == null) {
+ return "充电结束";
+ }
+ switch (endMode) {
+ case 0:
+ return "异常终止";
+ case 1:
+ return "主动终止";
+ case 2:
+ return "满电终止";
+ case 3:
+ return "费用不足终止";
+ default:
+ return "充电结束";
+ }
+ }
+
+ private static String defaultText(String value, String defaultValue) {
+ return StringUtils.hasText(value) ? value : defaultValue;
+ }
+
+ private static String limit(String value, int maxLength) {
+ if (value == null || value.length() <= maxLength) {
+ return value;
+ }
+ return value.substring(0, maxLength);
+ }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeTemplate.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeTemplate.java
new file mode 100644
index 0000000..e18f7a7
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/WxSubscribeTemplate.java
@@ -0,0 +1,25 @@
+package com.ruoyi.order.service.impl;
+
+public enum WxSubscribeTemplate {
+
+ SERVICE_EVALUATION("LwdykWyQ2P74AR0qk-pLZR6f0oFn0TC03H1eh6Mg2tY", "pagesMine/evaluateRecord/evaluateRecord"),
+ PRICE_DROP("7nCycRUZxQ2jxAb2UaYazjp2sPZy3qYyeLqVytyhHhY", "pagesHome/pileDetail/index"),
+ CHARGE_END("aetqMnzytun-66wQ40bT-7r97hMKu9pgcnvMkRyx5yA", "pagesMap/startCharge/chargeEnd"),
+ CHARGE_START("Zz42EOeBt_hlCt50T7bRzlLtpGrZvt1idULi7m9PW_8", "pagesMap/startCharge/startCharge");
+
+ private final String templateId;
+ private final String defaultPage;
+
+ WxSubscribeTemplate(String templateId, String defaultPage) {
+ this.templateId = templateId;
+ this.defaultPage = defaultPage;
+ }
+
+ public String getTemplateId() {
+ return templateId;
+ }
+
+ public String getDefaultPage() {
+ return defaultPage;
+ }
+}
--
Gitblit v1.7.1