This commit is contained in:
磷叶 2025-04-19 13:46:28 +08:00
parent 24f85ddb43
commit 78e294b7b9
40 changed files with 1045 additions and 38 deletions

View File

@ -54,11 +54,8 @@ public class AjaxResult extends HashMap<String, Object>
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data))
{
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息

View File

@ -1,13 +1,13 @@
package com.ruoyi.common.mybatis.typehandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
/**
* 非空Integer类型处理器为空时返回0
* @author wjh

View File

@ -1,14 +1,15 @@
package com.ruoyi.bst.device.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 设备对象 bst_device
*
@ -34,13 +35,17 @@ public class Device extends BaseEntity
@ApiModelProperty("在线状态")
private String onlineStatus;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "最后在线时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("最后在线时间")
private Date lastOnlineTime;
private LocalDateTime lastOnlineTime;
@Excel(name = "最新数据")
@ApiModelProperty("最新数据")
private String dataPoints;
@Excel(name = "备注")
@ApiModelProperty("备注")
private String remark;
}

View File

@ -8,4 +8,6 @@ public class DeviceQuery extends DeviceVO {
// 精准设备MAC
private String eqMac;
// MAC前缀
private String macPrefix;
}

View File

@ -5,4 +5,9 @@ import lombok.Data;
@Data
public class DeviceVO extends Device {
// 在线网关数
private Integer onlineGatewayCount;
// 离线网关数
private Integer offlineGatewayCount;
}

View File

@ -1,11 +1,13 @@
package com.ruoyi.bst.device.mapper;
import java.util.List;
import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.DeviceQuery;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
/**
* 设备Mapper接口
*
@ -71,4 +73,12 @@ public interface DeviceMapper
* @return 结果
*/
public int deleteDeviceByIds(Long[] ids);
/**
* 查询设备MAC列表
*
* @param query 设备查询条件
* @return 设备MAC列表
*/
List<String> selectMacList(@Param("query") DeviceQuery query);
}

View File

@ -14,7 +14,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bd.create_time,
bd.online_status,
bd.last_online_time,
bd.data_points
bd.data_points,
bd.remark
from bst_device bd
</sql>
@ -24,6 +25,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.type != null and query.type != ''"> and bd.type = #{query.type}</if>
<if test="query.onlineStatus != null and query.onlineStatus != ''"> and bd.online_status = #{query.onlineStatus}</if>
<if test="query.eqMac != null and query.eqMac != ''"> and bd.mac = #{query.eqMac}</if>
<if test="query.remark != null and query.remark != ''"> and bd.remark like concat('%', #{query.remark}, '%')</if>
<if test="query.macPrefix != null and query.macPrefix != ''"> and bd.mac like concat(#{query.macPrefix}, '%')</if>
${query.params.dataScope}
</sql>
@ -48,6 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="onlineStatus != null and onlineStatus != ''">online_status,</if>
<if test="lastOnlineTime != null">last_online_time,</if>
<if test="dataPoints != null">data_points,</if>
<if test="remark != null and remark != ''">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="mac != null and mac != ''">#{mac},</if>
@ -56,6 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="onlineStatus != null and onlineStatus != ''">#{onlineStatus},</if>
<if test="lastOnlineTime != null">#{lastOnlineTime},</if>
<if test="dataPoints != null">#{dataPoints},</if>
<if test="remark != null and remark != ''">#{remark},</if>
</trim>
</insert>
@ -74,6 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.onlineStatus != null and data.onlineStatus != ''">online_status = #{data.onlineStatus},</if>
<if test="data.lastOnlineTime != null">last_online_time = #{data.lastOnlineTime},</if>
<if test="data.dataPoints != null">data_points = #{data.dataPoints},</if>
<if test="data.remark != null and data.remark != ''">remark = #{data.remark},</if>
</sql>
<delete id="deleteDeviceById" parameterType="Long">
@ -86,4 +92,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{id}
</foreach>
</delete>
<!-- selectMacList -->
<select id="selectMacList" parameterType="DeviceQuery" resultType="String">
select bd.mac
from bst_device bd
<where>
<include refid="searchCondition"/>
</where>
</select>
</mapper>

View File

@ -0,0 +1,14 @@
package com.ruoyi.bst.device.service;
import java.util.List;
import com.ruoyi.bst.device.domain.DeviceVO;
public interface DeviceAssembler {
/**
* 组装关联网关数
*/
void assembleGatewayCount(List<DeviceVO> list);
}

View File

@ -0,0 +1,5 @@
package com.ruoyi.bst.device.service;
public interface DeviceConverter {
}

View File

@ -68,4 +68,11 @@ public interface DeviceService
* @return 设备
*/
public DeviceVO selectDeviceByMac(String mac);
/**
* 根据数据点查询相似的设备MAC
* @param macPrefix MAC前缀
* @return 设备MAC
*/
public String selectOneMacByPrefix(String macPrefix);
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.bst.device.service.impl;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.gateway.domain.enums.GatewayOnlineStatus;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import com.ruoyi.bst.gatewayDevice.domain.vo.GatewayStatusCountGroupByDeviceVO;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceDashboard;
import com.ruoyi.common.utils.collection.CollectionUtils;
@Service
public class DeviceAssemblerImpl implements DeviceAssembler {
@Autowired
private GatewayDeviceDashboard gatewayDeviceDashboard;
@Override
public void assembleGatewayCount(List<DeviceVO> list) {
if (CollectionUtils.isEmptyElement(list)) {
return;
}
GatewayDeviceQuery query = new GatewayDeviceQuery();
query.setDeviceIds(CollectionUtils.map(list, DeviceVO::getId));
List<GatewayStatusCountGroupByDeviceVO> statusList = gatewayDeviceDashboard.selectGatewayStatusCountGroupByDevice(query);
for (DeviceVO device : list) {
// 在线网关数
GatewayStatusCountGroupByDeviceVO onlineStatus = statusList.stream()
.filter(status -> Objects.equals(status.getDeviceId(), device.getId())
&& GatewayOnlineStatus.ONLINE.getCode().equals(status.getGatewayOnlineStatus()))
.findFirst().orElse(null);
if (onlineStatus != null) {
device.setOnlineGatewayCount(onlineStatus.getCount());
} else {
device.setOnlineGatewayCount(0);
}
// 离线网关数
GatewayStatusCountGroupByDeviceVO offlineStatus = statusList.stream()
.filter(status -> Objects.equals(status.getDeviceId(), device.getId())
&& GatewayOnlineStatus.OFFLINE.getCode().equals(status.getGatewayOnlineStatus()))
.findFirst().orElse(null);
if (offlineStatus != null) {
device.setOfflineGatewayCount(offlineStatus.getCount());
} else {
device.setOfflineGatewayCount(0);
}
}
}
}

View File

@ -0,0 +1,10 @@
package com.ruoyi.bst.device.service.impl;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.device.service.DeviceConverter;
@Service
public class DeviceConverterImpl implements DeviceConverter {
}

View File

@ -12,7 +12,9 @@ import com.ruoyi.bst.gateway.service.GatewayIotService;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceVO;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceService;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.util.IotUtil;
@ -27,21 +29,24 @@ public class DeviceIotServiceImpl implements DeviceIotService {
@Override
public CommandResponse autoSend(DeviceSendDTO dto) {
if (dto == null || StringUtils.isBlank(dto.getMac()) || StringUtils.isBlank(dto.getCommand())) {
return null;
}
ServiceUtil.assertion(dto == null, "参数不能为空");
ServiceUtil.assertion(StringUtils.isBlank(dto.getMac()), "mac不能为空");
ServiceUtil.assertion(StringUtils.isBlank(dto.getCommand()), "命令不能为空");
// 尝试次数默认1次
int tryCount = dto.getTryCount() == null ? 1 : dto.getTryCount();
// 查询设备关联的网关
// TODO 按照优先级排序到时候找个方法来做优先级
PageHelper.startPage(1, tryCount);
// 排序优先级 最近心跳时间 > 网关最近在线时间 > 网关在线状态
PageHelper.orderBy("last_heart_time desc, gateway_last_online_time desc, gateway_online_status desc");
GatewayDeviceQuery query = new GatewayDeviceQuery();
query.setEqDeviceMac(dto.getMac());
List<GatewayDeviceVO> gatewayDeviceList = gatewayDeviceService.selectGatewayDeviceList(query);
// 发送命令
CommandResponse res = null;
ServiceUtil.assertion(CollectionUtils.isEmpty(gatewayDeviceList), "当前设备未关联网关");
for (GatewayDeviceVO vo : gatewayDeviceList) {
res = gatewayIotService.send(vo.getGatewayMac(), vo.getDeviceMac(), dto.getCommand(), dto.getTimeout());
// 若发送成功则直接返回

View File

@ -115,4 +115,16 @@ public class DeviceServiceImpl implements DeviceService
List<DeviceVO> list = deviceMapper.selectDeviceList(query);
return CollectionUtils.firstElement(list);
}
@Override
public String selectOneMacByPrefix(String macPrefix) {
if (StringUtils.isBlank(macPrefix)) {
return null;
}
PageHelper.startPage(1, 1);
DeviceQuery query = new DeviceQuery();
query.setMacPrefix(macPrefix);
List<String> list = deviceMapper.selectMacList(query);
return CollectionUtils.firstElement(list);
}
}

View File

@ -0,0 +1,47 @@
package com.ruoyi.bst.dsLog.domain;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 数据日志对象 bst_ds_log
*
* @author ruoyi
* @date 2025-04-18
*/
@Data
public class DsLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
@Excel(name = "网关MAC")
@ApiModelProperty("网关MAC")
private String gatewayMac;
@Excel(name = "实际设备MAC")
@ApiModelProperty("实际设备MAC")
private String deviceMac;
@Excel(name = "数据点")
@ApiModelProperty("数据点")
private String ds;
@Excel(name = "数据")
@ApiModelProperty("数据")
private String data;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "上报时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("上报时间")
private LocalDateTime at;
}

View File

@ -0,0 +1,8 @@
package com.ruoyi.bst.dsLog.domain;
import lombok.Data;
@Data
public class DsLogQuery extends DsLogVO {
}

View File

@ -0,0 +1,8 @@
package com.ruoyi.bst.dsLog.domain;
import lombok.Data;
@Data
public class DsLogVO extends DsLog {
}

View File

@ -0,0 +1,74 @@
package com.ruoyi.bst.dsLog.mapper;
import java.util.List;
import com.ruoyi.bst.dsLog.domain.DsLog;
import com.ruoyi.bst.dsLog.domain.DsLogVO;
import com.ruoyi.bst.dsLog.domain.DsLogQuery;
import org.apache.ibatis.annotations.Param;
/**
* 数据日志Mapper接口
*
* @author ruoyi
* @date 2025-04-18
*/
public interface DsLogMapper
{
/**
* 查询数据日志
*
* @param id 数据日志主键
* @return 数据日志
*/
DsLogVO selectDsLogById(Long id);
/**
* 查询数据日志列表
*
* @param query 数据日志
* @return 数据日志集合
*/
List<DsLogVO> selectDsLogList(@Param("query")DsLogQuery query);
/**
* 新增数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
int insertDsLog(DsLog dsLog);
/**
* 批量新增数据日志
*/
int batchInsert(@Param("list") List<? extends DsLog> list);
/**
* 批量修改数据日志
*/
int batchUpdate(@Param("list") List<? extends DsLog> list);
/**
* 修改数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
public int updateDsLog(@Param("data") DsLog dsLog);
/**
* 删除数据日志
*
* @param id 数据日志主键
* @return 结果
*/
int deleteDsLogById(Long id);
/**
* 批量删除数据日志
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDsLogByIds(Long[] ids);
}

View File

@ -0,0 +1,110 @@
<?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="com.ruoyi.bst.dsLog.mapper.DsLogMapper">
<resultMap type="DsLogVO" id="DsLogResult" autoMapping="true"/>
<sql id="selectDsLogVo">
select
bdl.id,
bdl.gateway_mac,
bdl.device_mac,
bdl.ds,
bdl.data,
bdl.at
from bst_ds_log bdl
</sql>
<sql id="searchCondition">
<if test="query.id != null "> and bdl.id = #{query.id}</if>
<if test="query.gatewayMac != null and query.gatewayMac != ''"> and bdl.gateway_mac like concat('%', #{query.gatewayMac}, '%')</if>
<if test="query.deviceMac != null and query.deviceMac != ''"> and bdl.device_mac like concat('%', #{query.deviceMac}, '%')</if>
<if test="query.ds != null and query.ds != ''"> and bdl.ds like concat('%', #{query.ds}, '%')</if>
${query.params.dataScope}
</sql>
<select id="selectDsLogList" parameterType="DsLogQuery" resultMap="DsLogResult">
<include refid="selectDsLogVo"/>
<where>
<include refid="searchCondition"/>
</where>
</select>
<select id="selectDsLogById" parameterType="Long" resultMap="DsLogResult">
<include refid="selectDsLogVo"/>
where bdl.id = #{id}
</select>
<insert id="insertDsLog" parameterType="DsLog" useGeneratedKeys="true" keyProperty="id">
insert into bst_ds_log
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="gatewayMac != null">gateway_mac,</if>
<if test="deviceMac != null">device_mac,</if>
<if test="ds != null">ds,</if>
<if test="data != null">data,</if>
<if test="at != null">at,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="gatewayMac != null">#{gatewayMac},</if>
<if test="deviceMac != null">#{deviceMac},</if>
<if test="ds != null">#{ds},</if>
<if test="data != null">#{data},</if>
<if test="at != null">#{at},</if>
</trim>
</insert>
<insert id="batchInsert" parameterType="DsLog" useGeneratedKeys="true" keyProperty="id">
insert into bst_ds_log
<trim prefix="(" suffix=")" suffixOverrides=",">
gateway_mac,
device_mac,
ds,
data,
at,
</trim>
values
<foreach collection="list" item="i" separator=",">
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="i.gatewayMac != null ">#{i.gatewayMac},</if>
<if test="i.gatewayMac == null ">default,</if>
<if test="i.deviceMac != null ">#{i.deviceMac},</if>
<if test="i.deviceMac == null ">default,</if>
<if test="i.ds != null ">#{i.ds},</if>
<if test="i.ds == null ">default,</if>
<if test="i.data != null ">#{i.data},</if>
<if test="i.data == null ">default,</if>
<if test="i.at != null ">#{i.at},</if>
<if test="i.at == null ">default,</if>
</trim>
</foreach>
</insert>
<update id="updateDsLog" parameterType="DsLog">
update bst_ds_log
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
where id = #{data.id}
</update>
<sql id="updateColumns">
<if test="data.gatewayMac != null">gateway_mac = #{data.gatewayMac},</if>
<if test="data.deviceMac != null">device_mac = #{data.deviceMac},</if>
<if test="data.ds != null">ds = #{data.ds},</if>
<if test="data.data != null">data = #{data.data},</if>
<if test="data.at != null">at = #{data.at},</if>
</sql>
<delete id="deleteDsLogById" parameterType="Long">
delete from bst_ds_log where id = #{id}
</delete>
<delete id="deleteDsLogByIds" parameterType="String">
delete from bst_ds_log where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,64 @@
package com.ruoyi.bst.dsLog.service;
import java.util.List;
import com.ruoyi.bst.dsLog.domain.DsLog;
import com.ruoyi.bst.dsLog.domain.DsLogQuery;
import com.ruoyi.bst.dsLog.domain.DsLogVO;
/**
* 数据日志Service接口
*
* @author ruoyi
* @date 2025-04-18
*/
public interface DsLogService
{
/**
* 查询数据日志
*
* @param id 数据日志主键
* @return 数据日志
*/
public DsLogVO selectDsLogById(Long id);
/**
* 查询数据日志列表
*
* @param dsLog 数据日志
* @return 数据日志集合
*/
public List<DsLogVO> selectDsLogList(DsLogQuery dsLog);
/**
* 新增数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
public int insertDsLog(DsLog dsLog);
/**
* 修改数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
public int updateDsLog(DsLog dsLog);
/**
* 批量删除数据日志
*
* @param ids 需要删除的数据日志主键集合
* @return 结果
*/
public int deleteDsLogByIds(Long[] ids);
/**
* 删除数据日志信息
*
* @param id 数据日志主键
* @return 结果
*/
public int deleteDsLogById(Long id);
}

View File

@ -0,0 +1,97 @@
package com.ruoyi.bst.dsLog.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.dsLog.domain.DsLog;
import com.ruoyi.bst.dsLog.domain.DsLogQuery;
import com.ruoyi.bst.dsLog.domain.DsLogVO;
import com.ruoyi.bst.dsLog.mapper.DsLogMapper;
import com.ruoyi.bst.dsLog.service.DsLogService;
/**
* 数据日志Service业务层处理
*
* @author ruoyi
* @date 2025-04-18
*/
@Service
public class DsLogServiceImpl implements DsLogService
{
@Autowired
private DsLogMapper dsLogMapper;
/**
* 查询数据日志
*
* @param id 数据日志主键
* @return 数据日志
*/
@Override
public DsLogVO selectDsLogById(Long id)
{
return dsLogMapper.selectDsLogById(id);
}
/**
* 查询数据日志列表
*
* @param dsLog 数据日志
* @return 数据日志
*/
@Override
public List<DsLogVO> selectDsLogList(DsLogQuery dsLog)
{
return dsLogMapper.selectDsLogList(dsLog);
}
/**
* 新增数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
@Override
public int insertDsLog(DsLog dsLog)
{
return dsLogMapper.insertDsLog(dsLog);
}
/**
* 修改数据日志
*
* @param dsLog 数据日志
* @return 结果
*/
@Override
public int updateDsLog(DsLog dsLog)
{
return dsLogMapper.updateDsLog(dsLog);
}
/**
* 批量删除数据日志
*
* @param ids 需要删除的数据日志主键
* @return 结果
*/
@Override
public int deleteDsLogByIds(Long[] ids)
{
return dsLogMapper.deleteDsLogByIds(ids);
}
/**
* 删除数据日志信息
*
* @param id 数据日志主键
* @return 结果
*/
@Override
public int deleteDsLogById(Long id)
{
return dsLogMapper.deleteDsLogById(id);
}
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.bst.gateway.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GatewayOnlineStatus {
ONLINE("1", "在线"),
OFFLINE("0", "离线");
private final String code;
private final String name;
}

View File

@ -13,7 +13,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bg.online_status,
bg.create_time,
bg.last_online_time,
bg.imei
bg.imei,
bg.remark
from bst_gateway bg
</sql>
@ -22,6 +23,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.mac != null and query.mac != ''"> and bg.mac like concat('%', #{query.mac}, '%')</if>
<if test="query.onlineStatus != null and query.onlineStatus != ''"> and bg.online_status = #{query.onlineStatus}</if>
<if test="query.imei != null and query.imei != ''"> and bg.imei like concat('%', #{query.imei}, '%')</if>
<if test="query.remark != null and query.remark != ''"> and bg.remark like concat('%', #{query.remark}, '%')</if>
${query.params.dataScope}
</sql>
@ -45,6 +47,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null">create_time,</if>
<if test="lastOnlineTime != null">last_online_time,</if>
<if test="imei != null and imei != ''">imei,</if>
<if test="remark != null and remark != ''">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="mac != null and mac != ''">#{mac},</if>
@ -52,6 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null">#{createTime},</if>
<if test="lastOnlineTime != null">#{lastOnlineTime},</if>
<if test="imei != null and imei != ''">#{imei},</if>
<if test="remark != null and remark != ''">#{remark},</if>
</trim>
</insert>
@ -108,6 +112,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</otherwise>
</choose>
</foreach>
<foreach open="remark = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.remark != null ">
WHEN #{item.id} THEN #{item.remark}
</when>
<otherwise>
WHEN #{item.id} THEN `remark`
</otherwise>
</choose>
</foreach>
</trim>
where id in
<foreach item="item" collection="list" open="(" separator="," close=")">
@ -129,6 +143,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.createTime != null">create_time = #{data.createTime},</if>
<if test="data.lastOnlineTime != null">last_online_time = #{data.lastOnlineTime},</if>
<if test="data.imei != null and data.imei != ''">imei = #{data.imei},</if>
<if test="data.remark != null and data.remark != ''">remark = #{data.remark},</if>
</sql>
<delete id="deleteGatewayById" parameterType="Long">

View File

@ -10,10 +10,12 @@ import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.device.service.DeviceConverter;
import com.ruoyi.bst.gateway.domain.Gateway;
import com.ruoyi.bst.gateway.domain.GatewayVO;
import com.ruoyi.bst.gateway.mapper.GatewayMapper;
import com.ruoyi.bst.gateway.service.GatewayIotService;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.constants.IotConstants;
@ -37,6 +39,12 @@ public class GatewayIotServiceImpl implements GatewayIotService {
@Autowired
private GatewayMapper gatewayMapper;
@Autowired
private DeviceConverter deviceConverter;
@Autowired
private GatewayDeviceService gatewayDeviceService;
@Override
public void refresh(List<GatewayVO> gatewayList, String onlineType) {
if (CollectionUtils.isEmptyElement(gatewayList)) {

View File

@ -21,6 +21,8 @@ public class GatewayDevice extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
@Excel(name = "设备MAC")
@ApiModelProperty("设备MAC")
private String deviceMac;
@ -29,11 +31,13 @@ public class GatewayDevice extends BaseEntity
@ApiModelProperty("网关MAC")
private String gatewayMac;
private Long id;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "最后一次心跳时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("最后一次心跳时间")
private LocalDateTime lastHeartTime;
@Excel(name = "心跳设置")
@ApiModelProperty("心跳设置")
private Integer heartBeat;
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.bst.gatewayDevice.domain;
import java.util.List;
import lombok.Data;
@Data
@ -10,4 +12,13 @@ public class GatewayDeviceQuery extends GatewayDeviceVO {
// 精准设备MAC
private String eqDeviceMac;
// 网关MAC列表
private List<String> gatewayMacList;
// 设备MAC列表
private List<String> deviceMacList;
// 设备ID列表
private List<Long> deviceIds;
}

View File

@ -1,5 +1,9 @@
package com.ruoyi.bst.gatewayDevice.domain;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
@Data
@ -7,10 +11,14 @@ public class GatewayDeviceVO extends GatewayDevice {
// 网关ID
private Long gatewayId;
// 网关最近在线时间
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gatewayLastOnlineTime;
// 网关在线状态
private String gatewayOnlineStatus;
// 设备ID
private Long deviceId;
// 设备类型
private String deviceType;

View File

@ -0,0 +1,17 @@
package com.ruoyi.bst.gatewayDevice.domain.vo;
import lombok.Data;
@Data
public class GatewayStatusCountGroupByDeviceVO {
// 设备ID
private Long deviceId;
// 网关在线状态
private String gatewayOnlineStatus;
// 数量
private Integer count;
}

View File

@ -1,11 +1,14 @@
package com.ruoyi.bst.gatewayDevice.mapper;
import java.util.List;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDevice;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceVO;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDevice;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceVO;
import com.ruoyi.bst.gatewayDevice.domain.vo.GatewayStatusCountGroupByDeviceVO;
/**
* 网关设备Mapper接口
*
@ -71,4 +74,20 @@ public interface GatewayDeviceMapper
* @return 结果
*/
public int deleteGatewayDeviceByIds(Long[] ids);
/**
* 根据网关mac和设备mac更新网关设备
*
* @param data 网关设备
* @return 结果
*/
int updateByGatewayAndDeviceMac(@Param("data") GatewayDevice data);
/**
* 查询网关状态统计并按设备分组
*
* @param query 网关设备查询条件
* @return 网关状态统计结果
*/
List<GatewayStatusCountGroupByDeviceVO> selectGatewayStatusCountGroupByDevice(@Param("query") GatewayDeviceQuery query);
}

View File

@ -13,10 +13,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bgd.device_mac,
bgd.create_time,
bgd.last_heart_time,
bd.type as device_type,
bgd.heart_beat,
bg.id as gateway_id,
bg.last_online_time as gateway_last_online_time,
bg.online_status as gateway_online_status,
bd.type as device_type,
bd.id as device_id
from bst_gateway_device bgd
from <include refid="searchTables"/>
</sql>
<sql id="searchTables">
bst_gateway_device bgd
left join bst_gateway bg on bg.mac = bgd.gateway_mac
left join bst_device bd on bd.mac = bgd.device_mac
</sql>
@ -30,6 +37,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.eqGatewayMac != null "> and bg.mac = #{query.eqGatewayMac}</if>
<if test="query.eqDeviceMac != null "> and bd.mac = #{query.eqDeviceMac}</if>
<if test="query.deviceType != null "> and bd.type = #{query.deviceType}</if>
<if test="query.gatewayMacList != null and query.gatewayMacList.size() > 0">
and bg.mac in
<foreach item="item" index="index" collection="query.gatewayMacList"
open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="query.deviceMacList != null and query.deviceMacList.size() > 0">
and bd.mac in
<foreach item="item" index="index" collection="query.deviceMacList"
open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="query.deviceIds != null and query.deviceIds.size() > 0">
and bd.id in
<foreach item="item" index="index" collection="query.deviceIds"
open="(" separator="," close=")">
#{item}
</foreach>
</if>
${query.params.dataScope}
</sql>
@ -52,12 +80,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="deviceMac != null">device_mac,</if>
<if test="createTime != null">create_time,</if>
<if test="lastHeartTime != null">last_heart_time,</if>
<if test="heartBeat != null">heart_beat,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="gatewayMac != null">#{gatewayMac},</if>
<if test="deviceMac != null">#{deviceMac},</if>
<if test="createTime != null">#{createTime},</if>
<if test="lastHeartTime != null">#{lastHeartTime},</if>
<if test="heartBeat != null">#{heartBeat},</if>
</trim>
</insert>
@ -69,11 +99,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{data.id}
</update>
<update id="updateByGatewayAndDeviceMac" parameterType="GatewayDevice">
update bst_gateway_device
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
where gateway_mac = #{data.gatewayMac} and device_mac = #{data.deviceMac}
</update>
<sql id="updateColumns">
<if test="data.gatewayMac != null">gateway_mac = #{data.gatewayMac},</if>
<if test="data.deviceMac != null">device_mac = #{data.deviceMac},</if>
<if test="data.createTime != null">create_time = #{data.createTime},</if>
<if test="data.lastHeartTime != null">last_heart_time = #{data.lastHeartTime},</if>
<if test="data.heartBeat != null">heart_beat = #{data.heartBeat},</if>
</sql>
<delete id="deleteGatewayDeviceById" parameterType="Long">
@ -86,4 +125,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{id}
</foreach>
</delete>
<resultMap type="GatewayStatusCountGroupByDeviceVO" id="GatewayStatusCountGroupByDeviceResult" autoMapping="true">
<result column="count" property="count" typeHandler="com.ruoyi.common.mybatis.typehandler.NonNullIntegerTyperHandler"/>
</resultMap>
<select id="selectGatewayStatusCountGroupByDevice" parameterType="GatewayDeviceQuery" resultMap="GatewayStatusCountGroupByDeviceResult">
select
bd.id as device_id,
bg.online_status as gateway_online_status,
count(bgd.id) as `count`
from <include refid="searchTables"/>
<where>
<include refid="searchCondition"/>
</where>
group by device_id, gateway_online_status
</select>
</mapper>

View File

@ -0,0 +1,19 @@
package com.ruoyi.bst.gatewayDevice.service;
import java.util.List;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import com.ruoyi.bst.gatewayDevice.domain.vo.GatewayStatusCountGroupByDeviceVO;
public interface GatewayDeviceDashboard {
/**
* 查询网关状态统计并按设备分组
* @param query
* @return
*/
List<GatewayStatusCountGroupByDeviceVO> selectGatewayStatusCountGroupByDevice(GatewayDeviceQuery query);
}

View File

@ -61,4 +61,20 @@ public interface GatewayDeviceService
* @return 结果
*/
public int deleteGatewayDeviceById(Long id);
/**
* 根据网关mac列表查询网关设备列表
*
* @param gatewayMacList 网关mac列表
* @return 网关设备列表
*/
public List<GatewayDeviceVO> selectByGatewayMacList(List<String> gatewayMacList);
/**
* 插入或更新网关设备
*
* @param data 网关设备
* @return 结果
*/
public int insertOrUpdate(GatewayDevice data);
}

View File

@ -0,0 +1,24 @@
package com.ruoyi.bst.gatewayDevice.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceQuery;
import com.ruoyi.bst.gatewayDevice.domain.vo.GatewayStatusCountGroupByDeviceVO;
import com.ruoyi.bst.gatewayDevice.mapper.GatewayDeviceMapper;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceDashboard;
@Service
public class GatewayDeviceDashboardImpl implements GatewayDeviceDashboard {
@Autowired
private GatewayDeviceMapper gatewayDeviceMapper;
@Override
public List<GatewayStatusCountGroupByDeviceVO> selectGatewayStatusCountGroupByDevice(GatewayDeviceQuery query) {
return gatewayDeviceMapper.selectGatewayStatusCountGroupByDevice(query);
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.bst.gatewayDevice.service.impl;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
@ -11,6 +12,8 @@ import com.ruoyi.bst.gatewayDevice.domain.GatewayDeviceVO;
import com.ruoyi.bst.gatewayDevice.mapper.GatewayDeviceMapper;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceService;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
/**
* 网关设备Service业务层处理
@ -96,4 +99,32 @@ public class GatewayDeviceServiceImpl implements GatewayDeviceService
{
return gatewayDeviceMapper.deleteGatewayDeviceById(id);
}
@Override
public List<GatewayDeviceVO> selectByGatewayMacList(List<String> gatewayMacList) {
if (CollectionUtils.isEmptyElement(gatewayMacList)) {
return Collections.emptyList();
}
GatewayDeviceQuery query = new GatewayDeviceQuery();
query.setGatewayMacList(gatewayMacList);
return gatewayDeviceMapper.selectGatewayDeviceList(query);
}
@Override
public int insertOrUpdate(GatewayDevice data) {
if (data == null) {
return 0;
}
int rows = 0;
try {
// 尝试插入如果插入失败则更新
rows = this.insertGatewayDevice(data);
ServiceUtil.assertion(rows != 1, "插入失败");
} catch (Exception e) {
// 更新
rows = gatewayDeviceMapper.updateByGatewayAndDeviceMac(data);
ServiceUtil.assertion(rows != 1, "更新失败");
}
return rows;
}
}

View File

@ -48,5 +48,6 @@ public class IotConstants {
public static final String DS_VER = "VER"; // 版本号
public static final String DS_IMEI = "IMEI"; // 物联网卡号
public static final String DS_CSQ = "CSQ"; // 信号强度
public static final String DS_HB = "HB"; // 心跳设置
}

View File

@ -11,6 +11,7 @@ import com.ruoyi.iot.domain.CurrentDatastream;
import com.ruoyi.iot.domain.CurrentDeviceData;
import com.ruoyi.iot.domain.IotDeviceInfo;
import com.ruoyi.iot.service.IotConverter;
import com.ruoyi.iot.util.IotUtil;
import lombok.extern.slf4j.Slf4j;
@ -44,7 +45,7 @@ public class IotConverterImpl implements IotConverter {
String id = stream.getId(); // 数据点ID
String value = stream.getValue().toString(); // 数据点取值取最新的值
if (id.length() == 12) {
if (IotUtil.isSubDevice(id)) {
// 子设备数据
device.addSubData(stream);
} else {

View File

@ -1,13 +1,23 @@
package com.ruoyi.iot.service.impl;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.dsLog.domain.DsLog;
import com.ruoyi.bst.dsLog.service.DsLogService;
import com.ruoyi.bst.gatewayDevice.domain.GatewayDevice;
import com.ruoyi.bst.gatewayDevice.service.GatewayDeviceService;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.domain.ReceiveMsg;
import com.ruoyi.iot.enums.ReceiveType;
import com.ruoyi.iot.service.IotReceiveService;
import com.ruoyi.ws.service.DeviceWebSocketService;
import com.ruoyi.iot.util.IotUtil;
import lombok.extern.slf4j.Slf4j;
@ -23,7 +33,13 @@ public class IotReceiveServiceImpl implements IotReceiveService {
private RedisCache redisCache;
@Autowired
private DeviceWebSocketService deviceWebSocketService;
private GatewayDeviceService gatewayDeviceService;
@Autowired
private DsLogService dsLogService;
@Autowired
private DeviceService deviceService;
@Override
public void handleReceive(ReceiveMsg msg) {
@ -32,10 +48,50 @@ public class IotReceiveServiceImpl implements IotReceiveService {
}
// 数据点推送
if (ReceiveType.DATA_POINT.getType().equals(msg.getType())) {
String dsId = msg.getDsId();
if (IotUtil.isSubDevice(dsId)) {
handleSubDevice(msg);
}
}
// 生命周期
else if (ReceiveType.DEVICE_STATUS.getType().equals(msg.getType())) {
}
}
private void handleSubDevice(ReceiveMsg msg) {
String gatewayMac = msg.getDevName();
String dsId = msg.getDsId();
Object value = msg.getValue();
LocalDateTime at = DateUtils.toLocalDateTime(msg.getAt());
// 获取设备MAC
String prefix = dsId;
// 判断dsId的倒数32位是否为ST如果是则查询相似的设备MAC
if (IotUtil.isSubDevicePart(dsId)) {
prefix = dsId.substring(0, 10);
}
String deviceMac = deviceService.selectOneMacByPrefix(prefix);
if (StringUtils.isBlank(deviceMac)) {
log.error("不存在前缀为{}的设备,无法存储日志", prefix);
return;
}
// 存储日志
DsLog dsLog = new DsLog();
dsLog.setGatewayMac(gatewayMac);
dsLog.setDeviceMac(deviceMac);
dsLog.setAt(at);
dsLog.setData(value.toString());
dsLog.setDs(dsId);
dsLogService.insertDsLog(dsLog);
// 更新设备网关数据
GatewayDevice gatewayDevice = new GatewayDevice();
gatewayDevice.setGatewayMac(gatewayMac);
gatewayDevice.setDeviceMac(deviceMac);
gatewayDevice.setLastHeartTime(at);
gatewayDevice.setHeartBeat(IotUtil.getHeartBeat(value));
gatewayDeviceService.insertOrUpdate(gatewayDevice);
}
}

View File

@ -28,6 +28,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.constants.IotConstants;
import com.ruoyi.iot.domain.CurrentDatastream;
import com.ruoyi.iot.domain.ObjBody;
import com.ruoyi.iot.domain.response.CommandResponse;
@ -223,4 +224,38 @@ public class IotUtil {
}
return IotConstants.SUB_SUCCESS.equals(res.getCmdResp());
}
// 是否是子设备
public static boolean isSubDevice(String dsId) {
return dsId != null && dsId.length() == 12;
}
/**
* 获取心跳设置
*/
public static Integer getHeartBeat(CurrentDatastream datastream) {
if (datastream == null) {
return 0;
}
Object value = datastream.getValue();
return getHeartBeat(value.toString());
}
public static Integer getHeartBeat(Object value) {
if (value == null) {
return 0;
}
JSONObject json = JSON.parseObject(JSON.toJSONString(value));
return json.getInteger(IotConstants.DS_HB);
}
/**
* 判断是否为子设备的一部分
*/
public static boolean isSubDevicePart(String dsId) {
if (StringUtils.isEmpty(dsId)) {
return false;
}
return dsId.matches(".*ST.$");
}
}

View File

@ -20,6 +20,7 @@ import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.dto.DeviceSendDTO;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.common.annotation.Log;
@ -45,6 +46,9 @@ public class DeviceController extends BaseController
@Autowired
private DeviceIotService deviceIotService;
@Autowired
private DeviceAssembler deviceAssembler;
/**
* 查询设备列表
*/
@ -55,6 +59,7 @@ public class DeviceController extends BaseController
startPage();
startOrderBy();
List<DeviceVO> list = deviceService.selectDeviceList(query);
deviceAssembler.assembleGatewayCount(list);
return getDataTable(list);
}

View File

@ -0,0 +1,110 @@
package com.ruoyi.web.bst;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.bst.dsLog.domain.DsLog;
import com.ruoyi.bst.dsLog.domain.DsLogQuery;
import com.ruoyi.bst.dsLog.domain.DsLogVO;
import com.ruoyi.bst.dsLog.service.DsLogService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
/**
* 数据日志Controller
*
* @author ruoyi
* @date 2025-04-18
*/
@RestController
@RequestMapping("/bst/dsLog")
public class DsLogController extends BaseController
{
@Autowired
private DsLogService dsLogService;
/**
* 查询数据日志列表
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:list')")
@GetMapping("/list")
public TableDataInfo list(DsLogQuery query)
{
startPage();
startOrderBy();
List<DsLogVO> list = dsLogService.selectDsLogList(query);
return getDataTable(list);
}
/**
* 导出数据日志列表
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:export')")
@Log(title = "数据日志", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, DsLogQuery query)
{
List<DsLogVO> list = dsLogService.selectDsLogList(query);
ExcelUtil<DsLogVO> util = new ExcelUtil<DsLogVO>(DsLogVO.class);
util.exportExcel(response, list, "数据日志数据");
}
/**
* 获取数据日志详细信息
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(dsLogService.selectDsLogById(id));
}
/**
* 新增数据日志
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:add')")
@Log(title = "数据日志", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody DsLog dsLog)
{
return toAjax(dsLogService.insertDsLog(dsLog));
}
/**
* 修改数据日志
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:edit')")
@Log(title = "数据日志", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DsLog dsLog)
{
return toAjax(dsLogService.updateDsLog(dsLog));
}
/**
* 删除数据日志
*/
@PreAuthorize("@ss.hasPermi('bst:dsLog:remove')")
@Log(title = "数据日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(dsLogService.deleteDsLogByIds(ids));
}
}