叫号消息订阅
This commit is contained in:
parent
fc51063872
commit
8f46ce129e
|
|
@ -1,5 +1,6 @@
|
|||
package com.czg.controller.admin;
|
||||
|
||||
import com.czg.account.dto.WxMsgSubDTO;
|
||||
import com.czg.account.dto.calltable.*;
|
||||
import com.czg.account.entity.CallConfig;
|
||||
import com.czg.account.entity.CallQueue;
|
||||
|
|
@ -173,4 +174,15 @@ public class CallTableController {
|
|||
public CzgResult<Boolean> updateConfig(@RequestBody UpdateConfigDTO configDTO) {
|
||||
return CzgResult.success(callTableService.updateConfig(StpKit.USER.getShopId(), configDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息订阅
|
||||
* @return 是否成功
|
||||
*/
|
||||
@PostMapping("subMsg")
|
||||
public CzgResult<?> subMsg(
|
||||
@Validated @RequestBody CallSubMsgDTO subMsgDTO
|
||||
) {
|
||||
return CzgResult.success(callTableService.subMsg(subMsgDTO));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.czg.account.dto.calltable;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@Data
|
||||
public class CallSubMsgDTO {
|
||||
/**
|
||||
* 店铺id
|
||||
*/
|
||||
@NotNull
|
||||
private Long shopId;
|
||||
/**
|
||||
* 队列id
|
||||
*/
|
||||
@NotNull
|
||||
private Long queueId;
|
||||
/**
|
||||
* openId
|
||||
*/
|
||||
@NotEmpty
|
||||
private String openId;
|
||||
}
|
||||
|
|
@ -21,4 +21,5 @@ public class CallTableNumDTO {
|
|||
* 号码
|
||||
*/
|
||||
private String callNum;
|
||||
private Long queueId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,6 @@ public interface CallTableService extends IService<CallTable> {
|
|||
CallConfig getConfig(Long shopId);
|
||||
|
||||
boolean updateConfig(Long shopId, UpdateConfigDTO configDTO);
|
||||
|
||||
boolean subMsg(CallSubMsgDTO subMsgDTO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.czg.resp.CzgResult;
|
|||
import com.czg.service.account.mapper.CallQueueMapper;
|
||||
import com.czg.service.account.mapper.CallTableMapper;
|
||||
import com.czg.service.account.util.FunUtil;
|
||||
import com.czg.service.account.util.WechatMiniMsgUtil;
|
||||
import com.czg.system.dto.SysParamsDTO;
|
||||
import com.czg.system.service.SysParamsService;
|
||||
import com.czg.utils.JoinQueryWrapper;
|
||||
|
|
@ -62,6 +63,8 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||
private CallQueueMapper callQueueMapper;
|
||||
@Resource
|
||||
private RabbitPublisher rabbitPublisher;
|
||||
@Resource
|
||||
private WechatMiniMsgUtil miniMsgUtil;
|
||||
|
||||
@Override
|
||||
public CallTablePage get(Long shopId, Long callTableId, Integer state) {
|
||||
|
|
@ -222,7 +225,7 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||
rabbitPublisher.printCallNumTicket(callQueue.getId());
|
||||
|
||||
return new CallTableNumDTO().setTableName(callTable.getName()).setTableNote(callTable.getNote())
|
||||
.setCallNum(callQueue.getCallNum());
|
||||
.setCallNum(callQueue.getCallNum()).setQueueId(callQueue.getId());
|
||||
}
|
||||
|
||||
private String getCallNumber(Long shopId, CallTable callTable) {
|
||||
|
|
@ -322,8 +325,8 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||
return -1;
|
||||
}
|
||||
|
||||
// wxMiniUtils.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(Integer.valueOf(callQueue.getState())),
|
||||
// callQueue.getCallNum(), current.isEmpty() ? "" : current.get(0).getCallNum(), "排号信息", callQueue.getOpenId(), false);
|
||||
miniMsgUtil.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(callQueue.getState()),
|
||||
callQueue.getCallNum(), current.isEmpty() ? "" : current.getFirst().getCallNum(), "排号信息", callQueue.getOpenId(), false);
|
||||
|
||||
CallConfig config = getConfig(shopId);
|
||||
// 临近用户提醒
|
||||
|
|
@ -334,11 +337,20 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||
.page(new Page<>(config.getNearNum(), 1)).getRecords();
|
||||
if (!nearList.isEmpty()) {
|
||||
CallQueue nearQueue = nearList.getFirst();
|
||||
// wxMiniUtils.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(Integer.valueOf(nearQueue.getState())),
|
||||
// nearQueue.getCallNum(), current.isEmpty() ? "" : current.get(0).getCallNum(), "排号信息", nearQueue.getOpenId(), true);
|
||||
miniMsgUtil.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(nearQueue.getState()),
|
||||
nearQueue.getCallNum(), current.isEmpty() ? "" : current.getFirst().getCallNum(), "排号信息", nearQueue.getOpenId(), true);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
private String getStrByState(Integer state) {
|
||||
return switch (state) {
|
||||
case -1 -> "已取消";
|
||||
case 0 -> "排队中";
|
||||
case 1 -> "已到号";
|
||||
case 3 -> "已过号";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateInfo(Long shopId, UpdateCallQueueDTO updateCallQueueDTO) {
|
||||
|
|
@ -543,4 +555,40 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||
tbCallConfig.setId(config.getId());
|
||||
return callConfigService.updateById(tbCallConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean subMsg(CallSubMsgDTO subMsgDTO) {
|
||||
CallQueue queue = callQueueService.getOne(new QueryWrapper()
|
||||
.eq(CallQueue::getShopId, subMsgDTO.getShopId())
|
||||
.eq(CallQueue::getId, subMsgDTO.getQueueId()));
|
||||
if (queue == null) {
|
||||
throw new ApiNotPrintException("您未排号请先排号");
|
||||
}
|
||||
|
||||
if (queue.getOpenId() != null && queue.getOpenId().equals(subMsgDTO.getOpenId()) && queue.getSubState() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(queue.getOpenId()) && queue.getSubState() == 1) {
|
||||
throw new ApiNotPrintException("此号码已被其他用户订阅");
|
||||
}
|
||||
|
||||
if (!subMsgDTO.getOpenId().equals(queue.getOpenId()) && queue.getSubState() == 0) {
|
||||
long count = callQueueService.queryChain()
|
||||
// .eq(CallQueue::getPhone, takeNumberDTO.getPhone())
|
||||
.eq(CallQueue::getOpenId, subMsgDTO.getOpenId())
|
||||
.eq(CallQueue::getShopId, subMsgDTO.getShopId())
|
||||
.eq(CallQueue::getCreateDay, DateUtil.date().toString("yyyy-MM-dd"))
|
||||
.in(CallQueue::getState, 0, 1)
|
||||
.ne(CallQueue::getIsPostpone, 2)
|
||||
.eq(CallQueue::getCallTableId, queue.getCallTableId()).count();
|
||||
if (count > 0) {
|
||||
throw new ApiNotPrintException("您已订阅其他号码,请勿重复订阅");
|
||||
}
|
||||
}
|
||||
|
||||
queue.setSubState(1);
|
||||
queue.setOpenId(subMsgDTO.getOpenId());
|
||||
return callQueueService.updateById(queue);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,256 @@
|
|||
package com.czg.service.account.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.czg.config.RedisCst;
|
||||
import com.czg.resp.CzgResult;
|
||||
import com.czg.service.RedisService;
|
||||
import com.czg.system.dto.SysParamsDTO;
|
||||
import com.czg.system.enums.SysParamCodeEnum;
|
||||
import com.czg.system.service.SysParamsService;
|
||||
import com.google.gson.JsonObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WechatMiniMsgUtil {
|
||||
@DubboReference(check = false)
|
||||
private SysParamsService sysParamsService;
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
@Resource
|
||||
private AliOssUtil aliOssUtil;
|
||||
|
||||
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
private static final String QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode";
|
||||
|
||||
|
||||
static LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
|
||||
linkedHashMap.put("40001", "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40003", "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID");
|
||||
linkedHashMap.put("40014", "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40037", "不合法的 template_id");
|
||||
linkedHashMap.put("43101", "用户未订阅消息");
|
||||
linkedHashMap.put("43107", "订阅消息能力封禁");
|
||||
linkedHashMap.put("43108", "并发下发消息给同一个粉丝");
|
||||
linkedHashMap.put("45168", "命中敏感词");
|
||||
linkedHashMap.put("47003", "参数错误");
|
||||
|
||||
}
|
||||
|
||||
public JSONObject sendTempMsg(String tempId, String toUserOpenId, Map<String, Object> data, String note) {
|
||||
log.info("开始发送" + note + "模板消息, 接收用户openId: {}, 消息数据: {}", toUserOpenId, data);
|
||||
String token= getAccessToken();
|
||||
|
||||
JSONObject object1=new JSONObject();
|
||||
|
||||
object1.put("template_id", tempId);
|
||||
object1.put("touser", toUserOpenId);
|
||||
object1.put("data",data);
|
||||
|
||||
object1.put("miniprogram_state","trial");
|
||||
object1.put("lang","zh_CN");
|
||||
|
||||
String response= HttpRequest.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=".concat(token)).body(object1.toString()).execute().body();
|
||||
log.info("微信模板消息发送成功,相应内容:{}",response);
|
||||
JSONObject resObj= JSONObject.parseObject(response);
|
||||
if(ObjectUtil.isNotEmpty(resObj)&&ObjectUtil.isNotNull(resObj)&&"0".equals(resObj.get("errcode")+"")){
|
||||
return resObj;
|
||||
}
|
||||
|
||||
throw new RuntimeException(linkedHashMap.getOrDefault(resObj.get("errcode") + "", "未知错误"));
|
||||
}
|
||||
|
||||
public void sendCurrentOrNearCallMsg(String shopName, String state, String callNum, String currentNum, String note, String openId, boolean isNear) {
|
||||
CzgResult<SysParamsDTO> callNear = sysParamsService.getParamsByCode("wx_mini_msg_call_near");
|
||||
CzgResult<SysParamsDTO> callCurrent = sysParamsService.getParamsByCode("wx_mini_msg_call_current");
|
||||
|
||||
Map<String, Object> data = new HashMap<String, Object>() {{
|
||||
put("thing1", new HashMap<String, Object>() {{
|
||||
put("value", shopName);
|
||||
}});
|
||||
put("phrase2", new HashMap<String, Object>() {{
|
||||
put("value", state);
|
||||
}});
|
||||
put("character_string3", new HashMap<String, Object>() {{
|
||||
put("value", callNum);
|
||||
}});
|
||||
put("character_string4", new HashMap<String, Object>() {{
|
||||
put("value", currentNum);
|
||||
}});
|
||||
put("thing5", new HashMap<String, Object>() {{
|
||||
put("value", note);
|
||||
}});
|
||||
}};
|
||||
try {
|
||||
sendTempMsg(isNear ? callNear.getData().getParamValue() : callCurrent.getData().getParamValue(), openId, data, "排队到号");
|
||||
} catch (Exception e) {
|
||||
log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPassCallMsg(String shopName, String state, String callNum, String currentNum, String note, String openId) {
|
||||
CzgResult<SysParamsDTO> callPass = sysParamsService.getParamsByCode("wx_mini_msg_call_pass");
|
||||
|
||||
Map<String, Object> data = new HashMap<String, Object>() {{
|
||||
put("thing1", new HashMap<String, Object>() {{
|
||||
put("value", shopName);
|
||||
}});
|
||||
put("character_string2", new HashMap<String, Object>() {{
|
||||
put("value", callNum);
|
||||
}});
|
||||
put("character_string3", new HashMap<String, Object>() {{
|
||||
put("value", currentNum);
|
||||
}});
|
||||
put("phrase4", new HashMap<String, Object>() {{
|
||||
put("value", state);
|
||||
}});
|
||||
put("thing5", new HashMap<String, Object>() {{
|
||||
put("value", note);
|
||||
}});
|
||||
}};
|
||||
try {
|
||||
sendTempMsg(callPass.getData().getParamValue(), openId, data, "过号");
|
||||
} catch (Exception e) {
|
||||
log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getAccountOpenId(String code) {
|
||||
CzgResult<SysParamsDTO> wxAccountAppId = sysParamsService.getParamsByCode("wx_account_app_id");
|
||||
CzgResult<SysParamsDTO> wxAccountSecrete = sysParamsService.getParamsByCode("wx_account_secrete");
|
||||
String accountAppId = wxAccountAppId.getData().getParamValue();
|
||||
String accountSecrete = wxAccountSecrete.getData().getParamValue();
|
||||
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?";
|
||||
Map<String, Object> requestUrlParam = new HashMap<>();
|
||||
// https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
|
||||
//小程序appId
|
||||
requestUrlParam.put("appid", accountAppId);
|
||||
//小程序secret
|
||||
requestUrlParam.put("secret", accountSecrete);
|
||||
//小程序端返回的code
|
||||
requestUrlParam.put("code", code);
|
||||
//默认参数
|
||||
requestUrlParam.put("grant_type", "authorization_code");
|
||||
log.info("微信获取openid请求报文:{}", requestUrlParam);
|
||||
//发送post请求读取调用微信接口获取openid用户唯一标识
|
||||
String resp = HttpUtil.post(requestUrl, requestUrlParam);
|
||||
log.info("响应报文{}", resp);
|
||||
return JSONObject.parseObject(resp).getString("openid");
|
||||
}
|
||||
|
||||
//获取小程序token
|
||||
private String getAccessToken() {
|
||||
CzgResult<SysParamsDTO> wxMiniAppId = sysParamsService.getParamsByCode("wx_mini_app_id");
|
||||
CzgResult<SysParamsDTO> wxMiniSecrete = sysParamsService.getParamsByCode("wx_mini_secrete");
|
||||
String appId = wxMiniAppId.getData().getParamValue();
|
||||
String secrete = wxMiniSecrete.getData().getParamValue();
|
||||
String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s", TOKEN_URL, appId, secrete);
|
||||
String response = HttpUtil.get(url);
|
||||
JSONObject jsonResponse = JSONObject.parseObject(response);
|
||||
if (!jsonResponse.containsKey("access_token")) {
|
||||
throw new RuntimeException("Failed to retrieve access token: " + response);
|
||||
}
|
||||
return jsonResponse.getString("access_token");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 小程序码 跳转对应页面
|
||||
*
|
||||
*/
|
||||
public String getFetchQrCode(Map<String, Object> params) throws Exception {
|
||||
String url = aliOssUtil.upload(fetchQrCode(params), aliOssUtil.getPath("shopVip", "png"));
|
||||
redisService.set(RedisCst.SHOP_VIP_CODE + params.get("shopId"), url);
|
||||
return url;
|
||||
}
|
||||
|
||||
//生成页面地址
|
||||
private InputStream fetchQrCode(Map<String, Object> params) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
//路径
|
||||
jsonObject.addProperty("path", sysParamsService.getSysParamValue(SysParamCodeEnum.WX_MINI_VIP_URL.getCode()) + "?shopId=" + params.get("shopId"));
|
||||
//是否需要透明底色,为 true 时,生成透明底色的小程序码
|
||||
jsonObject.addProperty("is_hyaline", true);
|
||||
//正式版为 release,体验版为 trial,开发版为 develop
|
||||
if (params.containsKey("env_version")) {
|
||||
jsonObject.addProperty("env_version", "trial");
|
||||
}
|
||||
String accessToken = getAccessToken();
|
||||
String url = String.format("%s?access_token=%s", QR_CODE_URL, accessToken);
|
||||
return HttpUtil.createPost(url)
|
||||
.body(jsonObject.toString(), "application/json")
|
||||
.execute()
|
||||
.bodyStream();
|
||||
}
|
||||
|
||||
|
||||
public JSONObject getSession(String code) {
|
||||
CzgResult<SysParamsDTO> wxMiniSecrete = sysParamsService.getParamsByCode("wx_mini_secrete");
|
||||
CzgResult<SysParamsDTO> wxMiniAppId = sysParamsService.getParamsByCode("wx_mini_app_id");
|
||||
String appId = wxMiniAppId.getData().getParamValue();
|
||||
String secrete = wxMiniSecrete.getData().getParamValue();
|
||||
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
|
||||
Map<String, Object> requestUrlParam = new HashMap<>();
|
||||
// https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
|
||||
//小程序appId
|
||||
requestUrlParam.put("appid", appId);
|
||||
//小程序secret
|
||||
requestUrlParam.put("secret", secrete);
|
||||
//小程序端返回的code
|
||||
requestUrlParam.put("js_code", code);
|
||||
//默认参数
|
||||
requestUrlParam.put("grant_type", "authorization_code");
|
||||
//发送post请求读取调用微信接口获取openid用户唯一标识
|
||||
String resp = HttpUtil.post(requestUrl, requestUrlParam);
|
||||
JSONObject jsonObject = JSON.parseObject(resp);
|
||||
log.info("微信获取openid响应报文:{}", resp);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
public String getSessionKey(String code, String key) {
|
||||
JSONObject session = getSession(code);
|
||||
String info = session.getString(key);
|
||||
if (StrUtil.isBlank(info)) {
|
||||
throw new RuntimeException(key + "获取失败");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public String getSessionKeyOrOpenId(String code, boolean isAccount) {
|
||||
return getSessionKey(code, "openid");
|
||||
}
|
||||
|
||||
public static String decrypt(String sessionKey, @NotBlank(message = "数据不能为空") String encryptedData, String iv) {
|
||||
// Base64 解码
|
||||
byte[] keyBytes = Base64.decode(sessionKey);
|
||||
byte[] encryptedBytes = Base64.decode(encryptedData);
|
||||
byte[] ivBytes = Base64.decode(iv);
|
||||
|
||||
// 使用 Hutool 进行 AES-CBC 解密
|
||||
AES aes = new AES("CBC", "PKCS5Padding", keyBytes, ivBytes);
|
||||
byte[] decryptedBytes = aes.decrypt(encryptedBytes);
|
||||
|
||||
return new String(decryptedBytes, java.nio.charset.StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue