diff --git a/db/qywecahtpush.sql b/db/qywecahtpush.sql index 856be1b..834ee77 100644 --- a/db/qywecahtpush.sql +++ b/db/qywecahtpush.sql @@ -35,3 +35,19 @@ CREATE TABLE api_setting( INSERT INTO api_setting(id,phone_number,access_token,msgtype,touser,agentid,enable_duplicate_check,duplicate_check_interval) VALUES (1,'18281561650','gKGCDSgWV82XbU0H','textcard','@all',1000002,1,1800); + +CREATE TABLE device_info ( + id INT PRIMARY KEY AUTO_INCREMENT, + is_Monitored INT, + time_out_period BIGINT, + device_brand VARCHAR(255), + android_version VARCHAR(255), + sn VARCHAR(255), + device_model VARCHAR(255), + last_online_time BIGINT, + status INT +); + +INSERT INTO device_info (is_monitored, time_out_period, device_brand, android_version, sn, device_model, last_online_time, status) VALUES + (1, 600, 'google', 'Android 10', UNIX_TIMESTAMP(), 1), + diff --git a/src/main/java/com/kimgo/wepush/WePushApplication.java b/src/main/java/com/kimgo/wepush/WePushApplication.java index 55b3fe0..407717c 100644 --- a/src/main/java/com/kimgo/wepush/WePushApplication.java +++ b/src/main/java/com/kimgo/wepush/WePushApplication.java @@ -3,8 +3,10 @@ package com.kimgo.wepush; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling @MapperScan("com.kimgo.wepush.mapper") public class WePushApplication { diff --git a/src/main/java/com/kimgo/wepush/controller/HeartbeatController.java b/src/main/java/com/kimgo/wepush/controller/HeartbeatController.java new file mode 100644 index 0000000..05c1515 --- /dev/null +++ b/src/main/java/com/kimgo/wepush/controller/HeartbeatController.java @@ -0,0 +1,32 @@ +package com.kimgo.wepush.controller; + +import com.kimgo.wepush.model.CallInfo; +import com.kimgo.wepush.model.DeviceInfo; +import com.kimgo.wepush.response.ServerResponseEntity; +import com.kimgo.wepush.service.HeartBeatService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HeartbeatController { + @Autowired + private HeartBeatService heartBeatService; + + @PostMapping("/heartbeat") + public ServerResponseEntity receiveHeartbeat(@RequestHeader("accessToken") String accessToken, + @RequestBody @Valid DeviceInfo deviceInfo) { + // 处理心跳请求 + // 更新客户端的“最后活跃时间” + if(accessToken == null){ + return ServerResponseEntity.fail("accessToken cannot be empty."); + } + if (deviceInfo == null || deviceInfo.hasInvalidFields()){ + return ServerResponseEntity.fail("json body value error."); + } + return heartBeatService.handleHeartbeatSignal(accessToken,deviceInfo); + } +} \ No newline at end of file diff --git a/src/main/java/com/kimgo/wepush/mapper/ApiSettingMapper.java b/src/main/java/com/kimgo/wepush/mapper/ApiSettingMapper.java index 14e8ea8..0ef666c 100644 --- a/src/main/java/com/kimgo/wepush/mapper/ApiSettingMapper.java +++ b/src/main/java/com/kimgo/wepush/mapper/ApiSettingMapper.java @@ -2,6 +2,7 @@ package com.kimgo.wepush.mapper; import com.kimgo.wepush.model.ApiSetting; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; /** * @author wangsiyuan @@ -9,6 +10,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; * @createDate 2023-12-05 01:25:35 * @Entity com.kimgo.wepush.model.ApiSetting */ +@Mapper public interface ApiSettingMapper extends BaseMapper { } diff --git a/src/main/java/com/kimgo/wepush/mapper/DeviceInfoDAOMapper.java b/src/main/java/com/kimgo/wepush/mapper/DeviceInfoDAOMapper.java new file mode 100644 index 0000000..bf69f58 --- /dev/null +++ b/src/main/java/com/kimgo/wepush/mapper/DeviceInfoDAOMapper.java @@ -0,0 +1,10 @@ +package com.kimgo.wepush.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.kimgo.wepush.model.ApiSetting; +import com.kimgo.wepush.model.DeviceInfoDAO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface DeviceInfoDAOMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/com/kimgo/wepush/mapper/QyWeChatAppInfoMapper.java b/src/main/java/com/kimgo/wepush/mapper/QyWeChatAppInfoMapper.java index f70f9a1..0b934e0 100644 --- a/src/main/java/com/kimgo/wepush/mapper/QyWeChatAppInfoMapper.java +++ b/src/main/java/com/kimgo/wepush/mapper/QyWeChatAppInfoMapper.java @@ -2,6 +2,8 @@ package com.kimgo.wepush.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.kimgo.wepush.model.QyWeChatAppInfo; +import org.apache.ibatis.annotations.Mapper; +@Mapper public interface QyWeChatAppInfoMapper extends BaseMapper { } diff --git a/src/main/java/com/kimgo/wepush/mapper/QyWeChatURLMapper.java b/src/main/java/com/kimgo/wepush/mapper/QyWeChatURLMapper.java index 52b4e3e..2bc620f 100644 --- a/src/main/java/com/kimgo/wepush/mapper/QyWeChatURLMapper.java +++ b/src/main/java/com/kimgo/wepush/mapper/QyWeChatURLMapper.java @@ -4,5 +4,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.kimgo.wepush.model.QyWeChatURL; import org.apache.ibatis.annotations.Mapper; +@Mapper public interface QyWeChatURLMapper extends BaseMapper { } diff --git a/src/main/java/com/kimgo/wepush/model/DeviceInfo.java b/src/main/java/com/kimgo/wepush/model/DeviceInfo.java new file mode 100644 index 0000000..d58a634 --- /dev/null +++ b/src/main/java/com/kimgo/wepush/model/DeviceInfo.java @@ -0,0 +1,24 @@ +package com.kimgo.wepush.model; + +import lombok.Data; + +@Data +public class DeviceInfo { + private String androidVersion; + private String deviceBrand; + private String SN; + private String deviceModel; + public boolean hasInvalidFields() { + return isNullOrInvalid(androidVersion) || isNullOrInvalid(SN) || isNullOrInvalid(deviceModel) || isNullOrInvalid(deviceBrand); + } + + /** + * 检查给定的字符串是否为 null、"null" 或空字符串。 + * + * @param value 要检查的字符串。 + * @return 如果字符串为 null、"null" 或空字符串,则返回 true;否则返回 false。 + */ + private boolean isNullOrInvalid(String value) { + return value == null || value.equals("null") || value.isEmpty(); + } +} diff --git a/src/main/java/com/kimgo/wepush/model/DeviceInfoDAO.java b/src/main/java/com/kimgo/wepush/model/DeviceInfoDAO.java new file mode 100644 index 0000000..97ccb8f --- /dev/null +++ b/src/main/java/com/kimgo/wepush/model/DeviceInfoDAO.java @@ -0,0 +1,23 @@ +package com.kimgo.wepush.model; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@Data +@TableName("device_info") +public class DeviceInfoDAO { + @TableId(type = IdType.AUTO) + private Integer id; + private int isMonitored; + private long timeOutPeriod; + private String deviceBrand; + private String androidVersion; + @TableField("sn") + private String SN; + private String deviceModel; + private long lastOnlineTime; + private int status; +} diff --git a/src/main/java/com/kimgo/wepush/request/Request.java b/src/main/java/com/kimgo/wepush/request/Request.java new file mode 100644 index 0000000..990b5ae --- /dev/null +++ b/src/main/java/com/kimgo/wepush/request/Request.java @@ -0,0 +1,60 @@ +package com.kimgo.wepush.request; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.kimgo.wepush.model.TextCardMessage; +import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse; +import com.kimgo.wepush.service.QyWeChatURLService; +import com.kimgo.wepush.service.TokenService; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; + +public class Request { + private final Logger logger = LoggerFactory.getLogger(Request.class); + + public QyWeChatSendMessageApiResponse okhttpRequest(String url,String accessToken,T object){ + OkHttpClient client = new OkHttpClient(); + + // 使用Jackson进行序列化 + ObjectMapper objectMapper = new ObjectMapper(); + String jsonBody = null; + try { + jsonBody = objectMapper.writeValueAsString(object); + logger.info("jsonBody: {}",jsonBody); + } catch (JsonProcessingException e) { + logger.error("JSON processing error", e); + return null; + } + // 构建请求体 + RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonBody); + okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(body).build(); + try (Response response = client.newCall(request).execute()) { + String responseBody = response.body().string(); + logger.info("request sendMessage api ResponseBody: {}", responseBody); + + ObjectMapper objectMapper1 = new ObjectMapper(); + QyWeChatSendMessageApiResponse apiResponse = objectMapper1.readValue(responseBody, QyWeChatSendMessageApiResponse.class); + + if (apiResponse.getErrcode() == 0) { + logger.debug("Request qyWeChat Success."); + return apiResponse; + } else if (apiResponse.getErrcode() == 42001 || apiResponse.getErrcode() == 40014) { + logger.info("Access token expired. Refreshing token..."); + // 一个方法来刷新accessToken + return apiResponse; + } else { + // 处理其他错误情况 + logger.error("Error: {}", apiResponse.getErrmsg()); + return null; + } + } catch (IOException e) { + logger.error("OkHttp request error", e); + return null; + } + } +} diff --git a/src/main/java/com/kimgo/wepush/response/ServerResponseEntity.java b/src/main/java/com/kimgo/wepush/response/ServerResponseEntity.java index 445dbbf..0300a29 100644 --- a/src/main/java/com/kimgo/wepush/response/ServerResponseEntity.java +++ b/src/main/java/com/kimgo/wepush/response/ServerResponseEntity.java @@ -62,8 +62,9 @@ public class ServerResponseEntity implements Serializable { public static ServerResponseEntity success(){ ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); serverResponseEntity.setCode(ResponseEnum.OK.value()); - serverResponseEntity.getMsg(); + serverResponseEntity.setMsg(ResponseEnum.OK.getMsg()); serverResponseEntity.setTimestamp(Instant.now().getEpochSecond()); + serverResponseEntity.setData(null); return serverResponseEntity; } diff --git a/src/main/java/com/kimgo/wepush/service/CallService.java b/src/main/java/com/kimgo/wepush/service/CallService.java index 0320bd8..47f1900 100644 --- a/src/main/java/com/kimgo/wepush/service/CallService.java +++ b/src/main/java/com/kimgo/wepush/service/CallService.java @@ -2,7 +2,6 @@ package com.kimgo.wepush.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.kimgo.wepush.common.PhoneNumberValidation; import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse; import com.kimgo.wepush.model.CallInfo; import com.kimgo.wepush.model.TextCardMessage; diff --git a/src/main/java/com/kimgo/wepush/service/HeartBeatService.java b/src/main/java/com/kimgo/wepush/service/HeartBeatService.java new file mode 100644 index 0000000..8957eed --- /dev/null +++ b/src/main/java/com/kimgo/wepush/service/HeartBeatService.java @@ -0,0 +1,119 @@ +package com.kimgo.wepush.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.kimgo.wepush.mapper.DeviceInfoDAOMapper; +import com.kimgo.wepush.model.DeviceInfo; +import com.kimgo.wepush.model.DeviceInfoDAO; +import com.kimgo.wepush.response.ServerResponseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class HeartBeatService { + private final Logger logger = LoggerFactory.getLogger(HeartBeatService.class); + private final long TIME_OUT_PERIOD = 600; + @Autowired + TokenService tokenService; + @Autowired + private DeviceInfoDAOMapper deviceInfoDAOMapper; + public ServerResponseEntity handleHeartbeatSignal(String accessToken,DeviceInfo deviceInfo) { + String correctAccessToken = tokenService.getApiAccessToken(); + logger.info("accessToken: {} correctAccessToken: {}",accessToken,correctAccessToken); + if (!correctAccessToken.equals(accessToken)){ + return ServerResponseEntity.fail("Invalid accessToken"); + } + DeviceInfoDAO deviceInfoDAO = getDeviceInfoBySN(deviceInfo.getSN()); + if (deviceInfoDAO == null) { + // 设备信息不存在,可能是无效的设备 + boolean isSuccess = addDeviceInfo(deviceInfo); + if (isSuccess){ + return ServerResponseEntity.success(); + } else { + logger.info("add to mysql error"); + } + } + try { + updateClientStatus(deviceInfo); + return ServerResponseEntity.success(); + } catch (Exception e) { + logger.error("update client status error."); + // 处理更新状态时的异常 + return ServerResponseEntity.fail("Error updating client status"); + } + } + + + public void updateClientStatus(DeviceInfo deviceInfo) { + long currentTimeMillis = System.currentTimeMillis(); + // 更新该客户端的最后活跃时间 + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("sn", deviceInfo.getSN()); + updateWrapper.set("last_online_time", currentTimeMillis); // 设置新的 accessToken + + int result = deviceInfoDAOMapper.update(null, updateWrapper); + if (result > 0) { + logger.debug("Update successful"); + } else { + logger.warn("Update failed: No rows affected"); + } + } + + public boolean addDeviceInfo(DeviceInfo deviceInfo) { + try { + DeviceInfoDAO deviceInfoDAO = convertToDeviceInfoDAO(deviceInfo); + int result = deviceInfoDAOMapper.insert(deviceInfoDAO); + logger.info("result: {}",result); + return result > 0; + } catch (Exception e) { + logger.error("Error adding device info", e); + return false; + } + } + + /** + * 根据 androidId 查询 DeviceInfo。 + * + * @param SN 要查询的 SN。 + * @return 与 androidId 对应的 DeviceInfoDAO 对象,如果没有找到,则返回 null。 + */ + public DeviceInfoDAO getDeviceInfoBySN(String SN) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("sn", SN); + try { + DeviceInfoDAO deviceInfoDAO = deviceInfoDAOMapper.selectOne(queryWrapper); + if (deviceInfoDAO != null) { + logger.info("Data is queried in the database based on the device id"); + return deviceInfoDAO; + } + } catch (Exception e) { + logger.error("Error querying device info by android ID", e); + } + return null; + } + + + /** + * 将 DeviceInfo 转换为 DeviceInfoDAO。 + * + * @param deviceInfo 要转换的 DeviceInfo 对象。 + * @return 转换后的 DeviceInfoDAO 对象。 + */ + private DeviceInfoDAO convertToDeviceInfoDAO(DeviceInfo deviceInfo) { + // 这里需要实现将 DeviceInfo 转换为 DeviceInfoDAO 的逻辑 + // 示例代码(根据实际字段调整): + DeviceInfoDAO deviceInfoDAO = new DeviceInfoDAO(); + deviceInfoDAO.setIsMonitored(1); + deviceInfoDAO.setDeviceBrand(deviceInfo.getDeviceBrand()); + deviceInfoDAO.setSN(deviceInfo.getSN()); + deviceInfoDAO.setDeviceModel(deviceInfo.getDeviceModel()); + deviceInfoDAO.setAndroidVersion(deviceInfo.getAndroidVersion()); + deviceInfoDAO.setTimeOutPeriod(TIME_OUT_PERIOD); + deviceInfoDAO.setLastOnlineTime(System.currentTimeMillis()); + deviceInfoDAO.setStatus(1); + return deviceInfoDAO; + } +} diff --git a/src/main/java/com/kimgo/wepush/service/MonitorService.java b/src/main/java/com/kimgo/wepush/service/MonitorService.java new file mode 100644 index 0000000..89a6cb6 --- /dev/null +++ b/src/main/java/com/kimgo/wepush/service/MonitorService.java @@ -0,0 +1,131 @@ +package com.kimgo.wepush.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.kimgo.wepush.mapper.DeviceInfoDAOMapper; +import com.kimgo.wepush.model.DeviceInfoDAO; +import com.kimgo.wepush.model.TextCardMessage; +import com.kimgo.wepush.request.Request; +import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.List; + +@Service +public class MonitorService { + @Autowired + private ApiSettingService apiSettingService; + @Autowired + private QyWeChatURLService qyWeChatURLService; + @Autowired + private TokenService tokenService; + private final Logger logger = LoggerFactory.getLogger(MonitorService.class); + @Autowired + private DeviceInfoDAOMapper deviceInfoDAOMapper; + + @Scheduled(fixedDelay = 480000) + public void monitorOnlineDevices() { + List devices = getAllDevices(); + if (devices.isEmpty()) { + // 没有找到数据的处理逻辑 + logger.info("no data was queried."); + } + for (DeviceInfoDAO device : devices) { + try { + if (device.getIsMonitored() != 1){ + logger.info("device is not monitored ,device SN: {}",device.getSN()); + continue; + } + long currentTimeMillis = System.currentTimeMillis(); + if (currentTimeMillis - device.getLastOnlineTime() > device.getTimeOutPeriod()) { + updateStatus(device, 0); // 设备离线 + sendNotification(device); + } else { + updateStatus(device, 1); // 设备在线 + } + } catch (Exception e) { + logger.error("Error processing device with SN " + device.getSN(), e); + } + } + } + + /** + * 获取数据库中所有DeviceInfoDAO对象的id。 + * + * @return 包含所有id的列表。 + */ + private List getAllDevices() { + try { + // 查询所有DeviceInfoDAO对象 + List deviceInfoDAOs = deviceInfoDAOMapper.selectList(new QueryWrapper<>()); + // 提取并返回所有对象的id + return deviceInfoDAOs; + } catch (Exception e) { + logger.error("Error querying all device info IDs", e); + } + return Collections.emptyList(); + } + + + private void sendNotification(DeviceInfoDAO deviceInfoDAO){ + Request request = new Request(); + String accessToken = tokenService.getAccessToken(); + String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken; + QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = request.okhttpRequest(url,accessToken,setTextCardMessage(deviceInfoDAO)); + if (qyWeChatSendMessageApiResponse ==null){ + logger.info("Send Notification Fail"); + } + if (qyWeChatSendMessageApiResponse != null && qyWeChatSendMessageApiResponse.getErrcode() == 0) { + logger.info("Send Notification Success"); + } + } + public TextCardMessage setTextCardMessage(DeviceInfoDAO deviceInfoDAO) { + TextCardMessage textCardMessage = new TextCardMessage(); + + textCardMessage.setTouser(apiSettingService.getApiSetting().getTouser()); + textCardMessage.setMsgtype(apiSettingService.getApiSetting().getMsgtype()); + textCardMessage.setAgentid(apiSettingService.getApiSetting().getAgentid()); + textCardMessage.setEnable_duplicate_check(0); + + TextCardMessage.TextCard textCard = new TextCardMessage.TextCard(); + textCard.setTitle("设备掉线通知"); + String formattedCurrentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + textCard.setDescription("
设备信息
" + + "
品牌: " + deviceInfoDAO.getDeviceBrand() + "
" + + "
设备型号: " + deviceInfoDAO.getDeviceModel() + "
" + + "
安卓版本: " + deviceInfoDAO.getAndroidVersion() + "
" + + "
SN: " + deviceInfoDAO.getSN() + "
" + + "
监测到掉线时间: " + formattedCurrentTime + "
" + + "检测到设备已掉线,请及时查看设备状态."); + textCard.setUrl("https://kimgo.cn"); + // 将TextCard对象设置到TextCardMessage中 + textCardMessage.setTextcard(textCard); + + logger.info("TextCardMessage: {}", textCardMessage); + + return textCardMessage; + } + private void updateStatus(DeviceInfoDAO device, int status) { + try { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("SN", device.getSN()); + updateWrapper.set("status", status); + + int result = deviceInfoDAOMapper.update(null, updateWrapper); + if (result > 0) { + logger.debug("Update successful for SN: " + device.getSN()); + } else { + logger.warn("Update failed: No rows affected for SN: " + device.getSN()); + } + } catch (Exception e) { + logger.error("Error updating status for SN: " + device.getSN(), e); + } + } +} diff --git a/src/main/java/com/kimgo/wepush/service/QyWeChatURLService.java b/src/main/java/com/kimgo/wepush/service/QyWeChatURLService.java index 677cb43..0c0df9e 100644 --- a/src/main/java/com/kimgo/wepush/service/QyWeChatURLService.java +++ b/src/main/java/com/kimgo/wepush/service/QyWeChatURLService.java @@ -18,7 +18,6 @@ public class QyWeChatURLService { private QyWeChatURLMapper qyWeChatURLMapper; @Autowired private UserConfig userConfig; - private String urlName; private String sendTextCardMessageUrl; private String qyWechatGetTokenUrl; diff --git a/src/test/java/com/kimgo/wepush/service/HeartBeatServiceTest.java b/src/test/java/com/kimgo/wepush/service/HeartBeatServiceTest.java new file mode 100644 index 0000000..0cdd908 --- /dev/null +++ b/src/test/java/com/kimgo/wepush/service/HeartBeatServiceTest.java @@ -0,0 +1,31 @@ +package com.kimgo.wepush.service; + +import com.kimgo.wepush.model.DeviceInfo; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class HeartBeatServiceTest { + @Autowired + HeartBeatService heartBeatService; + @Test + void updateClientStatus() { + DeviceInfo deviceInfo = new DeviceInfo(); + deviceInfo.setDeviceModel("Model B"); + deviceInfo.setSerialNumber("SN1234567891"); + deviceInfo.setAndroidId("android_id_2"); + heartBeatService.updateClientStatus(deviceInfo); + } + + @Test + void addDeviceInfo() { + DeviceInfo deviceInfo = new DeviceInfo(); + deviceInfo.setDeviceModel("Model C"); + deviceInfo.setSerialNumber("SN1234567899"); + deviceInfo.setAndroidId("dadwdwfcev"); + heartBeatService.addDeviceInfo(deviceInfo); + } +} \ No newline at end of file diff --git a/src/test/java/com/kimgo/wepush/service/MonitorServiceTest.java b/src/test/java/com/kimgo/wepush/service/MonitorServiceTest.java new file mode 100644 index 0000000..844ab75 --- /dev/null +++ b/src/test/java/com/kimgo/wepush/service/MonitorServiceTest.java @@ -0,0 +1,17 @@ +package com.kimgo.wepush.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MonitorServiceTest { + @Autowired + private MonitorService monitorService; + + @Test + void monitorOnlineDevices() { + + monitorService.monitorOnlineDevices(); + } +} \ No newline at end of file