Compare commits
109 Commits
97e480a873
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b67d8ce01 | |||
| d6acc01422 | |||
| f27abbd081 | |||
| fa1c872897 | |||
| 9462f5caab | |||
| 7020497109 | |||
| 07be57d5cf | |||
| 7a8fdc2f4e | |||
| bbb7bedc94 | |||
| f9c47e25e4 | |||
| 3d01fffebe | |||
| 2f424e837d | |||
| f89bbbc551 | |||
| 03e6decd71 | |||
| 919e75f2b1 | |||
| e8e7d397d5 | |||
| 7fef7a0b1d | |||
| 8ac1e83691 | |||
| a18a200a4b | |||
| 5a7e05c6ca | |||
| ef38dc1413 | |||
| f5d120817d | |||
| c18e126dfd | |||
| 69800cd27b | |||
| 9eb3f6c61c | |||
| dd1ca0bef7 | |||
| e826383b4f | |||
| d8e207eda7 | |||
| fcf84f81d6 | |||
| e0b0c62871 | |||
| 4fefc8136f | |||
| 3ebb79170c | |||
| b9c55d3928 | |||
| 5f8f281bec | |||
| 48efd66cbf | |||
| 2bd7e21741 | |||
| 4e4084e15d | |||
| f74a1cf019 | |||
| a67580c387 | |||
| 19ca3c8ae9 | |||
| b277822315 | |||
| 71d5b10011 | |||
| 3e5eae24e6 | |||
| 531c759230 | |||
| c0fa4b79fb | |||
| d0456431d6 | |||
| f540a68ae9 | |||
| fbaf0b5888 | |||
| c7284d2c8e | |||
| 345199f48a | |||
| 941f10f9cc | |||
| b64e124419 | |||
| dc366f16a5 | |||
| 9668802a46 | |||
| 28c97c4819 | |||
| 2834117f0f | |||
| e1ea384d94 | |||
| e1db480067 | |||
| 4b7b719563 | |||
| e9272c2b44 | |||
| 82356f0fb4 | |||
| 42133da898 | |||
| f7d7e9e551 | |||
| 6139d0b080 | |||
| 56fb88a0c0 | |||
| d95a725934 | |||
| 8772a041f3 | |||
| ae889a2447 | |||
| ceaf3ac24d | |||
| fb1022ad98 | |||
| 31f4d13a7a | |||
| ff590d8c05 | |||
| 645c2ddd90 | |||
| 5478362706 | |||
| 62c056ef8c | |||
| a53cfbab00 | |||
| 058649bcaa | |||
| d8720df188 | |||
| 4c15732ec3 | |||
| 75c58c5379 | |||
| 6609c2eb4d | |||
| c2bf0b3040 | |||
| c9106a2b94 | |||
| b315a4b2df | |||
| 4f1a67f622 | |||
| 96ccb45a7f | |||
| bac5cf2ff9 | |||
| 7d4ee71add | |||
| b86f94e1dd | |||
| 5fc60a0335 | |||
| 79e1784eed | |||
| 3ac2599f0a | |||
| b06943d6c9 | |||
| de8653e1a3 | |||
| 1d865e72ad | |||
| 2d3d3aa676 | |||
| fa8595d030 | |||
| f79383363c | |||
| 94d9d8bab6 | |||
| 21b90672d2 | |||
| 36402c784e | |||
| fd740dbc52 | |||
| 28b8588073 | |||
| 1baf697227 | |||
| 0e9014b90d | |||
| 0ffa5fbe98 | |||
| 533c1efb63 | |||
| 529e7c7aa9 | |||
| a2ac8c8920 |
@@ -35,3 +35,20 @@ CREATE TABLE api_setting(
|
|||||||
INSERT INTO api_setting(id,phone_number,access_token,msgtype,touser,agentid,enable_duplicate_check,duplicate_check_interval)
|
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);
|
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,
|
||||||
|
remark VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO device_info (is_monitored, time_out_period, device_brand, android_version, sn, device_model, last_online_time, status,remark) VALUES
|
||||||
|
(1, 1800, 'google', 'Android 10',,'Pixel 3', UNIX_TIMESTAMP(), 1,'转发设备'),
|
||||||
|
|
||||||
|
|||||||
17
docker/Dockerfile
Normal file
17
docker/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 基础镜像使用OpenJDK官方镜像,标签为17-jdk-slim,这是Java 17的精简版环境
|
||||||
|
FROM openjdk:17-jdk-slim
|
||||||
|
|
||||||
|
# 设置容器内的工作目录。如果该目录不存在,Docker将自动为你创建它。
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 将构建生成的可执行jar包复制到容器内。假设jar包位于target/目录下,并命名为wePush-1.0.0.jar。
|
||||||
|
COPY ../target/wePushApp-1.2.0.jar ./app.jar
|
||||||
|
|
||||||
|
# 配置容器启动时运行的命令,这里用来启动Spring Boot应用。
|
||||||
|
# 使用非root用户运行Java应用是一个好的安全实践。
|
||||||
|
# 这里可以根据需要添加其他JVM参数,比如内存限制等。
|
||||||
|
CMD ["java", "-jar", "/app/app.jar"]
|
||||||
|
|
||||||
|
# 暴露8080端口,这是Spring Boot应用默认的端口。
|
||||||
|
# 注意,实际部署时可能需要根据应用的具体配置来调整端口号。
|
||||||
|
EXPOSE 8080
|
||||||
@@ -3,8 +3,10 @@ package com.kimgo.wepush;
|
|||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
@MapperScan("com.kimgo.wepush.mapper")
|
@MapperScan("com.kimgo.wepush.mapper")
|
||||||
public class WePushApplication {
|
public class WePushApplication {
|
||||||
|
|
||||||
|
|||||||
22
src/main/java/com/kimgo/wepush/common/JsonConverter.java
Normal file
22
src/main/java/com/kimgo/wepush/common/JsonConverter.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package com.kimgo.wepush.common;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
public class JsonConverter<T> {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public JsonConverter () {
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将对象序列化为 JSON 字符串
|
||||||
|
public String serialize(T object) throws JsonProcessingException {
|
||||||
|
return objectMapper.writeValueAsString(object);
|
||||||
|
}
|
||||||
|
// 将 JSON 字符串反序列化为对象
|
||||||
|
public T deserialize(String json, Class<T> clazz) throws JsonProcessingException {
|
||||||
|
return objectMapper.readValue(json, clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ public class PhoneNumberValidation {
|
|||||||
try {
|
try {
|
||||||
Phonenumber.PhoneNumber phoneNumber = phoneNumberUtil.parse(number, "CN");
|
Phonenumber.PhoneNumber phoneNumber = phoneNumberUtil.parse(number, "CN");
|
||||||
boolean isCorrect = phoneNumberUtil.isValidNumber(phoneNumber);
|
boolean isCorrect = phoneNumberUtil.isValidNumber(phoneNumber);
|
||||||
logger.info("号码:{},是否合法: {}",number,isCorrect);
|
logger.debug("号码:{},是否合法: {}",number,isCorrect);
|
||||||
return isCorrect;
|
return isCorrect;
|
||||||
} catch (NumberParseException e) {
|
} catch (NumberParseException e) {
|
||||||
logger.error("NumberParseException was thrown: ",e);
|
logger.error("NumberParseException was thrown: ",e);
|
||||||
|
|||||||
@@ -20,14 +20,18 @@ public class CallerController {
|
|||||||
public ServerResponseEntity receiveCall(@RequestHeader("accessToken") String accessToken,
|
public ServerResponseEntity receiveCall(@RequestHeader("accessToken") String accessToken,
|
||||||
@RequestBody @Valid CallInfo callInfo) {
|
@RequestBody @Valid CallInfo callInfo) {
|
||||||
logger.info("CallInfoO: {}",callInfo.toString());
|
logger.info("CallInfoO: {}",callInfo.toString());
|
||||||
if(accessToken == null || callInfo == null){
|
if(accessToken == null){
|
||||||
return ServerResponseEntity.fail("accessToken cannot be empty.");
|
return ServerResponseEntity.fail("accessToken cannot be empty.");
|
||||||
}
|
}
|
||||||
if(callInfo.getPhoneNumber().equals("null") || callInfo.getCallTime().equals("null")){
|
if (callInfo == null || isNullOrEmptyOrLiteralNull(callInfo.getPhoneNumber()) ||
|
||||||
logger.info("CallInfo has null String.");
|
isNullOrEmptyOrLiteralNull(callInfo.getCallTime())) {
|
||||||
|
logger.debug("CallInfo has invalid fields.");
|
||||||
return ServerResponseEntity.fail("json body value error.");
|
return ServerResponseEntity.fail("json body value error.");
|
||||||
}
|
}
|
||||||
return callService.getPhoneNumber(accessToken,callInfo);
|
return callService.getPhoneNumber(accessToken,callInfo);
|
||||||
}
|
}
|
||||||
|
private boolean isNullOrEmptyOrLiteralNull(String value) {
|
||||||
|
return value == null || value.isEmpty() || "null".equals(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
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;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(CallerController.class);
|
||||||
|
|
||||||
|
@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()){
|
||||||
|
logger.info("DeviceInfo: {}",deviceInfo.toString());
|
||||||
|
return ServerResponseEntity.fail("json body value error.");
|
||||||
|
}
|
||||||
|
return heartBeatService.handleHeartbeatSignal(accessToken,deviceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,16 +21,14 @@ public class SMSController {
|
|||||||
public ServerResponseEntity receiveSMS(@RequestHeader("accessToken") String accessToken,
|
public ServerResponseEntity receiveSMS(@RequestHeader("accessToken") String accessToken,
|
||||||
@RequestBody SMSInfo smsInfo) {
|
@RequestBody SMSInfo smsInfo) {
|
||||||
|
|
||||||
logger.info("SMSInfo: {},accessToken: {}",smsInfo.toString(),accessToken);
|
logger.info("SMSInfo: {}",smsInfo.toString());
|
||||||
if(accessToken == null){
|
if(accessToken == null){
|
||||||
return ServerResponseEntity.fail("accessToken cannot be empty.");
|
return ServerResponseEntity.fail("accessToken cannot be empty.");
|
||||||
}
|
}
|
||||||
if (smsInfo.isNullString()){
|
|
||||||
return ServerResponseEntity.fail("json body value error.");
|
|
||||||
}
|
|
||||||
if (smsInfo == null || !smsInfo.validateFields()) {
|
if (smsInfo == null || !smsInfo.validateFields()) {
|
||||||
return ServerResponseEntity.fail("SMS info fields must not be blank.");
|
return ServerResponseEntity.fail("SMS info fields must not be blank.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return smsService.getSMSInfo(accessToken,smsInfo);
|
return smsService.getSMSInfo(accessToken,smsInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.kimgo.wepush.controller;
|
||||||
|
|
||||||
|
import com.kimgo.wepush.model.WeChatMsg;
|
||||||
|
import com.kimgo.wepush.response.ServerResponseEntity;
|
||||||
|
import com.kimgo.wepush.service.HandleWeChatMsgService;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Validated
|
||||||
|
@RequestMapping(value = "/api",method = RequestMethod.POST)
|
||||||
|
public class WeChatMsgForwarderController {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(WeChatMsgForwarderController.class);
|
||||||
|
|
||||||
|
private final HandleWeChatMsgService handleWeChatMsgService;
|
||||||
|
|
||||||
|
public WeChatMsgForwarderController(HandleWeChatMsgService handleWeChatMsgService) {
|
||||||
|
this.handleWeChatMsgService = handleWeChatMsgService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收微信消息。
|
||||||
|
*
|
||||||
|
* @param accessToken 访问令牌,用于验证请求的合法性。该参数通过请求头传递。
|
||||||
|
* @param weChatMsg 微信消息体,包含消息的各种内容。该参数通过请求体传递,并需通过验证。
|
||||||
|
* @return 返回处理结果,如果处理成功,返回成功状态和处理信息;如果失败,返回失败状态和错误信息。
|
||||||
|
*/
|
||||||
|
@PostMapping("/wechat")
|
||||||
|
public ServerResponseEntity receiveMsg(@RequestHeader("accessToken") String accessToken,
|
||||||
|
@RequestBody @Valid WeChatMsg weChatMsg){
|
||||||
|
logger.info("weChatMsg: {}",weChatMsg.toString());
|
||||||
|
// 检查accessToken是否为空,为空则返回错误信息
|
||||||
|
if(accessToken == null){
|
||||||
|
logger.warn("Access token is missing or empty");
|
||||||
|
return ServerResponseEntity.fail("accessToken cannot be empty.");
|
||||||
|
}
|
||||||
|
// 检查weChatMsg消息体是否有效,无效则返回错误信息
|
||||||
|
if (weChatMsg.isInvalid()) {
|
||||||
|
logger.debug("CallInfo has invalid fields.");
|
||||||
|
return ServerResponseEntity.fail("json body value error.");
|
||||||
|
}
|
||||||
|
// 调用服务处理微信消息
|
||||||
|
return handleWeChatMsgService.handleMsg(accessToken,weChatMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.kimgo.wepush.exceptions;
|
||||||
|
|
||||||
|
public class DatabaseQueryException extends Exception {
|
||||||
|
public DatabaseQueryException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseQueryException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.kimgo.wepush.exceptions;
|
||||||
|
|
||||||
|
public class DatabaseUpdateException extends Exception{
|
||||||
|
public DatabaseUpdateException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseUpdateException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.kimgo.wepush.mapper;
|
|||||||
|
|
||||||
import com.kimgo.wepush.model.ApiSetting;
|
import com.kimgo.wepush.model.ApiSetting;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author wangsiyuan
|
* @author wangsiyuan
|
||||||
@@ -9,6 +10,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
* @createDate 2023-12-05 01:25:35
|
* @createDate 2023-12-05 01:25:35
|
||||||
* @Entity com.kimgo.wepush.model.ApiSetting
|
* @Entity com.kimgo.wepush.model.ApiSetting
|
||||||
*/
|
*/
|
||||||
|
@Mapper
|
||||||
public interface ApiSettingMapper extends BaseMapper<ApiSetting> {
|
public interface ApiSettingMapper extends BaseMapper<ApiSetting> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<DeviceInfoDAO> {
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package com.kimgo.wepush.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.kimgo.wepush.model.QyWeChatAppInfo;
|
import com.kimgo.wepush.model.QyWeChatAppInfo;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
public interface QyWeChatAppInfoMapper extends BaseMapper<QyWeChatAppInfo> {
|
public interface QyWeChatAppInfoMapper extends BaseMapper<QyWeChatAppInfo> {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
import com.kimgo.wepush.model.QyWeChatURL;
|
import com.kimgo.wepush.model.QyWeChatURL;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
public interface QyWeChatURLMapper extends BaseMapper<QyWeChatURL> {
|
public interface QyWeChatURLMapper extends BaseMapper<QyWeChatURL> {
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/main/java/com/kimgo/wepush/model/DeviceInfo.java
Normal file
24
src/main/java/com/kimgo/wepush/model/DeviceInfo.java
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/java/com/kimgo/wepush/model/DeviceInfoDAO.java
Normal file
25
src/main/java/com/kimgo/wepush/model/DeviceInfoDAO.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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;
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -24,16 +24,12 @@ public class SMSInfo {
|
|||||||
* @return 如果所有字段都非空,则返回 true,否则返回 false。
|
* @return 如果所有字段都非空,则返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
public boolean validateFields() {
|
public boolean validateFields() {
|
||||||
return StringUtils.isNotBlank(smsNumber) &&
|
return isNotBlankAndNotLiteralNull(smsNumber) &&
|
||||||
StringUtils.isNotBlank(smsAcceptanceTime) &&
|
isNotBlankAndNotLiteralNull(smsAcceptanceTime) &&
|
||||||
StringUtils.isNotBlank(smsContent);
|
isNotBlankAndNotLiteralNull(smsContent);
|
||||||
}
|
}
|
||||||
public boolean isNullString(){
|
|
||||||
String nullString = "null";
|
private boolean isNotBlankAndNotLiteralNull(String value) {
|
||||||
if (this.smsNumber.equals(nullString) || this.smsContent.equals(nullString) || this.smsAcceptanceTime.equals(nullString)){
|
return StringUtils.isNotBlank(value) && !"null".equals(value);
|
||||||
logger.info("SMSInfo has null String.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
25
src/main/java/com/kimgo/wepush/model/TextMessage.java
Normal file
25
src/main/java/com/kimgo/wepush/model/TextMessage.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.kimgo.wepush.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class TextMessage {
|
||||||
|
private String touser;
|
||||||
|
private String toparty;
|
||||||
|
private String totag;
|
||||||
|
private String msgtype;
|
||||||
|
private int agentid;
|
||||||
|
private Text text;
|
||||||
|
private int safe;
|
||||||
|
private int enable_id_trans;
|
||||||
|
private int enable_duplicate_check;
|
||||||
|
private int duplicate_check_interval;
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Text {
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/main/java/com/kimgo/wepush/model/WeChatMsg.java
Normal file
22
src/main/java/com/kimgo/wepush/model/WeChatMsg.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package com.kimgo.wepush.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WeChatMsg {
|
||||||
|
private String packageName;
|
||||||
|
private String appName;
|
||||||
|
private String title;
|
||||||
|
private String sender;
|
||||||
|
private String message;
|
||||||
|
private String currentTime;
|
||||||
|
public boolean isInvalid() {
|
||||||
|
return !isStringValid(packageName) || !isStringValid(appName)
|
||||||
|
|| !isStringValid(title) || !isStringValid(sender)
|
||||||
|
|| !isStringValid(message) || !isStringValid(currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStringValid(String value) {
|
||||||
|
return value != null && !value.isEmpty() && !"null".equals(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.kimgo.wepush.request;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.kimgo.wepush.common.JsonConverter;
|
||||||
|
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
||||||
|
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 SendQyWeChatMsgRequest {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SendQyWeChatMsgRequest.class);
|
||||||
|
private final OkHttpClient client = new OkHttpClient();
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final JsonConverter<Object> jsonConverter = new JsonConverter<>();
|
||||||
|
|
||||||
|
public SendQyWeChatMsgRequest(TokenService tokenService) {
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> QyWeChatSendMessageApiResponse okhttpRequest(String baseurl, String accessToken, T object) {
|
||||||
|
int retryCount = 0;
|
||||||
|
final int maxRetries = 2;
|
||||||
|
|
||||||
|
while (retryCount <= maxRetries) {
|
||||||
|
String url = baseurl + "?access_token=" + accessToken;
|
||||||
|
// 使用Jackson进行序列化
|
||||||
|
String jsonBody;
|
||||||
|
try {
|
||||||
|
jsonBody = jsonConverter.serialize(object);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
return new QyWeChatSendMessageApiResponse(-1, "json serialization exception");
|
||||||
|
}
|
||||||
|
// 构建请求体
|
||||||
|
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
RequestBody body = RequestBody.create(jsonBody, mediaType);
|
||||||
|
|
||||||
|
okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(body).build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
String responseBody = null;
|
||||||
|
if (response.body() != null) {
|
||||||
|
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
|
||||||
|
tokenService.setAccessToken();
|
||||||
|
accessToken = tokenService.getAccessToken();
|
||||||
|
retryCount++;
|
||||||
|
} else {
|
||||||
|
// 处理成功或其他错误情况
|
||||||
|
return apiResponse;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("OkHttp request error", e);
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
return new QyWeChatSendMessageApiResponse(-1,"the maximum number of attempts exceeded");
|
||||||
|
}
|
||||||
|
retryCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new QyWeChatSendMessageApiResponse(-1,"request error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,35 +12,51 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class WeChatAPI {
|
public class WeChatAPI {
|
||||||
private final Logger logger = LoggerFactory.getLogger(WeChatAPI.class);
|
private final Logger logger = LoggerFactory.getLogger(WeChatAPI.class);
|
||||||
|
/**
|
||||||
|
* 获取企业微信访问令牌
|
||||||
|
* @param corpid 企业ID
|
||||||
|
* @param corpsecret 企业密钥
|
||||||
|
* @return 返回获取到的访问令牌,如果获取失败则返回null
|
||||||
|
*/
|
||||||
public String getAccessToken(String corpid,String corpsecret){
|
public String getAccessToken(String corpid,String corpsecret){
|
||||||
|
// 创建OkHttpClient实例
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
|
// 设置请求URL并添加查询参数
|
||||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||||
urlBuilder.addQueryParameter("corpid", corpid);
|
urlBuilder.addQueryParameter("corpid", corpid);
|
||||||
urlBuilder.addQueryParameter("corpsecret", corpsecret);
|
urlBuilder.addQueryParameter("corpsecret", corpsecret);
|
||||||
|
|
||||||
|
// 构建请求
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(urlBuilder.build())
|
.url(urlBuilder.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
// 从响应中读取body内容
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
logger.info("get qyWeChat AccessToken responseBody: {}", responseBody);
|
logger.info("get qyWeChat AccessToken responseBody: {}", responseBody);
|
||||||
|
|
||||||
|
// 对响应内容进行JSON解析
|
||||||
ObjectMapper objectMapper1 = new ObjectMapper();
|
ObjectMapper objectMapper1 = new ObjectMapper();
|
||||||
QyWeChatGetAccessTokenApiResponse qyWeChatGetAccessTokenApiResponse = objectMapper1.readValue(responseBody, QyWeChatGetAccessTokenApiResponse.class);
|
QyWeChatGetAccessTokenApiResponse qyWeChatGetAccessTokenApiResponse = objectMapper1.readValue(responseBody, QyWeChatGetAccessTokenApiResponse.class);
|
||||||
|
// 检查是否获取令牌成功
|
||||||
if (qyWeChatGetAccessTokenApiResponse.getErrcode() == 0){
|
if (qyWeChatGetAccessTokenApiResponse.getErrcode() == 0){
|
||||||
logger.info("get qyWeChat AccessToken Success");
|
logger.debug("get qyWeChat AccessToken Success");
|
||||||
|
// 返回访问令牌
|
||||||
return qyWeChatGetAccessTokenApiResponse.getAccess_token();
|
return qyWeChatGetAccessTokenApiResponse.getAccess_token();
|
||||||
} else {
|
} else {
|
||||||
|
// 记录获取令牌失败的日志
|
||||||
logger.error("getAccessToken error: {}",qyWeChatGetAccessTokenApiResponse.getErrmsg());
|
logger.error("getAccessToken error: {}",qyWeChatGetAccessTokenApiResponse.getErrmsg());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
// 记录异常信息
|
||||||
logger.error("getAccessToken error:",e);
|
logger.error("getAccessToken error:",e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.kimgo.wepush.response;
|
package com.kimgo.wepush.response;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
public class QyWeChatGetAccessTokenApiResponse {
|
public class QyWeChatGetAccessTokenApiResponse {
|
||||||
long errcode;
|
long errcode;
|
||||||
String errmsg;
|
String errmsg;
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package com.kimgo.wepush.response;
|
package com.kimgo.wepush.response;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
public class QyWeChatSendMessageApiResponse {
|
public class QyWeChatSendMessageApiResponse {
|
||||||
private int errcode;
|
private int errcode;
|
||||||
private String errmsg;
|
private String errmsg;
|
||||||
private String msgid;
|
private String msgid;
|
||||||
|
|
||||||
|
public QyWeChatSendMessageApiResponse(int errcode, String errmsg) {
|
||||||
|
this.errcode = errcode;
|
||||||
|
this.errmsg = errmsg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -62,8 +62,9 @@ public class ServerResponseEntity <T> implements Serializable {
|
|||||||
public static <T> ServerResponseEntity<T> success(){
|
public static <T> ServerResponseEntity<T> success(){
|
||||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||||
serverResponseEntity.setCode(ResponseEnum.OK.value());
|
serverResponseEntity.setCode(ResponseEnum.OK.value());
|
||||||
serverResponseEntity.getMsg();
|
serverResponseEntity.setMsg(ResponseEnum.OK.getMsg());
|
||||||
serverResponseEntity.setTimestamp(Instant.now().getEpochSecond());
|
serverResponseEntity.setTimestamp(Instant.now().getEpochSecond());
|
||||||
|
serverResponseEntity.setData(null);
|
||||||
return serverResponseEntity;
|
return serverResponseEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,34 +7,62 @@ import com.kimgo.wepush.mapper.ApiSettingMapper;
|
|||||||
import com.kimgo.wepush.model.ApiSetting;
|
import com.kimgo.wepush.model.ApiSetting;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ApiSettingService {
|
public class ApiSettingService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(ApiSettingService.class);
|
private final Logger logger = LoggerFactory.getLogger(ApiSettingService.class);
|
||||||
private ApiSetting apiSetting;
|
private ApiSetting apiSetting;
|
||||||
@Autowired
|
private final ApiSettingMapper apiSettingMapper;
|
||||||
private ApiSettingMapper apiSettingMapper;
|
private final UserConfig userConfig;
|
||||||
@Autowired
|
|
||||||
private UserConfig userConfig;
|
public ApiSettingService(ApiSettingMapper apiSettingMapper, UserConfig userConfig) {
|
||||||
|
this.apiSettingMapper = apiSettingMapper;
|
||||||
|
this.userConfig = userConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public ApiSetting getApiSetting() {
|
public ApiSetting getApiSetting() {
|
||||||
if (apiSetting == null) {
|
if (apiSetting == null) {
|
||||||
queryApiSetting();
|
queryApiSetting();
|
||||||
}
|
}
|
||||||
return apiSetting;
|
return apiSetting;
|
||||||
}
|
}
|
||||||
private void queryApiSetting(){
|
|
||||||
|
/**
|
||||||
|
* 查询API设置信息。
|
||||||
|
* 该方法不接受参数,也不返回值,但会根据用户配置的电话号码查询数据库中的API设置信息,并更新相关的成员变量。
|
||||||
|
* 主要步骤包括:
|
||||||
|
* 1. 从用户配置中获取电话号码。
|
||||||
|
* 2. 构建查询条件。
|
||||||
|
* 3. 根据查询条件从数据库中查询唯一的API设置信息。
|
||||||
|
* 4. 记录查询结果的日志信息。
|
||||||
|
* 5. 如果查询结果为空,则更新API设置为null,并记录错误日志。
|
||||||
|
* 6. 最后,更新API设置成员变量为查询结果,并记录相应日志。
|
||||||
|
*/
|
||||||
|
private void queryApiSetting() {
|
||||||
|
// 从用户配置中获取电话号码
|
||||||
String phoneNumberToSearch = userConfig.getPhoneNumber();
|
String phoneNumberToSearch = userConfig.getPhoneNumber();
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
QueryWrapper<ApiSetting> wrapper = new QueryWrapper<>();
|
QueryWrapper<ApiSetting> wrapper = new QueryWrapper<>();
|
||||||
wrapper.eq("phone_number", phoneNumberToSearch);
|
wrapper.eq("phone_number", phoneNumberToSearch);
|
||||||
|
|
||||||
|
// 根据查询条件查询API设置信息
|
||||||
ApiSetting result = apiSettingMapper.selectOne(wrapper);
|
ApiSetting result = apiSettingMapper.selectOne(wrapper);
|
||||||
logger.info("result: {}",result);
|
// 记录查询结果
|
||||||
if (result == null){
|
logger.info("result: {}", result);
|
||||||
|
|
||||||
|
// 处理查询结果为空的情况
|
||||||
|
if (result == null) {
|
||||||
apiSetting = null;
|
apiSetting = null;
|
||||||
|
// 记录错误日志
|
||||||
logger.error("updateAccessToken error");
|
logger.error("updateAccessToken error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新API设置成员变量
|
||||||
apiSetting = result;
|
apiSetting = result;
|
||||||
logger.info("get apiSetting from mysql,accessToken: {}",apiSetting);
|
// 记录更新日志
|
||||||
|
logger.info("get apiSetting from mysql,accessToken: {}", apiSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.kimgo.wepush.service;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.kimgo.wepush.common.PhoneNumberValidation;
|
|
||||||
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
||||||
import com.kimgo.wepush.model.CallInfo;
|
import com.kimgo.wepush.model.CallInfo;
|
||||||
import com.kimgo.wepush.model.TextCardMessage;
|
import com.kimgo.wepush.model.TextCardMessage;
|
||||||
@@ -12,7 +11,6 @@ import okhttp3.OkHttpClient;
|
|||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
|
|
||||||
@@ -21,83 +19,133 @@ import java.io.IOException;
|
|||||||
@Service
|
@Service
|
||||||
public class CallService {
|
public class CallService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(CallService.class);
|
private final Logger logger = LoggerFactory.getLogger(CallService.class);
|
||||||
|
private final QyWeChatURLService qyWeChatURLService;
|
||||||
private ServerResponseEntity serverResponseEntity;
|
private final ApiSettingService apiSettingService;
|
||||||
@Autowired
|
final
|
||||||
private QyWeChatURLService qyWeChatURLService;
|
|
||||||
@Autowired
|
|
||||||
private ApiSettingService apiSettingService;
|
|
||||||
@Autowired
|
|
||||||
TokenService tokenService;
|
TokenService tokenService;
|
||||||
public ServerResponseEntity getPhoneNumber(String accessToken, CallInfo callInfo){
|
|
||||||
|
public CallService(QyWeChatURLService qyWeChatURLService, ApiSettingService apiSettingService, TokenService tokenService) {
|
||||||
|
this.qyWeChatURLService = qyWeChatURLService;
|
||||||
|
this.apiSettingService = apiSettingService;
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据提供的accessToken和呼叫信息获取电话号码。
|
||||||
|
* 首先验证提供的accessToken的有效性,然后通过调用信息(电话号码和呼叫时间)向企业微信发送消息,
|
||||||
|
* 并根据企业微信的响应返回成功或失败的服务器响应。
|
||||||
|
*
|
||||||
|
* @param accessToken 用户提供的accessToken用于身份验证。
|
||||||
|
* @param callInfo 呼叫信息,包含电话号码和呼叫时间。
|
||||||
|
* @return 返回一个服务器响应实体,如果成功则包含消息ID,如果失败则包含错误信息。
|
||||||
|
*/
|
||||||
|
public ServerResponseEntity getPhoneNumber(String accessToken, CallInfo callInfo) {
|
||||||
|
// 通过token服务获取正确的accessToken
|
||||||
String correctAccessToken = tokenService.getApiAccessToken();
|
String correctAccessToken = tokenService.getApiAccessToken();
|
||||||
logger.info("accessToken: {} correctAccessToken: {}",accessToken,correctAccessToken);
|
logger.info("accessToken: {} correctAccessToken: {}", accessToken, correctAccessToken);
|
||||||
if (!correctAccessToken.equals(accessToken)){
|
// 验证提供的accessToken是否有效
|
||||||
|
if (!correctAccessToken.equals(accessToken)) {
|
||||||
return ServerResponseEntity.fail("Invalid accessToken");
|
return ServerResponseEntity.fail("Invalid accessToken");
|
||||||
}
|
}
|
||||||
|
// 从呼叫信息中获取电话号码和呼叫时间
|
||||||
String phoneNumber = callInfo.getPhoneNumber();
|
String phoneNumber = callInfo.getPhoneNumber();
|
||||||
String callTime = callInfo.getCallTime();
|
String callTime = callInfo.getCallTime();
|
||||||
|
// 获取企业微信的访问令牌
|
||||||
String qyAccessToken = tokenService.getAccessToken();
|
String qyAccessToken = tokenService.getAccessToken();
|
||||||
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = requestWithOkhttp(qyAccessToken,phoneNumber,callTime);
|
// 使用企业微信的accessToken、电话号码和呼叫时间发送消息,并获取响应
|
||||||
|
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = requestWithOkhttp(qyAccessToken, phoneNumber, callTime);
|
||||||
|
// 检查发送消息的响应代码
|
||||||
int code = qyWeChatSendMessageApiResponse.getErrcode();
|
int code = qyWeChatSendMessageApiResponse.getErrcode();
|
||||||
if (code == 0){
|
if (code == 0) {
|
||||||
|
// 如果消息发送成功,构建并返回成功的服务器响应
|
||||||
SendMessageResonse sendMessageResonse = new SendMessageResonse();
|
SendMessageResonse sendMessageResonse = new SendMessageResonse();
|
||||||
sendMessageResonse.setMsgid(qyWeChatSendMessageApiResponse.getMsgid());
|
sendMessageResonse.setMsgid(qyWeChatSendMessageApiResponse.getMsgid());
|
||||||
return ServerResponseEntity.success(sendMessageResonse);
|
return ServerResponseEntity.success(sendMessageResonse);
|
||||||
} else {
|
} else {
|
||||||
|
// 如果消息发送失败,返回失败的服务器响应
|
||||||
return ServerResponseEntity.fail("request qyWeChat error,please check server error log.");
|
return ServerResponseEntity.fail("request qyWeChat error,please check server error log.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文本卡片消息的内容。
|
||||||
|
*
|
||||||
|
* @param phoneNumber 来电电话号码
|
||||||
|
* @param callTime 来电时间
|
||||||
|
* @return TextCardMessage 文本卡片消息对象,包含了设置好的内容
|
||||||
|
*/
|
||||||
public TextCardMessage setTextCardMessage(String phoneNumber, String callTime) {
|
public TextCardMessage setTextCardMessage(String phoneNumber, String callTime) {
|
||||||
TextCardMessage textCardMessage = new TextCardMessage();
|
TextCardMessage textCardMessage = new TextCardMessage();
|
||||||
|
// 从api设置中获取用户信息,并设置到textCardMessage中
|
||||||
textCardMessage.setTouser(apiSettingService.getApiSetting().getTouser());
|
textCardMessage.setTouser(apiSettingService.getApiSetting().getTouser());
|
||||||
textCardMessage.setMsgtype(apiSettingService.getApiSetting().getMsgtype());
|
textCardMessage.setMsgtype(apiSettingService.getApiSetting().getMsgtype());
|
||||||
textCardMessage.setAgentid(apiSettingService.getApiSetting().getAgentid());
|
textCardMessage.setAgentid(apiSettingService.getApiSetting().getAgentid());
|
||||||
textCardMessage.setEnable_duplicate_check(apiSettingService.getApiSetting().getEnableDuplicateCheck());
|
textCardMessage.setEnable_duplicate_check(apiSettingService.getApiSetting().getEnableDuplicateCheck());
|
||||||
textCardMessage.setDuplicate_check_interval(apiSettingService.getApiSetting().getDuplicateCheckInterval());
|
textCardMessage.setDuplicate_check_interval(apiSettingService.getApiSetting().getDuplicateCheckInterval());
|
||||||
|
|
||||||
|
// 创建TextCard对象,并设置其内容
|
||||||
TextCardMessage.TextCard textCard = new TextCardMessage.TextCard();
|
TextCardMessage.TextCard textCard = new TextCardMessage.TextCard();
|
||||||
textCard.setTitle("新来电通知");
|
textCard.setTitle("新来电通知");
|
||||||
|
// 设置描述信息,包含来电号码和来电时间
|
||||||
textCard.setDescription("<div class='gray'>您有一通新来电</div><div class='highlight'>来电号码: " + phoneNumber + "</div><div class='highlight'>来电时间: " + callTime + "</div>如果您认识这个号码,请及时回电。如果不认识,请注意可能是垃圾电话或诈骗电话。");
|
textCard.setDescription("<div class='gray'>您有一通新来电</div><div class='highlight'>来电号码: " + phoneNumber + "</div><div class='highlight'>来电时间: " + callTime + "</div>如果您认识这个号码,请及时回电。如果不认识,请注意可能是垃圾电话或诈骗电话。");
|
||||||
textCard.setUrl("https://kimgo.cn");
|
textCard.setUrl("https://kimgo.cn");
|
||||||
|
|
||||||
// 将TextCard对象设置到TextCardMessage中
|
// 将TextCard对象设置到TextCardMessage中
|
||||||
textCardMessage.setTextcard(textCard);
|
textCardMessage.setTextcard(textCard);
|
||||||
|
|
||||||
logger.info("TextCardMessage: {}", textCardMessage.toString());
|
logger.info("TextCardMessage: {}", textCardMessage);
|
||||||
|
|
||||||
return textCardMessage;
|
return textCardMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken,String phoneNumber,String callTime){
|
|
||||||
|
/**
|
||||||
|
* 使用OkHttp发送企业微信文本卡片消息。
|
||||||
|
*
|
||||||
|
* @param accessToken 访问令牌,用于授权访问企业微信API。
|
||||||
|
* @param phoneNumber 接收消息的手机号码。
|
||||||
|
* @param callTime 预约的呼叫时间。
|
||||||
|
* @return 返回企业微信发送消息的API响应,如果失败则返回null。
|
||||||
|
*/
|
||||||
|
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken, String phoneNumber, String callTime) {
|
||||||
|
// 构建发送文本卡片消息的URL
|
||||||
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
|
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
TextCardMessage textCardMessage = setTextCardMessage(phoneNumber,callTime);
|
// 设置文本卡片消息内容
|
||||||
// 使用Jackson进行序列化
|
TextCardMessage textCardMessage = setTextCardMessage(phoneNumber, callTime);
|
||||||
|
// 将消息对象序列化为JSON字符串
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
String jsonBody = null;
|
String jsonBody;
|
||||||
try {
|
try {
|
||||||
jsonBody = objectMapper.writeValueAsString(textCardMessage);
|
jsonBody = objectMapper.writeValueAsString(textCardMessage);
|
||||||
logger.info("jsonBody: {}",jsonBody);
|
logger.info("jsonBody: {}", jsonBody);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
logger.error("JSON processing error", e);
|
logger.error("JSON processing error", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 构建请求体
|
|
||||||
|
// 构建HTTP请求体
|
||||||
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonBody);
|
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonBody);
|
||||||
Request request = new Request.Builder().url(url).post(body).build();
|
Request request = new Request.Builder().url(url).post(body).build();
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
// 从响应中读取内容
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
logger.info("request sendMessage api ResponseBody: {}", responseBody);
|
logger.info("request sendMessage api ResponseBody: {}", responseBody);
|
||||||
|
|
||||||
|
// 反序列化API响应
|
||||||
ObjectMapper objectMapper1 = new ObjectMapper();
|
ObjectMapper objectMapper1 = new ObjectMapper();
|
||||||
QyWeChatSendMessageApiResponse apiResponse = objectMapper1.readValue(responseBody, QyWeChatSendMessageApiResponse.class);
|
QyWeChatSendMessageApiResponse apiResponse = objectMapper1.readValue(responseBody, QyWeChatSendMessageApiResponse.class);
|
||||||
|
|
||||||
|
// 处理不同的API响应结果
|
||||||
if (apiResponse.getErrcode() == 0) {
|
if (apiResponse.getErrcode() == 0) {
|
||||||
|
logger.debug("Request qyWeChat Success.");
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
} else if (apiResponse.getErrcode() == 42001 || apiResponse.getErrcode() == 40014) {
|
} else if (apiResponse.getErrcode() == 42001 || apiResponse.getErrcode() == 40014) {
|
||||||
|
// 处理访问令牌过期的情况,刷新令牌后重新发送消息
|
||||||
logger.info("Access token expired. Refreshing token...");
|
logger.info("Access token expired. Refreshing token...");
|
||||||
tokenService.setAccessToken(); // 一个方法来刷新accessToken
|
tokenService.setAccessToken(); // 刷新accessToken的方法
|
||||||
String newAccessToken = tokenService.getAccessToken();
|
String newAccessToken = tokenService.getAccessToken();
|
||||||
return requestWithOkhttp(newAccessToken, phoneNumber, callTime);
|
return requestWithOkhttp(newAccessToken, phoneNumber, callTime);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.kimgo.wepush.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.kimgo.wepush.model.TextMessage;
|
||||||
|
import com.kimgo.wepush.model.WeChatMsg;
|
||||||
|
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
||||||
|
import com.kimgo.wepush.response.ServerResponseEntity;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HandleWeChatMsgService {
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final ApiSettingService apiSettingService;
|
||||||
|
private final QyWeChatURLService qyWeChatURLService;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(HandleWeChatMsgService.class);
|
||||||
|
|
||||||
|
public HandleWeChatMsgService(TokenService tokenService, ApiSettingService apiSettingService, QyWeChatURLService qyWeChatURLService) {
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.apiSettingService = apiSettingService;
|
||||||
|
this.qyWeChatURLService = qyWeChatURLService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponseEntity handleMsg(String accessToken, WeChatMsg weChatMsg){
|
||||||
|
String correctAccessToken = tokenService.getApiAccessToken();
|
||||||
|
logger.info("accessToken: {} correctAccessToken: {}",accessToken,correctAccessToken);
|
||||||
|
if (!correctAccessToken.equals(accessToken)){
|
||||||
|
return ServerResponseEntity.fail("Invalid accessToken");
|
||||||
|
}
|
||||||
|
String qyAccessToken = tokenService.getAccessToken();
|
||||||
|
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = requestWithOkhttp(qyAccessToken,weChatMsg);
|
||||||
|
if (qyWeChatSendMessageApiResponse != null){
|
||||||
|
return ServerResponseEntity.success(qyWeChatSendMessageApiResponse.getMsgid());
|
||||||
|
}
|
||||||
|
return ServerResponseEntity.fail("request qyWeChat error,please check server error log.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextMessage setTextMessage(WeChatMsg weChatMsg){
|
||||||
|
TextMessage textMessage = new TextMessage();
|
||||||
|
textMessage.setTouser(apiSettingService.getApiSetting().getTouser());
|
||||||
|
textMessage.setMsgtype("text");
|
||||||
|
textMessage.setAgentid(apiSettingService.getApiSetting().getAgentid());
|
||||||
|
textMessage.setEnable_duplicate_check(apiSettingService.getApiSetting().getEnableDuplicateCheck());
|
||||||
|
textMessage.setDuplicate_check_interval(apiSettingService.getApiSetting().getDuplicateCheckInterval());
|
||||||
|
TextMessage.Text text = new TextMessage.Text();
|
||||||
|
text.setContent("你有一条微信通知\n"+
|
||||||
|
"\nsender: " + weChatMsg.getSender() +
|
||||||
|
"\ntitle: "+ weChatMsg.getTitle() +
|
||||||
|
"\n接受时间: " +weChatMsg.getCurrentTime() +
|
||||||
|
"\n\n内容: \n"+ weChatMsg.getMessage());
|
||||||
|
textMessage.setText(text);
|
||||||
|
logger.info("TextMessage: {}", textMessage);
|
||||||
|
return textMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private<T> Request bulidRequest(String accessToken, T object){
|
||||||
|
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
|
||||||
|
// 使用Jackson进行序列化
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
String jsonBody;
|
||||||
|
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);
|
||||||
|
Request request = new Request.Builder().url(url).post(body).build();
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken, WeChatMsg weChatMsg){
|
||||||
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
TextMessage textMessage = setTextMessage(weChatMsg);
|
||||||
|
Request request = bulidRequest(accessToken,textMessage);
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
logger.info("Response: {}", responseBody);
|
||||||
|
|
||||||
|
ObjectMapper objectMapper1 = new ObjectMapper();
|
||||||
|
QyWeChatSendMessageApiResponse apiResponse = objectMapper1.readValue(responseBody, QyWeChatSendMessageApiResponse.class);
|
||||||
|
|
||||||
|
if (apiResponse.getErrcode() == 0) {
|
||||||
|
return apiResponse;
|
||||||
|
} else if (apiResponse.getErrcode() == 42001 || apiResponse.getErrcode() == 40014) {
|
||||||
|
logger.info("Access token expired. Refreshing token...");
|
||||||
|
tokenService.setAccessToken(); // 一个方法来刷新accessToken
|
||||||
|
String newAccessToken = tokenService.getAccessToken();
|
||||||
|
return requestWithOkhttp(newAccessToken, weChatMsg);
|
||||||
|
} else {
|
||||||
|
// 处理其他错误情况
|
||||||
|
logger.error("Error: {}", apiResponse.getErrmsg());
|
||||||
|
return apiResponse;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("OkHttp request error", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
136
src/main/java/com/kimgo/wepush/service/HeartBeatService.java
Normal file
136
src/main/java/com/kimgo/wepush/service/HeartBeatService.java
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
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.exceptions.DatabaseQueryException;
|
||||||
|
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.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HeartBeatService {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(HeartBeatService.class);
|
||||||
|
private final long TIME_OUT_PERIOD = 600;
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final DeviceInfoDAOMapper deviceInfoDAOMapper;
|
||||||
|
|
||||||
|
public HeartBeatService(TokenService tokenService, DeviceInfoDAOMapper deviceInfoDAOMapper) {
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.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");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DeviceInfoDAO deviceInfoDAO = getDeviceInfoBySN(deviceInfo.getSN());
|
||||||
|
if (deviceInfoDAO == null) {
|
||||||
|
// 设备信息不存在,可能是无效的设备
|
||||||
|
logger.info("device not find,start add device to mysql.");
|
||||||
|
boolean isSuccess = addDeviceInfo(deviceInfo);
|
||||||
|
if (isSuccess) {
|
||||||
|
return ServerResponseEntity.success();
|
||||||
|
} else {
|
||||||
|
logger.info("add to mysql error");
|
||||||
|
return ServerResponseEntity.fail("server error,please check log.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (DatabaseQueryException e) {
|
||||||
|
// 处理查询失败的情况
|
||||||
|
logger.error("Database query failed.", e);
|
||||||
|
return ServerResponseEntity.fail("Database query failed");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("update client status error.");
|
||||||
|
return ServerResponseEntity.fail("Error updating client status");
|
||||||
|
}
|
||||||
|
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<DeviceInfoDAO> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.eq("sn", deviceInfo.getSN());
|
||||||
|
updateWrapper.set("last_online_time", currentTimeMillis);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
logger.info("start add device. device info: {}", 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) throws DatabaseQueryException {
|
||||||
|
QueryWrapper<DeviceInfoDAO> 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 SN");
|
||||||
|
return deviceInfoDAO;
|
||||||
|
}
|
||||||
|
return null; // 数据不存在
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error querying device info by SN", e);
|
||||||
|
throw new DatabaseQueryException("Error querying device info by SN", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 DeviceInfo 转换为 DeviceInfoDAO。
|
||||||
|
*
|
||||||
|
* @param deviceInfo 要转换的 DeviceInfo 对象。
|
||||||
|
* @return 转换后的 DeviceInfoDAO 对象。
|
||||||
|
*/
|
||||||
|
private DeviceInfoDAO convertToDeviceInfoDAO(DeviceInfo deviceInfo) {
|
||||||
|
// logger.info("start convert to device info dao");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
src/main/java/com/kimgo/wepush/service/MonitorService.java
Normal file
208
src/main/java/com/kimgo/wepush/service/MonitorService.java
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
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.exceptions.DatabaseQueryException;
|
||||||
|
import com.kimgo.wepush.exceptions.DatabaseUpdateException;
|
||||||
|
import com.kimgo.wepush.mapper.DeviceInfoDAOMapper;
|
||||||
|
import com.kimgo.wepush.model.DeviceInfoDAO;
|
||||||
|
import com.kimgo.wepush.model.TextCardMessage;
|
||||||
|
import com.kimgo.wepush.request.SendQyWeChatMsgRequest;
|
||||||
|
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MonitorService {
|
||||||
|
private final ApiSettingService apiSettingService;
|
||||||
|
private final QyWeChatURLService qyWeChatURLService;
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(MonitorService.class);
|
||||||
|
private final DeviceInfoDAOMapper deviceInfoDAOMapper;
|
||||||
|
|
||||||
|
public MonitorService(ApiSettingService apiSettingService, QyWeChatURLService qyWeChatURLService, TokenService tokenService, DeviceInfoDAOMapper deviceInfoDAOMapper) {
|
||||||
|
this.apiSettingService = apiSettingService;
|
||||||
|
this.qyWeChatURLService = qyWeChatURLService;
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.deviceInfoDAOMapper = deviceInfoDAOMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时监控在线设备的状态。
|
||||||
|
* 该方法每隔固定的时间间隔(480000毫秒)执行一次,用于检查数据库中的设备是否在线。
|
||||||
|
* 对于每个设备,如果其最后上线时间与当前时间的差值超过了设备的超时周期,则认为该设备已离线,并更新设备状态及发送通知。
|
||||||
|
*
|
||||||
|
* @Scheduled 注解指定了方法的定时执行策略,fixedDelay属性表示执行完一次后,隔多长时间再执行下一次。
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedDelay = 480000)
|
||||||
|
public void monitorOnlineDevices() {
|
||||||
|
|
||||||
|
List<DeviceInfoDAO> devices;
|
||||||
|
try {
|
||||||
|
// 尝试从数据库获取所有设备信息
|
||||||
|
devices = getAllDevices();
|
||||||
|
} catch (DatabaseQueryException e) {
|
||||||
|
// 查询设备信息出错时的处理逻辑
|
||||||
|
logger.error("monitor online devices error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (devices.isEmpty()) {
|
||||||
|
// 如果查询到的设备列表为空,记录日志并返回
|
||||||
|
logger.info("no data was queried.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DeviceInfoDAO device : devices) {
|
||||||
|
try {
|
||||||
|
// 如果设备不被监控,则跳过该设备的处理
|
||||||
|
if (device.getIsMonitored() != 1) {
|
||||||
|
logger.info("device is not monitored ,device SN: {}", device.getSN());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 计算设备最后上线时间与当前时间的差值
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
long timeDifferenceMillis = currentTimeMillis - device.getLastOnlineTime();
|
||||||
|
long timeDifferenceSeconds = timeDifferenceMillis / 1000;
|
||||||
|
// 如果设备超出设定的超时周期未上线,则认为设备离线,并进行相应处理
|
||||||
|
if (timeDifferenceSeconds > device.getTimeOutPeriod()) {
|
||||||
|
//logger.info("Device offline. SN: {}, LastOnlineTime: {}, TimeDifference: {}, TimeoutPeriod: {}",
|
||||||
|
//device.getSN(), device.getLastOnlineTime(), timeDifferenceSeconds, device.getTimeOutPeriod());
|
||||||
|
updateStatus(device, 0); // 更新设备状态为离线
|
||||||
|
logger.info("Update the device status to offline.");
|
||||||
|
sendNotification(device); // 发送设备离线通知
|
||||||
|
} else {
|
||||||
|
// 如果设备仍在设定的超时周期内上线,则认为设备在线,并更新设备状态
|
||||||
|
updateStatus(device, 1); // 更新设备状态为在线
|
||||||
|
//logger.info("update the device status to online.");
|
||||||
|
}
|
||||||
|
} catch (DatabaseUpdateException dbe) {
|
||||||
|
// 更新数据库出错时的处理逻辑
|
||||||
|
logger.error("update to mysql error.");
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 处理设备信息处理过程中出现的其他异常
|
||||||
|
logger.error("Error processing device with SN " + device.getSN(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据库中所有DeviceInfoDAO对象的id。
|
||||||
|
*
|
||||||
|
* @return 包含所有id的列表。
|
||||||
|
*/
|
||||||
|
private List<DeviceInfoDAO> getAllDevices() throws DatabaseQueryException {
|
||||||
|
try {
|
||||||
|
// 查询所有DeviceInfoDAO对象
|
||||||
|
List<DeviceInfoDAO> deviceInfoDAOs = deviceInfoDAOMapper.selectList(new QueryWrapper<>());
|
||||||
|
// 提取并返回所有对象的id
|
||||||
|
return deviceInfoDAOs;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error querying all device info IDs", e);
|
||||||
|
throw new DatabaseQueryException("get all devices error.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送通知消息到企业微信。
|
||||||
|
*
|
||||||
|
* @param deviceInfoDAO 设备信息数据访问对象,用于获取发送通知所需的信息。
|
||||||
|
* 本函数不返回任何值,但会通过日志记录发送成功或失败的信息。
|
||||||
|
*/
|
||||||
|
private void sendNotification(DeviceInfoDAO deviceInfoDAO) {
|
||||||
|
// 创建发送企业微信消息的请求对象,并初始化它
|
||||||
|
SendQyWeChatMsgRequest sendQyWeChatMsgRequest = new SendQyWeChatMsgRequest(tokenService);
|
||||||
|
// 获取访问令牌
|
||||||
|
String accessToken = tokenService.getAccessToken();
|
||||||
|
// 获取发送文本卡片消息的URL
|
||||||
|
String url = qyWeChatURLService.getSendTextCardMessageUrl();
|
||||||
|
// 发送请求,并获取响应
|
||||||
|
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = sendQyWeChatMsgRequest.okhttpRequest(url,
|
||||||
|
accessToken, setTextCardMessage(deviceInfoDAO));
|
||||||
|
// 如果发送响应为空,则记录发送失败
|
||||||
|
if (qyWeChatSendMessageApiResponse == null) {
|
||||||
|
logger.info("Send Notification Fail");
|
||||||
|
}
|
||||||
|
// 如果发送响应不为空且错误码为0,记录发送成功
|
||||||
|
if (qyWeChatSendMessageApiResponse != null && qyWeChatSendMessageApiResponse.getErrcode() == 0) {
|
||||||
|
logger.info("Send Notification Success");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建并设置文本卡片消息的内容。
|
||||||
|
*
|
||||||
|
* @param deviceInfoDAO 设备信息数据访问对象,用于获取设备的详细信息。
|
||||||
|
* @return TextCardMessage 返回构建完成的文本卡片消息对象。
|
||||||
|
*/
|
||||||
|
public TextCardMessage setTextCardMessage(DeviceInfoDAO deviceInfoDAO) {
|
||||||
|
TextCardMessage textCardMessage = new TextCardMessage();
|
||||||
|
|
||||||
|
// 设置消息的基本信息,如接收用户、消息类型和企业ID
|
||||||
|
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("<div class='gray'>设备信息</div>" +
|
||||||
|
"<div class='highlight'>品牌: " + deviceInfoDAO.getDeviceBrand() + "</div>" +
|
||||||
|
"<div class='highlight'>设备型号: " + deviceInfoDAO.getDeviceModel() + "</div>" +
|
||||||
|
"<div class='highlight'>安卓版本: " + deviceInfoDAO.getAndroidVersion() + "</div>" +
|
||||||
|
"<div class='highlight'>SN: " + deviceInfoDAO.getSN() + "</div>" +
|
||||||
|
"<div class='highlight'>监测到掉线时间: " + formattedCurrentTime + "</div>" +
|
||||||
|
"<div class='highlight'>备注: " + deviceInfoDAO.getRemark() + "</div>" +
|
||||||
|
"检测到设备已掉线,请及时查看设备状态.");
|
||||||
|
textCard.setUrl("https://kimgo.cn");
|
||||||
|
// 将构建的TextCard对象设置到TextCardMessage中
|
||||||
|
textCardMessage.setTextcard(textCard);
|
||||||
|
|
||||||
|
// 记录文本卡片消息的内容
|
||||||
|
logger.info("TextCardMessage: {}", textCardMessage);
|
||||||
|
|
||||||
|
return textCardMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新设备的状态信息。
|
||||||
|
*
|
||||||
|
* @param device 设备信息对象,包含需要更新的设备的SN(序列号)。
|
||||||
|
* @param status 需要更新到的新状态。
|
||||||
|
* @throws DatabaseUpdateException 如果数据库更新过程中发生异常,则抛出此异常。
|
||||||
|
*/
|
||||||
|
private void updateStatus(DeviceInfoDAO device, int status) throws DatabaseUpdateException {
|
||||||
|
try {
|
||||||
|
// 构建更新条件,设置更新的状态值
|
||||||
|
UpdateWrapper<DeviceInfoDAO> 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);
|
||||||
|
throw new DatabaseUpdateException("update to mysql error.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,26 +2,25 @@ package com.kimgo.wepush.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.kimgo.wepush.config.UserConfig;
|
import com.kimgo.wepush.config.UserConfig;
|
||||||
import com.kimgo.wepush.mapper.QyWeChatAppInfoMapper;
|
|
||||||
import com.kimgo.wepush.mapper.QyWeChatURLMapper;
|
import com.kimgo.wepush.mapper.QyWeChatURLMapper;
|
||||||
import com.kimgo.wepush.model.QyWeChatAppInfo;
|
|
||||||
import com.kimgo.wepush.model.QyWeChatURL;
|
import com.kimgo.wepush.model.QyWeChatURL;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class QyWeChatURLService {
|
public class QyWeChatURLService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(QyWeChatURLService.class);
|
private final Logger logger = LoggerFactory.getLogger(QyWeChatURLService.class);
|
||||||
@Autowired
|
private final QyWeChatURLMapper qyWeChatURLMapper;
|
||||||
private QyWeChatURLMapper qyWeChatURLMapper;
|
private final UserConfig userConfig;
|
||||||
@Autowired
|
|
||||||
private UserConfig userConfig;
|
|
||||||
private String urlName;
|
|
||||||
private String sendTextCardMessageUrl;
|
private String sendTextCardMessageUrl;
|
||||||
private String qyWechatGetTokenUrl;
|
private String qyWechatGetTokenUrl;
|
||||||
|
|
||||||
|
public QyWeChatURLService(QyWeChatURLMapper qyWeChatURLMapper, UserConfig userConfig) {
|
||||||
|
this.qyWeChatURLMapper = qyWeChatURLMapper;
|
||||||
|
this.userConfig = userConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSendTextCardMessageUrl() {
|
public String getSendTextCardMessageUrl() {
|
||||||
if (sendTextCardMessageUrl == null){
|
if (sendTextCardMessageUrl == null){
|
||||||
queryURL();
|
queryURL();
|
||||||
@@ -33,11 +32,13 @@ public class QyWeChatURLService {
|
|||||||
QueryWrapper<QyWeChatURL> wrapper = new QueryWrapper<>();
|
QueryWrapper<QyWeChatURL> wrapper = new QueryWrapper<>();
|
||||||
wrapper.eq("phone_number", phoneNumberToSearch);
|
wrapper.eq("phone_number", phoneNumberToSearch);
|
||||||
QyWeChatURL result = qyWeChatURLMapper.selectOne(wrapper);
|
QyWeChatURL result = qyWeChatURLMapper.selectOne(wrapper);
|
||||||
if (result == null){
|
if (result != null){
|
||||||
logger.error("queryURL error");
|
logger.error("queryURL error");
|
||||||
|
logger.info("queryURL result: {}",result);
|
||||||
|
sendTextCardMessageUrl = result.getSendTextCardMessageUrl();
|
||||||
|
qyWechatGetTokenUrl = result.getQyWechatGetTokenUrl();
|
||||||
|
} else {
|
||||||
|
logger.error("queryURL error.");
|
||||||
}
|
}
|
||||||
logger.info("queryURL result: {}",result.toString());
|
}
|
||||||
sendTextCardMessageUrl = result.getSendTextCardMessageUrl();
|
|
||||||
qyWechatGetTokenUrl = result.getQyWechatGetTokenUrl();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.kimgo.wepush.service;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.kimgo.wepush.model.TextMessage;
|
||||||
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
import com.kimgo.wepush.response.QyWeChatSendMessageApiResponse;
|
||||||
import com.kimgo.wepush.model.SMSInfo;
|
import com.kimgo.wepush.model.SMSInfo;
|
||||||
import com.kimgo.wepush.model.TextCardMessage;
|
import com.kimgo.wepush.model.TextCardMessage;
|
||||||
@@ -18,12 +19,15 @@ import java.io.IOException;
|
|||||||
public class SMSService {
|
public class SMSService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(SMSService.class);
|
private final Logger logger = LoggerFactory.getLogger(SMSService.class);
|
||||||
|
|
||||||
@Autowired
|
private final ApiSettingService apiSettingService;
|
||||||
private ApiSettingService apiSettingService;
|
private final TokenService tokenService;
|
||||||
@Autowired
|
private final QyWeChatURLService qyWeChatURLService;
|
||||||
private TokenService tokenService;
|
|
||||||
@Autowired
|
public SMSService(ApiSettingService apiSettingService, TokenService tokenService, QyWeChatURLService qyWeChatURLService) {
|
||||||
private QyWeChatURLService qyWeChatURLService;
|
this.apiSettingService = apiSettingService;
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.qyWeChatURLService = qyWeChatURLService;
|
||||||
|
}
|
||||||
|
|
||||||
public ServerResponseEntity getSMSInfo(String accessToken, SMSInfo smsInfo){
|
public ServerResponseEntity getSMSInfo(String accessToken, SMSInfo smsInfo){
|
||||||
String correctAccessToken = tokenService.getApiAccessToken();
|
String correctAccessToken = tokenService.getApiAccessToken();
|
||||||
@@ -41,22 +45,8 @@ public class SMSService {
|
|||||||
|
|
||||||
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken,SMSInfo smsInfo){
|
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken,SMSInfo smsInfo){
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
|
|
||||||
|
|
||||||
TextCardMessage textCardMessage = setTextCardMessage(smsInfo);
|
Request request = handleMessage(accessToken,smsInfo);
|
||||||
// 使用Jackson进行序列化
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
String jsonBody = null;
|
|
||||||
try {
|
|
||||||
jsonBody = objectMapper.writeValueAsString(textCardMessage);
|
|
||||||
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);
|
|
||||||
Request request = new Request.Builder().url(url).post(body).build();
|
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
logger.info("Response: {}", responseBody);
|
logger.info("Response: {}", responseBody);
|
||||||
@@ -102,4 +92,79 @@ public class SMSService {
|
|||||||
|
|
||||||
return textCardMessage;
|
return textCardMessage;
|
||||||
}
|
}
|
||||||
|
public TextMessage setTextMessage(SMSInfo smsInfo){
|
||||||
|
TextMessage textMessage = new TextMessage();
|
||||||
|
textMessage.setTouser(apiSettingService.getApiSetting().getTouser());
|
||||||
|
textMessage.setMsgtype("text");
|
||||||
|
textMessage.setAgentid(apiSettingService.getApiSetting().getAgentid());
|
||||||
|
textMessage.setEnable_duplicate_check(apiSettingService.getApiSetting().getEnableDuplicateCheck());
|
||||||
|
textMessage.setDuplicate_check_interval(apiSettingService.getApiSetting().getDuplicateCheckInterval());
|
||||||
|
TextMessage.Text text = new TextMessage.Text();
|
||||||
|
text.setContent("你有一条新短信\n短信号码: "+smsInfo.getSmsNumber() + "\n接受时间: " + smsInfo.getSmsAcceptanceTime() + "\n内容: \n"+ smsInfo.getSmsContent());
|
||||||
|
textMessage.setText(text);
|
||||||
|
logger.info("TextMessage: {}", textMessage.toString());
|
||||||
|
return textMessage;
|
||||||
|
}
|
||||||
|
private<T> Request bulidRequest(String accessToken,T object){
|
||||||
|
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
|
||||||
|
// 使用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);
|
||||||
|
Request request = new Request.Builder().url(url).post(body).build();
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
private Request handleMessage(String accessToken, SMSInfo smsInfo) {
|
||||||
|
TextCardMessage textCardMessage = setTextCardMessage(smsInfo);
|
||||||
|
int textCardMessageSize = checkMessageSize(textCardMessage.getTextcard().getDescription());
|
||||||
|
|
||||||
|
if (textCardMessageSize <= 512) {
|
||||||
|
return bulidRequest(accessToken, textCardMessage);
|
||||||
|
} else {
|
||||||
|
TextMessage textMessage = setTextMessage(smsInfo);
|
||||||
|
int textMessageSize = checkMessageSize(textMessage.getText().getContent());
|
||||||
|
|
||||||
|
if (textMessageSize <= 2048) {
|
||||||
|
return bulidRequest(accessToken, textMessage);
|
||||||
|
} else {
|
||||||
|
TextCardMessage wrMsg = setWarningTextCardMessage();
|
||||||
|
return bulidRequest(accessToken, wrMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextCardMessage setWarningTextCardMessage() {
|
||||||
|
TextCardMessage textCardMessage = new TextCardMessage();
|
||||||
|
|
||||||
|
textCardMessage.setTouser(apiSettingService.getApiSetting().getTouser());
|
||||||
|
textCardMessage.setMsgtype(apiSettingService.getApiSetting().getMsgtype());
|
||||||
|
textCardMessage.setAgentid(apiSettingService.getApiSetting().getAgentid());
|
||||||
|
textCardMessage.setEnable_duplicate_check(apiSettingService.getApiSetting().getEnableDuplicateCheck());
|
||||||
|
textCardMessage.setDuplicate_check_interval(apiSettingService.getApiSetting().getDuplicateCheckInterval());
|
||||||
|
|
||||||
|
TextCardMessage.TextCard textCard = new TextCardMessage.TextCard();
|
||||||
|
textCard.setTitle("警告消息");
|
||||||
|
textCard.setDescription("<div class='highlight'>警告: 短信消息过长请查看手机。</div>");
|
||||||
|
textCard.setUrl("https://kimgo.cn");
|
||||||
|
// 将TextCard对象设置到TextCardMessage中
|
||||||
|
textCardMessage.setTextcard(textCard);
|
||||||
|
|
||||||
|
logger.info("TextCardMessage: {}", textCardMessage.toString());
|
||||||
|
|
||||||
|
return textCardMessage;
|
||||||
|
}
|
||||||
|
public int checkMessageSize(String message){
|
||||||
|
byte[] messageBytes = message.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
int size = messageBytes.length;
|
||||||
|
logger.info("Message size in bytes: " + size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,21 +10,23 @@ import com.kimgo.wepush.model.QyWeChatAppInfo;
|
|||||||
import com.kimgo.wepush.request.WeChatAPI;
|
import com.kimgo.wepush.request.WeChatAPI;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class TokenService {
|
public class TokenService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(TokenService.class);
|
private final Logger logger = LoggerFactory.getLogger(TokenService.class);
|
||||||
@Autowired
|
private final UserConfig userConfig;
|
||||||
private UserConfig userConfig;
|
private final QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
|
||||||
@Autowired
|
private final ApiSettingMapper apiSettingMapper;
|
||||||
private QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
|
|
||||||
@Autowired
|
|
||||||
private ApiSettingMapper apiSettingMapper;
|
|
||||||
private String accessToken;
|
private String accessToken;
|
||||||
private String apiAccessToken;
|
private String apiAccessToken;
|
||||||
|
|
||||||
|
public TokenService(ApiSettingMapper apiSettingMapper, QyWeChatAppInfoMapper qyWeChatAppInfoMapper, UserConfig userConfig) {
|
||||||
|
this.apiSettingMapper = apiSettingMapper;
|
||||||
|
this.qyWeChatAppInfoMapper = qyWeChatAppInfoMapper;
|
||||||
|
this.userConfig = userConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAccessToken() {
|
public String getAccessToken() {
|
||||||
if (accessToken == null) {
|
if (accessToken == null) {
|
||||||
updateAccessToken();
|
updateAccessToken();
|
||||||
@@ -75,7 +77,7 @@ public class TokenService {
|
|||||||
|
|
||||||
int result = qyWeChatAppInfoMapper.update(null, updateWrapper);
|
int result = qyWeChatAppInfoMapper.update(null, updateWrapper);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
logger.info("Update successful");
|
logger.debug("Update successful");
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Update failed: No rows affected");
|
logger.warn("Update failed: No rows affected");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: prod
|
active: dev
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
|
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="info">
|
<root level="info">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
<appender-ref ref="FILE"/>
|
<appender-ref ref="FILE"/>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public class QyWeChatAppInfoMapperTest {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
|
private QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSelect() {
|
public void testSelect() {
|
||||||
System.out.println(("----- select by id test ------"));
|
System.out.println(("----- select by id test ------"));
|
||||||
@@ -41,12 +42,11 @@ public class QyWeChatAppInfoMapperTest {
|
|||||||
|
|
||||||
System.out.println("Test complete");
|
System.out.println("Test complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllColum(){
|
public void testAllColum() {
|
||||||
for (int i = 1; i < 12; i++) {
|
QyWeChatAppInfo qyWeChatAppInfo = qyWeChatAppInfoMapper.selectById(1);
|
||||||
QyWeChatAppInfo qyWeChatAppInfo = qyWeChatAppInfoMapper.selectById(i);
|
PhoneNumberValidation phoneNumberValidation = new PhoneNumberValidation();
|
||||||
PhoneNumberValidation phoneNumberValidation = new PhoneNumberValidation();
|
phoneNumberValidation.isValidPhoneNumber(qyWeChatAppInfo.getPhoneNumber());
|
||||||
phoneNumberValidation.isValidPhoneNumber(qyWeChatAppInfo.getPhoneNumber());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/test/java/com/kimgo/wepush/model/WeChatMsgTest.java
Normal file
55
src/test/java/com/kimgo/wepush/model/WeChatMsgTest.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package com.kimgo.wepush.model;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class WeChatMsgTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isInvalid_AllFieldsValid() {
|
||||||
|
WeChatMsg weChatMsg = new WeChatMsg();
|
||||||
|
weChatMsg.setPackageName("com.example.package");
|
||||||
|
weChatMsg.setAppName("Example App");
|
||||||
|
weChatMsg.setTitle("Example Title");
|
||||||
|
weChatMsg.setSender("Example Sender");
|
||||||
|
weChatMsg.setMessage("Example Message");
|
||||||
|
weChatMsg.setCurrentTime("2023-12-12 18:43:04");
|
||||||
|
assertFalse(weChatMsg.isInvalid(), "WeChatMsg should be valid when all fields are valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isInvalid_OneFieldIsNull() {
|
||||||
|
WeChatMsg weChatMsg = new WeChatMsg();
|
||||||
|
weChatMsg.setPackageName(null); // 设置一个字段为 null
|
||||||
|
weChatMsg.setAppName("Example App");
|
||||||
|
weChatMsg.setTitle("Example Title");
|
||||||
|
weChatMsg.setSender("Example Sender");
|
||||||
|
weChatMsg.setMessage("Example Message");
|
||||||
|
|
||||||
|
assertTrue(weChatMsg.isInvalid(), "WeChatMsg should be invalid if any field is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isInvalid_OneFieldIsEmpty() {
|
||||||
|
WeChatMsg weChatMsg = new WeChatMsg();
|
||||||
|
weChatMsg.setPackageName(""); // 设置一个字段为空字符串
|
||||||
|
weChatMsg.setAppName("Example App");
|
||||||
|
weChatMsg.setTitle("Example Title");
|
||||||
|
weChatMsg.setSender("Example Sender");
|
||||||
|
weChatMsg.setMessage("Example Message");
|
||||||
|
|
||||||
|
assertTrue(weChatMsg.isInvalid(), "WeChatMsg should be invalid if any field is an empty string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isInvalid_OneFieldIsStringNull() {
|
||||||
|
WeChatMsg weChatMsg = new WeChatMsg();
|
||||||
|
weChatMsg.setPackageName("null"); // 设置一个字段为字面字符串 "null"
|
||||||
|
weChatMsg.setAppName("Example App");
|
||||||
|
weChatMsg.setTitle("Example Title");
|
||||||
|
weChatMsg.setSender("Example Sender");
|
||||||
|
weChatMsg.setMessage("Example Message");
|
||||||
|
|
||||||
|
assertTrue(weChatMsg.isInvalid(), "WeChatMsg should be invalid if any field is the literal string \"null\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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();
|
||||||
|
System.out.printf(deviceInfo.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addDeviceInfo() {
|
||||||
|
DeviceInfo deviceInfo = new DeviceInfo();
|
||||||
|
System.out.printf(deviceInfo.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/test/java/com/kimgo/wepush/service/SMSServiceTest.java
Normal file
75
src/test/java/com/kimgo/wepush/service/SMSServiceTest.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package com.kimgo.wepush.service;
|
||||||
|
|
||||||
|
import com.kimgo.wepush.model.SMSInfo;
|
||||||
|
import com.kimgo.wepush.response.ServerResponseEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class SMSServiceTest {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SMSServiceTest.class);
|
||||||
|
@Autowired
|
||||||
|
private SMSService smsService;
|
||||||
|
public void testMessageSize(){
|
||||||
|
String message = "【话费剩余】\n" +
|
||||||
|
"您所在的家庭账户本月通信费87.90元,余额20.91元,其中本机本月累计消费87.90元(由家庭账户统一支付),家庭成员13541758298本月费用0.00元,家庭成员20935981572本月费用0.00元。新爱家58共享话费58.00元,当前剩余0.00元,\n" +
|
||||||
|
"【流量查询】\n" +
|
||||||
|
"国内通用流量共15.00GB,当前可使用14.95GB。回CXLL查明细。\n" +
|
||||||
|
"【套餐剩余】\n" +
|
||||||
|
"移动68套餐基本通话优惠分钟数余200分钟。\n" +
|
||||||
|
"发送“0000”到“10086”,订购业务清清楚楚,一键退订轻轻松松。\n" +
|
||||||
|
"【已开业务】回“1”\n" +
|
||||||
|
"【流量超市】回“2”\n" +
|
||||||
|
"【消费情况】回“3”\n" +
|
||||||
|
"【20元/月→4GB国内流量】回“4”\n" +
|
||||||
|
"【30元/月→10GB国内流量】回“5”\n" +
|
||||||
|
"【60元/月→30GB国内流量】回“6”\n" +
|
||||||
|
"【7天流量包】回“7”\n" +
|
||||||
|
"【19.9元/月→5GB国内流量+视频会员】回“8”\n" +
|
||||||
|
"【宽带专区】回“9”\n" +
|
||||||
|
"【在线客服】若您有话费/流量疑问、业务咨询/退订等其他需求可联系在线文字客服,点击官方认证通道: https://dx.10086.cn/Q_HZBg \n" +
|
||||||
|
"【为您推荐】尊敬的客户,您好!您有10元话费券待领取: http://dx.10086.cn/D/jEd1Jq ,点击查看话费使用详情: https://dx.10086.cn/38UvEQ 。。【中国移动】";
|
||||||
|
String shortMsg = "【腾讯科技】尊敬的用户,您的账号whj139****3736,经人工客服核实,已根据账号使用情况进行处理,请重新登录即可。";
|
||||||
|
String superMsg = generateMsg();
|
||||||
|
|
||||||
|
String accessToken = "gKGCDSgWV82XbU0H";
|
||||||
|
List<SMSInfo> smsInfoList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
SMSInfo smsInfo = new SMSInfo();
|
||||||
|
smsInfo.setSmsNumber("12321");
|
||||||
|
smsInfo.setSmsAcceptanceTime("1123321");
|
||||||
|
smsInfoList.add(smsInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
smsInfoList.get(0).setSmsContent(message);
|
||||||
|
smsInfoList.get(1).setSmsContent(shortMsg);
|
||||||
|
smsInfoList.get(2).setSmsContent(superMsg);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
ServerResponseEntity serverResponseEntity = smsService.getSMSInfo(accessToken,smsInfoList.get(i));
|
||||||
|
logger.info(serverResponseEntity.toString());
|
||||||
|
try {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private String generateMsg() {
|
||||||
|
StringBuilder msg = new StringBuilder("这是一条超长消息用于测试。");
|
||||||
|
int size = msg.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
|
||||||
|
|
||||||
|
while (size <= 2100) {
|
||||||
|
msg.append("这是一条超长消息用于测试。");
|
||||||
|
size = msg.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user