Compare commits

..

8 Commits

Author SHA1 Message Date
1d3eaf451f Merge branch 'dev' 2023-12-06 16:48:10 +08:00
e8ccd78a39 更新 pom.xml 2023-12-06 11:15:56 +08:00
3324865570 更新 pom.xml 2023-12-06 11:05:18 +08:00
9e42d4676a 创建 application.yml 2023-12-06 11:03:58 +08:00
6bb83e186d 更新 .gitignore 2023-12-06 11:03:54 +08:00
6923440a89 删除 application.yml 2023-12-06 10:46:53 +08:00
8d39b4938a 更新 .gitignore 2023-12-06 10:44:55 +08:00
b40f5cc0c4 更新 application.yml 2023-12-06 10:43:53 +08:00
38 changed files with 71 additions and 1057 deletions

View File

@@ -1,8 +0,0 @@
## wepush
## 打包
```
mvn clean package
```

View File

@@ -35,20 +35,3 @@ 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,
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,'转发设备'),

View File

@@ -1,17 +0,0 @@
# 基础镜像使用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

View File

@@ -9,11 +9,14 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 打包文件名版本信息 -->
<groupId>com.kimgo</groupId>
<artifactId>wePush</artifactId>
<version>1.0.0</version>
<name>wePush</name>
<description>wePush</description>
<properties>
<java.version>17</java.version>
</properties>

View File

@@ -3,10 +3,8 @@ 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 {

View File

@@ -1,22 +0,0 @@
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);
}
}

View File

@@ -1,36 +0,0 @@
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);
}
}

View File

@@ -1,49 +0,0 @@
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);
}
}

View File

@@ -1,11 +0,0 @@
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);
}
}

View File

@@ -1,11 +0,0 @@
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);
}
}

View File

@@ -2,7 +2,6 @@ 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
@@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @createDate 2023-12-05 01:25:35
* @Entity com.kimgo.wepush.model.ApiSetting
*/
@Mapper
public interface ApiSettingMapper extends BaseMapper<ApiSetting> {
}

View File

@@ -1,10 +0,0 @@
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> {
}

View File

@@ -2,8 +2,6 @@ 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<QyWeChatAppInfo> {
}

View File

@@ -4,6 +4,5 @@ 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<QyWeChatURL> {
}

View File

@@ -1,24 +0,0 @@
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();
}
}

View File

@@ -1,25 +0,0 @@
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;
}

View File

@@ -21,5 +21,6 @@ public class TextMessage {
public static class Text {
private String content;
}
}

View File

@@ -1,22 +0,0 @@
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);
}
}

View File

@@ -1,78 +0,0 @@
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.");
}
}

View File

@@ -12,51 +12,35 @@ import java.io.IOException;
public class WeChatAPI {
private final Logger logger = LoggerFactory.getLogger(WeChatAPI.class);
/**
* 获取企业微信访问令牌
* @param corpid 企业ID
* @param corpsecret 企业密钥
* @return 返回获取到的访问令牌如果获取失败则返回null
*/
public String getAccessToken(String corpid,String corpsecret){
// 创建OkHttpClient实例
OkHttpClient client = new OkHttpClient();
// 设置请求URL并添加查询参数
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addQueryParameter("corpid", corpid);
urlBuilder.addQueryParameter("corpsecret", corpsecret);
// 构建请求
Request request = new Request.Builder()
.url(urlBuilder.build())
.build();
try (Response response = client.newCall(request).execute()) {
// 从响应中读取body内容
String responseBody = response.body().string();
logger.info("get qyWeChat AccessToken responseBody: {}", responseBody);
// 对响应内容进行JSON解析
ObjectMapper objectMapper1 = new ObjectMapper();
QyWeChatGetAccessTokenApiResponse qyWeChatGetAccessTokenApiResponse = objectMapper1.readValue(responseBody, QyWeChatGetAccessTokenApiResponse.class);
// 检查是否获取令牌成功
if (qyWeChatGetAccessTokenApiResponse.getErrcode() == 0){
logger.debug("get qyWeChat AccessToken Success");
// 返回访问令牌
return qyWeChatGetAccessTokenApiResponse.getAccess_token();
} else {
// 记录获取令牌失败的日志
logger.error("getAccessToken error: {}",qyWeChatGetAccessTokenApiResponse.getErrmsg());
return null;
}
} catch (IOException e) {
// 记录异常信息
logger.error("getAccessToken error:",e);
return null;
}
}
}

View File

@@ -1,10 +1,8 @@
package com.kimgo.wepush.response;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class QyWeChatGetAccessTokenApiResponse {
long errcode;
String errmsg;

View File

@@ -1,17 +1,10 @@
package com.kimgo.wepush.response;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class QyWeChatSendMessageApiResponse {
private int errcode;
private String errmsg;
private String msgid;
public QyWeChatSendMessageApiResponse(int errcode, String errmsg) {
this.errcode = errcode;
this.errmsg = errmsg;
}
}

View File

@@ -62,9 +62,8 @@ public class ServerResponseEntity <T> implements Serializable {
public static <T> ServerResponseEntity<T> success(){
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setCode(ResponseEnum.OK.value());
serverResponseEntity.setMsg(ResponseEnum.OK.getMsg());
serverResponseEntity.getMsg();
serverResponseEntity.setTimestamp(Instant.now().getEpochSecond());
serverResponseEntity.setData(null);
return serverResponseEntity;
}

View File

@@ -7,62 +7,34 @@ import com.kimgo.wepush.mapper.ApiSettingMapper;
import com.kimgo.wepush.model.ApiSetting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ApiSettingService {
private final Logger logger = LoggerFactory.getLogger(ApiSettingService.class);
private ApiSetting apiSetting;
private final ApiSettingMapper apiSettingMapper;
private final UserConfig userConfig;
public ApiSettingService(ApiSettingMapper apiSettingMapper, UserConfig userConfig) {
this.apiSettingMapper = apiSettingMapper;
this.userConfig = userConfig;
}
@Autowired
private ApiSettingMapper apiSettingMapper;
@Autowired
private UserConfig userConfig;
public ApiSetting getApiSetting() {
if (apiSetting == null) {
queryApiSetting();
}
return apiSetting;
}
/**
* 查询API设置信息。
* 该方法不接受参数也不返回值但会根据用户配置的电话号码查询数据库中的API设置信息并更新相关的成员变量。
* 主要步骤包括:
* 1. 从用户配置中获取电话号码。
* 2. 构建查询条件。
* 3. 根据查询条件从数据库中查询唯一的API设置信息。
* 4. 记录查询结果的日志信息。
* 5. 如果查询结果为空则更新API设置为null并记录错误日志。
* 6. 最后更新API设置成员变量为查询结果并记录相应日志。
*/
private void queryApiSetting() {
// 从用户配置中获取电话号码
private void queryApiSetting(){
String phoneNumberToSearch = userConfig.getPhoneNumber();
// 构建查询条件
QueryWrapper<ApiSetting> wrapper = new QueryWrapper<>();
wrapper.eq("phone_number", phoneNumberToSearch);
// 根据查询条件查询API设置信息
ApiSetting result = apiSettingMapper.selectOne(wrapper);
// 记录查询结果
logger.info("result: {}", result);
// 处理查询结果为空的情况
if (result == null) {
logger.info("result: {}",result);
if (result == null){
apiSetting = null;
// 记录错误日志
logger.error("updateAccessToken error");
}
// 更新API设置成员变量
apiSetting = result;
// 记录更新日志
logger.info("get apiSetting from mysql,accessToken: {}", apiSetting);
logger.info("get apiSetting from mysql,accessToken: {}",apiSetting);
}
}

View File

@@ -2,6 +2,7 @@ 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;
@@ -11,6 +12,7 @@ import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import okhttp3.*;
@@ -19,133 +21,84 @@ import java.io.IOException;
@Service
public class CallService {
private final Logger logger = LoggerFactory.getLogger(CallService.class);
private final QyWeChatURLService qyWeChatURLService;
private final ApiSettingService apiSettingService;
final
private ServerResponseEntity serverResponseEntity;
@Autowired
private QyWeChatURLService qyWeChatURLService;
@Autowired
private ApiSettingService apiSettingService;
@Autowired
TokenService tokenService;
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
public ServerResponseEntity getPhoneNumber(String accessToken, CallInfo callInfo){
String correctAccessToken = tokenService.getApiAccessToken();
logger.info("accessToken: {} correctAccessToken: {}", accessToken, correctAccessToken);
// 验证提供的accessToken是否有效
if (!correctAccessToken.equals(accessToken)) {
logger.info("accessToken: {} correctAccessToken: {}",accessToken,correctAccessToken);
if (!correctAccessToken.equals(accessToken)){
return ServerResponseEntity.fail("Invalid accessToken");
}
// 从呼叫信息中获取电话号码和呼叫时间
String phoneNumber = callInfo.getPhoneNumber();
String callTime = callInfo.getCallTime();
// 获取企业微信的访问令牌
String qyAccessToken = tokenService.getAccessToken();
// 使用企业微信的accessToken、电话号码和呼叫时间发送消息并获取响应
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = requestWithOkhttp(qyAccessToken, phoneNumber, callTime);
// 检查发送消息的响应代码
QyWeChatSendMessageApiResponse qyWeChatSendMessageApiResponse = requestWithOkhttp(qyAccessToken,phoneNumber,callTime);
int code = qyWeChatSendMessageApiResponse.getErrcode();
if (code == 0) {
// 如果消息发送成功,构建并返回成功的服务器响应
if (code == 0){
SendMessageResonse sendMessageResonse = new SendMessageResonse();
sendMessageResonse.setMsgid(qyWeChatSendMessageApiResponse.getMsgid());
return ServerResponseEntity.success(sendMessageResonse);
} else {
// 如果消息发送失败,返回失败的服务器响应
return ServerResponseEntity.fail("request qyWeChat error,please check server error log.");
}
}
/**
* 设置文本卡片消息的内容。
*
* @param phoneNumber 来电电话号码
* @param callTime 来电时间
* @return TextCardMessage 文本卡片消息对象,包含了设置好的内容
*/
public TextCardMessage setTextCardMessage(String phoneNumber, String callTime) {
TextCardMessage textCardMessage = new TextCardMessage();
// 从api设置中获取用户信息并设置到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());
// 创建TextCard对象并设置其内容
TextCardMessage.TextCard textCard = new TextCardMessage.TextCard();
textCard.setTitle("新来电通知");
// 设置描述信息,包含来电号码和来电时间
textCard.setDescription("<div class='gray'>您有一通新来电</div><div class='highlight'>来电号码: " + phoneNumber + "</div><div class='highlight'>来电时间: " + callTime + "</div>如果您认识这个号码,请及时回电。如果不认识,请注意可能是垃圾电话或诈骗电话。");
textCard.setUrl("https://kimgo.cn");
// 将TextCard对象设置到TextCardMessage中
textCardMessage.setTextcard(textCard);
logger.info("TextCardMessage: {}", textCardMessage);
logger.info("TextCardMessage: {}", textCardMessage.toString());
return textCardMessage;
}
/**
* 使用OkHttp发送企业微信文本卡片消息。
*
* @param accessToken 访问令牌用于授权访问企业微信API。
* @param phoneNumber 接收消息的手机号码。
* @param callTime 预约的呼叫时间。
* @return 返回企业微信发送消息的API响应如果失败则返回null。
*/
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken, String phoneNumber, String callTime) {
// 构建发送文本卡片消息的URL
public QyWeChatSendMessageApiResponse requestWithOkhttp(String accessToken,String phoneNumber,String callTime){
String url = qyWeChatURLService.getSendTextCardMessageUrl() + "?access_token=" + accessToken;
OkHttpClient client = new OkHttpClient();
// 设置文本卡片消息内容
TextCardMessage textCardMessage = setTextCardMessage(phoneNumber, callTime);
// 将消息对象序列化为JSON字符串
TextCardMessage textCardMessage = setTextCardMessage(phoneNumber,callTime);
// 使用Jackson进行序列化
ObjectMapper objectMapper = new ObjectMapper();
String jsonBody;
String jsonBody = null;
try {
jsonBody = objectMapper.writeValueAsString(textCardMessage);
logger.info("jsonBody: {}", jsonBody);
logger.info("jsonBody: {}",jsonBody);
} catch (JsonProcessingException e) {
logger.error("JSON processing error", e);
return null;
}
// 构建HTTP请求体
// 构建请求体
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()) {
// 从响应中读取内容
String responseBody = response.body().string();
logger.info("request sendMessage api ResponseBody: {}", responseBody);
// 反序列化API响应
ObjectMapper objectMapper1 = new ObjectMapper();
QyWeChatSendMessageApiResponse apiResponse = objectMapper1.readValue(responseBody, QyWeChatSendMessageApiResponse.class);
// 处理不同的API响应结果
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...");
tokenService.setAccessToken(); // 刷新accessToken的方法
tokenService.setAccessToken(); // 一个方法来刷新accessToken
String newAccessToken = tokenService.getAccessToken();
return requestWithOkhttp(newAccessToken, phoneNumber, callTime);
} else {

View File

@@ -1,108 +0,0 @@
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;
}
}
}

View File

@@ -1,136 +0,0 @@
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;
}
}

View File

@@ -1,208 +0,0 @@
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);
}
}
}

View File

@@ -2,25 +2,26 @@ package com.kimgo.wepush.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kimgo.wepush.config.UserConfig;
import com.kimgo.wepush.mapper.QyWeChatAppInfoMapper;
import com.kimgo.wepush.mapper.QyWeChatURLMapper;
import com.kimgo.wepush.model.QyWeChatAppInfo;
import com.kimgo.wepush.model.QyWeChatURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class QyWeChatURLService {
private final Logger logger = LoggerFactory.getLogger(QyWeChatURLService.class);
private final QyWeChatURLMapper qyWeChatURLMapper;
private final UserConfig userConfig;
@Autowired
private QyWeChatURLMapper qyWeChatURLMapper;
@Autowired
private UserConfig userConfig;
private String urlName;
private String sendTextCardMessageUrl;
private String qyWechatGetTokenUrl;
public QyWeChatURLService(QyWeChatURLMapper qyWeChatURLMapper, UserConfig userConfig) {
this.qyWeChatURLMapper = qyWeChatURLMapper;
this.userConfig = userConfig;
}
public String getSendTextCardMessageUrl() {
if (sendTextCardMessageUrl == null){
queryURL();
@@ -32,13 +33,11 @@ public class QyWeChatURLService {
QueryWrapper<QyWeChatURL> wrapper = new QueryWrapper<>();
wrapper.eq("phone_number", phoneNumberToSearch);
QyWeChatURL result = qyWeChatURLMapper.selectOne(wrapper);
if (result != null){
if (result == null){
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();
};
}

View File

@@ -19,15 +19,12 @@ import java.io.IOException;
public class SMSService {
private final Logger logger = LoggerFactory.getLogger(SMSService.class);
private final ApiSettingService apiSettingService;
private final TokenService tokenService;
private final QyWeChatURLService qyWeChatURLService;
public SMSService(ApiSettingService apiSettingService, TokenService tokenService, QyWeChatURLService qyWeChatURLService) {
this.apiSettingService = apiSettingService;
this.tokenService = tokenService;
this.qyWeChatURLService = qyWeChatURLService;
}
@Autowired
private ApiSettingService apiSettingService;
@Autowired
private TokenService tokenService;
@Autowired
private QyWeChatURLService qyWeChatURLService;
public ServerResponseEntity getSMSInfo(String accessToken, SMSInfo smsInfo){
String correctAccessToken = tokenService.getApiAccessToken();

View File

@@ -10,23 +10,21 @@ import com.kimgo.wepush.model.QyWeChatAppInfo;
import com.kimgo.wepush.request.WeChatAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TokenService {
private final Logger logger = LoggerFactory.getLogger(TokenService.class);
private final UserConfig userConfig;
private final QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
private final ApiSettingMapper apiSettingMapper;
@Autowired
private UserConfig userConfig;
@Autowired
private QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
@Autowired
private ApiSettingMapper apiSettingMapper;
private String accessToken;
private String apiAccessToken;
public TokenService(ApiSettingMapper apiSettingMapper, QyWeChatAppInfoMapper qyWeChatAppInfoMapper, UserConfig userConfig) {
this.apiSettingMapper = apiSettingMapper;
this.qyWeChatAppInfoMapper = qyWeChatAppInfoMapper;
this.userConfig = userConfig;
}
public String getAccessToken() {
if (accessToken == null) {
updateAccessToken();

View File

@@ -1,3 +1,3 @@
spring:
profiles:
active: dev
active: prod

View File

@@ -11,6 +11,7 @@
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>

View File

@@ -15,7 +15,6 @@ public class QyWeChatAppInfoMapperTest {
@Autowired
private QyWeChatAppInfoMapper qyWeChatAppInfoMapper;
@Test
public void testSelect() {
System.out.println(("----- select by id test ------"));
@@ -42,11 +41,12 @@ public class QyWeChatAppInfoMapperTest {
System.out.println("Test complete");
}
@Test
public void testAllColum() {
QyWeChatAppInfo qyWeChatAppInfo = qyWeChatAppInfoMapper.selectById(1);
PhoneNumberValidation phoneNumberValidation = new PhoneNumberValidation();
phoneNumberValidation.isValidPhoneNumber(qyWeChatAppInfo.getPhoneNumber());
public void testAllColum(){
for (int i = 1; i < 12; i++) {
QyWeChatAppInfo qyWeChatAppInfo = qyWeChatAppInfoMapper.selectById(i);
PhoneNumberValidation phoneNumberValidation = new PhoneNumberValidation();
phoneNumberValidation.isValidPhoneNumber(qyWeChatAppInfo.getPhoneNumber());
}
}
}

View File

@@ -1,55 +0,0 @@
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\".");
}
}

View File

@@ -1,25 +0,0 @@
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());
}
}

View File

@@ -1,16 +0,0 @@
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();
}
}

View File

@@ -16,6 +16,7 @@ class SMSServiceTest {
private final Logger logger = LoggerFactory.getLogger(SMSServiceTest.class);
@Autowired
private SMSService smsService;
@Test
public void testMessageSize(){
String message = "【话费剩余】\n" +
"您所在的家庭账户本月通信费87.90元余额20.91元其中本机本月累计消费87.90元由家庭账户统一支付家庭成员13541758298本月费用0.00元家庭成员20935981572本月费用0.00元。新爱家58共享话费58.00元当前剩余0.00元,\n" +