Commit eaec19aa authored by ma's avatar ma

Merge branch 'for-yx' of http://120.77.240.215:9701/tianlai3/ioc_sixiang_license into for-yx

parents a7d5b2f3 6f87e226
......@@ -32,4 +32,4 @@ build/
### VS Code ###
.vscode/
application-test.yml
\ No newline at end of file
*-acc.yml
\ No newline at end of file
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
# -------------------- 平台应用相关,以pms开头 --------------------
DROP TABLE IF EXISTS `pms_use_log`;
CREATE TABLE `pms_use_log`
(
`id` int(10) NOT NULL AUTO_INCREMENT,
`sn` varchar(30) DEFAULT NULL COMMENT '设备编号',
`status` int(1) DEFAULT '1' COMMENT '状态 1:成功,0:失败',
`error_code` varchar(10) DEFAULT NULL COMMENT '如果失败,则这是失败的代号',
`message` varchar(200) DEFAULT NULL COMMENT '如果失败,则这里是失败的信息',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`deleted` int(1) DEFAULT '0' COMMENT '逻辑删除标识 1:删除,0:未删除',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8 COMMENT ='使用记录表';
ALTER TABLE `device`
ADD COLUMN `status` int(1) NULL DEFAULT NULL COMMENT '状态 0:未使用,1:已使用,2:失效' AFTER `app_id`;
ALTER TABLE `device`
ADD COLUMN `sn_bind` varchar(30) NULL DEFAULT NULL COMMENT '绑定的SN' AFTER `status`;
\ No newline at end of file
......@@ -10,7 +10,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableAsync
@ServletComponentScan(basePackages ="iot.sixiang.license")
@ServletComponentScan(basePackages = "iot.sixiang.license")
@SpringBootApplication
@EnableScheduling
@MapperScan(basePackages = "iot.sixiang.license.mapper")
......
......@@ -64,27 +64,62 @@ public class AuthManager {
}
}
// 终端设备鉴权
public boolean authTerminalDevice(String appId, String sn, String sign) {
if (!allApply.containsKey(appId)) {
log.info("no valid appId...");
return false;
}
if (!deviceManager.getContainSn(sn)) {
log.info("no this sn...");
return false;
}
DeviceVo device = deviceManager.getDevice(sn);
int deviceId = device.getDeviceId();
if (deviceBlackMap.containsKey(deviceId)) {
log.info("in black...");
return false;
}
Apply apply = allApply.get(appId);
String appKey = apply.getAppKey();
String input = "app_id=" + appId + "&sn=" + sn;
String valSHA1 = HmacUtil.encrypt(input, appKey, HmacUtil.HMAC_SHA1).toUpperCase();
if (CommonUtil.toUpperCaseByEnglish(sign).equals(CommonUtil.toUpperCaseByEnglish(valSHA1))) {
return true;
} else {
log.info("sign no valid:" + input);
return false;
}
}
public boolean auth(String appId, String sn, String sign) {
if (!allApply.containsKey(appId)) {
log.info("no valid appId...");
return false;
}
if (!deviceManager.getContainSn(sn)) {
log.info("no this sn...");
return false;
}
DeviceVo device = deviceManager.getDevice(sn);
// 未绑定
if (device.getSnBind() == null) {
log.info("bind sn null...");
return false;
}
int deviceId = device.getDeviceId();
if(deviceBlackMap.containsKey(deviceId)){
if (deviceBlackMap.containsKey(deviceId)) {
log.info("in black...");
return false;
}
Apply apply = allApply.get(appId);
String appKey = apply.getAppKey();
String input = "app_id=" + appId + "&sn=" + sn;
String input = "app_id=" + appId + "&sn=" + sn + "&sn_bind=" + device.getSnBind();
String valSHA1 = HmacUtil.encrypt(input, appKey, HmacUtil.HMAC_SHA1).toUpperCase();
if (CommonUtil.toUpperCaseByEnglish(sign).equals(CommonUtil.toUpperCaseByEnglish(valSHA1))) {
return true;
} else {
log.info("sign no valid:" + input);
return false;
}
}
......
......@@ -5,7 +5,10 @@ import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.DynamicParameter;
import com.github.xiaoymin.knife4j.annotations.DynamicParameters;
import io.swagger.annotations.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import iot.sixiang.license.device.DeviceManager;
import iot.sixiang.license.entity.DeviceBlack;
import iot.sixiang.license.log.BusinessType;
......@@ -20,7 +23,13 @@ import iot.sixiang.license.service.DeviceService;
import iot.sixiang.license.xss.XssUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
......@@ -82,12 +91,24 @@ public class DeviceController {
*/
@ApiOperation(value = "获取设备列表接口", notes = "用于获取设备列表")
@GetMapping("list")
@MyLog(title = "获取设备列表", optParam = "#{pageNo},#{pageSize},#{appName},#{userName}", businessType = BusinessType.SELECT)
@ApiImplicitParams({@ApiImplicitParam(name = "pageNo", value = "当前在第几页", required = true, dataType = "int"), @ApiImplicitParam(name = "pageSize", value = "每页显示多少页", required = true, dataType = "int"), @ApiImplicitParam(name = "appName", value = "应用名"), @ApiImplicitParam(name = "userName", value = "用户名")})
public PageResult<DeviceVo> getDeviceList(@RequestParam(value = "pageNo", defaultValue = "0") int pageNo, @RequestParam(value = "pageSize", defaultValue = "0") int pageSize, @RequestParam(value = "appName", required = false) String appName, @RequestParam(value = "userName", required = false) String userName) {
@MyLog(title = "获取设备列表", optParam = "#{pageNo},#{pageSize},#{appName},#{userName},#{sn},#{status}", businessType = BusinessType.SELECT)
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNo", value = "当前在第几页", required = true, dataType = "int"),
@ApiImplicitParam(name = "pageSize", value = "每页显示多少页", required = true, dataType = "int"),
@ApiImplicitParam(name = "appName", value = "应用名"),
@ApiImplicitParam(name = "userName", value = "用户名"),
@ApiImplicitParam(name = "sn", value = "设备编码"),
@ApiImplicitParam(name = "status", value = "状态", dataType = "int")
})
public PageResult<DeviceVo> getDeviceList(@RequestParam(value = "pageNo", defaultValue = "0") int pageNo,
@RequestParam(value = "pageSize", defaultValue = "0") int pageSize,
@RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "userName", required = false) String userName,
@RequestParam(value = "sn", required = false) String sn,
@RequestParam(value = "status", required = false) Integer status) {
appName = XssUtil.checkXSS(appName);
userName = XssUtil.checkXSS(userName);
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(pageNo, pageSize, appName, userName);
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(pageNo, pageSize, appName, userName, sn, status);
int total = records.getTotal();
int pages = total / pageSize;//pages为总页数
int mod = total % pageSize;
......
......@@ -23,7 +23,12 @@ import iot.sixiang.license.util.EmailUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
......
package iot.sixiang.license.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import iot.sixiang.license.entity.PmsUseLog;
import iot.sixiang.license.entity.SysOperLog;
import iot.sixiang.license.log.BusinessType;
import iot.sixiang.license.log.MyLog;
import iot.sixiang.license.model.PageInfoModel;
import iot.sixiang.license.model.PageResult;
import iot.sixiang.license.model.vo.DeviceVo;
import iot.sixiang.license.service.PmsUseService;
import iot.sixiang.license.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/iot_license/pms_use_log")
@Api(value = "使用记录模块", tags = {"使用记录模块"})
public class PmsUseLogController {
@Autowired
private PmsUseService pmsUseService;
/**
* 分页查询使用记录
*
* @param pageNo
* @param pageSize
* @return
*/
@ApiOperation(value = "获取使用记录列表接口", notes = "用于获取使用记录列表")
@GetMapping("list")
@MyLog(title = "获取使用记录列表", optParam = "#{pageNo},#{pageSize},#{sn},#{status}", businessType = BusinessType.SELECT)
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNo", value = "当前在第几页", required = true, dataType = "int"),
@ApiImplicitParam(name = "pageSize", value = "每页显示多少条", required = true, dataType = "int"),
@ApiImplicitParam(name = "sn", value = "设备编号"),
@ApiImplicitParam(name = "status", value = "状态 1:成功,0:失败", dataType = "int")
})
public PageResult<PmsUseLog> getPmsUseLogList(@RequestParam(value = "pageNo", defaultValue = "0") int pageNo,
@RequestParam(value = "pageSize", defaultValue = "0") int pageSize,
@RequestParam(value = "sn", required = false) String sn,
@RequestParam(value = "status", required = false) Integer status) {
PageInfoModel<PmsUseLog> records = pmsUseService.getPmsUseLogList(pageNo, pageSize, sn, status);
int total = records.getTotal();
int pages = total / pageSize;//pages为总页数
int mod = total % pageSize;
if (mod != 0) {
pages = pages + 1;
}
List<PmsUseLog> result = records.getResult();
return new PageResult(200, "查找成功", pageNo, pages, total, result);
}
}
package iot.sixiang.license.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import iot.sixiang.license.model.BaseResult;
import iot.sixiang.license.model.ResResult;
import iot.sixiang.license.model.dto.GetTerminalDeviceTokenDTO;
import iot.sixiang.license.model.dto.ReportErrorMsgDTO;
import iot.sixiang.license.model.dto.TerminalDevieBindDTO;
import iot.sixiang.license.service.TerminalDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/iot_license/terminal_device")
@Api(value = "终端设备模块", tags = {"终端设备模块"})
public class TerminalDeviceController {
@Autowired
TerminalDeviceService terminalDeviceService;
@GetMapping("/get_token")
@ApiOperation(value = "终端设备获取token", notes = "终端设备获取token")
public ResResult getToken(GetTerminalDeviceTokenDTO getTerminalDeviceTokenDTO) {
return terminalDeviceService.getToken(getTerminalDeviceTokenDTO);
}
@PostMapping("/report_error_msg")
@ApiOperation(value = "终端设备上报错误信息", notes = "终端设备上报错误信息")
public BaseResult reportErrorMsg(@RequestBody List<ReportErrorMsgDTO> reportErrorMsgDTO) {
return terminalDeviceService.reportErrorMsg(reportErrorMsgDTO);
}
@PostMapping("/bind")
@ApiOperation(value = "终端设备绑定接口", notes = "终端设备绑定接口")
public BaseResult terminalDevieBind(@RequestBody TerminalDevieBindDTO terminalDevieBindDTO) {
return terminalDeviceService.terminalDeviceBind(terminalDevieBindDTO);
}
}
......@@ -18,6 +18,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
public class DeviceManager {
......@@ -53,7 +54,7 @@ public class DeviceManager {
public void initDevices() {
allDevice = new HashMap<>();
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(1, 10000, "", "");
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(1, 10000, "", "", null, null);
List<DeviceVo> deviceList = records.getResult();
for (DeviceVo deviceVo : deviceList) {
......@@ -73,12 +74,12 @@ public class DeviceManager {
return allDevice;
}
public synchronized void putSession(String appId, SessionContext session) {
sessionContexts.put(appId, session);
public synchronized void putSession(String sn, SessionContext session) {
sessionContexts.put(sn, session);
}
public SessionContext getSessionContextByAppId(String appId) {
return sessionContexts.get(appId);
public SessionContext getSessionContextBySN(String sn) {
return sessionContexts.get(sn);
}
public SessionContext getSessionByChannelId(String channelId) {
......@@ -150,7 +151,7 @@ public class DeviceManager {
}
public PageInfoModel<DeviceDetailVo> getDeviceDetailList(int pageNo, int pageSize, String appName, String userName) {
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(pageNo, pageSize, appName, userName);
PageInfoModel<DeviceVo> records = deviceService.getDeviceList(pageNo, pageSize, appName, userName, null, null);
List<DeviceVo> deviceVos = records.getResult();
PageInfoModel<DeviceDetailVo> detailVoPageInfoModel = new PageInfoModel<>();
List<DeviceDetailVo> detailVos = new ArrayList<>();
......@@ -168,7 +169,7 @@ public class DeviceManager {
int status = session.getStatus();
String online = session.getOnline();
String offline = session.getOffline();
detailVo.setStatus(status);
detailVo.setCurStatus(status);
detailVo.setOnline(online);
detailVo.setOffline(offline);
}
......
......@@ -13,6 +13,7 @@ import iot.sixiang.license.event.DeviceClientInactiveEvent;
import iot.sixiang.license.event.EventPublisher;
import iot.sixiang.license.event.ForwardClientRequestEvent;
import iot.sixiang.license.model.SessionContext;
import iot.sixiang.license.service.PmsUseService;
import iot.sixiang.license.third_lib.LibHelper;
import iot.sixiang.license.util.CommonUtil;
import iot.sixiang.license.util.HexUtil;
......@@ -21,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
@Component
......@@ -29,6 +31,8 @@ import java.net.InetSocketAddress;
public class DeviceServerHandler extends SimpleChannelInboundHandler<Object> {
@Autowired
EventPublisher eventPublisher;
@Resource
private PmsUseService pmsUseService;
public DeviceServerHandler() {
super();
......@@ -128,11 +132,18 @@ public class DeviceServerHandler extends SimpleChannelInboundHandler<Object> {
log.info("设备鉴权信息和结果,{},{},{},{} ", appId, sn, sign, license);
String channelId = channel.id().asLongText();
if (license) {
// 创建一条使用记录
int useLogId = pmsUseService.createUseLog(sn);
if (useLogId <= 0) {
log.info("创建使用日志不成功...");
return false;
}
SessionContext session = new SessionContext();
session.setRemoteIp(remoteIp);
session.setRemotePort(remotePort);
session.setAppId(appId);
session.setSn(sn);
session.setUseLogId(useLogId);
session.setChannelId(channelId);
session.setClientChannel(channel);
session.setAuthStatus(true);
......@@ -140,12 +151,14 @@ public class DeviceServerHandler extends SimpleChannelInboundHandler<Object> {
session.setOnline(CommonUtil.getSystemTime());
DeviceManager deviceManager = SpringUtil.getBean(DeviceManager.class);
deviceManager.putSession(appId, session);
deviceManager.putSession(sn, session);
// 创建透传的客户端
CreateForwardClientEvent event = new CreateForwardClientEvent();
event.setAppId(appId);
event.setSn(sn);
eventPublisher.publishEvent(event);
} else {
pmsUseService.createFailUseLog(sn, "鉴权失败");
}
return license;
}
......
......@@ -34,9 +34,15 @@ public class Device implements Serializable {
@ApiModelProperty("设备编号")
private String sn;
@ApiModelProperty("绑定的SN")
private String snBind;
@ApiModelProperty("应用Id")
private String appId;
@ApiModelProperty("状态 0:未使用,1:已使用,2:失效")
private Integer status;
@ApiModelProperty("创建时间")
private Date createTime;
......
package iot.sixiang.license.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* Created by M=54G
* Date 11/23/22 3:12 PM
* Description
*/
@Data
public class PmsUseLog {
@ApiModelProperty("记录标识")
@TableId(type = IdType.AUTO)
private Integer id;
@ApiModelProperty("设备编号")
private String sn;
@ApiModelProperty("状态 1:成功,0:失败")
private Integer status;
@ApiModelProperty("如果失败,则这是失败的代号")
private String errorCode;
@ApiModelProperty("如果失败,则这里是失败的信息")
private String message;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("逻辑删除标识 1:删除,0:未删除")
private Integer deleted;
}
......@@ -5,5 +5,5 @@ import lombok.Data;
@Data
public class CreateForwardClientEvent extends BaseEvent {
private String appId;
private String sn;
}
......@@ -23,14 +23,14 @@ public class CreateForwardClientEventHandler {
@EventListener
public void handlerEvent(CreateForwardClientEvent event) {
String appId = event.getAppId();
String sn = event.getSn();
Server balanceServer = balanceManager.getBalanceServer();
if (balanceServer != null) {
String serverIp = balanceServer.getServerIp();
Integer port = balanceServer.getPort();
forwardManager.startTcpClient(serverIp, port, appId);
forwardManager.startTcpClient(serverIp, port, sn);
} else {
log.error("balanceServer is null");
log.error("balanceServer is null...");
}
}
}
......@@ -4,7 +4,6 @@ import lombok.Data;
@Data
public class DeviceClientBeForcedOfflineEvent extends BaseEvent {
private String appId;
private String sn;
}
......@@ -20,17 +20,17 @@ public class DeviceClientBeForcedOfflineEventHandler {
@EventListener
public void handlerEvent(DeviceClientBeForcedOfflineEvent event) {
String appId = event.getAppId();
String sn = event.getSn();
SessionContext deviceSessionContext = deviceManager.getSessionContextByAppId(appId);
SessionContext deviceSessionContext = deviceManager.getSessionContextBySN(sn);
if (deviceSessionContext != null) {
SocketChannel deviceClientChannel = deviceSessionContext.getClientChannel();
if (deviceClientChannel != null) {
deviceClientChannel.close();
log.debug("device client be forced offline success ..." + appId);
log.debug("device client be forced offline success ..." + sn);
}
} else {
log.debug("device client be forced offline undo ..." + appId);
log.debug("device client be forced offline undo ..." + sn);
}
}
......
......@@ -16,7 +16,6 @@ public class DeviceClientInactiveEventHandler {
EventPublisher eventPublisher;
public DeviceClientInactiveEventHandler() {
}
@EventListener
......@@ -25,24 +24,21 @@ public class DeviceClientInactiveEventHandler {
SessionContext session = deviceManager.getSessionByChannelId(channelId);
if (session == null) {
log.debug("device client inactive undo ...");
log.info("device client inactive undo ...");
return;
} else {
String appId = session.getAppId();
// boolean result = deviceManager.removeSessionByChannelId(channelId);
String sn = session.getSn();
// boolean result = deviceManager.removeSessionByChannelId(channelId);
boolean result = deviceManager.changeSessionOffline(channelId);
if (result) {
// TODO device client 离线需要强制中断该设备对应的forward client
// device client 离线需要强制中断该设备对应的forward client
ForwardClientBeForcedOfflineEvent forwardClientBeForcedOfflineEvent = new ForwardClientBeForcedOfflineEvent();
forwardClientBeForcedOfflineEvent.setAppId(appId);
forwardClientBeForcedOfflineEvent.setSn(sn);
eventPublisher.publishEvent(forwardClientBeForcedOfflineEvent);
log.debug("device client inactive success ...");
log.info("device client inactive success ...");
}
}
}
}
......
......@@ -5,6 +5,6 @@ import lombok.Data;
@Data
public class DeviceClientLicenseEvent extends BaseEvent {
private String appId;
private String sn;
private DeviceProtocol protocol;
}
......@@ -22,10 +22,10 @@ public class DeviceClientLicenseEventHandler {
@EventListener
public void handlerEvent(DeviceClientLicenseEvent event) {
String appId = event.getAppId();
String appId = event.getSn();
DeviceProtocol protocol = event.getProtocol();
SessionContext session = deviceManager.getSessionContextByAppId(appId);
SessionContext session = deviceManager.getSessionContextBySN(appId);
if (session == null) {
log.debug("device client license undo ...");
return;
......
......@@ -4,6 +4,6 @@ import lombok.Data;
@Data
public class ForwardClientBeForcedOfflineEvent extends BaseEvent {
private String appId;
private String sn;
}
......@@ -11,7 +11,6 @@ import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ForwardClientBeForcedOfflineEventHandler {
@Autowired
ForwardManager forwardManager;
......@@ -22,18 +21,16 @@ public class ForwardClientBeForcedOfflineEventHandler {
@EventListener
public void handlerEvent(ForwardClientBeForcedOfflineEvent event) {
String appId = event.getAppId();
SessionContext forwardSessionContext = forwardManager.getSessionContextByAppId(appId);
String sn = event.getSn();
SessionContext forwardSessionContext = forwardManager.getSessionContextBySN(sn);
if (forwardSessionContext != null) {
SocketChannel forwardClientChannel = forwardSessionContext.getClientChannel();
if (forwardClientChannel != null) {
forwardClientChannel.close();
log.debug("forward client be forced offline success ..." + appId);
log.debug("forward client be forced offline success ..." + sn);
}
} else {
log.debug("forward client be forced offline undo ..." + appId);
log.debug("forward client be forced offline undo ..." + sn);
}
}
}
......@@ -5,9 +5,8 @@ import lombok.Data;
@Data
public class ForwardClientConnectEvent extends BaseEvent {
private String appId;
private String sn;
private String channelId;
private SocketChannel channel;
}
......@@ -16,35 +16,28 @@ public class ForwardClientConnectEventHandler {
@Autowired
EventPublisher eventPublisher;
public ForwardClientConnectEventHandler() {
}
@EventListener
public void handlerEvent(ForwardClientConnectEvent event) {
String appId = event.getAppId();
String sn = event.getSn();
String channelId = event.getChannelId();
SocketChannel channel = event.getChannel();
SessionContext session = new SessionContext();
// session.setRemoteIp(remoteIp);
// session.setRemotePort(remotePort);
session.setAppId(appId);
// session.setAppKey(appKey);
// session.setToken(token);
// session.setRemoteIp(remoteIp);
// session.setRemotePort(remotePort);
session.setSn(sn);
// session.setAppKey(appKey);
// session.setToken(token);
session.setChannelId(channelId);
session.setClientChannel(channel);
forwardManager.putSession(appId, session);
forwardManager.putSession(sn, session);
log.debug("forward client connect:" + event);
}
}
......@@ -30,12 +30,12 @@ public class ForwardClientInactiveEventHandler {
log.debug("forward client inactive undo ...");
return;
} else {
String appId = session.getAppId();
String sn = session.getSn();
boolean result = forwardManager.removeSessionByChannelId(channelId);
if (result) {
// forward client 离线需要强制中断该设备对应的 device client
DeviceClientBeForcedOfflineEvent deviceClientBeForcedOfflineEvent = new DeviceClientBeForcedOfflineEvent();
deviceClientBeForcedOfflineEvent.setAppId(appId);
deviceClientBeForcedOfflineEvent.setSn(sn);
eventPublisher.publishEvent(deviceClientBeForcedOfflineEvent);
log.debug("forward client inactive success ...");
}
......
......@@ -30,10 +30,10 @@ public class ForwardClientRequestEventHandler {
DeviceProtocol protocol = event.getProtocol();
SessionContext deviceSessionContext = deviceManager.getSessionByChannelId(deviceChannelId);
String appId = deviceSessionContext.getAppId();
String sn = deviceSessionContext.getSn();
SessionContext forwardSessionContext = forwardManager.getSessionContextByAppId(appId);
log.debug("forward client request:" + appId + "," + forwardSessionContext);
SessionContext forwardSessionContext = forwardManager.getSessionContextBySN(sn);
log.info("forward client request:" + sn + "," + forwardSessionContext);
SocketChannel clientChannel = forwardSessionContext.getClientChannel();
clientChannel.writeAndFlush(protocol);
}
......
......@@ -5,11 +5,14 @@ import iot.sixiang.license.device.DeviceManager;
import iot.sixiang.license.device.DeviceProtocol;
import iot.sixiang.license.forward.ForwardManager;
import iot.sixiang.license.model.SessionContext;
import iot.sixiang.license.service.PmsUseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class ForwardMessageResponseEventHandler {
......@@ -19,32 +22,33 @@ public class ForwardMessageResponseEventHandler {
ForwardManager forwardManager;
@Autowired
EventPublisher eventPublisher;
@Resource
private PmsUseService pmsUseService;
public ForwardMessageResponseEventHandler() {
}
@EventListener
public void handlerEvent(ForwardMessageResponseEvent event) {
String channelId = event.getChannelId();
SocketChannel channel = event.getChannel();
//SocketChannel channel = event.getChannel();
DeviceProtocol protocol = event.getProtocol();
SessionContext forwardSessionContext = forwardManager.getSessionByChannelId(channelId);
String appId = forwardSessionContext.getAppId();
String sn = forwardSessionContext.getSn();
SessionContext deviceSessionContext = deviceManager.getSessionContextByAppId(appId);
SessionContext deviceSessionContext = deviceManager.getSessionContextBySN(sn);
if (deviceSessionContext != null) {
SocketChannel deviceClientChannel = deviceSessionContext.getClientChannel();
log.debug("forward client response..." + appId + ",forward session:" + deviceSessionContext);
log.info("forward client response..." + sn + ",forward session:" + deviceSessionContext);
deviceClientChannel.writeAndFlush(protocol);
} else {
log.debug("forward client response undo ..." + appId);
log.info("forward client response undo ..." + sn);
}
// 说明已经成功
if (protocol.getCmd() == 0xF2) {
pmsUseService.success(deviceSessionContext.getUseLogId());
}
}
}
......@@ -7,7 +7,6 @@ import iot.sixiang.license.consts.Consts;
import iot.sixiang.license.net.BaseChannelInitializer;
public class ForwardChannelInitializer extends BaseChannelInitializer {
private ForwardClientHandler handler;
static final EventExecutorGroup workGroup = new DefaultEventExecutorGroup(Consts.FORWARD_THREAD_NUM);
......@@ -18,10 +17,8 @@ public class ForwardChannelInitializer extends BaseChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new ForwardDecoder());
ch.pipeline().addLast("encoder", new ForwardEncoder());
ch.pipeline().addLast(workGroup, "handler", handler);
}
}
......@@ -5,12 +5,14 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import iot.sixiang.license.device.DeviceManager;
import iot.sixiang.license.net.TcpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Component
@Slf4j
......@@ -21,6 +23,8 @@ public class ForwardClient {
@Autowired
ForwardClientHandler handler;
@Resource
private DeviceManager deviceManager;
public ForwardClient() {
}
......@@ -41,12 +45,15 @@ public class ForwardClient {
}
}
public void startTcp(String host, int port, String appId) {
log.debug("桥接客户端,开始连接桥接服务:{},{},{}", host, port, appId);
public void startTcp(String host, int port, String sn) {
log.debug("桥接客户端,开始连接桥接服务:{},{},{}", host, port, sn);
ForwardConnectionListener listener = new ForwardConnectionListener();
listener.setAppId(appId);
listener.setSn(sn);
listener.setHost(host);
listener.setPort(port);
listener.setDeviceManager(this.deviceManager);
channelInitializer = new ForwardChannelInitializer(handler);
client = new TcpClient(host, port, channelInitializer, listener, bootstrap);
client.start();
}
......
......@@ -8,11 +8,14 @@ import iot.sixiang.license.device.DeviceProtocol;
import iot.sixiang.license.event.EventPublisher;
import iot.sixiang.license.event.ForwardClientInactiveEvent;
import iot.sixiang.license.event.ForwardMessageResponseEvent;
import iot.sixiang.license.service.PmsUseService;
import iot.sixiang.license.util.HexUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@ChannelHandler.Sharable
@Slf4j
......@@ -30,6 +33,7 @@ public class ForwardClientHandler extends SimpleChannelInboundHandler<Object> {
log.info("read message...");
SocketChannel channel = (SocketChannel) ctx.channel();
DeviceProtocol protocol = (DeviceProtocol) msg;
String channelId = channel.id().asLongText();
log.info("桥接客户端,channelRead0:{},{}", channelId, HexUtil.bytes2hex(protocol.getContent()));
......
......@@ -2,11 +2,13 @@ package iot.sixiang.license.forward;
import io.netty.channel.ChannelFuture;
import io.netty.channel.socket.SocketChannel;
import iot.sixiang.license.device.DeviceManager;
import iot.sixiang.license.device.DeviceProtocol;
import iot.sixiang.license.event.DeviceClientBeForcedOfflineEvent;
import iot.sixiang.license.event.DeviceClientLicenseEvent;
import iot.sixiang.license.event.EventPublisher;
import iot.sixiang.license.event.ForwardClientConnectEvent;
import iot.sixiang.license.model.SessionContext;
import iot.sixiang.license.net.BaseConnectionListener;
import iot.sixiang.license.operate.OperateManager;
import iot.sixiang.license.service.AlarmService;
......@@ -15,13 +17,18 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ForwardConnectionListener extends BaseConnectionListener {
private DeviceManager deviceManager;
public void setDeviceManager(DeviceManager deviceManager) {
this.deviceManager = deviceManager;
}
@Override
public void operationComplete(ChannelFuture channelFuture) {
AlarmService alarmService = SpringUtil.getBean(AlarmService.class);
if (!channelFuture.isSuccess()) {
// 失败进行告警
log.debug("桥接客户端,连接服务器失败:{},{},{}", this.host, this.port, this.appId);
log.debug("桥接客户端,连接服务器失败:{},{},{}", this.host, this.port, this.sn);
int typeId = 1;
String title = "连接服器失败";
......@@ -30,37 +37,46 @@ public class ForwardConnectionListener extends BaseConnectionListener {
// forward client连接失败,则强制踢掉设备客户端
DeviceClientBeForcedOfflineEvent deviceClientBeForcedOfflineEvent = new DeviceClientBeForcedOfflineEvent();
deviceClientBeForcedOfflineEvent.setAppId(this.appId);
deviceClientBeForcedOfflineEvent.setSn(this.sn);
EventPublisher eventPublisher = SpringUtil.getBean(EventPublisher.class);
eventPublisher.publishEvent(deviceClientBeForcedOfflineEvent);
} else {
log.debug("桥接客户端,连接服务器成功:{},{},{}", this.host, this.port, this.appId);
log.debug("桥接客户端,连接服务器成功:{},{},{}", this.host, this.port, this.sn);
OperateManager operateManager = SpringUtil.getBean(OperateManager.class);
operateManager.autoIncrement();
SocketChannel channel = (SocketChannel) channelFuture.channel();
String channelId = channel.id().asLongText();
ForwardClientConnectEvent forwardClientConnectEvent = new ForwardClientConnectEvent();
forwardClientConnectEvent.setAppId(this.appId);
forwardClientConnectEvent.setSn(this.sn);
forwardClientConnectEvent.setChannelId(channelId);
forwardClientConnectEvent.setChannel(channel);
EventPublisher eventPublisher = SpringUtil.getBean(EventPublisher.class);
eventPublisher.publishEvent(forwardClientConnectEvent);
// 将日志记录数据返回
SessionContext deviceSessionContext = deviceManager.getSessionContextBySN(this.sn);
int useLogId = deviceSessionContext.getUseLogId();
log.info("use log id:" + useLogId);
short stx = 21930;
byte ack = 0x0;
int len = 3;
int len = 6;
byte cmd = 0x1;
byte[] content = new byte[1];
byte[] content = new byte[4];
content[0] = 0x7e;
content[1] = (byte) useLogId;
content[2] = (byte) (useLogId >> 8);
content[3] = (byte) (useLogId >> 16);
byte end = 0x1;
DeviceProtocol protocol = new DeviceProtocol(stx, len, cmd, ack, content, end);
DeviceClientLicenseEvent deviceClientLicenseEvent = new DeviceClientLicenseEvent();
deviceClientLicenseEvent.setAppId(appId);
deviceClientLicenseEvent.setSn(sn);
deviceClientLicenseEvent.setProtocol(protocol);
eventPublisher.publishEvent(deviceClientLicenseEvent);
......
......@@ -43,7 +43,7 @@ public class ForwardDecoder extends ByteToMessageDecoder {
return;
}
// buffer.resetReaderIndex();//复位
// buffer.resetReaderIndex();//复位
// 读取data数据
byte[] content = new byte[real_len - cmd_ack_len];
......@@ -55,6 +55,4 @@ public class ForwardDecoder extends ByteToMessageDecoder {
DeviceProtocol protocol = new DeviceProtocol(stx, real_len, cmd, ack, content, end);
out.add(protocol);
}
}
\ No newline at end of file
......@@ -8,10 +8,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ForwardEncoder extends MessageToByteEncoder<DeviceProtocol> {
@Override
protected void encode(ChannelHandlerContext tcx, DeviceProtocol msg, ByteBuf out) {
out.writeShort(msg.getStx());
out.writeShortLE(msg.getLen());
out.writeByte(msg.getCmd());
......
......@@ -22,20 +22,19 @@ public class ForwardManager {
public ForwardManager() {
sessionContexts = new HashMap<String, SessionContext>();
sessionContexts = new HashMap<>();
}
public void startTcpClient(String serviceIP, int port, String appId) {
client.startTcp(serviceIP, port, appId);
public void startTcpClient(String serviceIP, int port, String sn) {
client.startTcp(serviceIP, port, sn);
}
public synchronized void putSession(String appId, SessionContext session) {
sessionContexts.put(appId, session);
public synchronized void putSession(String sn, SessionContext session) {
sessionContexts.put(sn, session);
}
public SessionContext getSessionContextByAppId(String appId) {
return sessionContexts.get(appId);
public SessionContext getSessionContextBySN(String sn) {
return sessionContexts.get(sn);
}
public SessionContext getSessionByChannelId(String channelId) {
......
......@@ -4,10 +4,15 @@ import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import iot.sixiang.license.xss.XssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -27,6 +32,9 @@ public class JwtFilter implements Filter {
private static final String url8 = "/webjars/";
private static final String url9 = "/check_code";
private static final String url10 = "/reset_pwd";
private static final String url11 = "/get_token";
private static final String url12 = "/report_error_msg";
private static final String url13 = "/bind";
@Override
public void init(FilterConfig filterConfig) {
......@@ -51,8 +59,8 @@ public class JwtFilter implements Filter {
boolean check = true;
String uri = request.getRequestURI();
if (uri.contains(url1) || uri.contains(url2) || uri.contains(url3) || uri.contains(url4) || uri.contains(url7) || uri.contains(url8) || uri.contains(url9) || uri.contains(url10)) {
if (uri.contains(url1)) {
if (uri.contains(url1) || uri.contains(url2) || uri.contains(url3) || uri.contains(url4) || uri.contains(url7) || uri.contains(url8) || uri.contains(url9) || uri.contains(url11)) {
if (uri.contains(url1) || uri.contains(url2)) {
uri = XssUtil.checkXSS(uri);
UserUtils.setUri(uri);
}
......@@ -64,20 +72,27 @@ public class JwtFilter implements Filter {
}
if (StringUtils.isEmpty(token)) {
request.setAttribute("msg", "token不能为空");
request.setAttribute("msg", "认证信息不能为空");
request.getRequestDispatcher("/iot_license/fail").forward(request, response);
return;
} else {
DecodedJWT jwt = JwtUtil.verifyToken(token);
if (jwt == null) {
request.setAttribute("msg", "非法token");
request.setAttribute("msg", "认证信息非法");
request.getRequestDispatcher("/iot_license/fail").forward(request, response);
return;
} else {
Map<String, Claim> userData = jwt.getClaims();
if (userData == null) {
request.setAttribute("msg", "非法token");
request.setAttribute("msg", "认证信息非法");
request.getRequestDispatcher("/iot_license/fail").forward(request, response);
return;
}
// 终端设备放行
if (uri.contains(url12) || uri.contains(url13)) {
filterChain.doFilter(request, response);
return;
}
String userId = userData.get("userId").asString();
String userName = userData.get("userName").asString();
String password = "";
......
package iot.sixiang.license.mapper;
import iot.sixiang.license.entity.Device;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import iot.sixiang.license.entity.Device;
import iot.sixiang.license.model.vo.DeviceVo;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author m33
* @since 2022-06-08
*/
public interface DeviceMapper extends BaseMapper<Device> {
List<DeviceVo> getDeviceList(String appName, String userName);
List<DeviceVo> getDeviceList(String appName, String userName, String sn, Integer status);
boolean addDevice(String sn, String appId);
}
package iot.sixiang.license.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import iot.sixiang.license.entity.PmsUseLog;
import iot.sixiang.license.model.vo.DeviceVo;
import java.util.List;
/**
* Created by M=54G
* Date 11/23/22 3:12 PM
* Description
*/
public interface PmsUseLogMapper extends BaseMapper<PmsUseLog> {
List<PmsUseLog> getPmsUseLogList(String sn, Integer status);
}
......@@ -75,7 +75,6 @@ public class ResResult<T> {
*/
public static ResResult failed() {
return new ResResult(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg());
}
public ResResult setCodeValue(long code) {
......
......@@ -11,6 +11,7 @@ public class SessionContext {
private String appId;
private String appKey;
private String sn;
private int useLogId;// 使用日志标识
private boolean authStatus;//授权验证状态
private int status;//当前状态,0 offline,1 online
private String online;//上线时间
......
package iot.sixiang.license.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class GetTerminalDeviceTokenDTO {
@ApiModelProperty("应用id")
private String appId;
@ApiModelProperty("设备编号")
private String sn;
@ApiModelProperty("签名")
private String sign;
}
package iot.sixiang.license.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class ReportErrorMsgDTO {
@ApiModelProperty("错误标识id")
private Integer id;
@ApiModelProperty("错误码")
private String errorCode;
@ApiModelProperty("错误信息")
private String errorMsg;
}
package iot.sixiang.license.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class TerminalDevieBindDTO {
@ApiModelProperty("设备编码")
private String sn;
@ApiModelProperty("绑定的SN")
private String snBind;
}
......@@ -8,7 +8,7 @@ import lombok.Data;
public class DeviceDetailVo extends DeviceVo {
@ApiModelProperty("当前状态,0 offline,1 online")
private int status;
private int curStatus;
@ApiModelProperty("上线时间")
private String online;
......
......@@ -11,7 +11,6 @@ import java.util.Date;
*/
@Data
public class DeviceVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("设备Id")
......@@ -26,6 +25,12 @@ public class DeviceVo implements Serializable {
@ApiModelProperty("设备编号")
private String sn;
@ApiModelProperty("绑定的设备编号")
private String snBind;
@ApiModelProperty("状态 0:未使用,1:已使用,2:失效")
private Integer status;
@ApiModelProperty("设备状态 0:正常 1:禁用")
private int blackFlag;
......@@ -34,5 +39,4 @@ public class DeviceVo implements Serializable {
@ApiModelProperty("更新时间")
private Date updateTime;
}
......@@ -28,6 +28,9 @@ public class ResourceVo {
@ApiModelProperty("设备编号")
private String sn;
@ApiModelProperty("设备状态")
private String status;
@ApiModelProperty("应用id")
private String appId;
}
\ No newline at end of file
......@@ -10,6 +10,16 @@ import lombok.Data;
@Data
public class UserVo extends User {
@ApiModelProperty("设备数量")
public int deviceCount;
@ApiModelProperty("总设备")
public int totalDevice;
@ApiModelProperty("已使用设备")
public int inUseDevice;
@ApiModelProperty("未使用设备")
public int unUseDevice;
@ApiModelProperty("失效设备")
public int failedDevice;
}
......@@ -8,7 +8,7 @@ import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
public class BaseConnectionListener implements ChannelFutureListener {
public String appId;
public String sn;
public String host;
public int port;
......
......@@ -79,6 +79,10 @@ public class ResourceManager {
cell = row.createCell((short)4); //第五个单元格
cell.setCellValue("sn");
cell.setCellStyle(styleRow);
cell = row.createCell((short)5); //第六个单元格
cell.setCellValue("状态");
cell.setCellStyle(styleRow);
//第五步插入数据
List<ResourceVo> resourceList = resourceService.getResource(userId);
for (int i = 0; i < resourceList.size(); i++) {
......@@ -106,9 +110,13 @@ public class ResourceManager {
cell = row.createCell((short)4); // 第五个单元格
cell.setCellValue(resourceVo.getSn());
cell.setCellStyle(style);
cell = row.createCell((short)5); // 第六个单元格
cell.setCellValue(resourceVo.getStatus());
cell.setCellStyle(style);
}
//在填完所有值以后,对每一列设置自适应宽度
for (int n = 0; n < 5; n++) {
for (int n = 0; n < 6; n++) {
sheet.autoSizeColumn(n);
}
wb.write(os);
......
......@@ -3,19 +3,19 @@ package iot.sixiang.license.service;
import com.baomidou.mybatisplus.extension.service.IService;
import iot.sixiang.license.entity.Device;
import iot.sixiang.license.model.PageInfoModel;
import iot.sixiang.license.model.dto.TerminalDevieBindDTO;
import iot.sixiang.license.model.vo.DeviceVo;
/**
* <p>
* 服务类
* </p>
*
* @author m33
* @since 2022-06-08
*/
public interface DeviceService extends IService<Device> {
PageInfoModel<DeviceVo> getDeviceList(int pageNo, int pageSize, String appName, String userName);
PageInfoModel<DeviceVo> getDeviceList(int pageNo, int pageSize, String appName, String userName, String sn, Integer status);
boolean addDevice(String appId, int count);
Boolean terminalDevieBind(TerminalDevieBindDTO terminalDevieBindDTO);
}
package iot.sixiang.license.service;
import iot.sixiang.license.entity.PmsUseLog;
import iot.sixiang.license.model.PageInfoModel;
import iot.sixiang.license.model.dto.ReportErrorMsgDTO;
/**
* Created by M=54G
* Date 11/23/22 3:09 PM
* Description
*/
public interface PmsUseService {
int createUseLog(String sn);
void createFailUseLog(String sn, String message);
void success(int useLogId);
PageInfoModel<PmsUseLog> getPmsUseLogList(int pageNo, int pageSize, String sn, Integer status);
boolean reportErrorMsg(ReportErrorMsgDTO reportErrorMsgDTO);
boolean deletePmsUseLogById(Integer id);
}
package iot.sixiang.license.service;
import iot.sixiang.license.model.BaseResult;
import iot.sixiang.license.model.ResResult;
import iot.sixiang.license.model.dto.GetTerminalDeviceTokenDTO;
import iot.sixiang.license.model.dto.ReportErrorMsgDTO;
import iot.sixiang.license.model.dto.TerminalDevieBindDTO;
import java.util.List;
public interface TerminalDeviceService {
ResResult getToken(GetTerminalDeviceTokenDTO getTerminalDeviceTokenDTO);
BaseResult reportErrorMsg(List<ReportErrorMsgDTO> reportErrorMsgDTO);
BaseResult terminalDeviceBind(TerminalDevieBindDTO terminalDevieBindDTO);
}
package iot.sixiang.license.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import iot.sixiang.license.consts.ResultCode;
import iot.sixiang.license.device.DeviceManager;
......@@ -7,6 +8,7 @@ import iot.sixiang.license.entity.Device;
import iot.sixiang.license.handler.IotLicenseException;
import iot.sixiang.license.mapper.DeviceMapper;
import iot.sixiang.license.model.PageInfoModel;
import iot.sixiang.license.model.dto.TerminalDevieBindDTO;
import iot.sixiang.license.model.vo.DeviceVo;
import iot.sixiang.license.service.DeviceService;
import iot.sixiang.license.util.CommonUtil;
......@@ -17,29 +19,27 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author m33
* @since 2022-06-08
*/
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
@Resource
private DeviceMapper deviceMapper;
@Override
public PageInfoModel<DeviceVo> getDeviceList(int pageNo, int pageSize, String appName, String userName) {
if(pageNo == 0 || pageSize == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(),ResultCode.VALIDATE_FAILED.getMsg());
public PageInfoModel<DeviceVo> getDeviceList(int pageNo, int pageSize, String appName, String userName, String sn, Integer status) {
if (pageNo == 0 || pageSize == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(), ResultCode.VALIDATE_FAILED.getMsg());
}
List<DeviceVo> deviceTypes = deviceMapper.getDeviceList(appName,userName);
List<DeviceVo> deviceTypes = deviceMapper.getDeviceList(appName, userName, sn, status);
deviceTypes = deviceTypes.stream().sorted(Comparator.comparing(DeviceVo::getCreateTime, Comparator.reverseOrder())).collect(Collectors.toList());
List<DeviceVo> result = new ArrayList<>();
int begin = (pageNo - 1) * pageSize;
......@@ -54,8 +54,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
@Override
public boolean addDevice(String appId, int count) {
if(StringUtils.isEmpty(appId) || count == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(),ResultCode.VALIDATE_FAILED.getMsg());
if (StringUtils.isEmpty(appId) || count == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(), ResultCode.VALIDATE_FAILED.getMsg());
}
for (int i = 0; i < count; i++) {
String sn = CommonUtil.genRandomNum(18);
......@@ -68,4 +68,32 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
deviceManager.initDevices();
return true;
}
@Override
public Boolean terminalDevieBind(TerminalDevieBindDTO terminalDevieBindDTO) {
String sn = terminalDevieBindDTO.getSn();
String snBind = terminalDevieBindDTO.getSnBind();
LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Device::getSn, sn).last("limit 1");
Device device = deviceMapper.selectOne(queryWrapper);
if (device != null) {
if (!StringUtils.isEmpty(device.getSnBind())) {
if (device.getSnBind().equals(snBind)) {
return true;
} else {
throw new IotLicenseException(403, "sn已被绑定");
}
}
device.setSnBind(snBind);
device.setStatus(1);
device.setUpdateTime(new Date());
int res = deviceMapper.updateById(device);
if (res >= 0) {
return true;
} else {
return false;
}
}
return false;
}
}
package iot.sixiang.license.service.impl;
import iot.sixiang.license.consts.ResultCode;
import iot.sixiang.license.entity.PmsUseLog;
import iot.sixiang.license.handler.IotLicenseException;
import iot.sixiang.license.mapper.PmsUseLogMapper;
import iot.sixiang.license.model.PageInfoModel;
import iot.sixiang.license.model.dto.ReportErrorMsgDTO;
import iot.sixiang.license.service.PmsUseService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created by M=54G
* Date 11/23/22 3:10 PM
* Description
*/
@Service
public class PmsUseServiceImpl implements PmsUseService {
@Resource
private PmsUseLogMapper pmsUseLogMapper;
@Override
public int createUseLog(String sn) {
PmsUseLog pmsUseLog = getPmsUseLog(sn);
pmsUseLogMapper.insert(pmsUseLog);
return pmsUseLog.getId();
}
@Override
public void createFailUseLog(String sn, String message) {
PmsUseLog pmsUseLog = getPmsUseLog(sn);
pmsUseLog.setStatus(0);
pmsUseLog.setMessage(message);
pmsUseLogMapper.insert(pmsUseLog);
}
@Override
public void success(int useLogId) {
PmsUseLog pmsUseLog = new PmsUseLog();
pmsUseLog.setId(useLogId);
pmsUseLog.setStatus(1);
pmsUseLogMapper.updateById(pmsUseLog);
}
@Override
public PageInfoModel<PmsUseLog> getPmsUseLogList(int pageNo, int pageSize, String sn, Integer status) {
if (pageNo == 0 || pageSize == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(), ResultCode.VALIDATE_FAILED.getMsg());
}
List<PmsUseLog> pmsUseLogs = pmsUseLogMapper.getPmsUseLogList(sn, status);
List<PmsUseLog> result = new ArrayList<>();
int begin = (pageNo - 1) * pageSize;
if (begin >= 0 && pmsUseLogs.size() > 0) {
result = pmsUseLogs.stream().skip(begin).limit(pageSize).collect(Collectors.toList());
}
PageInfoModel<PmsUseLog> pmsUseLogPageInfoModel = new PageInfoModel<>();
pmsUseLogPageInfoModel.setTotal(pmsUseLogs.size());
pmsUseLogPageInfoModel.setResult(result);
return pmsUseLogPageInfoModel;
}
@Override
public boolean reportErrorMsg(ReportErrorMsgDTO reportErrorMsgDTO) {
Integer id = reportErrorMsgDTO.getId();
String errorCode = reportErrorMsgDTO.getErrorCode();
String errorMsg = reportErrorMsgDTO.getErrorMsg();
PmsUseLog pmsUseLog = new PmsUseLog();
pmsUseLog.setId(id);
pmsUseLog.setStatus(0);
pmsUseLog.setErrorCode(errorCode);
pmsUseLog.setMessage(errorMsg);
pmsUseLog.setUpdateTime(new Date());
int res = pmsUseLogMapper.updateById(pmsUseLog);
return res > 0;
}
@Override
public boolean deletePmsUseLogById(Integer id) {
PmsUseLog pmsUseLog = new PmsUseLog();
pmsUseLog.setId(id);
pmsUseLog.setStatus(0);
pmsUseLog.setUpdateTime(new Date());
pmsUseLog.setDeleted(1);
int res = pmsUseLogMapper.updateById(pmsUseLog);
return res > 0;
}
private PmsUseLog getPmsUseLog(String sn) {
PmsUseLog pmsUseLog = new PmsUseLog();
Date date = new Date();
pmsUseLog.setCreateTime(date);
pmsUseLog.setUpdateTime(date);
pmsUseLog.setSn(sn);
return pmsUseLog;
}
}
package iot.sixiang.license.service.impl;
import iot.sixiang.license.auth.AuthManager;
import iot.sixiang.license.device.DeviceManager;
import iot.sixiang.license.handler.IotLicenseException;
import iot.sixiang.license.jwt.JwtUtil;
import iot.sixiang.license.jwt.LoginUser;
import iot.sixiang.license.model.BaseResult;
import iot.sixiang.license.model.ResResult;
import iot.sixiang.license.model.dto.GetTerminalDeviceTokenDTO;
import iot.sixiang.license.model.dto.ReportErrorMsgDTO;
import iot.sixiang.license.model.dto.TerminalDevieBindDTO;
import iot.sixiang.license.service.DeviceService;
import iot.sixiang.license.service.PmsUseService;
import iot.sixiang.license.service.TerminalDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
@Slf4j
@Service
public class TerminalDeviceServiceImpl implements TerminalDeviceService {
@Autowired
private AuthManager authManager;
@Autowired
private DeviceService deviceService;
@Autowired
private PmsUseService pmsUseService;
@Resource
private DeviceManager deviceManager;
@Override
public ResResult getToken(GetTerminalDeviceTokenDTO getTerminalDeviceTokenDTO) {
String appId = getTerminalDeviceTokenDTO.getAppId();
String sn = getTerminalDeviceTokenDTO.getSn();
String sign = getTerminalDeviceTokenDTO.getSign();
if (StringUtils.isEmpty(appId)) {
throw new IotLicenseException(403, "应用id不能为空");
}
if (StringUtils.isEmpty(sn)) {
throw new IotLicenseException(403, "终端编号不能为空");
}
if (StringUtils.isEmpty(sign)) {
throw new IotLicenseException(403, "签名不能为空");
}
boolean authResult = authManager.authTerminalDevice(appId, sn, sign);
if (authResult) {
LoginUser user = new LoginUser();
user.setUserId(appId);
user.setUserName(sn);
String token = JwtUtil.createToken(user);
return ResResult.success().goRecord(token);
} else {
return ResResult.validate_failed();
}
}
@Override
public BaseResult reportErrorMsg(List<ReportErrorMsgDTO> reportErrorMsgDTOs) {
if (reportErrorMsgDTOs == null || reportErrorMsgDTOs.size() == 0) {
return BaseResult.validate_failed();
}
reportErrorMsgDTOs.sort(Comparator.comparingInt(ReportErrorMsgDTO::getId));
for (int i = 0; i < reportErrorMsgDTOs.size() - 1; i++) {
ReportErrorMsgDTO reportErrorMsgDTO = reportErrorMsgDTOs.get(i);
Integer id = reportErrorMsgDTO.getId();
if (id == null || id == 0) {
return BaseResult.validate_failed();
}
pmsUseService.deletePmsUseLogById(id);
}
ReportErrorMsgDTO reportErrorMsgDTO = reportErrorMsgDTOs.get(reportErrorMsgDTOs.size() - 1);
if (reportErrorMsgDTO.getId() == null || reportErrorMsgDTO.getId() == 0 || reportErrorMsgDTO.getErrorCode() == null) {
return BaseResult.validate_failed();
} else {
if ("0".equals(reportErrorMsgDTO.getErrorCode())) {
pmsUseService.success(reportErrorMsgDTO.getId());
return BaseResult.success();
} else {
boolean res = pmsUseService.reportErrorMsg(reportErrorMsgDTO);
if (res) {
return BaseResult.success();
} else {
return BaseResult.failed();
}
}
}
}
@Override
public BaseResult terminalDeviceBind(TerminalDevieBindDTO terminalDevieBindDTO) {
String sn = terminalDevieBindDTO.getSn();
String snBind = terminalDevieBindDTO.getSnBind();
if (StringUtils.isEmpty(sn) || StringUtils.isEmpty(snBind)) {
return BaseResult.validate_failed();
}
Boolean res = deviceService.terminalDevieBind(terminalDevieBindDTO);
if (res) {
deviceManager.initDevices();
return BaseResult.success();
} else {
return BaseResult.failed();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" scanPeriod="10 seconds">
<!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 -->
<!-- <logger name="iot.sixiang.license" level="debug" />-->
<!--控制台输出的格式设置 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 控制台输出的日志 的格式 -->
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %5level %logger{96}:%line - %msg%n
</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 只是DEBUG级别以上的日志才显示 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<!--文件输出的格式设置 -->
<appender name="ALL_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志日常打印文件 -->
<file>logs/license.log</file>
<!-- 配置日志所生成的目录以及生成文件名的规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/license.log-%d{yyyy-MM-dd}.%i</fileNamePattern>
<!-- 如果按天来回滚,则最大保存时间为365天,365天之前的都将被清理掉 -->
<maxHistory>365</maxHistory>
<!-- 日志总保存量为10GB -->
<totalSizeCap>100GB</totalSizeCap>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!--文件达到 最大128MB时会被压缩和切割 -->
<maxFileSize>40 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<!-- 文件输出的日志 的格式 -->
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %5level %logger{96}:%line - %msg%n
</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
<prudent>false</prudent>
</appender>
<!--文件输出的格式设置 -->
<appender name="MSG_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志日常打印文件 -->
<file>logs/message.log</file>
<!-- 配置日志所生成的目录以及生成文件名的规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/message.log-%d{yyyy-MM-dd}.%i</fileNamePattern>
<!-- 如果按天来回滚,则最大保存时间为365天,365天之前的都将被清理掉 -->
<maxHistory>365</maxHistory>
<!-- 日志总保存量为10GB -->
<totalSizeCap>100GB</totalSizeCap>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!--文件达到 最大128MB时会被压缩和切割 -->
<maxFileSize>40 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 文件输出的日志 的格式 -->
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %5level %logger{96}:%line - %msg%n
</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
<prudent>false</prudent>
</appender>
<!-- Enable FILE and STDOUT appenders for all log messages. By default,
only log at level INFO and above. -->
<!--这里选择INFO就代表,进行INFO级别输出记录,那么在控制台也好,log文件也好只记录INFO及以上级别的日志,这里相当于第一道设置-->
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="ALL_FILE" />
<appender-ref ref="MSG_FILE" />
</root>
</configuration>
\ No newline at end of file
......@@ -3,7 +3,8 @@
<mapper namespace="iot.sixiang.license.mapper.DeviceMapper">
<select id="getDeviceList" resultType="iot.sixiang.license.model.vo.DeviceVo">
SELECT de.device_id,app_name,user_name,sn,de.create_time,de.update_time,de.device_id IN (select device_id from device_black) AS blackFlag FROM device AS de
SELECT de.device_id,app_name,user_name,sn,sn_bind,de.status,de.create_time,de.update_time,de.device_id IN (select
device_id from device_black) AS blackFlag FROM device AS de
JOIN apply AS app ON de.app_id = app.app_id
JOIN USER AS us ON us.user_id = app.user_id
where 1=1
......@@ -13,10 +14,17 @@
<if test="null != userName and '' != userName">
and user_name like concat('%',#{userName},'%')
</if>
<if test="null != sn and '' != sn">
and sn like concat('%',#{sn},'%')
</if>
<if test="null != status">
and status = #{status}
</if>
</select>
<insert id="addDevice" parameterType="iot.sixiang.license.entity.Device">
insert into device(sn, app_id, create_time, update_time) values (#{sn},#{appId}, now(), now())
insert into device(sn, app_id, create_time, update_time)
values (#{sn}, #{appId}, now(), now())
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="iot.sixiang.license.mapper.PmsUseLogMapper">
<select id="getPmsUseLogList" resultType="iot.sixiang.license.entity.PmsUseLog">
SELECT * FROM pms_use_log
where 1=1
<if test="null != sn and '' != sn">
and sn like concat('%',#{sn},'%')
</if>
<if test="null != status">
and status = #{status}
</if>
and deleted = 0
order by create_time desc
</select>
</mapper>
\ No newline at end of file
......@@ -3,7 +3,8 @@
<mapper namespace="iot.sixiang.license.mapper.ResourceMapper">
<select id="getResource" resultType="iot.sixiang.license.model.vo.ResourceVo">
SELECT a.user_name,a.password,a.company,b.app_name,b.app_key,c.sn, b.app_id
SELECT a.user_name,a.password,a.company,b.app_name,b.app_key,c.sn, b.app_id,
(CASE c.`status` WHEN 1 THEN '已使用' WHEN 2 THEN '失效' ELSE '未使用' END) `status`
FROM USER a, apply b, device c WHERE a.user_id = b.user_id
AND b.app_id = c.app_id AND a.user_id = #{userId}
</select>
......
......@@ -15,7 +15,14 @@
</update>
<select id="getUserList" resultType="iot.sixiang.license.model.vo.UserVo">
SELECT user.user_id, user_name, PASSWORD, company, user.create_time, user.update_time ,COUNT(device.`device_id`) deviceCount FROM USER LEFT JOIN apply ON user.user_id = apply.user_id LEFT JOIN device ON apply.app_id = device.app_id
SELECT user.user_id, user_name, password, company, user.create_time, user.update_time,
COUNT(device.`device_id`) totalDevice,
COUNT(if(device.device_id is NOT NULL, if (device.`status` = 0 or device.`status` is NULL, 1, NULL), NULL)) unUseDevice,
COUNT(if(device.device_id is NOT NULL and device.`status` = 1, 1, NULL)) inUseDevice,
COUNT(if(device.device_id is NOT NULL and device.`status` = 2, 1, NULL)) failedDevice
FROM USER
LEFT JOIN apply ON user.user_id = apply.user_id
LEFT JOIN device ON apply.app_id = device.app_id
where 1=1
<if test="null != userName and '' != userName">
and user_name like concat('%',#{userName},'%')
......
package iot.sixiang.license;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
/**
* Created by M=54G
* Date 9/14/22 3:08 PM
* Description
*/
@SpringBootTest
@ActiveProfiles({"test-acc"})
@Slf4j
public class BaseTest {
protected void log(Object object) {
log.info(JSON.toJSONString(object));
}
}
\ No newline at end of file
......@@ -22,14 +22,47 @@ import java.util.Random;
*/
public class DeviceControllerTest {
@Test
void terminalDeviceSign() {
String appId = "7j26zr7en9fiyoyvjz";
String sn = "8HOE8JH4EFSEV28534";
// String snBind = "snabcd";
String appKey = "2";
String string = "app_id=" + appId + "&sn=" + sn;
String sign = HmacUtil.encrypt(string, appKey, HmacUtil.HMAC_SHA1);
System.out.println("token appId:" + appId);
System.out.println("token sn:" + sn);
System.out.println("token sign:" + sign);
}
@Test
void tokenSign() {
String appId = "ebsh71dp5t1ck948l5";
String sn = "ERE54S619LNYMPKVN9";
String snBind = "snabcd";
String appKey = "110801";
String string = "app_id=" + appId + "&sn=" + sn + "&sn_bind=" + snBind;
String sign = HmacUtil.encrypt(string, appKey, HmacUtil.HMAC_SHA1);
System.out.println("token appId:" + appId);
System.out.println("token sn:" + sn);
System.out.println("token sign:" + sign);
}
@Test
void auth() {
String appId = "ebsh71dp5t1ck948l5";
String sn = "ERE54S619LNYMPKVN9";
String snBind = "snabcd";
String appKey = "110801";
//byte[] baseKeyBytes = "nAOq38p4bGQyF4FG".getBytes();
//System.out.println(baseKeyBytes.length);
byte[] allBytes = getAuthSendBytes(appId, sn, appKey);
byte[] allBytes = getAuthSendBytes(appId, sn, snBind, appKey);
System.out.println(bytes2Hex(allBytes));
}
......@@ -39,8 +72,9 @@ public class DeviceControllerTest {
int serverPort = 18889;
String appId = "ebsh71dp5t1ck948l5";
String sn = "ERE54S619LNYMPKVN9";
String snBind = "ZA2207G5NQ";
String appKey = "110801";
socket(serverIp, serverPort, appId, sn, appKey);
socket(serverIp, serverPort, appId, sn, snBind, appKey);
}
@Test
......@@ -49,18 +83,19 @@ public class DeviceControllerTest {
int serverPort = 18889;
String appId = "mny3i9pgg0xxs520qf";
String sn = "IU23404BR1CQJOC63Q";
String snBind = "snabcd";
String appKey = "20221114";
socket(serverIp, serverPort, appId, sn, appKey);
socket(serverIp, serverPort, appId, sn, snBind, appKey);
}
@SneakyThrows
void socket(String serverIp, int serverPort, String appId, String sn, String appKey) {
void socket(String serverIp, int serverPort, String appId, String sn, String snBind, String appKey) {
Socket socket = new Socket(serverIp, serverPort);
socket.setKeepAlive(true);
System.out.println("connect success...");
// 事先组装好要发送的鉴权信息
byte[] authSendBytes = getAuthSendBytes(appId, sn, appKey);
byte[] authSendBytes = getAuthSendBytes(appId, sn, snBind, appKey);
OutputStream outputStream = socket.getOutputStream();
......@@ -103,7 +138,7 @@ public class DeviceControllerTest {
return requestBytes;
}
private byte[] getAuthSendBytes(String appId, String sn, String appKey) {
private byte[] getAuthSendBytes(String appId, String sn, String snBind, String appKey) {
byte[] stxBytes = {(byte) 0x55, (byte) 0xaa};
// 这部分生成内容
......@@ -118,7 +153,7 @@ public class DeviceControllerTest {
for (int i = 0; i < randomBytes.length; i++) {
sm4KeyBytes[i] = (byte) (randomBytes[i] ^ baseKeyBytes[i]);
}
String string = "app_id=" + appId + "&sn=" + sn;
String string = "app_id=" + appId + "&sn=" + sn + "&sn_bind=" + snBind;
String sign = HmacUtil.encrypt(string, appKey, HmacUtil.HMAC_SHA1);
// 组装
AuthData authData = new AuthData();
......
package iot.sixiang.license.service;
import iot.sixiang.license.BaseTest;
import org.junit.jupiter.api.Test;
import javax.annotation.Resource;
/**
* Created by M=54G
* Date 11/23/22 3:39 PM
* Description
*/
public class PmsUseServiceTest extends BaseTest {
@Resource
private PmsUseService pmsUseService;
@Test
void addLog() {
log(pmsUseService.createUseLog("abcd"));
}
@Test
void updateLog() {
pmsUseService.success(1);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment