1. 微信服务添加用户相关接口

2. 微信服务添加云平台中订单相关接口
3. 云平台服务完善优化MQTT启动相关流程
4. 云平台服务开启多线程处理MQTT业务消息
5. 云平台服务添加换电新流程中请求订单的处理逻辑
6. 云平台服务换电新流程添加 MQTT消息测试
This commit is contained in:
Administrator 2024-12-20 14:24:39 +08:00
parent 7011024e46
commit dda394c898
44 changed files with 1252 additions and 187 deletions

View File

@ -71,4 +71,17 @@ public interface HDConstant {
String HD_STATION_SECRET_KEY_AES_PREFIX = "hd:station:secretKey:aes:";
/**
* 微信服务请求头中权限验证字段
*/
String WECHAT_SERVER_AUTHORIZATION_KEY = "WXUID";
/**
* 微信登录缓存数据前缀
*/
String openidPrefix = "hd:wechat:login:openid:";
String unionidPrefix = "hd:wechat:login:unionid:";
String sessionKeyPrefix = "hd:wechat:login:sessionKey:";
}

View File

@ -1,7 +1,7 @@
package com.evotech.hd.wechat.dao;
package com.evotech.hd.common.core.dao.wechat;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.wechat.entity.WechatUser;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
/**
* @author zrb

View File

@ -12,6 +12,8 @@ import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
@ -30,30 +32,34 @@ public class OrderSwapBatteryPre implements Serializable {
private Integer pkId;
@Schema(description = "来源1-小程序2-云端3-站端", requiredMode = RequiredMode.REQUIRED)
private Integer from;
@NotNull
private Integer source;
@Schema(description = "来源是站端时记录发送Id其他来源不需要", hidden = true)
private String sourceId;
@Schema(description = "预约人编码")
@Schema(description = "预约人编码", requiredMode = RequiredMode.REQUIRED)
private String ucode;
@Schema(description = "预约人姓名")
@Schema(description = "预约人姓名", requiredMode = RequiredMode.REQUIRED)
private String uname;
@Schema(description = "手机号码")
@Schema(description = "手机号码", requiredMode = RequiredMode.REQUIRED)
private String phone;
@Schema(description = "车牌号")
@Schema(description = "车牌号", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String plateNum;
@Schema(description = "换电站编码")
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String stationCode;
@Schema(description = "换电站名称")
@Schema(description = "换电站名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String stationName;
@Schema(description = "预约时间", hidden = true)
@Schema(description = "预约时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date reservationTime;

View File

@ -1,4 +1,4 @@
package com.evotech.hd.cloud.entity.request;
package com.evotech.hd.common.core.entity.cloud;
import java.util.Date;
@ -23,6 +23,9 @@ public class PageListSwapOrderRequest extends BasePageRequest {
@Schema(description = "车牌号")
private String plateNum;
@Schema(description = "多车牌号")
private String plateNums;
@Schema(description = "订单时间开始", example = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@ -1,4 +1,4 @@
package com.evotech.hd.wechat.entity;
package com.evotech.hd.common.core.entity.wechat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
@ -20,13 +20,14 @@ import lombok.Setter;
*/
@Getter
@Setter
@TableName("yt_t_wechat_user")
@Schema(name = "WechatUser", description = "小程序用户信息")
@TableName("hd_wechat.yt_t_wechat_user")
@Schema(name = "小程序用户信息")
public class WechatUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(description = "id", hidden = true)
private Integer pkId;
@Schema(description = "微信用户id")
@ -46,9 +47,21 @@ public class WechatUser implements Serializable {
@Schema(description = "用户头像")
private String avatarUrl;
@Schema(description = "名称")
private String name;
@Schema(description = "用户性别1-男2-女")
private Boolean gender;
private Integer gender;
@Schema(description = "类型1-独立账户2-公司员工子账户")
private Integer type;
@Schema(description = "父账户编码")
private String pcode;
@Schema(description = "父账户名称")
private String pname;
@Schema(description = "创建人", hidden = true)
private String creater;

View File

@ -0,0 +1,52 @@
package com.evotech.hd.cloud.controller;
import java.util.List;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.cloud.entity.request.PageListWechatUserRequest;
import com.evotech.hd.cloud.service.WechatUserService;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@Tag(name = "微信用户")
@RestController
@RequestMapping("/wechat/user")
@ApiSupport(order = 11)
public class WechatUserController {
@Resource
private WechatUserService wechatUserService;
@Operation(summary = "查询")
@GetMapping("/list")
public Result<List<WechatUser>> list(@ParameterObject PageListWechatUserRequest plwur) {
return wechatUserService.list(plwur);
}
@Operation(summary = "更新手机号/名称")
@PostMapping("/alterphone")
public Result<Integer> alterPhone(String wuid, String phone, String name) {
return wechatUserService.alterPhone(wuid, phone, name);
}
@Operation(summary = "关联公司")
@PostMapping("/companyrelation")
public Result<Integer> companyRelation(String wuid, String pcode, String pname) {
return wechatUserService.companyRelation(wuid, pcode, pname);
}
}

View File

@ -21,7 +21,7 @@ import jakarta.annotation.Resource;
import jakarta.validation.Valid;
@Tag(name = "交易详情")
@Tag(name = "充值订单")
@ApiSupport(order = 25)
@RestController
@RequestMapping("/order/recharge")

View File

@ -8,12 +8,12 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.cloud.entity.request.PageListSwapOrderRequest;
import com.evotech.hd.cloud.service.OrderSwapBatteryService;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import com.evotech.hd.common.core.entity.cloud.PageListSwapOrderRequest;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
@ -51,8 +51,8 @@ public class OrderSwapBatteryController {
@Operation(summary = "查询预约")
@GetMapping("/pre/list")
@ApiOperationSupport(order = 3)
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String userId) {
return orderSwapBatteryService.listPre(plateNum, status, userId);
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String ucode, String stationCode) {
return orderSwapBatteryService.listPre(plateNum, status, ucode, stationCode);
}

View File

@ -0,0 +1,81 @@
package com.evotech.hd.cloud.controller.test;
import java.util.Date;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.cloud.controller.test.service.MqttTestUtil;
import com.evotech.hd.cloud.mqtt.enums.MqttMessageTypeEnum;
import com.evotech.hd.cloud.mqtt.enums.RequestFunctionTypesEnum;
import com.evotech.hd.cloud.mqtt.enums.StateFunctionTypesEnum;
import com.evotech.hd.cloud.mqtt.message.MqttMessageHeader;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.order.OrderByPlateNumReq;
import com.evotech.hd.cloud.mqtt.message.dto.newer.state.OrderStatus;
import com.evotech.hd.common.core.entity.Result;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@Tag(name = "Mqtt消息测试")
@ApiSupport(order = 700)
@RestController
@RequestMapping("/test/mqtt")
public class MqttMessageTestController {
public static String topicPrefix = "ZZHD/{}/S2M/{}";
@Resource
private MqttTestUtil mqttTestUtil;
@Operation(summary = "车牌号生成订单")
@PostMapping("/orderByPlateNum")
@ApiOperationSupport(order = 1)
public Result<String> orderByPlateNum(String plateNum, String stationCode) {
OrderByPlateNumReq req = new OrderByPlateNumReq();
req.setOrderRequestId("O" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_FORMATTER));
req.setPlateNum(plateNum);
req.setStationCode(stationCode);
JSONObject jo = JSONUtil.parseObj(req);
String topic = mqttTestUtil.getTopic(stationCode, MqttMessageTypeEnum.REQUEST.getType());
MqttMessageHeader header = mqttTestUtil.getHeader(RequestFunctionTypesEnum.FUN_ORDERBYPLATENUM.getFunction());
mqttTestUtil.publishMessage(stationCode, jo, topic, header);
return new Result<String>().success("OK");
}
@Operation(summary = "反馈订单状态")
@PostMapping("/orderStatus")
@ApiOperationSupport(order = 2)
public Result<String> orderStatus(@ParameterObject OrderStatus orderStatus, String stationCode) {
JSONObject jo = JSONUtil.parseObj(orderStatus);
String topic = mqttTestUtil.getTopic(stationCode, MqttMessageTypeEnum.STATE.getType());
MqttMessageHeader header = mqttTestUtil.getHeader(StateFunctionTypesEnum.FUN_ORDERSTATUS.getFunction());
mqttTestUtil.publishMessage(stationCode, jo, topic, header);
return new Result<String>().success("OK");
}
}

View File

@ -1,9 +1,7 @@
package com.evotech.hd.cloud.mqtt.config;
package com.evotech.hd.cloud.controller.test.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
@ -11,34 +9,25 @@ import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@Configuration
@Service
@Slf4j
public class MqttConfig {
@Resource
private Environment env;
private MqttClient cloudClient;
public class MqttConfigService {
@Bean
MqttProperties mpProperties() {
return new MqttProperties();
}
public static String mqttUrl = "tcp://192.168.5.210:1883";
@Resource
private MqttTestCallback mqttTestCallback;
/**
* 客户端连接服务端
*/
public void connect() {
int port = env.getProperty("server.port", Integer.class) == null?8080:env.getProperty("server.port", Integer.class);
public MqttClient connect() {
String address = "127.0.0.1";
try {
address = InetAddress.getLocalHost().getHostAddress();
@ -46,14 +35,15 @@ public class MqttConfig {
address = "192.168.5.213";
e.printStackTrace();
}
String clientId = address + "-" + port + "-" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER);
String clientId = "testClient" + "-" + address;
try {
cloudClient = new MqttClient(mpProperties().getUrl(), clientId, new MemoryPersistence());
MqttClient testClient = new MqttClient(mqttUrl, clientId, new MemoryPersistence());
//连接设置
MqttConnectOptions options = connectOptions();
//设置回调
cloudClient.setCallback(new MyMqttCallback());
cloudClient.connect(options);
testClient.setCallback(mqttTestCallback);
testClient.connect(options);
return testClient;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("连接MQTT服务出错" + e.getMessage());
@ -74,20 +64,20 @@ public class MqttConfig {
*/
options.setCleanSession(true);
//设置连接用户名
options.setUserName(mpProperties().getUsername());
options.setUserName("test");
//设置连接密码
options.setPassword(mpProperties().getPassword().toCharArray());
options.setPassword("test1234".toCharArray());
//设置超时时间单位为秒
options.setConnectionTimeout(mpProperties().getConnectionTimeout());
options.setConnectionTimeout(30);
//设置心跳时间 单位为秒
options.setKeepAliveInterval(mpProperties().getKeepAliveInterval());
options.setKeepAliveInterval(60);
//设置遗嘱消息的话题若客户端和服务器之间的连接意外断开服务器将发布客户端的遗嘱信息
// options.setWill("willTopic", (clientId + "与服务器断开连接").getBytes(), 0, false);
return options;
}
public void publish(String message, String topic) {
public void publish(MqttClient testClient, String message, String topic) {
MqttMessage mqttMessage = new MqttMessage();
/**
* Qos 0 : 这个消息最多发送一次不能被持久化到磁盘不能通过网络被传递一般内部消息转换
@ -100,7 +90,7 @@ public class MqttConfig {
// 消息内容
mqttMessage.setPayload(message.getBytes());
// 主题的目的地用于发布/订阅信息
MqttTopic mqttTopic = cloudClient.getTopic(topic);
MqttTopic mqttTopic = testClient.getTopic(topic);
// 提供一种机制来跟踪消息的传递进度
// 用于在以非阻塞方式在后台运行执行发布是跟踪消息的传递进度
MqttDeliveryToken token;
@ -112,6 +102,8 @@ public class MqttConfig {
} catch (MqttException e) {
e.printStackTrace();
}
}
@ -119,13 +111,13 @@ public class MqttConfig {
* 订阅主题
*/
public void subscribe(String topic) {
try {
cloudClient.subscribe(topic, mpProperties().getQos());
log.info("\r\n=====>>>MQTT订阅主题 {} 成功...", topic);
} catch (MqttException e) {
e.printStackTrace();
log.error("\r\n=====>>>MQTT订阅主题 {} 失败。。。", topic);
}
// try {
// cloudClient.subscribe(topic, mpProperties().getQos());
// log.info("\r\n=====>>>MQTT订阅主题 {} 成功...", topic);
// } catch (MqttException e) {
// e.printStackTrace();
// log.error("\r\n=====>>>MQTT订阅主题 {} 失败。。。", topic);
// }
}

View File

@ -0,0 +1,52 @@
package com.evotech.hd.cloud.controller.test.service;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class MqttTestCallback implements MqttCallbackExtended {
/**
* 建立连接后
*/
@Override
public void connectComplete(boolean reconnect, String serverURI) {
log.info("\r\n=====>>>MQTT测试客户端连接成功{}", serverURI);
}
/**
* 与服务器断开的回调
*/
@Override
public void connectionLost(Throwable cause) {
log.error("\r\n=====>>>MQTT与服务器断开连接{}", cause.getMessage());
cause.printStackTrace();
}
/**
* 消息到达的回调
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("\r\n=====>>>MQTT接收到消息主题{}---{}", topic, message);
}
/**
* 消息发布成功的回调
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
log.info("\r\n=====>>>MQTT消息发送成功id{}", token.getMessageId());
}
}

View File

@ -0,0 +1,79 @@
package com.evotech.hd.cloud.controller.test.service;
import java.util.Date;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.springframework.stereotype.Component;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import com.evotech.hd.cloud.mqtt.message.MqttMessageHeader;
import com.evotech.hd.cloud.mqtt.message.MyMqttMessage;
import com.evotech.hd.cloud.mqtt.message.handle.MessageUtilService;
import com.evotech.hd.common.core.utils.SnowflakeUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
@Component
public class MqttTestUtil {
@Resource
private MqttConfigService mqttService;
@Resource
private MessageUtilService messageUtilService;
public void publishMessage(String stationCode, JSONObject jo, String topic, MqttMessageHeader header) {
// 1. 获取AES
JSONObject aesJo = messageUtilService.getAESKey(stationCode);
SymmetricCrypto aes = new AES(Mode.CBC, Padding.PKCS5Padding, aesJo.getStr("aesSecretKey").getBytes(), aesJo.getStr("aesIv").getBytes());
// 2. 数据
MyMqttMessage message = new MyMqttMessage();
message.setHeader(header);
message.setDataBody(jo);
String encrypt = aes.encryptBase64(JSONUtil.toJsonStr(message));
// 3. 发送MQTT消息
MqttClient testClient = mqttService.connect();
mqttService.publish(testClient, encrypt, topic);
// 4. 断开连接
try {
testClient.disconnect(1000L);
testClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public MqttMessageHeader getHeader(String funType) {
MqttMessageHeader header = new MqttMessageHeader();
header.setVersion("V1");
header.setIndex(SnowflakeUtil.getIdStr());
header.setTimeStamp(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMATTER));
header.setFunction(funType);
return header;
}
public String getTopic(String stationCode, String messageType) {
MessageTopic topic = new MessageTopic();
topic.setDataDirection("S2M");
topic.setStationCode(stationCode);
topic.setMessageType(messageType);
return topic.toString();
}
}

View File

@ -0,0 +1,36 @@
package com.evotech.hd.cloud.entity.request;
import com.evotech.hd.common.core.entity.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@Schema(name = "查询微信用户请求参数", hidden = true)
@EqualsAndHashCode(callSuper=false)
public class PageListWechatUserRequest extends BasePageRequest {
@Schema(description = "微信用户id")
private String wuid;
@Schema(description = "手机号")
private String phoneNumber;
@Schema(description = "用户昵称")
private String nickName;
@Schema(description = "用户性别1-男2-女")
private Integer gender;
@Schema(description = "名称")
private String name;
@Schema(description = "类型1-独立账户2-公司员工子账户")
private Integer type;
@Schema(description = "父账户编码")
private String pcode;
}

View File

@ -1,54 +0,0 @@
package com.evotech.hd.cloud.mqtt;
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evotech.hd.cloud.dao.BatteryStationDao;
import com.evotech.hd.cloud.mqtt.config.MqttConfig;
import com.evotech.hd.common.core.entity.cloud.BatteryStation;
import jakarta.annotation.Resource;
@Component
@Order(value = 20)
public class MqttInit implements ApplicationRunner {
@Resource
private MqttConfig mqttConfig;
@Resource
private BatteryStationDao batteryStationDao;
private String[] mqttMessageTypeArr = {"state", "event", "request", "response", "keepalive", "encryptKeyReq"};
@Override
public void run(ApplicationArguments args) throws Exception {
// 连接mqtt
mqttConfig.connect();
// 订阅主题
List<BatteryStation> stationList = batteryStationDao.selectList(new QueryWrapper<BatteryStation>().eq("del_flag", 0));
if (stationList.isEmpty()) {
return;
}
List<String> stationCodeList = stationList.stream().map(i -> i.getCode()).toList();
for (int i = 0; i < stationCodeList.size(); i++) {
String stationCode = stationCodeList.get(i);
String topic = "ZZHD/" + stationCode + "/S2M/";
for (int j = 0; j < mqttMessageTypeArr.length; j++) {
mqttConfig.subscribe(topic + mqttMessageTypeArr[j]);
}
}
}
}

View File

@ -0,0 +1,117 @@
package com.evotech.hd.cloud.mqtt.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@Component
@Order(value = 20)
@Slf4j
public class MqttConnectInit implements ApplicationRunner {
// 用来存放客户端 ConcurrentMap是线程安全的
static ConcurrentHashMap<String, MqttClient> mqttClientMap = new ConcurrentHashMap<>();
@Resource
private MyMqttCallback mqttCallback;
@Resource
private MqttProperties mpProperties;
@Override
public void run(ApplicationArguments args) throws Exception {
connect();
}
/**
* 客户端连接服务端
*/
public MqttClient connect() {
String address = "127.0.0.1";
try {
address = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
address = "192.168.5.213";
e.printStackTrace();
}
// String clientId = address + "-" + port + "-" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER);
String clientId = "cloudClient" + "-" + address + "-" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER);;
try {
MqttClient cloudClient = new MqttClient(mpProperties.getUrl(), clientId, new MemoryPersistence());
//连接设置
MqttConnectOptions options = connectOptions();
//设置回调
cloudClient.setCallback(mqttCallback);
cloudClient.connect(options);
mqttClientMap.put("cloudClient", cloudClient);
return cloudClient;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("连接MQTT服务出错" + e.getMessage());
}
}
private MqttConnectOptions connectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
/**
* 1. 这个类是代理类其实是MqttAsyncClient干活
* 使用无阻塞运行在后台的轻量级链接server基于Tcp包含了ssl
* 2. 为了确保消息通过网络被送达和重启客户端带有质量标志的消息要求被保存直到重新被送达
* mqtt提供了一种自己持久化机制来保存这些消息MqttDefaultFilePersistence是默认方式
* 如果为null则为瞬时消息保存在内存中MqttClientPersistence可以自己现实接口;
* 3. 如果在connecting中MqttConnectOptions.setCleanSession(boolean)这个flag
* 为true也就说如果client掉线disconnect下次重连将清空内存persistence消息
* 如果为false就会使用持久化机制去重传
*/
options.setCleanSession(true);
//设置连接用户名
options.setUserName(mpProperties.getUsername());
//设置连接密码
options.setPassword(mpProperties.getPassword().toCharArray());
//设置超时时间单位为秒
options.setConnectionTimeout(mpProperties.getConnectionTimeout());
//设置心跳时间 单位为秒
options.setKeepAliveInterval(mpProperties.getKeepAliveInterval());
//设置遗嘱消息的话题若客户端和服务器之间的连接意外断开服务器将发布客户端的遗嘱信息
// options.setWill("willTopic", (clientId + "与服务器断开连接").getBytes(), 0, false);
// 自动重连
options.setAutomaticReconnect(true);
return options;
}
@PreDestroy
public void PreDestroyComplete() {
log.info("===>>>程序要关闭了...");
MqttClient cloudClient = MqttConnectInit.mqttClientMap.get("cloudClient");
try {
cloudClient.disconnect();
cloudClient.close();
log.info("=====>>>关闭了MQTT");
} catch (MqttException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,41 @@
package com.evotech.hd.cloud.mqtt.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.springframework.stereotype.Component;
@Component
public class MqttPublishMessage {
public void publish(String message, String topic) {
MqttClient cloudClient = MqttConnectInit.mqttClientMap.get("cloudClient");
MqttMessage mqttMessage = new MqttMessage();
/**
* Qos 0 : 这个消息最多发送一次不能被持久化到磁盘不能通过网络被传递一般内部消息转换
* Qos 1 : 这个消息至少发送一次能被重传能持久化能通过网络传递需要实现MqttConnectOptions中的持久化否则挂了以后不能重传
* Qos 2 : 这个消息精准只发一次能持久化能通过网络传递客户端和服务器都会收到消息确认
*/
mqttMessage.setQos(2);
// 是否保留最后一条消息
mqttMessage.setRetained(false);
// 消息内容
mqttMessage.setPayload(message.getBytes());
// 主题的目的地用于发布/订阅信息
MqttTopic mqttTopic = cloudClient.getTopic(topic);
// 提供一种机制来跟踪消息的传递进度
// 用于在以非阻塞方式在后台运行执行发布是跟踪消息的传递进度
MqttDeliveryToken token;
try {
// 将指定消息发布到主题但不等待消息传递完成返回的token可用于跟踪消息的传递状态
// 一旦此方法干净地返回消息就已被客户端接受发布当连接可用将在后台完成消息传递
token = mqttTopic.publish(mqttMessage);
token.waitForCompletion();
} catch (MqttException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,56 @@
package com.evotech.hd.cloud.mqtt.config;
import java.util.List;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evotech.hd.cloud.dao.BatteryStationDao;
import com.evotech.hd.common.core.entity.cloud.BatteryStation;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@Service
@EnableAsync
@Slf4j
public class MqttSubscribeService {
static String[] mqttMessageTypeArr = {"state", "event", "request", "response", "keepalive", "encryptKeyReq"};
@Resource
private BatteryStationDao batteryStationDao;
@Resource
private MqttProperties mpProperties;
@Async("taskExecutor")
public void subscribe(MqttClient cloudClient) {
log.info("\r\n=====>>>MQTT开始订阅主题>>>");
// 订阅主题
List<BatteryStation> stationList = batteryStationDao.selectList(new QueryWrapper<BatteryStation>().eq("del_flag", 0));
if (stationList.isEmpty()) {
return;
}
List<String> stationCodeList = stationList.stream().map(i -> i.getCode()).toList();
for (int i = 0; i < stationCodeList.size(); i++) {
String stationCode = stationCodeList.get(i);
String topicPrefix = "YTHD/" + stationCode + "/S2M/";
for (int j = 0; j < mqttMessageTypeArr.length; j++) {
String topic = topicPrefix + mqttMessageTypeArr[j];
try {
cloudClient.subscribe(topic, mpProperties.getQos());
log.info("\r\n=====>>>MQTT订阅主题 {} 成功...", topic);
} catch (MqttException e) {
e.printStackTrace();
log.error("\r\n=====>>>MQTT订阅主题 {} 失败。。。", topic);
}
}
}
log.info("\r\n=====>>>MQTT订阅完成<<<===");
}
}

View File

@ -2,12 +2,12 @@ package com.evotech.hd.cloud.mqtt.config;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import com.evotech.hd.cloud.mqtt.message.handle.MqttMessageHandle;
import com.evotech.hd.cloud.mqtt.message.handle.MqttMessageHandleService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@ -16,10 +16,13 @@ import lombok.extern.slf4j.Slf4j;
public class MyMqttCallback implements MqttCallbackExtended {
@Resource
private MqttMessageHandle messageHandle;
private MqttMessageHandleService messageService;
@Resource
private MqttSubscribeService mqttSubscribeService;
/**
@ -28,7 +31,14 @@ public class MyMqttCallback implements MqttCallbackExtended {
@Override
public void connectComplete(boolean reconnect, String serverURI) {
log.info("\r\n=====>>>MQTT连接成功{}", serverURI);
// 在回调中订阅这样自动重连后还会自动订阅
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
MqttClient cloudClient = MqttConnectInit.mqttClientMap.get("cloudClient");
mqttSubscribeService.subscribe(cloudClient);
}
@ -48,12 +58,12 @@ public class MyMqttCallback implements MqttCallbackExtended {
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("\r\n=====>>>MQTT接收到消息主题{}---{}", topic, message);
String[] topicArr = topic.split("/");
if ("ZZHD".equals(topicArr[0]) && topicArr.length == 4) {
if ("YTHD".equals(topicArr[0]) && topicArr.length == 4) {
MessageTopic mt = new MessageTopic();
mt.setDataDirection(topicArr[2]);
mt.setMessageType(topicArr[3]);
mt.setStationCode(topicArr[1]);
messageHandle.handle(mt, message);
messageService.handle(mt, message);
}
}

View File

@ -11,7 +11,7 @@ public class MessageTopic implements Serializable {
private static final long serialVersionUID = 7238663818955151985L;
private String businessType = "ZZHD";
private String businessType = "YTHD";
private String stationCode;

View File

@ -2,6 +2,8 @@ package com.evotech.hd.cloud.mqtt.message.dto.newer.state;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
/**
@ -14,6 +16,7 @@ public class OrderStatus {
private Integer status;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date statusTime;
private OrderStatusData statusDate;

View File

@ -5,7 +5,7 @@ import java.util.Date;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Service;
import com.evotech.hd.cloud.mqtt.config.MqttConfig;
import com.evotech.hd.cloud.mqtt.config.MqttPublishMessage;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import cn.hutool.core.date.DateUtil;
@ -20,14 +20,14 @@ public class KeepaliveMessageService {
@Resource
private MqttConfig mqttConfig;
private MqttPublishMessage publishMessage;
public void keepAlive(MessageTopic topic, MqttMessage message) {
String msg = DateUtil.formatDateTime(new Date());
String tp = topic.toString().replace("S2M", "M2S");
mqttConfig.publish(msg, tp);
publishMessage.publish(msg, tp);
}
}

View File

@ -12,7 +12,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evotech.hd.cloud.dao.BatteryStationSecretKeyDao;
import com.evotech.hd.cloud.entity.BatteryStationSecretKey;
import com.evotech.hd.cloud.entity.MessageMqtt;
import com.evotech.hd.cloud.mqtt.config.MqttConfig;
import com.evotech.hd.cloud.mqtt.config.MqttPublishMessage;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import com.evotech.hd.cloud.mqtt.message.MqttMessageHeader;
import com.evotech.hd.cloud.mqtt.message.MyMqttMessage;
@ -47,7 +47,7 @@ public class MessageUtilService {
@Resource
private BatteryStationSecretKeyDao batteryStationSecretKeyDao;
@Resource
private MqttConfig mqttConfig;
private MqttPublishMessage publishMessage;
@ -83,7 +83,7 @@ public class MessageUtilService {
public void publishRSAMessage(MessageTopic topic, String msg, String publicKey) {
RSA rsa = SecureUtil.rsa(null, publicKey);
String encrypt = rsa.encryptBase64(msg, KeyType.PublicKey);
mqttConfig.publish(encrypt, topic.toString());
publishMessage.publish(encrypt, topic.toString());
}
/**
@ -102,7 +102,7 @@ public class MessageUtilService {
message.setDataBody(dataBody);
String encrypt = aes.encryptBase64(JSONUtil.toJsonStr(message));
// 3. 发送MQTT消息
mqttConfig.publish(encrypt, topic.toString());
publishMessage.publish(encrypt, topic.toString());
}
@ -131,9 +131,9 @@ public class MessageUtilService {
public JSONObject decryptAesMessage(MessageTopic topic, MqttMessage message) {
JSONObject aesJo = getAESKey(topic.getStationCode());
SymmetricCrypto aes = new AES(Mode.CBC, Padding.PKCS5Padding, aesJo.getStr("aesSecretKey").getBytes(), aesJo.getStr("aesIv").getBytes());
byte[] decrypt = aes.decrypt(message.getPayload());
String decrypt = aes.decryptStr(new String(message.getPayload()));
return JSONUtil.parseObj(new String(decrypt));
return JSONUtil.parseObj(decrypt);
}
@ -171,7 +171,7 @@ public class MessageUtilService {
public JSONObject setAesKey(String stationCode) {
SecretKey key = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue());
String base64KeyStr = Base64.getEncoder().encodeToString(key.getEncoded());
String iv = RandomUtil.randomString(12);
String iv = RandomUtil.randomString(16);
batteryStationSecretKeyDao.delete(new QueryWrapper<BatteryStationSecretKey>().eq("type", 2).eq("station_code", stationCode));
BatteryStationSecretKey bssk = new BatteryStationSecretKey();
bssk.setStationCode(stationCode);

View File

@ -1,7 +1,7 @@
package com.evotech.hd.cloud.mqtt.message.handle;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.evotech.hd.cloud.mqtt.enums.MqttMessageTypeEnum;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
@ -13,8 +13,8 @@ import jakarta.annotation.Resource;
/**
* Mqtt消息处理
*/
@Component
public class MqttMessageHandle {
@Service
public class MqttMessageHandleService {
@Resource
private EncryptKeyReqMessageService encryptKeyReqService;
@ -51,7 +51,7 @@ public class MqttMessageHandle {
// 3.2 分类处理
// 3.2.1 state
if (MqttMessageTypeEnum.STATE.getType().equals(topic.getMessageType())) {
// 多线程处理
stateMessageService.state(topic, header, dataBody);
return;
}

View File

@ -5,19 +5,32 @@ import java.util.Date;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.evotech.hd.cloud.dao.BatteryStationDcDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryPreDao;
import com.evotech.hd.cloud.dao.VehicleWechatUserRelationDao;
import com.evotech.hd.cloud.mqtt.enums.MqttMessageTypeEnum;
import com.evotech.hd.cloud.mqtt.enums.RequestFunctionTypesEnum;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import com.evotech.hd.cloud.mqtt.message.MqttMessageHeader;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.battery.BatteryInfoReq;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.carinfo.CarInfoReq;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.carinfo.CarInfoResponse;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.carinfo.VehicleData;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.order.OrderByPlateNumReq;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.order.OrderByPlateNumResponse;
import com.evotech.hd.cloud.mqtt.message.dto.newer.req.order.OrderData;
import com.evotech.hd.cloud.service.OrderSwapBatteryService;
import com.evotech.hd.cloud.utils.CommonUtil;
import com.evotech.hd.common.core.entity.cloud.BatteryStationDc;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.VehicleWechatUserRelation;
import cn.hutool.core.date.DatePattern;
@ -36,8 +49,16 @@ public class RequestMessageService {
private VehicleWechatUserRelationDao vehicleWechatUserRelationDao;
@Resource
private MessageUtilService messageUtilService;
@Resource
private BatteryStationDcDao batteryStationDcDao;
@Resource
private OrderSwapBatteryDao orderSwapBatteryDao;
@Resource
private OrderSwapBatteryPreDao orderSwapBatteryPreDao;
@Resource
private OrderSwapBatteryService orderSwapBatteryService;
@Async("taskExecutor")
public void request(MessageTopic topic, MqttMessageHeader header, JSONObject dataBody) {
switch (RequestFunctionTypesEnum.getFunctionType(header.getFunction())) {
case FUN_CARINFO:
@ -45,16 +66,16 @@ public class RequestMessageService {
handleCarInfo(topic, header, carInfoReq);
break;
case FUN_BATTERYINFO:
BatteryInfoReq batteryInfoReq = JSONUtil.toBean(dataBody, BatteryInfoReq.class);
handleBatteryInfo(topic, header, batteryInfoReq);
break;
case FUN_PREORDER:
break;
case FUN_ORDERBYPLATENUM:
OrderByPlateNumReq orderByPlateNumReq = JSONUtil.toBean(dataBody, OrderByPlateNumReq.class);
handleOrderByPlateNum(topic, header, orderByPlateNumReq);
break;
case FUN_CANCELORDER:
@ -67,6 +88,86 @@ public class RequestMessageService {
}
/**
* OrderByPlateNum请求处理
* @param topic
* @param header
* @param orderByPlateNumReq
*/
private void handleOrderByPlateNum(MessageTopic topic, MqttMessageHeader header, OrderByPlateNumReq orderByPlateNumReq) {
OrderByPlateNumResponse response = new OrderByPlateNumResponse();
Date d = new Date();
// 查预约
OrderSwapBatteryPre osbp = orderSwapBatteryPreDao.selectOne(new QueryWrapper<OrderSwapBatteryPre>()
.eq("plate_num", orderByPlateNumReq.getPlateNum())
.eq("station_code", orderByPlateNumReq.getStationCode())
.last("limit 1"));
if (osbp == null) {
response.setOrderRequestId(orderByPlateNumReq.getOrderRequestId());
response.setCode(0);
response.setMsg("车辆未预约,请扫码进入");
} else {
OrderSwapBattery osb = orderSwapBatteryDao.selectOne(new QueryWrapper<OrderSwapBattery>()
.eq("plate_num", orderByPlateNumReq.getPlateNum())
.eq("station_code", orderByPlateNumReq.getStationCode())
.eq("order_pre_id", osbp.getPkId())
.eq("status", 1));
if (osb == null) {
// 生成订单
osb = new OrderSwapBattery();
osb.setOrderNo(CommonUtil.swapBatteryOrderNo(orderByPlateNumReq.getStationCode()));
osb.setOrderPreId(osbp.getPkId());
osb.setOrderPrePhone(osbp.getPhone());
osb.setOrderPreUid(osbp.getUcode());
osb.setOrderPreUname(osbp.getUname());
osb.setPlateNum(osbp.getPlateNum());
osb.setOrderTime(d);
osb.setOrderType(1);
osb.setStatus(1);
osb.setStationCode(osbp.getStationCode());
osb.setStationName(osbp.getStationName());
orderSwapBatteryService.add(osb);
}
// 返回数据
OrderData od = new OrderData();
BeanUtils.copyProperties(osb, od);
response.setOrderRequestId(orderByPlateNumReq.getOrderRequestId());
response.setCode(1);
response.setMsg("OK");
response.setOrderData(od);
}
// 发送
topic.setDataDirection("M2S");
topic.setMessageType(MqttMessageTypeEnum.RESPONSE.getType());
header.setFunction(RequestFunctionTypesEnum.FUN_ORDERBYPLATENUM.getReFunction());
header.setTimeStamp(DateUtil.format(d, DatePattern.NORM_DATETIME_FORMATTER));
messageUtilService.publishAESMessage(topic, header, JSONUtil.parseObj(response));
}
/**
* BatteryInfo请求处理
* @param topic
* @param header
* @param batteryInfoReq
*/
private void handleBatteryInfo(MessageTopic topic, MqttMessageHeader header, BatteryInfoReq batteryInfoReq) {
String batCode = batteryInfoReq.getBatCode();
int pageNo = 0;
int pageSize = 500;
Page<BatteryStationDc> page = new Page<BatteryStationDc>(pageNo, pageSize);
do {
pageNo += 1;
page = new Page<BatteryStationDc>(pageNo, pageSize);
page = batteryStationDcDao.selectPage(page, new QueryWrapper<BatteryStationDc>().eq(StringUtils.hasText(batCode), "bat_code", batCode));
} while (page.getCurrent() < page.getPages());
}
/**
* CarInfo请求处理
@ -81,7 +182,7 @@ public class RequestMessageService {
Page<VehicleWechatUserRelation> page = new Page<VehicleWechatUserRelation>(pageNo, pageSize);
do {
pageNo += 1;
page = new Page<VehicleWechatUserRelation>(pageNo, 500);
page = new Page<VehicleWechatUserRelation>(pageNo, pageSize);
page = vehicleWechatUserRelationDao.selectPage(page, new QueryWrapper<VehicleWechatUserRelation>()
.eq(StringUtils.hasText(plateNum), "plate_num", plateNum));
CarInfoResponse response = new CarInfoResponse();

View File

@ -1,11 +1,27 @@
package com.evotech.hd.cloud.mqtt.message.handle;
import java.util.Date;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evotech.hd.cloud.dao.OrderSwapBatteryDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryPreDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryStepDao;
import com.evotech.hd.cloud.mqtt.enums.StateFunctionTypesEnum;
import com.evotech.hd.cloud.mqtt.message.MessageTopic;
import com.evotech.hd.cloud.mqtt.message.MqttMessageHeader;
import com.evotech.hd.cloud.mqtt.message.dto.newer.state.OrderStatus;
import com.evotech.hd.cloud.mqtt.message.dto.newer.state.OrderStatusData;
import com.evotech.hd.cloud.mqtt.message.dto.newer.state.SwapStep;
import com.evotech.hd.cloud.service.BatteryStationDcService;
import com.evotech.hd.common.core.entity.cloud.BatteryTrace;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
@ -19,16 +35,32 @@ public class StateMessageService {
@Resource
private MessageUtilService messageUtilService;
@Resource
private OrderSwapBatteryDao orderSwapBatteryDao;
@Resource
private OrderSwapBatteryPreDao orderSwapBatteryPreDao;
@Resource
private OrderSwapBatteryStepDao orderSwapBatteryStepDao;
@Resource
private BatteryStationDcService batteryStationDcService;
@Async("taskExecutor")
public void state(MessageTopic topic, MqttMessageHeader header, JSONObject dataBody) {
switch (StateFunctionTypesEnum.getFunctionType(header.getFunction())) {
case FUN_ORDERSTATUS:
OrderStatus orderStatus = JSONUtil.toBean(dataBody, OrderStatus.class);
handleOrderStatus(orderStatus);
OrderStatusData statusData = JSONUtil.toBean(dataBody.getJSONObject("statusDate"), OrderStatusData.class);
handleOrderStatus(orderStatus, statusData);
break;
case FUN_SWAPSTEP:
SwapStep swapStep = JSONUtil.toBean(dataBody, SwapStep.class);
// 记录换电步骤
OrderSwapBatteryStep step = new OrderSwapBatteryStep();
BeanUtils.copyProperties(swapStep, step);
step.setCtime(new Date());
step.setCreater("SYS");
orderSwapBatteryStepDao.insert(step);
break;
default:
@ -38,31 +70,98 @@ public class StateMessageService {
}
private void handleOrderStatus(OrderStatus orderStatus) {
/**
* 订单状态更新处理
* @param orderStatus
* @param statusData
*/
private void handleOrderStatus(OrderStatus orderStatus, OrderStatusData statusData) {
switch (orderStatus.getStatus()) {
case 2:
// 修改订单状态
// 添加换电步骤1-车辆进站
orderStatus2(orderStatus);
break;
case 3:
// 修改订单状态和数据
OrderSwapBattery osb = alterOrderStatus(orderStatus, statusData);
// 换电步骤添加最后一条 TODO
// 添加2块电池的溯源记录
addBatteryTrace(orderStatus, statusData, osb);
break;
case 4:
// 修改订单状态
alterOrderStatus(orderStatus, null);
break;
case 5:
// 修改订单状态和数据
alterOrderStatus(orderStatus, statusData);
break;
default:
break;
}
}
/**
* 添加电池轨迹记录
* @param orderStatus
* @param statusData
* @param osb
*/
private void addBatteryTrace(OrderStatus orderStatus, OrderStatusData statusData, OrderSwapBattery osb) {
BatteryTrace bt1 = new BatteryTrace();
bt1.setBatCode(statusData.getRentBatCode());
bt1.setBeginTime(orderStatus.getStatusTime());
bt1.setPointType(2);
bt1.setPointCode(osb.getPlateNum());
bt1.setCreater("SYS");
BatteryTrace bt2 = new BatteryTrace();
BeanUtils.copyProperties(bt1, bt2);
bt2.setBatCode(statusData.getReturnBatCode());
bt2.setPointType(1);
bt2.setPointCode(osb.getStationCode());
bt2.setPointName(osb.getStationName());
batteryStationDcService.addTrace(bt1);
batteryStationDcService.addTrace(bt2);
}
/**
* 换电开始时的处理
* @param orderStatus
*/
private void orderStatus2(OrderStatus orderStatus) {
// 修改订单状态
OrderSwapBattery osb = alterOrderStatus(orderStatus, null);
// 添加换电步骤1-车辆进站
OrderSwapBatteryStep osbs = new OrderSwapBatteryStep();
osbs.setOrderNo(orderStatus.getOrderNo());
osbs.setStep(1);
osbs.setStepTime(orderStatus.getStatusTime());
osbs.setCtime(new Date());
osbs.setCreater("SYS");
orderSwapBatteryStepDao.insert(osbs);
// 修改预约订单为完成
OrderSwapBatteryPre osbp = new OrderSwapBatteryPre();
osbp.setStatus(2);
osbp.setPkId(osb.getOrderPreId());
orderSwapBatteryPreDao.updateById(osbp);
}
/**
* 修改订单状态方法
* @param orderStatus
* @param statusData
* @return
*/
private OrderSwapBattery alterOrderStatus(OrderStatus orderStatus, OrderStatusData statusData) {
OrderSwapBattery osb = orderSwapBatteryDao.selectOne(new QueryWrapper<OrderSwapBattery>().eq("order_no", orderStatus.getOrderNo()));
osb.setStatus(orderStatus.getStatus());
if (orderStatus.getStatus() == 3 || orderStatus.getStatus() == 5) {
BeanUtils.copyProperties(statusData, osb);
}
orderSwapBatteryDao.updateById(osb);
return osb;
}

View File

@ -2,11 +2,11 @@ package com.evotech.hd.cloud.service;
import java.util.List;
import com.evotech.hd.cloud.entity.request.PageListSwapOrderRequest;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import com.evotech.hd.common.core.entity.cloud.PageListSwapOrderRequest;
public interface OrderSwapBatteryService {
@ -14,7 +14,7 @@ public interface OrderSwapBatteryService {
public Result<Integer> cancelPre(Integer id, Integer status);
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String userId);
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String ucode, String stationCode);
public Result<Integer> add(OrderSwapBattery osb);

View File

@ -0,0 +1,17 @@
package com.evotech.hd.cloud.service;
import java.util.List;
import com.evotech.hd.cloud.entity.request.PageListWechatUserRequest;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
public interface WechatUserService {
public Result<List<WechatUser>> list(PageListWechatUserRequest plwur);
public Result<Integer> alterPhone(String wuid, String phone, String name);
public Result<Integer> companyRelation(String wuid, String pcode, String pname);
}

View File

@ -1,5 +1,6 @@
package com.evotech.hd.cloud.service.impl;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -11,12 +12,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.evotech.hd.cloud.dao.OrderSwapBatteryDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryPreDao;
import com.evotech.hd.cloud.dao.OrderSwapBatteryStepDao;
import com.evotech.hd.cloud.entity.request.PageListSwapOrderRequest;
import com.evotech.hd.cloud.service.OrderSwapBatteryService;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import com.evotech.hd.common.core.entity.cloud.PageListSwapOrderRequest;
import com.evotech.hd.common.core.enums.CodeMsg;
import jakarta.annotation.Resource;
@ -39,6 +40,10 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
// 2. 检查预约人
// 3. 添加预约
Date d = new Date();
osbp.setStatus(1);
osbp.setReservationTime(osbp.getReservationTime() == null ? d : osbp.getReservationTime());
osbp.setCtime(d);
int n = orderSwapBatteryPreDao.insert(osbp);
if (n == 1) {
return new Result<Integer>().success(n);
@ -50,7 +55,7 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
public Result<Integer> cancelPre(Integer id, Integer status) {
OrderSwapBatteryPre osbp = new OrderSwapBatteryPre();
osbp.setPkId(id);
osbp.setStatus(status != null? status : 2);
osbp.setStatus(status != null? status : 3);
int n = orderSwapBatteryPreDao.updateById(osbp);
if (n == 1) {
return new Result<Integer>().success(n);
@ -59,11 +64,12 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
}
@Override
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String userId) {
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String ucode, String stationCode) {
List<OrderSwapBatteryPre> list = orderSwapBatteryPreDao.selectList(new QueryWrapper<OrderSwapBatteryPre>()
.eq(StringUtils.hasText(userId), "user_id", userId)
.eq(StringUtils.hasText(ucode), "ucode", ucode)
.eq(status != null, "status", status)
.eq(StringUtils.hasText(plateNum), "plate_num", plateNum)
.eq(StringUtils.hasText(stationCode), "station_code", stationCode)
.orderByDesc("pk_id"));
if (list.isEmpty()) {
return new Result<List<OrderSwapBatteryPre>>().error(CodeMsg.DATABASE_RESULT_NULL);
@ -108,7 +114,8 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
page = orderSwapBatteryDao.selectPage(page, new QueryWrapper<OrderSwapBattery>()
.eq(StringUtils.hasText(plsor.getOrderNo()), "order_no", plsor.getOrderNo())
.like(StringUtils.hasText(plsor.getPlateNum()), "plate_num", plsor.getPlateNum())
.eq(StringUtils.hasText(plsor.getPlateNum()), "plate_num", plsor.getPlateNum())
.in(StringUtils.hasText(plsor.getPlateNums()), "plate_num", Arrays.asList(plsor.getPlateNums().split(",")))
.ge(plsor.getOrderTimeBegin() != null, "order_time", plsor.getOrderTimeBegin())
.le(plsor.getOrderTimeEnd() != null, "order_time", plsor.getOrderTimeEnd())
.eq(StringUtils.hasText(plsor.getStationCode()), "station_code", plsor.getStationCode())

View File

@ -0,0 +1,80 @@
package com.evotech.hd.cloud.service.impl;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.evotech.hd.cloud.entity.request.PageListWechatUserRequest;
import com.evotech.hd.cloud.service.WechatUserService;
import com.evotech.hd.common.core.dao.wechat.WechatUserDao;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
import com.evotech.hd.common.core.enums.CodeMsg;
import jakarta.annotation.Resource;
@Service
public class WechatUserServiceImpl implements WechatUserService {
@Resource
private WechatUserDao wechatUserDao;
@Override
public Result<List<WechatUser>> list(PageListWechatUserRequest plwur) {
Page<WechatUser> page = new Page<WechatUser>(plwur.getPageNo(), plwur.getPageSize());
page = wechatUserDao.selectPage(page, new QueryWrapper<WechatUser>()
.eq(StringUtils.hasText(plwur.getWuid()), "wuid", plwur.getWuid())
.like(StringUtils.hasText(plwur.getPhoneNumber()), "phone_number", plwur.getPhoneNumber())
.eq(plwur.getGender() != null, "gender", plwur.getGender())
.eq(plwur.getType() != null, "type", plwur.getType())
.eq(StringUtils.hasText(plwur.getNickName()), "nick_name", plwur.getNickName())
.eq(StringUtils.hasText(plwur.getName()), "name", plwur.getName())
.eq(StringUtils.hasText(plwur.getPcode()), "pcode", plwur.getPcode())
.orderByDesc("pk_id"));
if (page.getRecords().isEmpty()) {
return new Result<List<WechatUser>>().error(CodeMsg.DATABASE_RESULT_NULL);
}
return new Result<List<WechatUser>>().success(page);
}
@Override
public Result<Integer> alterPhone(String wuid, String phone, String name) {
if (!(StringUtils.hasText(phone) || StringUtils.hasText(name))) {
return new Result<Integer>().error(CodeMsg.PARAM_IS_NULL);
}
WechatUser user = new WechatUser();
user.setPhoneNumber(phone);
user.setName(name);
int n = wechatUserDao.update(user, new QueryWrapper<WechatUser>().eq("wuid", wuid));
if (n == 1) {
return new Result<Integer>().success(n);
}
return new Result<Integer>().error("修改微信用户信息出错!");
}
@Override
public Result<Integer> companyRelation(String wuid, String pcode, String pname) {
if (!(StringUtils.hasText(pcode) && StringUtils.hasText(pname))) {
return new Result<Integer>().error(CodeMsg.PARAM_IS_NULL);
}
WechatUser user = new WechatUser();
user.setPcode(pcode);
user.setPname(pname);
int n = wechatUserDao.update(user, new QueryWrapper<WechatUser>().eq("wuid", wuid));
if (n == 1) {
return new Result<Integer>().success(n);
}
return new Result<Integer>().error("关联公司信息出错!");
}
}

View File

@ -0,0 +1,19 @@
package com.evotech.hd.cloud.utils;
import java.util.Date;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
public class CommonUtil {
public static String swapBatteryOrderNo(String stationCode) {
String orderNoPrefix = "YTSO";
String orderNoMiddle1 = stationCode.length() <= 8?stationCode : stationCode.substring(stationCode.length() - 8);
String orderNoMiddle2 = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_FORMATTER);
String orderNoSuffix = RandomUtil.randomNumbers(2) + RandomUtil.randomChar("ABC");
return orderNoPrefix + orderNoMiddle1 + orderNoMiddle2 + orderNoSuffix;
}
}

View File

@ -48,14 +48,20 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
}
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
String uri = request.getURI().toString();
// 微信服务先放行后面再加验证规则
if (uri.contains("/gateway/wechat/")) {
return Mono.just(new AuthorizationDecision(true));
}
// 1. 对应跨域的预检请求直接放行
if (request.getMethod() == HttpMethod.OPTIONS) {
return Mono.just(new AuthorizationDecision(true));
}
// 微信服务将UID放入请求头验证一下
if (uri.contains("/gateway/wechat/")) {
String wuid = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_AUTHORIZATION_KEY);
if (redisUtil.hasKey(HDConstant.sessionKeyPrefix + wuid) ) {
return Mono.just(new AuthorizationDecision(true));
} else {
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.TOKEN_INVALID.getCode(), CodeMsg.TOKEN_INVALID.getMsg(), uri));
}
}
// 2. token验证
/**
* 这个类主要是处理权限Authorization对于身份authentication

View File

@ -0,0 +1,18 @@
package com.evotech.hd.gateway.oauth2;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
@ConfigurationProperties(prefix = "security.oauth2", ignoreUnknownFields = true)
@Data
@Component
public class ResourceIgnoreUri {
private List<String> ignoreUris = new ArrayList<String>();
}

View File

@ -4,8 +4,6 @@ import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
@ -17,6 +15,8 @@ import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import cn.hutool.core.convert.Convert;
import jakarta.annotation.Resource;
@ -24,8 +24,8 @@ import jakarta.annotation.Resource;
@Configuration
public class ResourceServerConfig {
@Value("${security.oauth2.ignore_uri:{}}")
private String[] ignoreUriArr;
@Resource
private ResourceIgnoreUri ignoreUri;
@Resource
private AuthorizationManager authorizationManager;
@ -63,7 +63,7 @@ public class ResourceServerConfig {
http.authorizeExchange(exchange ->
exchange
.pathMatchers(ignoreUriArr).permitAll()
.pathMatchers(Convert.toStrArray(ignoreUri.getIgnoreUris())).permitAll()
.pathMatchers(ignoreFixedUris()).permitAll()
// .anyExchange().authenticated()
// 其他走自定义逻辑

View File

@ -34,6 +34,12 @@
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- openfein -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>

View File

@ -4,12 +4,14 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan("com.evotech.hd.**")
@MapperScan({"com.evotech.hd.wechat.dao.**", "com.evotech.hd.common.core.dao.**"})
@EnableFeignClients
public class WechatServerApplication {
public static void main(String[] args) {

View File

@ -0,0 +1,72 @@
package com.evotech.hd.wechat.controller;
import java.util.List;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import com.evotech.hd.common.core.entity.cloud.PageListSwapOrderRequest;
import com.evotech.hd.wechat.service.CloudService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@Tag(name = "云平台API")
@ApiSupport(order = 13)
@RestController
@RequestMapping("/cloud")
public class CloudServeController {
@Resource
private CloudService cloudService;
@Operation(summary = "增加预约")
@PostMapping("/order/swap/pre/add")
@ApiOperationSupport(order = 1)
public Result<Integer> addPre(@Valid @ParameterObject OrderSwapBatteryPre osbp) {
return cloudService.addPre(osbp);
}
@Operation(summary = "取消预约")
@PostMapping("/order/swap/pre/cancel")
@ApiOperationSupport(order = 2)
public Result<Integer> cancelPre(@NotNull Integer id) {
return cloudService.cancelPre(id, null);
}
@Operation(summary = "查询预约")
@GetMapping("/order/swap/pre/list")
@ApiOperationSupport(order = 3)
public Result<List<OrderSwapBatteryPre>> listPre(String plateNum, Integer status, String ucode, String stationCode) {
return cloudService.listPre(plateNum, status, ucode, stationCode);
}
@Operation(summary = "查询订单")
@GetMapping("/order/swap/list")
@ApiOperationSupport(order = 7)
public Result<List<OrderSwapBattery>> list(@ParameterObject PageListSwapOrderRequest plsor) {
return cloudService.list(plsor);
}
@Operation(summary = "查询换电步骤")
@GetMapping("/order/swap/step/list")
@ApiOperationSupport(order = 8)
public Result<List<OrderSwapBatteryStep>> listStep(@NotBlank String orderNo) {
return cloudService.listStep(orderNo);
}
}

View File

@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.wechat.service.LoginService;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -15,6 +16,7 @@ import jakarta.annotation.Resource;
@Tag(name = "登陆")
@RestController
@RequestMapping("/login")
@ApiSupport(order = 10)
public class LoginController {
@Resource

View File

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.wechat.service.WechatPayService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.wechat.pay.java.service.payments.jsapi.model.Detail;
import io.swagger.v3.oas.annotations.Hidden;
@ -23,6 +24,7 @@ import jakarta.validation.constraints.NotBlank;
@Tag(name = "微信支付")
@RestController
@RequestMapping("/wechatpay")
@ApiSupport(order = 12)
public class WechatPayController {

View File

@ -0,0 +1,40 @@
package com.evotech.hd.wechat.controller;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
import com.evotech.hd.wechat.service.WechatUserService;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@Tag(name = "微信用户")
@RestController
@RequestMapping("/wechat/user")
@ApiSupport(order = 11)
public class WechatUserController {
@Resource
private WechatUserService wechatUserService;
@Operation(summary = "修改")
@PostMapping("/update")
public Result<String> update(@ParameterObject WechatUser wuser) {
return wechatUserService.update(wuser);
}
@Operation(summary = "查询")
@PostMapping("/userbyuid")
public Result<WechatUser> userByUid(String wuid) {
return wechatUserService.userByUid(wuid);
}
}

View File

@ -0,0 +1,40 @@
package com.evotech.hd.wechat.service;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBattery;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryPre;
import com.evotech.hd.common.core.entity.cloud.OrderSwapBatteryStep;
import com.evotech.hd.common.core.entity.cloud.PageListSwapOrderRequest;
@FeignClient(value = "cloud-server")
public interface CloudService {
@PostMapping(value = "/cloud/order/swap/pre/add",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<Integer> addPre(OrderSwapBatteryPre osbp);
@PostMapping(value = "/cloud/order/swap/pre/cancel",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<Integer> cancelPre(@RequestParam Integer id, @RequestParam Integer status);
@GetMapping(value = "/cloud/order/swap/pre/list",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<List<OrderSwapBatteryPre>> listPre(@RequestParam String plateNum, @RequestParam Integer status, @RequestParam String userId, @RequestParam String stationCode);
@GetMapping(value = "/cloud/order/swap/list",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<List<OrderSwapBattery>> list(PageListSwapOrderRequest plsor);
@GetMapping(value = "/cloud/order/swap/step/list",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<List<OrderSwapBatteryStep>> listStep(String orderNo);
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.wechat.service;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
public interface WechatUserService {
public Result<String> update(WechatUser wuser);
public Result<WechatUser> userByUid(String wuid);
}

View File

@ -6,13 +6,13 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.evotech.hd.common.core.constant.HDConstant;
import com.evotech.hd.common.core.dao.wechat.WechatUserDao;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
import com.evotech.hd.common.core.enums.CodeMsg;
import com.evotech.hd.common.redis.utils.RedisUtil;
import com.evotech.hd.wechat.config.XcxProperties;
import com.evotech.hd.wechat.dao.WechatUserDao;
import com.evotech.hd.wechat.entity.WechatUser;
import com.evotech.hd.wechat.service.AccessTokenService;
import com.evotech.hd.wechat.service.LoginService;
import com.evotech.hd.wechat.utils.xcx.LoginUtil;
@ -29,10 +29,6 @@ import jakarta.annotation.Resource;
@Service
public class LoginServiceImpl implements LoginService {
public static String openidPrefix = "hd:wechat:login:openid:";
public static String unionidPrefix = "hd:wechat:login:unionid:";
public static String sessionKeyPrefix = "hd:wechat:login:sessionKey:";
@Resource
private XcxProperties xcxProperties;
@Resource
@ -55,7 +51,7 @@ public class LoginServiceImpl implements LoginService {
// 是否登陆过
WechatUser wuser = wechatUserDao.selectOne(new QueryWrapper<WechatUser>().eq("openid", openid));
if (wuser == null) {
String wuid = MD5.create().digestHex(xcxProperties.getAppid() + openid);
String wuid = MD5.create().digestHex(xcxProperties.getAppid() + openid + "HBYT");
wuser = new WechatUser();
wuser.setAppid(xcxProperties.getAppid());
wuser.setCtime(new Date());
@ -65,20 +61,20 @@ public class LoginServiceImpl implements LoginService {
wechatUserDao.insert(wuser);
}
// 缓存数据
redisUtil.set(openidPrefix + wuser.getWuid(), openid);
redisUtil.set(unionidPrefix + wuser.getWuid(), unionid);
redisUtil.set(sessionKeyPrefix + wuser.getWuid(), sessionKey);
redisUtil.set(HDConstant.openidPrefix + wuser.getWuid(), openid);
redisUtil.set(HDConstant.unionidPrefix + wuser.getWuid(), unionid);
redisUtil.set(HDConstant.sessionKeyPrefix + wuser.getWuid(), sessionKey);
return new Result<String>().success(wuser.getWuid());
}
@Override
public Result<Boolean> checkSessionKey(String wuid) {
if (!redisUtil.hasKey(openidPrefix + wuid) || !redisUtil.hasKey(sessionKeyPrefix + wuid)) {
if (!redisUtil.hasKey(HDConstant.openidPrefix + wuid) || !redisUtil.hasKey(HDConstant.sessionKeyPrefix + wuid)) {
return new Result<Boolean>().error(CodeMsg.WECHAT_SERRION_ERROR);
}
String openid = redisUtil.get(openidPrefix + wuid).toString();
String sessionKey = redisUtil.get(sessionKeyPrefix + wuid).toString();
String openid = redisUtil.get(HDConstant.openidPrefix + wuid).toString();
String sessionKey = redisUtil.get(HDConstant.sessionKeyPrefix + wuid).toString();
HMac hmac = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, sessionKey.getBytes());
String res = LoginUtil.code2Session(xcxProperties.getAppid(), openid, hmac.digestHex(""));
JSONObject jo = JSONUtil.parseObj(res);
@ -94,18 +90,19 @@ public class LoginServiceImpl implements LoginService {
if (StringUtils.hasText(wuser.getPhoneNumber())) {
return new Result<String>().success("ok", wuser.getPhoneNumber());
}
if (!redisUtil.hasKey(openidPrefix + wuid)) {
if (!redisUtil.hasKey(HDConstant.openidPrefix + wuid)) {
return new Result<String>().error(CodeMsg.WECHAT_SERRION_ERROR);
}
String openid = redisUtil.get(openidPrefix + wuid).toString();
String res = PhoneNumberUtil.getPhoneNumber(accessTokenService.getAccessToken(), openid, code);
// String openid = redisUtil.get(HDConstant.openidPrefix + wuid).toString();
String res = PhoneNumberUtil.getPhoneNumber(accessTokenService.getAccessToken(), code);
JSONObject jo = JSONUtil.parseObj(res);
if (jo.getInt("errcode") == 0) {
String phoneNumber = jo.getJSONObject("phone_info").getStr("phoneNumber");
WechatUser wu = new WechatUser();
wu.setWuid(wuid);
wechatUserDao.update(wu, new UpdateWrapper<WechatUser>().set("phone_number", phoneNumber));
}
wu.setPhoneNumber(phoneNumber);
wechatUserDao.update(wu, new QueryWrapper<WechatUser>().eq("wuid", wuid));
return new Result<String>().success(phoneNumber);
}
return new Result<String>().error(CodeMsg.WECHAT_API_ERROR, jo);
}

View File

@ -0,0 +1,40 @@
package com.evotech.hd.wechat.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evotech.hd.common.core.dao.wechat.WechatUserDao;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.wechat.WechatUser;
import com.evotech.hd.common.core.enums.CodeMsg;
import com.evotech.hd.wechat.service.WechatUserService;
import jakarta.annotation.Resource;
@Service
public class WechatUserServiceImpl implements WechatUserService {
@Resource
private WechatUserDao wechatUserDao;
@Override
public Result<String> update(WechatUser wuser) {
int n = wechatUserDao.update(wuser, new QueryWrapper<WechatUser>().eq("wuid", wuser.getWuid()));
if (n == 1) {
return new Result<String>().success(1);
}
return new Result<String>().error("修改微信用户信息出错");
}
@Override
public Result<WechatUser> userByUid(String wuid) {
WechatUser user = wechatUserDao.selectOne(new QueryWrapper<WechatUser>().eq("wuid", wuid));
if (user == null) {
return new Result<WechatUser>().error(CodeMsg.DATABASE_RESULT_NULL);
}
return new Result<WechatUser>().success(user);
}
}

View File

@ -3,7 +3,8 @@ package com.evotech.hd.wechat.utils.xcx;
import java.util.HashMap;
import java.util.Map;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
/**
* 小程序手机号
@ -20,11 +21,15 @@ private static String getPhoneNumberUrl = "https://api.weixin.qq.com/wxa/busines
* @param
* @return
*/
public static String getPhoneNumber(String access_token, String openid, String code) {
public static String getPhoneNumber(String access_token, String code) {
Map<String, Object> m = new HashMap<String, Object>();
m.put("openid", openid);
m.put("code", code);
String res = HttpUtil.post(getPhoneNumberUrl + access_token, m);
String body = JSONUtil.toJsonStr(m);
String res = HttpRequest.post(getPhoneNumberUrl + access_token)
.header("Content-Type", "application/json")
.body(body)
.execute()
.body();
return res;
}