九、附近门店
附近门店
新建模板share-rule

封装门店规则信息接口
FeeRuleApiController
java
package com.share.rules.api;
import com.share.common.core.domain.R;
import com.share.rules.domain.vo.FeeRule;
import com.share.rules.service.IFeeRuleService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/feeRule")
@SuppressWarnings({"unchecked", "rawtypes"})
public class FeeRuleApiController {
@Autowired
private IFeeRuleService feeRuleService;
//批量获取费用规则列表
@Operation(summary = "批量获取费用规则信息")
@PostMapping(value = "/getFeeRuleList")
public R<List<FeeRule>> getFeeRuleList(@RequestBody List<Long> feeRuleIdList) {
List<FeeRule> feeRules = feeRuleService.listByIds(feeRuleIdList);
return R.ok(feeRules);
}
//根据id获取规则详情
@Operation(summary = "获取费用规则详细信息")
@GetMapping(value = "/getFeeRule/{id}")
public R<FeeRule> getFeeRule(@PathVariable("id") Long id) {
FeeRule byId = feeRuleService.getById(id);
return R.ok(byId);
}
}
搭建规则服务远程接口模块
在share-api模块下新建子模块share-api-rule
pom.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.share</groupId>
<artifactId>share-api</artifactId>
<version>3.6.3</version>
</parent>
<artifactId>share-api-rule</artifactId>
<description>
share-api-rule规则接口模块
</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>com.share</groupId>
<artifactId>share-common-core</artifactId>
</dependency>
</dependencies>
</project>
pom.xml
share-rule模块引入api依赖
java
<dependency>
<groupId>com.share</groupId>
<artifactId>share-api-rule</artifactId>
<version>3.6.3</version>
</dependency>
FeeRule
将FeeRule对象提升到share-api-rule模块com.share.rules.api.domain包下面,订单模块要使用该对象,share-rule的FeeRule删除
openFeign接口定义
操作模块:share-api-rule

ServiceNameConstants.java
java
/**
* 规则服务
*/
public static final String RULE_SERVICE = "share-rule";
RemoteFeeRuleService
java
package com.share.rules.api;
import com.share.common.core.constant.ServiceNameConstants;
import com.share.common.core.domain.R;
import com.share.rules.domain.FeeRule;
import com.share.rules.factory.RemoteFeeRuleFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* 用户服务
*
* @author share
*/
@FeignClient(contextId = "remoteFeeRuleService",
value = ServiceNameConstants.RULE_SERVICE,
fallbackFactory = RemoteFeeRuleFallbackFactory.class)
public interface RemoteFeeRuleService {
@PostMapping(value = "/feeRule/getFeeRuleList")
public R<List<FeeRule>> getFeeRuleList(@RequestBody List<Long> feeRuleIdList);
@GetMapping(value = "/feeRule/getFeeRule/{id}")
public R<FeeRule> getFeeRule(@PathVariable("id") Long id);
}
RemoteFeeRuleFallbackFactory
java
package com.share.rules.factory;
import com.share.common.core.exception.ServiceException;
import com.share.rules.api.RemoteFeeRuleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 用户服务降级处理
*
* @author share
*/
@Component
public class RemoteFeeRuleFallbackFactory implements FallbackFactory<RemoteFeeRuleService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteFeeRuleFallbackFactory.class);
@Override
public RemoteFeeRuleService create(Throwable throwable)
{
log.error("用户服务调用失败:{}", throwable.getMessage());
throw new ServiceException("调用出现错误");
}
}
加载配置类
resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
java
com.share.rules.api.factory.RemoteFeeRuleFallbackFactory
计算到门店的距离方法
IMapService
java
package com.share.device.service;
public interface IMapService {
//计算距离
// 四个参数:开始经纬度, 目标经纬度
Double calculateDistance(String startLongitude, String startLatitude,
String endLongitude, String endLatitude);
}
MapServiceImpl
java
package com.share.device.service.impl;
import cn.hutool.json.JSONObject;
import com.share.common.core.exception.ServiceException;
import com.share.device.service.IMapService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class MapServiceImpl implements IMapService {
@Autowired
private RestTemplate restTemplate;
private String key = "";
//计算距离
// 四个参数:开始经纬度, 目标经纬度
@Override
public Double calculateDistance(String startLongitude, String startLatitude,
String endLongitude, String endLatitude) {
String url = "https://apis.map.qq.com/ws/direction/v1/walking/?from={from}&to={to}&key={key}";
Map<String, String> map = new HashMap<>();
map.put("from", startLatitude + "," + startLongitude);
map.put("to", endLatitude + "," + endLongitude);
map.put("key", key);
JSONObject result = restTemplate.getForObject(url, JSONObject.class, map);
if (result.getInt("status") != 0) {
String message = result.getStr("message");
System.out.println("*****===============" + message);
throw new ServiceException("地图服务调用失败");
}
//返回第一条最佳线路
JSONObject route = result.getJSONObject("result").getJSONArray("routes").getJSONObject(0);
// 单位:米
return route.getBigDecimal("distance").doubleValue();
}
}
搜索附近门店接口
模块share-device
创建包api
pom.xml
java
<dependency>
<groupId>com.share</groupId>
<artifactId>share-api-user</artifactId>
<version>3.6.3</version>
</dependency>
DeviceApiController
java
package com.share.device.api;
import com.share.common.core.web.controller.BaseController;
import com.share.common.core.web.domain.AjaxResult;
import com.share.common.security.annotation.RequiresLogin;
import com.share.device.domain.StationVo;
import com.share.device.service.IDeviceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "站点接口管理")
@RestController
@RequestMapping("/device")
public class DeviceApiController extends BaseController {
@Autowired
private IDeviceService deviceService;
@Operation(summary = "根据经纬度搜索附近门店(站点)")
@RequiresLogin
@GetMapping("/nearbyStation/{latitude}/{longitude}")
public AjaxResult nearbyStation(@PathVariable String latitude,
@PathVariable String longitude) {
List<StationVo> stationVoList =
deviceService.nearbyStation(latitude, longitude);
return success(stationVoList);
}
}
DeviceServiceImpl
java
package com.share.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.share.common.core.constant.SecurityConstants;
import com.share.common.core.domain.R;
import com.share.device.domain.Cabinet;
import com.share.device.domain.Station;
import com.share.device.domain.StationLocation;
import com.share.device.domain.StationVo;
import com.share.device.service.ICabinetService;
import com.share.device.service.IDeviceService;
import com.share.device.service.IMapService;
import com.share.device.service.IStationService;
import com.share.rules.api.RemoteFeeRuleService;
import com.share.rules.domain.FeeRule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class DeviceServiceImpl implements IDeviceService {
@Autowired
private IStationService stationService;
@Autowired
private ICabinetService cabinetService;
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private IMapService mapService;
@Autowired
private RemoteFeeRuleService remoteFeeRuleService;
@Override
public List<StationVo> nearbyStation(String latitude, String longitude) {
//坐标,确定中心点
//GeoJsonPoint(double x, double y) x 表示经度,y 表示纬度。
GeoJsonPoint geoJsonPoint =
new GeoJsonPoint(Double.parseDouble(longitude)
, Double.parseDouble(latitude));
//画圈的半径,50km范围
Distance distance = new Distance(50, Metrics.KILOMETERS);
//画了一个圆圈
Circle circle = new Circle(geoJsonPoint, distance);
//查询mongoDB
Query query = Query.query(Criteria.where("location").withinSphere(circle));
List<StationLocation> stationLocationList = mongoTemplate.find(query, StationLocation.class);
//查询其他需要数据,进行封装
//根据查询mongoDB的list集合里获取站点其他数据
//从list获取所有站点id,组装数据
List<Long> stationIdList = stationLocationList.stream().map(StationLocation::getStationId).collect(Collectors.toList());
//根据所有站点id获取对应站点数量
LambdaQueryWrapper<Station> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(Station::getId, stationIdList);
// queryWrapper.isNotNull(Station::getCabinetId);
List<Station> stationList = stationService.list(queryWrapper);
List<StationVo> stationVoList = new ArrayList<>();
//遍历
stationList.forEach(station -> {
StationVo stationVo = new StationVo();
BeanUtils.copyProperties(station, stationVo);
// 计算距离
Double distanceStation = mapService
.calculateDistance(longitude,
latitude,
station.getLongitude().toString(),
station.getLatitude().toString());
stationVo.setDistance(distanceStation);
// 获取柜机信息
Long cabinetId = station.getCabinetId();
Cabinet cabinet = cabinetService.getById(cabinetId);
//可用充电宝数量大于0,可借用
if (cabinet.getAvailableNum() > 0) {
stationVo.setIsUsable("1");
} else {
stationVo.setIsUsable("0");
}
// 获取空闲插槽数量大于0,可归还
if (cabinet.getFreeSlots() > 0) {
stationVo.setIsReturn("1");
} else {
stationVo.setIsReturn("0");
}
// 获取费用规则
Long feeRuleId = station.getFeeRuleId();
R<FeeRule> feeRuleResult = remoteFeeRuleService.getFeeRule(feeRuleId);
FeeRule feeRule = feeRuleResult.getData();
String description = feeRule.getDescription();
stationVo.setFeeRule(description);
stationVoList.add(stationVo);
});
return stationVoList;
}
}
测试过程中,可能出现超过调用次数限制,为了测试可以使用随机数
MapServiceImpl.java
java
package com.share.device.service.impl;
import cn.hutool.json.JSONObject;
import com.share.common.core.exception.ServiceException;
import com.share.device.service.IMapService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class MapServiceImpl implements IMapService {
@Autowired
private RestTemplate restTemplate;
private String key = "";
//计算距离
// 四个参数:开始经纬度, 目标经纬度
@Override
public Double calculateDistance(String startLongitude, String startLatitude,
String endLongitude, String endLatitude) {
// String url = "https://apis.map.qq.com/ws/direction/v1/walking/?from={from}&to={to}&key={key}";
//
// Map<String, String> map = new HashMap<>();
// map.put("from", startLatitude + "," + startLongitude);
// map.put("to", endLatitude + "," + endLongitude);
// map.put("key", key);
//
// JSONObject result = restTemplate.getForObject(url, JSONObject.class, map);
// if (result.getInt("status") != 0) {
// String message = result.getStr("message");
// System.out.println("*****===============" + message);
// throw new ServiceException("地图服务调用失败");
// }
//
// //返回第一条最佳线路
// JSONObject route = result.getJSONObject("result").getJSONArray("routes").getJSONObject(0);
// // 单位:米
// return route.getBigDecimal("distance").doubleValue();
Random random = new Random();
BigDecimal randomDouble = BigDecimal.valueOf(random.nextDouble(100));
// 保留两位小数,并进行四舍五入
BigDecimal roundedValue = randomDouble.setScale(1, RoundingMode.HALF_UP);
double roundedDoubleValue = roundedValue.doubleValue();
return roundedDoubleValue;
}
}

门店详情

DeviceApiController.java
java
@Operation(summary = "根据id获取门店详情")
@RequiresLogin
@GetMapping("/getStation/{id}/{latitude}/{longitude}")
public AjaxResult getStation(@PathVariable Long id, @PathVariable String latitude, @PathVariable String longitude)
{
return success(deviceService.getStation(id, latitude, longitude));
}
在"IDeviceService'中创建方法'getStation
java
//门店详情
StationVo getStation(Long id, String latitude, String longitude);
实现方法
java
//门店详情
@Override
public StationVo getStation(Long id, String latitude, String longitude) {
Station station = stationService.getById(id);
StationVo stationVo = new StationVo();
BeanUtils.copyProperties(station, stationVo);
// 计算距离
Double distance = mapService.calculateDistance(longitude, latitude, station.getLongitude().toString(), station.getLatitude().toString());
stationVo.setDistance(distance);
// 获取柜机信息
Cabinet cabinet = cabinetService.getById(station.getCabinetId());
//可用充电宝数量大于0,可借用
if (cabinet.getAvailableNum() > 0) {
stationVo.setIsUsable("1");
} else {
stationVo.setIsUsable("0");
}
// 获取空闲插槽数量大于0,可归还
if (cabinet.getFreeSlots() > 0) {
stationVo.setIsReturn("1");
} else {
stationVo.setIsReturn("0");
}
// 获取费用规则
FeeRule feeRule =
remoteFeeRuleService.getFeeRule(station.getFeeRuleId()).getData();
stationVo.setFeeRule(feeRule.getDescription());
//返回数据
return stationVo;
}

十、MQTT消息服务
系统消息处理
操作模块:share-device
连接测试
pom.xml
java
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
</dependency>
测试类MqttDemo
java
package com.share.device.emqx;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class MqttDemo {
public static void main(String[] args) {
String subTopic = "testtopic/#";
String pubTopic = "testtopic/1";
String content = "Hello World";
int qos = 2;
String broker = "tcp://192.168.3.137:1883";
String clientId = "emqx_test";
MemoryPersistence persistence = new MemoryPersistence();
try {
//创建mqtt客户端
MqttClient client = new MqttClient(broker, clientId, persistence);
//连接emqx
MqttConnectOptions connectOptions = new MqttConnectOptions();
connectOptions.setUserName("emqx_test");
connectOptions.setPassword("emqx_test_password".toCharArray());
// 保留会话
connectOptions.setCleanSession(true);
//进行连接
System.out.println("Connecting to broker: " + broker);
client.connect(connectOptions);
System.out.println("Connected");
System.out.println("Publishing message: " + content);
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
});
//订阅topic
client.subscribe(subTopic);
//发送消息
MqttMessage message = new MqttMessage(content.getBytes());
message.setQos(2);
client.publish(pubTopic, message);
System.out.println("Message published");
client.disconnect();
client.close();
System.exit(0);
} catch (MqttException e) {
throw new RuntimeException(e);
}
}
}

封装mqtt
share-device-dev.yml

EmqxProperties
java
package com.share.device.emqx.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "emqx.client")
public class EmqxProperties {
private String clientId;
private String username;
private String password;
private String serverURI;
private int keepAliveInterval;
private int connectionTimeout;
}
EmqxConstants
系统使用到的Topic信息
java
package com.share.device.emqx.constant;
/**
* Emqx常量信息
*
*/
public class EmqxConstants {
//充电宝插入,订阅设备端Topic, 服务器监听
public final static String TOPIC_POWERBANK_CONNECTED = "/sys/powerBank/connected";
/** 扫码提交指令Topic,柜机监听 */
public final static String TOPIC_SCAN_SUBMIT = "/sys/scan/submit/%s";
/** 柜机解锁充电宝,服务器监听 */
public final static String TOPIC_POWERBANK_UNLOCK = "/sys/powerBank/unlock";
/** 柜机属性上报,服务器监听 */
public final static String TOPIC_PROPERTY_POST = "/sys/property/post";
}
EmqxClientWrapper
封装mqtt连接、订阅与发布接口
java
package com.share.device.emqx;//package com.atguigu.iot.mqtt.emqx;
import com.share.device.emqx.callback.OnMessageCallback;
import com.share.device.emqx.config.EmqxProperties;
import com.share.device.emqx.constant.EmqxConstants;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* https://docs.emqx.com/zh/emqx/v5.0/connect-emqx/java.html
*/
@Slf4j
@Component
public class EmqxClientWrapper {
@Autowired
private EmqxProperties emqxProperties;
private MqttClient client;
@Autowired
private OnMessageCallback onMessageCallback;
@PostConstruct
private void init() {
MqttClientPersistence mqttClientPersistence = new MemoryPersistence();
try {
//新建客户端 参数:MQTT服务的地址,客户端名称,持久化
client = new MqttClient(emqxProperties.getServerURI(), emqxProperties.getClientId(), mqttClientPersistence);
// 设置回调
client.setCallback(onMessageCallback);
// 建立连接
connect();
} catch (MqttException e) {
log.info("MqttClient创建失败");
throw new RuntimeException(e);
}
}
public Boolean connect() {
// 设置连接的配置
try {
client.connect(mqttConnectOptions());
log.info("连接成功");
// 订阅
String[] topics = {EmqxConstants.TOPIC_POWERBANK_CONNECTED, EmqxConstants.TOPIC_POWERBANK_UNLOCK, EmqxConstants.TOPIC_PROPERTY_POST};
client.subscribe(topics);
return true;
} catch (MqttException e) {
log.info("连接失败");
e.printStackTrace();
}
return false;
}
/*创建MQTT配置类*/
private MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(emqxProperties.getUsername());
options.setPassword(emqxProperties.getPassword().toCharArray());
options.setAutomaticReconnect(true);//是否自动重新连接
options.setCleanSession(true);//是否清除之前的连接信息
options.setConnectionTimeout(emqxProperties.getConnectionTimeout());//连接超时时间
options.setKeepAliveInterval(emqxProperties.getKeepAliveInterval());//心跳
return options;
}
/**
* 发布消息
* @param topic
* @param data
*/
public void publish(String topic, String data) {
try {
MqttMessage message = new MqttMessage(data.getBytes());
message.setQos(2);
client.publish(topic, message);
} catch (MqttException e) {
log.info("消息发布失败");
e.printStackTrace();
}
}
}
OnMessageCallback
回调消息处理类
java
package com.share.device.emqx.callback;
import com.alibaba.fastjson2.JSONObject;
import com.share.device.emqx.ProtocolConvertUtil;
import com.share.device.emqx.factory.MessageHandlerFactory;
import com.share.device.emqx.handler.MassageHandler;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class OnMessageCallback implements MqttCallback {
@Autowired
private MessageHandlerFactory messageHandlerFactory;
// @Autowired
// private EmqxClientWrapper emqxClientWrapper;
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
// emqxClientWrapper.connect();
}
@Override
public void messageArrived(String topic, MqttMessage message) {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
try {
// 根据主题选择不同的处理逻辑
MassageHandler massageHandler = messageHandlerFactory.getMassageHandler(topic);
if(null != massageHandler) {
JSONObject jsonMessage = ProtocolConvertUtil.convertJson(new String(message.getPayload()));
massageHandler.handleMessage(jsonMessage);
}
} catch (Exception e) {
e.printStackTrace();
log.error("mqtt消息异常:{}", new String(message.getPayload()));
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}
MassageHandler
定义策略接口
java
package com.share.device.emqx.handler;
import com.alibaba.fastjson2.JSONObject;
public interface MassageHandler {
/**
* 策略接口
* @param message
*/
void handleMessage(JSONObject message);
}
具体Handler处理
GuiguEmqx
自定义注解
java
package com.share.device.emqx.annotation;
import java.lang.annotation.*;
// 自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GuiguEmqx {
String topic();
}
PowerBankConnectedHandler
充电宝插入处理类
java
package com.share.device.emqx.handler.impl;
import com.alibaba.fastjson2.JSONObject;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_POWERBANK_CONNECTED)
public class PowerBankConnectedHandler implements MassageHandler {
@Transactional(rollbackFor = Exception.class)
@Override
public void handleMessage(JSONObject message) {
log.info("handleMessage: {}", message.toJSONString());
}
}
PowerBankUnlockHandler
充电宝弹出处理类
java
package com.share.device.emqx.handler.impl;
import com.alibaba.fastjson2.JSONObject;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_POWERBANK_UNLOCK)
public class PowerBankUnlockHandler implements MassageHandler {
@Transactional(rollbackFor = Exception.class)
@Override
public void handleMessage(JSONObject message) {
log.info("handleMessage: {}", message.toJSONString());
}
}
PropertyPostHandler
充电宝属性上报处理类,上报电量等信息
java
package com.share.device.emqx.handler.impl;
import com.alibaba.fastjson2.JSONObject;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_PROPERTY_POST)
public class PropertyPostHandler implements MassageHandler {
/**
* 处理消息:
*
* @param message
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void handleMessage(JSONObject message) {
log.info("handleMessage: {}", message.toJSONString());
}
}
创建Handler工厂类
MessageHandlerFactory
java
package com.share.device.emqx.factory;
import com.share.device.emqx.handler.MassageHandler;
/**
* 平台消息工厂
*/
public interface MessageHandlerFactory {
MassageHandler getMassageHandler(String topic);
}
Handler工厂初始化
MessageHandlerFactoryImpl
java
package com.share.device.emqx.factory.impl;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.factory.MessageHandlerFactory;
import com.share.device.emqx.handler.MassageHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class MessageHandlerFactoryImpl implements MessageHandlerFactory, ApplicationContextAware {
private Map<String, MassageHandler> handlerMap = new HashMap<>();
/**
* 初始化bean对象
*
* @param ioc
*/
@Override
public void setApplicationContext(ApplicationContext ioc) {
// 获取对象
Map<String, MassageHandler> beanMap = ioc.getBeansOfType(MassageHandler.class);
for (MassageHandler massageHandler : beanMap.values()) {
GuiguEmqx guiguEmqx = AnnotatedElementUtils.findAllMergedAnnotations(massageHandler.getClass(), GuiguEmqx.class).iterator().next();
if (null != guiguEmqx) {
String topic = guiguEmqx.topic();
// 初始化到map
handlerMap.put(topic, massageHandler);
}
}
}
@Override
public MassageHandler getMassageHandler(String topic) {
return handlerMap.get(topic);
}
}
OnMessageCallback
补充handler处理逻辑
java
package com.share.device.emqx.callback;
import com.alibaba.fastjson2.JSONObject;
import com.share.device.emqx.ProtocolConvertUtil;
import com.share.device.emqx.factory.MessageHandlerFactory;
import com.share.device.emqx.handler.MassageHandler;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class OnMessageCallback implements MqttCallback {
@Autowired
private MessageHandlerFactory messageHandlerFactory;
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
try {
// 根据主题选择不同的处理逻辑
MassageHandler massageHandler = messageHandlerFactory.getMassageHandler(topic);
if(null != massageHandler) {
JSONObject jsonMessage = ProtocolConvertUtil.convertJson(new String(message.getPayload()));
massageHandler.handleMessage(jsonMessage);
}
} catch (Exception e) {
e.printStackTrace();
log.error("mqtt消息异常:{}", new String(message.getPayload()));
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}
十一、扫码
扫码接口
扫码前判断:是否免押金
java
@Operation(summary = "是否免押金")
@RequiresLogin
@GetMapping("/isFreeDeposit")
public AjaxResult isFreeDeposit() {
return success(userInfoService.isFreeDeposit());
}
在'UserlnfoService'中创建方法'isFreeDeposit'
java
//扫码前判断:是否免押金
Boolean isFreeDeposit();
实现方法
java
//扫码前判断:是否免押金
@Override
public Boolean isFreeDeposit() {
// 默认免押金,模拟实现
//根据用户id查询用户信息
UserInfo userInfo = userInfoMapper.selectById(SecurityContextHolder.getUserId());
//设置押金状态
userInfo.setDepositStatus("1");
this.updateById(userInfo);
return true;
}
扫码前判断:未完成订单
新建模块share-order、share-api-order


远程接口
OrderInfoApiController
java
package com.share.order.api;
import com.share.common.core.domain.R;
import com.share.common.core.web.controller.BaseController;
import com.share.common.core.web.domain.AjaxResult;
import com.share.common.security.annotation.RequiresLogin;
import com.share.common.security.utils.SecurityUtils;
import com.share.order.domain.OrderInfo;
import com.share.order.service.IOrderInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 订单Controller
*
* @author atguigu
* @date 2024-02-22
*/
@Tag(name = "订单接口管理")
@RestController
@RequestMapping("/orderInfo")
public class OrderInfoApiController extends BaseController
{
@Autowired
private IOrderInfoService orderInfoService;
@Operation(summary = "获取未完成订单")
@RequiresLogin
@GetMapping("getNoFinishOrder")
public AjaxResult getNoFinishOrder() {
return success(orderInfoService.getNoFinishOrder(SecurityUtils.getUserId()));
}
@Operation(summary = "获取未完成订单")
@GetMapping("getNoFinishOrder/{userId}")
public R<OrderInfo> getNoFinishOrder(@PathVariable Long userId) {
return R.ok(orderInfoService.getNoFinishOrder(userId));
}
}
IOrderInfoService
java
//获取未完成订单
OrderInfo getNoFinishOrder(Long userId);
OrderInfoServiceImpl
java
package com.share.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.share.order.domain.OrderInfo;
import com.share.order.mapper.OrderInfoMapper;
import com.share.order.service.IOrderInfoService;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* 订单Service业务层处理
*
* @author atguigu
* @date 2024-02-22
*/
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
//获取未完成订单
@Override
public OrderInfo getNoFinishOrder(Long userId) {
// 查询用户是否有使用中与未支付订单
LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderInfo::getUserId, userId)
.in(OrderInfo::getStatus, Arrays.asList("0", "1"))// 订单状态:0:充电中 1:未支付 2:已支付
.orderByDesc(OrderInfo::getId)
.last("limit 1");
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
return orderInfo;
}
}
搭建订单服务远程接口模块
操作模块:share-api-order
pom.xml
java
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>com.share</groupId>
<artifactId>share-common-core</artifactId>
</dependency>
</dependencies>
pom.xml
share-order模块引入api依赖
java
<dependency>
<groupId>com.share</groupId>
<artifactId>share-api-order</artifactId>
<version>3.6.3</version>
</dependency>
实体类OrderInfo、OrderBill、UserInfoVo
openFeign接口定义

RemoteOrderInfoService
java
package com.share.order.api;
import com.share.common.core.domain.R;
import com.share.order.domain.OrderInfo;
import com.share.order.factory.RemoteOrderInfoFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 用户服务
*
* @author share
*/
@FeignClient(contextId = "remoteOrderInfoService",
value = "share-order",
fallbackFactory = RemoteOrderInfoFallbackFactory.class)
public interface RemoteOrderInfoService {
/**
* 获取用户未完成订单信息
*
* @param userId 用户ID,通过路径变量传递
* @return 返回一个Result对象,其中包含OrderInfo类型的未完成订单数据
*/
@GetMapping("/orderInfo/getNoFinishOrder/{userId}")
public R<OrderInfo> getNoFinishOrder(@PathVariable("userId") Long userId);
}
ServiceNameConstants
java
public static final String ORDER_SERVICE = "share-order";
RemoteOrderInfoFallbackFactory
java
package com.share.order.factory;
import com.share.common.core.exception.ServiceException;
import com.share.order.api.RemoteOrderInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 用户服务降级处理
*
* @author share
*/
@Component
public class RemoteOrderInfoFallbackFactory implements FallbackFactory<RemoteOrderInfoService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteOrderInfoFallbackFactory.class);
@Override
public RemoteOrderInfoService create(Throwable throwable)
{
log.error("订单服务调用失败:{}", throwable.getMessage());
throw new ServiceException("获取用户未完成订单失败");
}
}
加载配置类
resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
java
com.share.order.api.factory.RemoteOrderInfoFallbackFactory
基础接口封装
根据柜号获取对应的柜体对象getBtCabinetNo
ICabinetService
java
/**
* 根据柜号获取对应的柜体对象
*/
Cabinet getBtCabinetNo(String cabinetNo);
CabinetServiceImpl
实现方法
java
@Override
/**
* 根据柜号获取柜子信息
* @param cabinetNo 柜号
* @return Cabinet 返回匹配的柜子对象,如果没有找到则返回null
*/
public Cabinet getBtCabinetNo(String cabinetNo) {
LambdaQueryWrapper<Cabinet> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件:柜号等于传入的cabinetNo
queryWrapper.eq(Cabinet::getCabinetNo, cabinetNo);
// 执行查询并返回结果
Cabinet cabinet = cabinetMapper.selectOne(queryWrapper);
return cabinet;
}
根据机柜ID和槽位号获取槽位信息getBtSlotNo
ICabinetSlotService
java
CabinetSlot getBtSlotNo(Long cabinetId, String slotNo);
CabinetSlotServiceImpl
java
package com.share.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.share.device.domain.CabinetSlot;
import com.share.device.mapper.CabinetSlotMapper;
import com.share.device.service.ICabinetSlotService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 柜机插槽Service业务层处理
*
* @author atguigu
* @date 2024-10-22
*/
@Service
public class CabinetSlotServiceImpl extends ServiceImpl<CabinetSlotMapper, CabinetSlot> implements ICabinetSlotService {
@Autowired
private CabinetSlotMapper cabinetSlotMapper;
@Override
public CabinetSlot getBtSlotNo(Long cabinetId, String slotNo) {
LambdaQueryWrapper<CabinetSlot> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CabinetSlot::getCabinetId, cabinetId)
.eq(CabinetSlot::getSlotNo, slotNo);
CabinetSlot cabinetSlot = cabinetSlotMapper.selectOne(queryWrapper);
return cabinetSlot;
}
}
根据充电宝编号获取充电宝信息getByPowerBankNo
IPowerBankService
java
//根据充电宝编号获取充电宝信息
PowerBank getByPowerBankNo(String powerBankNo);
PowerBankServiceImpl
java
/**
* 根据充电宝编号查询充电宝信息
*
* @param powerBankNo 充电宝编号
* @return 返回查询到的充电宝对象,如果未找到则返回null
*/
@Override
public PowerBank getByPowerBankNo(String powerBankNo) {
LambdaQueryWrapper<PowerBank> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件:充电宝编号等于传入的powerBankNo
queryWrapper.eq(PowerBank::getPowerBankNo, powerBankNo);
// 执行查询并返回结果
PowerBank powerBank = powerBankMapper.selectOne(queryWrapper);
return powerBank;
}
扫码业务接口
DeviceApiController
java
//扫码充电接口
@Operation(summary = "扫码充电")
@RequiresLogin
@GetMapping("scanCharge/{cabinetNo}")
public AjaxResult scanCharge(@PathVariable String cabinetNo) {
return success(deviceService.scanCharge(cabinetNo));
}
ScanChargeVo
java
package com.share.device.domain;
@Data
@Schema(description = "扫码充电返回对象")
public class ScanChargeVo
{
/** 状态(1是 0否) */
@Schema(description = "状态:1:成功 2:有未归还充电宝 3:有未支付订单")
private String status;
@Schema(description = "消息")
private String message;
}
IDeviceService
在'IDeviceService'中创建方法'scanCharge
java
/**
* 扫描充电操作的方法
*/
ScanChargeVo scanCharge(String cabinetNo);
添加依赖pom.xml
java
<dependency>
<groupId>com.share</groupId>
<artifactId>share-api-order</artifactId>
<version>3.6.3</version>
</dependency>
RemoteUserService.java
java
/**
* 根据用户ID获取用户信息的接口方法
*
* @param id 用户ID,通过路径变量传递
* @return 返回一个R对象,其中包含UserInfo类型的用户数据
*/
@GetMapping(value = "/userInfo/getUserInfo/{id}")
public R<UserInfo> getInfo(@PathVariable("id") Long id);
DeviceServiceImpl
实现方法
java
@Autowired
private RemoteUserService remoteUserService;
@Autowired
private RemoteOrderInfoService remoteOrderInfoService;
@Autowired
private EmqxClientWrapper emqxClientWrapper;
@Override
public ScanChargeVo scanCharge(String cabinetNo) {
//1 远程调用:根据当前登录用户id查询用户信息,
// 从用户信息获取是否支持免押金充电
R<UserInfo> userInfoR = remoteUserService.getInfo(SecurityContextHolder.getUserId());
UserInfo userInfo = userInfoR.getData();
//判断用户信息
if(userInfo == null) {
throw new ServiceException("获取用户信息失败");
}
//判断是否免押金
if("0".equals(userInfo.getDepositStatus())) {
throw new ServiceException("未申请免押金使用");
}
ScanChargeVo scanChargeVo = new ScanChargeVo();
//2 远程调用:判断用户是否有未完成订单
R<OrderInfo> orderInfoR = remoteOrderInfoService.getNoFinishOrder(SecurityUtils.getUserId());
OrderInfo orderInfo = orderInfoR.getData();
if(orderInfo != null) {//有 未完成订单
String status = userInfo.getStatus();
if("0".equals(status)) {
scanChargeVo.setStatus("2");
scanChargeVo.setMessage("有未归还充电宝,请归还后使用");
return scanChargeVo;
}
if("1".equals(status)) {
scanChargeVo.setStatus("3");
scanChargeVo.setMessage("有未支付订单,去支付");
return scanChargeVo;
}
}
//3 从柜机里面获取最优充电宝
AvailableProwerBankVo availableProwerBankVo =
this.checkAvailableProwerBank(cabinetNo);
if(null == availableProwerBankVo) {
throw new ServiceException("无可用充电宝");
}
if(!StringUtils.isEmpty(availableProwerBankVo.getErrMessage())) {
throw new ServiceException(availableProwerBankVo.getErrMessage());
}
//4 把选择最优充电宝弹出
// 使用MQTT弹出充电宝
// 生成借取指令,弹出充电宝
JSONObject object = new JSONObject();
object.put("uId", SecurityContextHolder.getUserId());//SecurityUtils.getUserId()
object.put("mNo", "mm"+ RandomUtil.randomString(8));
object.put("cNo", cabinetNo);
object.put("pNo", availableProwerBankVo.getPowerBankNo());
object.put("sNo", availableProwerBankVo.getSlotNo());
String topic = String.format(EmqxConstants.TOPIC_SCAN_SUBMIT, cabinetNo);
String message = ProtocolConvertUtil.convertString(object);
emqxClientWrapper.publish(topic, message);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//5 返回封装需要数据
scanChargeVo.setStatus("1");
return scanChargeVo;
}
@Autowired
private ICabinetSlotService cabinetSlotService;
@Autowired
private IPowerBankService powerBankService;
//获取柜机的充电宝信息
private AvailableProwerBankVo checkAvailableProwerBank(String cabinetNo) {
//1 创建AvailableProwerBankVo对象
AvailableProwerBankVo availableProwerBankVo = new AvailableProwerBankVo();
//2 根据cabinetNo柜机编号查询柜机信息
Cabinet cabinet = cabinetService.getBtCabinetNo(cabinetNo);
//3 判断柜机可用充电宝数量是否大于0
Integer availableNum = cabinet.getAvailableNum();
if(availableNum == 0) {
availableProwerBankVo.setErrMessage("无可用充电宝");
return availableProwerBankVo;
}
//4 根据柜机id查询插槽列表,返回list集合
LambdaQueryWrapper<CabinetSlot> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CabinetSlot::getCabinetId,cabinet.getId());
wrapper.eq(CabinetSlot::getStatus, "1");
List<CabinetSlot> cabinetSlotList = cabinetSlotService.list(wrapper);
//5 从第四步返回插槽列表list集合 获取对应充电宝id集合
List<Long> powerBankIdList = cabinetSlotList.stream()
.filter(item -> null != item.getPowerBankId())
.map(CabinetSlot::getPowerBankId).collect(Collectors.toList());
//6 根据充电宝id列表查询对应充电宝信息
LambdaQueryWrapper<PowerBank> wrapperPowerBank = new LambdaQueryWrapper<>();
wrapperPowerBank.in(PowerBank::getId,powerBankIdList);
wrapperPowerBank.eq(PowerBank::getStatus,"1");
List<PowerBank> powerBankList = powerBankService.list(wrapperPowerBank);
//判断集合不为空
if(CollectionUtils.isEmpty(powerBankList)) {
availableProwerBankVo.setErrMessage("无可用充电宝");
return availableProwerBankVo;
}
//7 把上一步获取充电宝信息集合进行排序(根据电量降序)
if(powerBankList.size()>1) {
Collections.sort(powerBankList,
(o1,o2)->o2.getElectricity().compareTo(o1.getElectricity()));
}
//8 获取电量最多充电宝信息
PowerBank powerBank = powerBankList.get(0);
//9 获取电量最多的充电宝对应插槽信息
CabinetSlot cabinetSlot = cabinetSlotList.stream()
.filter(item -> null != item.getPowerBankId()
&& item.getPowerBankId().equals(powerBank.getId())).collect(Collectors.toList()).get(0);
//10 锁定插槽(更新插槽状态)
cabinetSlot.setStatus("2");
cabinetSlotService.updateById(cabinetSlot);
//11 返回需要vo数据
availableProwerBankVo.setPowerBankNo(powerBank.getPowerBankNo());
availableProwerBankVo.setSlotNo(cabinetSlot.getSlotNo());
return availableProwerBankVo;
}

充电宝弹出业务处理
PowerBankUnlockHandler
java
package com.share.device.emqx.handler.impl;
import com.alibaba.fastjson2.JSONObject;
import com.share.common.core.utils.StringUtils;
import com.share.device.domain.Cabinet;
import com.share.device.domain.CabinetSlot;
import com.share.device.domain.PowerBank;
import com.share.device.domain.Station;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import com.share.device.service.ICabinetService;
import com.share.device.service.ICabinetSlotService;
import com.share.device.service.IPowerBankService;
import com.share.device.service.IStationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_POWERBANK_UNLOCK)
public class PowerBankUnlockHandler implements MassageHandler {
@Autowired
private ICabinetService cabinetService;
@Autowired
private IPowerBankService powerBankService;
@Autowired
private ICabinetSlotService cabinetSlotService;
@Autowired
private IStationService stationService;
@Autowired
private RedisTemplate redisTemplate;
@Transactional(rollbackFor = Exception.class)
@Override
public void handleMessage(JSONObject message) {
log.info("handleMessage: {}", message.toJSONString());
//消息编号
String messageNo = message.getString("mNo");
//防止重复请求
String key = "powerBank:unlock:" + messageNo;
boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, messageNo, 1, TimeUnit.HOURS);
if (!isExist) {
log.info("重复请求: {}", message.toJSONString());
return;
}
//柜机编号
String cabinetNo = message.getString("cNo");
//充电宝编号
String powerBankNo = message.getString("pNo");
//插槽编号
String slotNo = message.getString("sNo");
//用户id
Long userId = message.getLong("uId");
if (StringUtils.isEmpty(cabinetNo)
|| StringUtils.isEmpty(powerBankNo)
|| StringUtils.isEmpty(slotNo)
|| null == userId) {
log.info("参数为空: {}", message.toJSONString());
return;
}
//获取柜机
Cabinet cabinet = cabinetService.getBtCabinetNo(cabinetNo);
// 获取充电宝
PowerBank powerBank = powerBankService.getByPowerBankNo(powerBankNo);
// 获取插槽
CabinetSlot cabinetSlot = cabinetSlotService.getBtSlotNo(cabinet.getId(), slotNo);
// 获取站点
Station station = stationService.getByCabinetId(cabinet.getId());
//更新充电宝状态
// 状态(0:未投放 1:可用 2:已租用 3:充电中 4:故障)
powerBank.setStatus("2");
powerBankService.updateById(powerBank);
//更新插槽状态
// 状态(1:占用 0:空闲)
cabinetSlot.setStatus("0");
cabinetSlot.setPowerBankId(null);
cabinetSlot.setUpdateTime(new Date());
cabinetSlotService.updateById(cabinetSlot);
//更新柜机信息
int freeSlots = cabinet.getFreeSlots() + 1;
cabinet.setFreeSlots(freeSlots);
int usedSlots = cabinet.getUsedSlots() - 1;
cabinet.setUsedSlots(usedSlots);
//可以借用
int availableNum = cabinet.getAvailableNum() - 1;
cabinet.setAvailableNum(availableNum);
cabinet.setUpdateTime(new Date());
cabinetService.updateById(cabinet);
//发送消息构建订单
//TODO
}
}
IStationService
在'StationService'中创建方法'getByCabinetld'
java
/**
* 根据机柜ID获取站点信息
*
* @param id 机柜ID,类型为Long
* @return 返回对应的站点对象
*/
Station getByCabinetId(Long id);
StationServiceI
实现方法
java
/**
* 根据柜子ID获取站点信息
*
* @param id 柜子ID
* @return 返回对应的站点对象,如果不存在则返回null
*/
@Override
public Station getByCabinetId(Long id) {
LambdaQueryWrapper<Station> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件:柜子ID等于传入的id
queryWrapper.eq(Station::getCabinetId, id);
// 执行查询,获取符合条件的站点信息
Station station = stationMapper.selectOne(queryWrapper);
// 返回查询结果
return station;
}
订单详情接口
OrderInfoApiController
java
@Operation(summary = "获取订单详细信息")
@RequiresLogin
@GetMapping(value = "/getOrderInfo/{id}")
public AjaxResult getOrderInfo(@PathVariable("id") Long id)
{
return success(orderInfoService.selectOrderInfoById(id));
}
IOrderInfoService
在'IOrderlnfoService'中创建方法'selectOrderlnfoByld'
java
/**
* 根据ID查询订单信息
*
* @param id 订单ID
* @return 返回订单信息对象
*/
Object selectOrderInfoById(Long id);
实现方法
java
//获取订单详细信息
@Override
public Object selectOrderInfoById(Long id) {
//根据id查询订单信息
OrderInfo orderInfo = baseMapper.selectById(id);
//判断订单状态充电中 0
if ("0".equals(orderInfo.getStatus())) {
//计算充电时间
// 计算从订单开始时间到现在经过的分钟数
// 使用Minutes类的minutesBetween方法计算两个DateTime对象之间的分钟差
int minutes = Minutes.minutesBetween(new DateTime(orderInfo.getStartTime()), // 订单的开始时间
new DateTime()).getMinutes(); // 当前时间
//充电时间大于0
if (minutes > 0) {
orderInfo.setDuration(minutes);
FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
feeRuleRequestForm.setDuration(minutes);
feeRuleRequestForm.setFeeRuleId(orderInfo.getFeeRuleId());
R<FeeRuleResponseVo> feeRuleResponseVoR = remoteFeeRuleService.calculateOrderFee(feeRuleRequestForm);
FeeRuleResponseVo responseVo = feeRuleResponseVoR.getData();
//设置到orderInfo里面
orderInfo.setTotalAmount(responseVo.getTotalAmount());
orderInfo.setDeductAmount(new BigDecimal(0));
orderInfo.setRealAmount(responseVo.getTotalAmount());
} else {
orderInfo.setDuration(0);
orderInfo.setTotalAmount(new BigDecimal(0));
orderInfo.setDeductAmount(new BigDecimal(0));
orderInfo.setRealAmount(new BigDecimal(0));
}
}
//OrderBill
List<OrderBill> orderBillList = orderBillMapper.selectList(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderId, id));
orderInfo.setOrderBillList(orderBillList);
R<UserInfo> userInfoR = remoteUserInfoService.getInfo(orderInfo.getUserId());
UserInfo userInfo = userInfoR.getData();
UserInfoVo userInfoVo = new UserInfoVo();
BeanUtils.copyProperties(userInfo, userInfoVo);
orderInfo.setUserInfoVo(userInfoVo);
//返回对象
return orderInfo;
}