Merge pull request 'feature/simcards_checker' (#2) from feature/simcards_checker into develop
Reviewed-on: #2
This commit is contained in:
commit
31b4047d86
@ -12,4 +12,8 @@ public interface GnssDeviceMapper extends MPJBaseMapper<GnssDevice> {
|
||||
|
||||
@Update({"update gnssdevices set syn=false where group_id=#{group_id}"})
|
||||
int setSynFlagByGroupId(int group_id);
|
||||
|
||||
@Update({"update gnssdevices set iccid=#{iccid} where deviceid=#{deviceId}"})
|
||||
int updateIccidByDeviceId(String deviceId, String iccid);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("traffic_cards")
|
||||
public class TrafficCard {
|
||||
public static final int STATUS_UNKNOWN = -1; // 未知
|
||||
public static final int STATUS_WAIT_ACTIVE = 1; // 待激活
|
||||
public static final int STATUS_ACTIVATED = 2; // 已激活
|
||||
public static final int STATUS_SUSPENDED = 3; // 停机
|
||||
public static final int STATUS_CANCELLED = 4; // 注销
|
||||
public static final int STATUS_IN_STOCK = 5; // 库存
|
||||
public static final int STATUS_TESTABLE = 6; // 可测试
|
||||
public static final int STATUS_INVALID = 7; // 失效
|
||||
public static final int STATUS_NOT_EXIST = 99; // 号码不存在
|
||||
|
||||
// 查询状态常量定义
|
||||
public static final int QUERY_STATUS_NORMAL = 0; // 正常状态
|
||||
public static final int QUERY_STATUS_NOT_CURRENT_VENDOR = 1; // 非当前卡商
|
||||
public static final int QUERY_STATUS_OTHER_ERROR = 2; // 其他错误
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@ExcelProperty("ID")
|
||||
private Integer id;
|
||||
|
||||
@ExcelProperty("ICCID")
|
||||
private String iccid;
|
||||
|
||||
@ExcelProperty("物联卡号码")
|
||||
private String msisdn;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty("剩余流量(MB)")
|
||||
private Integer remaining;
|
||||
|
||||
@ExcelProperty("总流量(MB)")
|
||||
private Integer total;
|
||||
|
||||
@ExcelProperty("已用流量(MB)")
|
||||
private Integer used;
|
||||
|
||||
@TableField("update_time")
|
||||
@ExcelProperty("更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
@TableField("query_status")
|
||||
@ExcelProperty("查询状态")
|
||||
private Integer queryStatus;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface TrafficCardMapper extends BaseMapper<TrafficCard> {
|
||||
|
||||
@Select("select * from traffic_cards where iccid = #{iccid} limit 1")
|
||||
TrafficCard findByIccid(String iccid);
|
||||
|
||||
@Update("UPDATE traffic_cards SET " +
|
||||
"update_time = #{updateTime}, " +
|
||||
"msisdn = #{msisdn}, " +
|
||||
"status = #{status}, " +
|
||||
"remaining = #{remaining}, " +
|
||||
"total = #{total}, " +
|
||||
"used = #{used} " +
|
||||
"WHERE iccid = #{iccid}")
|
||||
int updateCardInfo(TrafficCard trafficCard);
|
||||
|
||||
@Update("UPDATE traffic_cards SET query_status = #{queryStatus} WHERE iccid = #{iccid}")
|
||||
int updateQueryStatus(String iccid,int queryStatus);
|
||||
|
||||
@Update("UPDATE traffic_cards SET " +
|
||||
"update_time = #{updateTime}, " +
|
||||
"remaining = #{remaining}, " +
|
||||
"total = #{total}, " +
|
||||
"used = #{used} " +
|
||||
"WHERE iccid = #{iccid} AND iccid IS NOT NULL AND iccid != ''")
|
||||
int updateCardTrafficInfo(TrafficCard trafficCard);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("traffic_device_mappings")
|
||||
public class TrafficDeviceMapping {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
private String deviceid;
|
||||
|
||||
private String iccid;
|
||||
|
||||
@TableField("start_time")
|
||||
private Date startTime;
|
||||
|
||||
@TableField("end_time")
|
||||
private Date endTime; // 结束使用时间,NULL表示当前正在使用
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface TrafficDeviceMappingMapper extends BaseMapper<TrafficDeviceMapping> {
|
||||
@Select("SELECT * FROM traffic_device_mappings WHERE deviceid = #{deviceId} AND end_time IS NULL LIMIT 1")
|
||||
TrafficDeviceMapping findActiveByDeviceId(String deviceId);
|
||||
|
||||
@Select("SELECT * FROM traffic_device_mappings WHERE iccid = #{iccid} AND end_time IS NULL LIMIT 1")
|
||||
TrafficDeviceMapping findActiveByIccid(String iccid);
|
||||
|
||||
@Select("SELECT * FROM traffic_device_mappings WHERE deviceid = #{deviceId} ORDER BY start_time DESC")
|
||||
List<TrafficDeviceMapping> findHistoryByDeviceId(String deviceId);
|
||||
|
||||
@Update("UPDATE traffic_device_mappings SET end_time = NOW() WHERE id = #{id}")
|
||||
int endMapping(Integer id);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("traffic_records")
|
||||
public class TrafficRecord {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
private String iccid;
|
||||
|
||||
@TableField("record_time")
|
||||
private Date recordTime;
|
||||
|
||||
private Integer remaining; // 剩余流量(MB×1000)
|
||||
private Integer used; // 已用流量(MB×1000)
|
||||
private Integer total; // 总流量(MB×1000)
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.imdroid.secapi.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface TrafficRecordMapper extends BaseMapper<TrafficRecord> {
|
||||
|
||||
}
|
||||
@ -4,9 +4,7 @@ import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imdroid.secapi.dto.GnssStatusJoin;
|
||||
import com.imdroid.secapi.dto.SimCard;
|
||||
import com.imdroid.secapi.dto.SimCardsMapper;
|
||||
import com.imdroid.secapi.dto.*;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
@ -20,7 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
|
||||
@ -35,7 +32,10 @@ public class SimCardQueryServiceImpl implements SimCardQueryService{
|
||||
private String KEY;
|
||||
|
||||
@Autowired
|
||||
SimCardsMapper simCardsMapper;
|
||||
TrafficCardMapper trafficCardMapper;
|
||||
|
||||
@Autowired
|
||||
GnssDeviceMapper gnssDeviceMapper;
|
||||
|
||||
@Override
|
||||
public BaseResponse<CardInfoData> queryCardInfo(GnssStatusJoin device) {
|
||||
@ -59,20 +59,53 @@ public class SimCardQueryServiceImpl implements SimCardQueryService{
|
||||
|
||||
private <T> BaseResponse<T> executeQuery(GnssStatusJoin device, String path, Class<T> responseType) {
|
||||
try {
|
||||
BaseResponse<T> response = queryByParams(device.getIccid(), path, responseType);
|
||||
|
||||
/*
|
||||
系统中存在一些奇怪的卡 :
|
||||
|
||||
向 DTU发送AT指令查询 ICCID 返回的值比如是:89861124224084565106,但实际是 8986112422408456510B,
|
||||
从而导致卡商无法查询到该卡,正确的方法是舍弃最后一位,比如 8986112422408456510
|
||||
|
||||
所以,当出现 " 无效的卡号 " 错误的时候,应该尝试去舍弃最后一位, 这样才是正确的 ICCID 号码
|
||||
|
||||
*/
|
||||
if (shouldTryTruncatedIccid(response) && device.getIccid() != null && device.getIccid().length() > 1) {
|
||||
String truncatedIccid = device.getIccid().substring(0, device.getIccid().length() - 1);
|
||||
BaseResponse<T> retryResponse = queryByParams(truncatedIccid, path, responseType);
|
||||
if (retryResponse != null && retryResponse.getStatus() == 1) {
|
||||
updateGnssDeviceIccid(device, truncatedIccid);
|
||||
device.setIccid(truncatedIccid);
|
||||
return retryResponse;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
logger.error("查询失败: 设备={}, 错误={}", device.getDeviceid(), e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> BaseResponse<T> queryByParams(String iccid, String path, Class<T> responseType) throws Exception {
|
||||
Map<String, String> params = buildQueryParams(iccid);
|
||||
String response = sendHttpPost(path, params);
|
||||
logger.debug("查询响应: ICCID={}, 响应={}", iccid, response);
|
||||
|
||||
return parseResponse(response, responseType);
|
||||
}
|
||||
|
||||
private Map<String, String> buildQueryParams(String iccid) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("username", USERNAME);
|
||||
params.put("key", KEY);
|
||||
params.put("card", device.getIccid());
|
||||
params.put("card", iccid);
|
||||
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
params.put("signature", calculateSignature(params));
|
||||
return params;
|
||||
}
|
||||
|
||||
String signature = calculateSignature(params);
|
||||
params.put("signature", signature);
|
||||
|
||||
logger.info("Request params: {}", params);
|
||||
String response = sendHttpPost(path, params);
|
||||
logger.info("查询响应: 设备={}, ICCID={}, 响应={}",
|
||||
device.getDeviceid(), device.getIccid(), response);
|
||||
|
||||
private <T> BaseResponse<T> parseResponse(String response, Class<T> responseType) throws Exception {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
|
||||
|
||||
@ -93,11 +126,13 @@ public class SimCardQueryServiceImpl implements SimCardQueryService{
|
||||
|
||||
JavaType type = mapper.getTypeFactory().constructParametricType(BaseResponse.class, responseType);
|
||||
return mapper.readValue(response, type);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询失败: 设备={}, 错误={}", device.getDeviceid(), e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean shouldTryTruncatedIccid(BaseResponse<?> response) {
|
||||
return response != null &&
|
||||
(response.getStatus() == 0 || response.getStatus() == -6) &&
|
||||
response.getMessage() != null &&
|
||||
response.getMessage().contains("无效的卡号");
|
||||
}
|
||||
|
||||
private String calculateSignature(Map<String, String> params) {
|
||||
@ -147,30 +182,34 @@ public class SimCardQueryServiceImpl implements SimCardQueryService{
|
||||
return device.getIccid() != null && !device.getIccid().trim().isEmpty();
|
||||
}
|
||||
|
||||
public SimCard CreateOrUpdateSimCard(GnssStatusJoin device) {
|
||||
SimCard simCard = simCardsMapper.queryByDeviceId(device.getDeviceid());
|
||||
if (simCard == null) {
|
||||
simCard = createNewSimCard(device);
|
||||
public TrafficCard createOrUpdateTrafficCard(GnssStatusJoin device) {
|
||||
TrafficCard card = trafficCardMapper.selectById(device.getIccid());
|
||||
if (card == null) {
|
||||
card = new TrafficCard();
|
||||
card.setIccid(device.getIccid());
|
||||
card.setStatus(-1);
|
||||
card.setUpdateTime(new Date());
|
||||
trafficCardMapper.insert(card);
|
||||
}
|
||||
return simCard;
|
||||
}
|
||||
|
||||
public SimCard createNewSimCard(GnssStatusJoin device) {
|
||||
SimCard newCard = new SimCard();
|
||||
newCard.setDeviceid(device.getDeviceid());
|
||||
newCard.setUpdatetime(new Date());
|
||||
newCard.setIccid(device.getIccid());
|
||||
newCard.setStatus(-1);
|
||||
newCard.setMsisdn("");
|
||||
newCard.setRemaining(BigDecimal.ZERO);
|
||||
newCard.setUsed(BigDecimal.ZERO);
|
||||
newCard.setTotal(BigDecimal.ZERO);
|
||||
|
||||
simCardsMapper.insert(newCard);
|
||||
return newCard;
|
||||
return card;
|
||||
}
|
||||
|
||||
public boolean isValidResponse(BaseResponse<?> response) {
|
||||
return response != null && response.getStatus() == 1;
|
||||
}
|
||||
|
||||
private void updateGnssDeviceIccid(GnssStatusJoin device, String newIccid) {
|
||||
try {
|
||||
String originalIccid = device.getIccid();
|
||||
|
||||
GnssDevice gnssDevice = gnssDeviceMapper.queryByDeviceId(device.getDeviceid());
|
||||
if (gnssDevice != null) {
|
||||
gnssDeviceMapper.updateIccidByDeviceId(device.getDeviceid(), newIccid);
|
||||
logger.debug("更新设备ICCID: 设备={}, 原ICCID={}, 新ICCID={}",
|
||||
device.getDeviceid(), originalIccid, newIccid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新设备ICCID失败: 设备={}, 错误={}", device.getDeviceid(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
package com.imdroid.beidou_ehm.simcard;
|
||||
|
||||
import com.imdroid.secapi.dto.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Service
|
||||
public class TrafficCardService {
|
||||
|
||||
@Autowired
|
||||
private TrafficCardMapper trafficCardMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficDeviceMappingMapper mappingMapper;
|
||||
|
||||
@Autowired
|
||||
private GnssDeviceMapper deviceMapper;
|
||||
|
||||
@Transactional
|
||||
public boolean checkAndHandleICCIDChanges(GnssStatusJoin device) {
|
||||
if (device == null || device.getIccid() == null || device.getIccid().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String currentIccid = device.getIccid().trim();
|
||||
|
||||
// 查询当前设备的活跃映射记录
|
||||
TrafficDeviceMapping activeMapping = mappingMapper.findActiveByDeviceId(device.getDeviceid());
|
||||
if (activeMapping == null) {
|
||||
// 第一次为设备创建映射
|
||||
createNewMapping(device.getDeviceid(), currentIccid);
|
||||
return true;
|
||||
} else if (!currentIccid.equals(activeMapping.getIccid())) {
|
||||
// ICCID变更,关闭旧映射,创建新映射
|
||||
closeExistingMapping(activeMapping);
|
||||
createNewMapping(device.getDeviceid(), currentIccid);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public TrafficCard getOrCreateTrafficCard(String iccid) {
|
||||
TrafficCard card = trafficCardMapper.findByIccid(iccid);
|
||||
|
||||
if (card == null) {
|
||||
card = new TrafficCard();
|
||||
card.setIccid(iccid);
|
||||
card.setMsisdn("");
|
||||
card.setStatus(TrafficCard.STATUS_UNKNOWN);
|
||||
card.setRemaining(0);
|
||||
card.setTotal(0);
|
||||
card.setUsed(0);
|
||||
card.setUpdateTime(new Date());
|
||||
card.setQueryStatus(TrafficCard.QUERY_STATUS_NORMAL);
|
||||
trafficCardMapper.insert(card);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateDeviceSimMapping(String deviceId, String iccid) {
|
||||
getOrCreateTrafficCard(iccid);
|
||||
|
||||
// 查询现有映射
|
||||
TrafficDeviceMapping activeMapping = mappingMapper.findActiveByDeviceId(deviceId);
|
||||
|
||||
if (activeMapping != null) {
|
||||
// 如果ICCID变化,关闭现有映射并创建新映射
|
||||
if (!iccid.equals(activeMapping.getIccid())) {
|
||||
closeExistingMapping(activeMapping);
|
||||
createNewMapping(deviceId, iccid);
|
||||
}
|
||||
} else {
|
||||
// 没有现有映射,创建新映射
|
||||
createNewMapping(deviceId, iccid);
|
||||
}
|
||||
}
|
||||
|
||||
public void markCardAsNotCurrentVendor(String iccid) {
|
||||
trafficCardMapper.updateQueryStatus(iccid, TrafficCard.QUERY_STATUS_NOT_CURRENT_VENDOR);
|
||||
}
|
||||
|
||||
public void markCardAsQueryFailed(String iccid) {
|
||||
trafficCardMapper.updateQueryStatus(iccid, TrafficCard.QUERY_STATUS_OTHER_ERROR);
|
||||
}
|
||||
|
||||
private void createNewMapping(String deviceId, String iccid) {
|
||||
TrafficDeviceMapping newMapping = new TrafficDeviceMapping();
|
||||
newMapping.setDeviceid(deviceId);
|
||||
newMapping.setIccid(iccid);
|
||||
newMapping.setStartTime(new Date());
|
||||
newMapping.setEndTime(null);
|
||||
mappingMapper.insert(newMapping);
|
||||
}
|
||||
|
||||
private void closeExistingMapping(TrafficDeviceMapping mapping) {
|
||||
mappingMapper.endMapping(mapping.getId());
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
package com.imdroid.beidou_ehm.task;
|
||||
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.imdroid.beidou_ehm.service.WarningService;
|
||||
import com.imdroid.beidou_ehm.simcard.*;
|
||||
import com.imdroid.common.util.ThreadManager;
|
||||
@ -13,8 +12,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -24,29 +21,38 @@ import java.util.concurrent.TimeUnit;
|
||||
public class SimStatusChecker {
|
||||
// SIM 卡流量和状态查询定时任务
|
||||
// 1. 每小时任务
|
||||
// a. 通过CardInfo 检查 iccid 和 sim卡号
|
||||
// b. 通过QueryCardStatus 检查SIM卡当前的状态
|
||||
// a. 通过 CardInfo 检查 iccid 和 sim卡号
|
||||
// b. 通过 QueryCardStatus 检查SIM卡当前的状态
|
||||
// 2. 每两小时任务
|
||||
// a. QueryGprs 检查 SIM 流量
|
||||
final Logger logger = LoggerFactory.getLogger(SimStatusChecker.class);
|
||||
|
||||
@Autowired
|
||||
private GnssStatusMapper gnssStatusMapper;
|
||||
|
||||
@Autowired
|
||||
private SimCardsMapper simCardsMapper;
|
||||
private GnssDeviceMapper deviceMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficCardMapper trafficCardMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficRecordMapper trafficRecordMapper;
|
||||
|
||||
@Autowired
|
||||
private WarningService warningService;
|
||||
|
||||
@Autowired
|
||||
private TrafficCardService trafficCardService;
|
||||
|
||||
@Autowired
|
||||
private SimCardQueryServiceImpl simCardQueryServiceImpl;
|
||||
@Autowired
|
||||
WarningService warningService;
|
||||
|
||||
// 每小时执行一次状态检查调度
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
//@Scheduled(cron = "0 */10 * * * ?")
|
||||
private void scheduleSimCardStatusCheck() {
|
||||
List<GnssStatusJoin> onlineDevices = gnssStatusMapper.queryOnline();
|
||||
logger.debug("当前在线设备数量: {}", onlineDevices.size());
|
||||
|
||||
// logger.debug("当前在线设备数量: {}", onlineDevices.size());
|
||||
for (GnssStatusJoin onlineDevice : onlineDevices) {
|
||||
int delay = Math.abs(onlineDevice.getDeviceid().hashCode() % 3600 );
|
||||
//logger.debug("- 设备: {}, SIM状态查询,延迟执行: {}秒", onlineDevice.getDeviceid(), delay);
|
||||
@ -79,13 +85,23 @@ public class SimStatusChecker {
|
||||
|
||||
private void checkDeviceSimCardStatus(GnssStatusJoin device) {
|
||||
try {
|
||||
// 不允许尚未从自检得到 ICCID 的设备参加 SIM 卡状态查询
|
||||
if (!simCardQueryServiceImpl.hasValidIccid(device)) {
|
||||
return;
|
||||
}
|
||||
// SimCards 表中没有数据就初始化,这其实说明它在自检中刚获得属于自己的 ICCID 号
|
||||
SimCard simCard = simCardQueryServiceImpl.CreateOrUpdateSimCard(device);
|
||||
updateSimCardInfo(device, simCard);
|
||||
boolean iccidChanged = trafficCardService.checkAndHandleICCIDChanges(device);
|
||||
if (iccidChanged) {
|
||||
logger.info("设备 {} ICCID变更已处理: {}", device.getDeviceid(), device.getIccid());
|
||||
}
|
||||
|
||||
TrafficCard trafficCard = trafficCardService.getOrCreateTrafficCard(device.getIccid());
|
||||
|
||||
// 如果 MSISDN 为空,说明是首次创建的记录,要先去查询基本信息
|
||||
if (trafficCard.getMsisdn() == null || trafficCard.getMsisdn().isEmpty()) {
|
||||
updateTrafficCardBasicInfoFromAPI(device, trafficCard);
|
||||
}
|
||||
|
||||
// 查询SIM卡状态
|
||||
updateTrafficCardStatusFromAPI(device, trafficCard);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("设备{}状态查询失败: ", device.getDeviceid(), e);
|
||||
@ -94,42 +110,63 @@ public class SimStatusChecker {
|
||||
|
||||
private void checkDeviceSimCardTraffic(GnssStatusJoin device) {
|
||||
try {
|
||||
// 不允许尚未从自检得到 ICCID 的设备参加 SIM 卡状态查询
|
||||
if (!simCardQueryServiceImpl.hasValidIccid(device)) {
|
||||
return;
|
||||
}
|
||||
// SimCards 表中没有数据就初始化,这说明它在自检中获得属于自己的 ICCID 号
|
||||
SimCard simCard = simCardQueryServiceImpl.CreateOrUpdateSimCard(device);
|
||||
// 如果该卡状态不是已激活,而是其他状态,那么不运行它参与 SIM 卡流量检测
|
||||
if(simCard.getStatus() != SimCard.STATUS_ACTIVATED){
|
||||
|
||||
TrafficCard trafficCard = trafficCardService.getOrCreateTrafficCard(device.getIccid());
|
||||
|
||||
// 检查查询状态,如果已标记为非当前卡商或查询失败,则跳过
|
||||
if (trafficCard.getQueryStatus() != null &&
|
||||
trafficCard.getQueryStatus() != TrafficCard.QUERY_STATUS_NORMAL) {
|
||||
logger.debug("设备 {} 的SIM卡 {} 查询状态为 {},跳过流量查询",
|
||||
device.getDeviceid(),
|
||||
device.getIccid(),
|
||||
trafficCard.getQueryStatus());
|
||||
return;
|
||||
}
|
||||
updateSimCardTrafficFromAPI(device, simCard);
|
||||
|
||||
// 如果该卡状态不是已激活,跳过流量查询
|
||||
if(trafficCard.getStatus() != TrafficCard.STATUS_ACTIVATED) {
|
||||
logger.debug("设备 {} 的SIM卡 {} 状态为 {},跳过流量查询",
|
||||
device.getDeviceid(),
|
||||
device.getIccid(),
|
||||
trafficCard.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询流量信息
|
||||
try {
|
||||
updateTrafficCardTrafficFromAPI(device, trafficCard);
|
||||
} catch (Exception e) {
|
||||
// 如果查询失败,标记为查询失败状态
|
||||
if (e.getMessage() != null && e.getMessage().contains("不属于当前卡商")) {
|
||||
trafficCardService.markCardAsNotCurrentVendor(device.getIccid());
|
||||
} else {
|
||||
trafficCardService.markCardAsQueryFailed(device.getIccid());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("设备{}查询失败: ", device.getDeviceid(), e);
|
||||
logger.error("设备{}流量查询失败: ", device.getDeviceid(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSimCardInfo(GnssStatusJoin device, SimCard simCard) throws Exception {
|
||||
// 自检中只是获取保存了设备的 ICCID ,所有如果判断如果没有 MSISDN,先更新基本信息
|
||||
if (StringUtils.isBlank(simCard.getMsisdn())) {
|
||||
updateSimCardBasicInfoFromAPI(device, simCard);
|
||||
}
|
||||
// 更新状态
|
||||
updateSimCardStatusFromAPI(device, simCard);
|
||||
}
|
||||
private void updateSimCardBasicInfoFromAPI(GnssStatusJoin device, SimCard simCard) {
|
||||
private void updateTrafficCardBasicInfoFromAPI(GnssStatusJoin device, TrafficCard trafficCard) {
|
||||
try {
|
||||
BaseResponse<CardInfoData> response = simCardQueryServiceImpl.queryCardInfo(device);
|
||||
if (!simCardQueryServiceImpl.isValidResponse(response)) {
|
||||
if (response == null || response.getStatus() != 1 || response.getData() == null) {
|
||||
logger.warn("设备 {} 的SIM卡基本信息查询失败: {}",
|
||||
device.getDeviceid(),
|
||||
response != null ? response.getMessage() : "无响应");
|
||||
return;
|
||||
}
|
||||
|
||||
CardInfoData info = response.getData();
|
||||
simCard.setUpdatetime(new Date());
|
||||
simCard.setMsisdn(info.getMsisdn());
|
||||
simCardsMapper.updateSimCardInfo(simCard);
|
||||
trafficCard.setUpdateTime(new Date());
|
||||
trafficCard.setMsisdn(info.getMsisdn());
|
||||
trafficCardMapper.updateCardInfo(trafficCard);
|
||||
|
||||
logger.debug("更新SIM卡基本信息 - imsi: {}, msisdn: {}, iccid: {}",
|
||||
info.getImsi(), info.getMsisdn(), info.getIccid());
|
||||
@ -138,31 +175,38 @@ public class SimStatusChecker {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
private void updateSimCardStatusFromAPI(GnssStatusJoin device, SimCard simCard) {
|
||||
|
||||
private void updateTrafficCardStatusFromAPI(GnssStatusJoin device, TrafficCard trafficCard) {
|
||||
try {
|
||||
BaseResponse<CardStatusData> response = simCardQueryServiceImpl.queryCardStatus(device);
|
||||
if (!simCardQueryServiceImpl.isValidResponse(response)) {
|
||||
if (response == null || response.getStatus() != 1 || response.getData() == null) {
|
||||
logger.warn("设备 {} 的SIM卡状态查询失败: {}",
|
||||
device.getDeviceid(),
|
||||
response != null ? response.getMessage() : "无响应");
|
||||
return;
|
||||
}
|
||||
CardStatusData status = response.getData();
|
||||
simCard.setUpdatetime(new Date());
|
||||
simCard.setStatus(status.getStatusCode());
|
||||
simCardsMapper.updateCardStatusInfo(simCard);
|
||||
|
||||
checkSimCardStatus(device, simCard);
|
||||
CardStatusData status = response.getData();
|
||||
trafficCard.setUpdateTime(new Date());
|
||||
trafficCard.setStatus(status.getStatusCode());
|
||||
trafficCardMapper.updateCardInfo(trafficCard);
|
||||
|
||||
checkTrafficCardStatus(device, trafficCard);
|
||||
|
||||
logger.debug("更新SIM卡状态 - Code: {}, 描述: {}",
|
||||
status.getStatusCode(), status.getStatusDesc());
|
||||
} catch (Exception e) {
|
||||
logger.error("更新设备{}的SIM卡状态失败: ", device.getDeviceid(), e);
|
||||
// throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSimCardTrafficFromAPI(GnssStatusJoin device, SimCard simCard) {
|
||||
private void updateTrafficCardTrafficFromAPI(GnssStatusJoin device, TrafficCard trafficCard) {
|
||||
try {
|
||||
BaseResponse<GprsData> response = simCardQueryServiceImpl.queryGprs(device);
|
||||
if (!simCardQueryServiceImpl.isValidResponse(response)) {
|
||||
if (response == null || response.getStatus() != 1 || response.getData() == null) {
|
||||
logger.warn("设备 {} 的SIM卡流量查询失败: {}",
|
||||
device.getDeviceid(),
|
||||
response != null ? response.getMessage() : "无响应");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -173,43 +217,55 @@ public class SimStatusChecker {
|
||||
return;
|
||||
}
|
||||
|
||||
simCard.setUpdatetime(new Date());
|
||||
simCard.setRemaining(BigDecimal.valueOf(usage.getLeft()));
|
||||
simCard.setUsed(BigDecimal.valueOf(usage.getUsed()));
|
||||
simCard.setTotal(BigDecimal.valueOf(usage.getTotal()));
|
||||
simCardsMapper.updateCardTrafficInfo(simCard);
|
||||
trafficCard.setUpdateTime(new Date());
|
||||
// 将浮点数转换为整数存储(MB值×1000)
|
||||
trafficCard.setRemaining((int)(usage.getLeft() * 1000));
|
||||
trafficCard.setUsed((int)(usage.getUsed() * 1000));
|
||||
trafficCard.setTotal((int)(usage.getTotal() * 1000));
|
||||
|
||||
checkSimCardTraffic(device, simCard);
|
||||
trafficCardMapper.updateCardTrafficInfo(trafficCard);
|
||||
|
||||
TrafficRecord record = new TrafficRecord();
|
||||
record.setIccid(trafficCard.getIccid());
|
||||
record.setRecordTime(new Date());
|
||||
record.setRemaining(trafficCard.getRemaining());
|
||||
record.setTotal(trafficCard.getTotal());
|
||||
record.setUsed(trafficCard.getUsed());
|
||||
trafficRecordMapper.insert(record);
|
||||
|
||||
checkTrafficCardTraffic(device, trafficCard);
|
||||
|
||||
logger.debug("更新流量信息成功 - deviceId: {}, 剩余: {}MB, 总量: {}MB, 已用: {}MB",
|
||||
device.getIccid(),
|
||||
simCard.getRemaining(),
|
||||
simCard.getTotal(),
|
||||
simCard.getUsed());
|
||||
trafficCard.getRemaining() / 1000.0,
|
||||
trafficCard.getTotal() / 1000.0,
|
||||
trafficCard.getUsed() / 1000.0);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("设备{}更新SIM卡流量失败: ", device.getDeviceid(), e);
|
||||
//throw e;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSimCardTraffic(GnssStatusJoin device, SimCard simCard) {
|
||||
public void checkTrafficCardTraffic(GnssStatusJoin device, TrafficCard trafficCard) {
|
||||
GnssStatus status = gnssStatusMapper.getByDeviceId(device.getDeviceid());
|
||||
if (status == null) return;
|
||||
|
||||
boolean isUpdated = false;
|
||||
|
||||
BigDecimal usedPercentage = simCard.getUsed()
|
||||
.divide(simCard.getTotal(), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
// 计算流量使用百分比
|
||||
int usedPercentage = 0;
|
||||
if (trafficCard.getTotal() > 0) {
|
||||
usedPercentage = (int)((trafficCard.getUsed() * 100.0) / trafficCard.getTotal());
|
||||
}
|
||||
|
||||
// 检查流量使用情况
|
||||
if (warningService.check(status, WarningCfg.TYPE_SIM_LOW_TRAFFIC,
|
||||
WarningCfg.TYPE_NAME_SIM_LOW_TRAFFIC,
|
||||
false, // 大于等于流量门限值,那么就报警
|
||||
usedPercentage.intValue(),
|
||||
usedPercentage,
|
||||
null,
|
||||
String.format("流量已使用 %.2f%%", usedPercentage.doubleValue()))) {
|
||||
String.format("流量已使用 %d%%", usedPercentage))) {
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
@ -219,27 +275,26 @@ public class SimStatusChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查SIM卡状态
|
||||
public void checkSimCardStatus(GnssStatusJoin device, SimCard simCard) {
|
||||
public void checkTrafficCardStatus(GnssStatusJoin device, TrafficCard trafficCard) {
|
||||
GnssStatus status = gnssStatusMapper.getByDeviceId(device.getDeviceid());
|
||||
if (status == null) return;
|
||||
|
||||
boolean isUpdated = false;
|
||||
|
||||
// 检查SIM卡状态是否异常(停机、注销、失效)
|
||||
if (simCard.getStatus() == SimCard.STATUS_SUSPENDED ||
|
||||
simCard.getStatus() == SimCard.STATUS_CANCELLED ||
|
||||
simCard.getStatus() == SimCard.STATUS_INVALID) {
|
||||
if (trafficCard.getStatus() == TrafficCard.STATUS_SUSPENDED ||
|
||||
trafficCard.getStatus() == TrafficCard.STATUS_CANCELLED ||
|
||||
trafficCard.getStatus() == TrafficCard.STATUS_INVALID) {
|
||||
|
||||
String statusDesc;
|
||||
switch(simCard.getStatus()) {
|
||||
case SimCard.STATUS_SUSPENDED:
|
||||
switch(trafficCard.getStatus()) {
|
||||
case TrafficCard.STATUS_SUSPENDED:
|
||||
statusDesc = "停机";
|
||||
break;
|
||||
case SimCard.STATUS_CANCELLED:
|
||||
case TrafficCard.STATUS_CANCELLED:
|
||||
statusDesc = "注销";
|
||||
break;
|
||||
case SimCard.STATUS_INVALID:
|
||||
case TrafficCard.STATUS_INVALID:
|
||||
statusDesc = "失效";
|
||||
break;
|
||||
default:
|
||||
@ -248,12 +303,13 @@ public class SimStatusChecker {
|
||||
|
||||
if (warningService.check(status, WarningCfg.TYPE_SIM_STATUS_ABNORMAL,
|
||||
WarningCfg.TYPE_NAME_SIM_STATUS_ABNORMAL, false,
|
||||
simCard.getStatus(), null,
|
||||
"SIM卡状态: " + statusDesc)) {
|
||||
trafficCard.getStatus(),
|
||||
null,
|
||||
"SIM卡状态异常: " + statusDesc)) {
|
||||
isUpdated = true;
|
||||
}
|
||||
} else if (simCard.getStatus() == SimCard.STATUS_ACTIVATED) {
|
||||
// 状态正常(已激活),清除告警
|
||||
} else {
|
||||
// 清除状态异常告警
|
||||
if ((status.getWarningcode() & WarningCfg.TYPE_SIM_STATUS_ABNORMAL) != 0) {
|
||||
warningService.clearWarning(status, WarningCfg.TYPE_SIM_STATUS_ABNORMAL);
|
||||
isUpdated = true;
|
||||
@ -265,5 +321,4 @@ public class SimStatusChecker {
|
||||
gnssStatusMapper.updateById(status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,4 +18,8 @@ app.format.date = yyyy-MM-dd
|
||||
app.format.time = HH:mm:ss
|
||||
app.format.datetime = yyyy-MM-dd HH:mm:ss
|
||||
|
||||
sim.url = http://120.78.169.220:8089
|
||||
sim.username = gzyzdz
|
||||
sim.key = 632629d1269a202c9d49a574623e4e4c
|
||||
|
||||
mybatis-plus.configuration.map-underscore-to-camel-case=false
|
||||
|
||||
@ -30,6 +30,8 @@ public class APIController extends BasicController{
|
||||
DeviceCacheCmdMapper cacheCmdMapper;
|
||||
@Autowired
|
||||
WarningMsgMapper warningMsgMapper;
|
||||
@Autowired
|
||||
GnssStatusMapper gnssStatusMapper;
|
||||
|
||||
/****** config ack *******/
|
||||
@PostMapping(value = "/api/config_ack")
|
||||
@ -151,6 +153,7 @@ public class APIController extends BasicController{
|
||||
// 保存
|
||||
saveMsg(deviceId, tenantId,0xD312, uploadCmd, true);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -189,10 +192,8 @@ public class APIController extends BasicController{
|
||||
// 保存
|
||||
saveMsg(deviceId, tenantId,cacheCmd.getMsgtype(), cacheCmd.getCmd(), true);
|
||||
}
|
||||
|
||||
// 检查iccid
|
||||
// 设备上线后,检查是否需要重新查询更新 ICCID
|
||||
checkAndAskICCID(device);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -244,8 +245,20 @@ public class APIController extends BasicController{
|
||||
msgMapper.insert(gnssMsg);
|
||||
}
|
||||
|
||||
void checkAndAskICCID(GnssDevice device){
|
||||
if(device.getIccid() == null || device.getIccid().trim().isEmpty()) {
|
||||
void checkAndAskICCID(GnssDevice device) {
|
||||
GnssStatus status = gnssStatusMapper.getByDeviceId(device.getDeviceid());
|
||||
|
||||
// 1. 检查设备是否有 ICCID记录 (初始化)
|
||||
// 2. 检查设备是否从离线恢复 (防止更换 SIM 卡)
|
||||
// 3. 如果两种情况都不满足,则不查询 ICCID
|
||||
|
||||
boolean isDeviceReconnecting = status != null && status.getState() == GnssStatus.STATE_OFFLINE;
|
||||
boolean isIccidEmpty = device.getIccid() == null || device.getIccid().trim().isEmpty();
|
||||
|
||||
if (!isDeviceReconnecting && !isIccidEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sendCmd = "AT+ICCID";
|
||||
int msgType = 0xD310 + 10; // DTU
|
||||
short len = (short) (sendCmd.length() + 5);
|
||||
@ -254,7 +267,6 @@ public class APIController extends BasicController{
|
||||
"01" + HexUtil.String2HexString(sendCmd);
|
||||
rtcmClient.config(device.getDeviceid(), sendCmd);
|
||||
}
|
||||
}
|
||||
void updateICCID(GnssDevice device, String dtuAck) {
|
||||
// 只检查 "ICCID:" 的十六进制部分
|
||||
if(!dtuAck.contains("49434349443a")){
|
||||
@ -262,7 +274,6 @@ public class APIController extends BasicController{
|
||||
}
|
||||
String content = HexUtil.HexString2String(dtuAck);
|
||||
if(content.contains("+ICCID:")){
|
||||
//System.out.println(content);
|
||||
String iccid = content.substring(content.indexOf("+ICCID:") + 8).trim();
|
||||
iccid = iccid.split("\r\n")[0].trim();
|
||||
device.setIccid(iccid);
|
||||
|
||||
@ -34,7 +34,16 @@ public class SimCardController extends BasicController {
|
||||
private String KEY;
|
||||
|
||||
@Autowired
|
||||
private SimCardsMapper simCardsMapper;
|
||||
private GnssDeviceMapper gnssDeviceMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficCardMapper trafficCardMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficDeviceMappingMapper trafficDeviceMappingMapper;
|
||||
|
||||
@Autowired
|
||||
private TrafficRecordMapper trafficRecordMapper;
|
||||
|
||||
@RequestMapping("/page/sim_status")
|
||||
public String simStatus(Model m, HttpSession session) {
|
||||
@ -43,6 +52,20 @@ public class SimCardController extends BasicController {
|
||||
return "/page/sim_status";
|
||||
}
|
||||
|
||||
@RequestMapping("/page/sim_traffic_records")
|
||||
public String simTrafficRecords(Model m, HttpSession session) {
|
||||
initModel(m, session);
|
||||
|
||||
return "/page/sim_traffic_records";
|
||||
}
|
||||
|
||||
@RequestMapping("/page/sim_device_mapping")
|
||||
public String simDeviceMapping(Model m, HttpSession session) {
|
||||
initModel(m, session);
|
||||
|
||||
return "/page/sim_device_mapping";
|
||||
}
|
||||
|
||||
@RequestMapping("/sim/list")
|
||||
@ResponseBody
|
||||
public JSONObject list(HttpSession session,
|
||||
@ -51,37 +74,98 @@ public class SimCardController extends BasicController {
|
||||
String searchType,
|
||||
String searchContent,
|
||||
Integer status) {
|
||||
Page<SimCard> pageable = new Page<>(page, limit);
|
||||
|
||||
QueryWrapper<SimCard> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
QueryWrapper<GnssDevice> deviceQueryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (!StringUtils.isEmpty(searchContent)) {
|
||||
switch(searchType) {
|
||||
case "deviceId":
|
||||
queryWrapper.like("deviceid", searchContent.trim());
|
||||
deviceQueryWrapper.like("deviceid", searchContent.trim());
|
||||
break;
|
||||
case "iccid":
|
||||
queryWrapper.like("iccid", searchContent.trim());
|
||||
deviceQueryWrapper.like("iccid", searchContent.trim());
|
||||
break;
|
||||
case "simNumber":
|
||||
queryWrapper.like("msisdn", searchContent.trim());
|
||||
// 通过SIM号查找对应的ICCID,然后查询设备
|
||||
TrafficCard cardByMsisdn = trafficCardMapper.selectOne(
|
||||
new QueryWrapper<TrafficCard>().like("msisdn", searchContent.trim())
|
||||
);
|
||||
if (cardByMsisdn != null) {
|
||||
deviceQueryWrapper.eq("iccid", cardByMsisdn.getIccid());
|
||||
} else {
|
||||
deviceQueryWrapper.eq("iccid", "");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 只查询有ICCID的设备
|
||||
deviceQueryWrapper.isNotNull("iccid");
|
||||
deviceQueryWrapper.ne("iccid", "");
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
List<TrafficCard> cardsWithStatus = trafficCardMapper.selectList(
|
||||
new QueryWrapper<TrafficCard>().eq("status", status)
|
||||
);
|
||||
if (cardsWithStatus.isEmpty()) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", 0);
|
||||
jsonObject.put("msg", "");
|
||||
jsonObject.put("count", 0);
|
||||
jsonObject.put("data", new ArrayList<>());
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("updatetime");
|
||||
IPage<SimCard> cs = simCardsMapper.selectPage(pageable, queryWrapper);
|
||||
List<String> iccids = new ArrayList<>();
|
||||
for (TrafficCard card : cardsWithStatus) {
|
||||
iccids.add(card.getIccid());
|
||||
}
|
||||
deviceQueryWrapper.in("iccid", iccids);
|
||||
}
|
||||
|
||||
deviceQueryWrapper.orderByDesc("deviceid");
|
||||
|
||||
Page<GnssDevice> pageable = new Page<>(page, limit);
|
||||
IPage<GnssDevice> devices = gnssDeviceMapper.selectPage(pageable, deviceQueryWrapper);
|
||||
|
||||
List<Map<String, Object>> enrichedData = new ArrayList<>();
|
||||
for (GnssDevice device : devices.getRecords()) {
|
||||
TrafficCard trafficCard = null;
|
||||
if (device.getIccid() != null && !device.getIccid().trim().isEmpty()) {
|
||||
trafficCard = trafficCardMapper.findByIccid(device.getIccid());
|
||||
}
|
||||
|
||||
Map<String, Object> deviceData = new HashMap<>();
|
||||
deviceData.put("id", device.getId());
|
||||
deviceData.put("deviceid", device.getDeviceid());
|
||||
deviceData.put("iccid", device.getIccid());
|
||||
|
||||
if (trafficCard != null) {
|
||||
deviceData.put("msisdn", trafficCard.getMsisdn());
|
||||
deviceData.put("status", trafficCard.getStatus());
|
||||
deviceData.put("remaining", trafficCard.getRemaining());
|
||||
deviceData.put("total", trafficCard.getTotal());
|
||||
deviceData.put("used", trafficCard.getUsed());
|
||||
deviceData.put("updateTime", trafficCard.getUpdateTime());
|
||||
deviceData.put("queryStatus", trafficCard.getQueryStatus());
|
||||
} else {
|
||||
deviceData.put("msisdn", "-");
|
||||
deviceData.put("status", TrafficCard.STATUS_UNKNOWN);
|
||||
deviceData.put("remaining", 0);
|
||||
deviceData.put("total", 0);
|
||||
deviceData.put("used", 0);
|
||||
deviceData.put("updateTime", null);
|
||||
deviceData.put("queryStatus", TrafficCard.QUERY_STATUS_NORMAL);
|
||||
}
|
||||
|
||||
enrichedData.add(deviceData);
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", 0);
|
||||
jsonObject.put("msg", "");
|
||||
jsonObject.put("count", cs.getTotal());
|
||||
jsonObject.put("data", cs.getRecords());
|
||||
System.out.println(jsonObject.toString());
|
||||
jsonObject.put("count", devices.getTotal());
|
||||
jsonObject.put("data", enrichedData);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
@ -114,11 +198,11 @@ public class SimCardController extends BasicController {
|
||||
if (content.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("设备ID不能为空");
|
||||
}
|
||||
SimCard simCard = simCardsMapper.queryByDeviceId(content.trim());
|
||||
if (simCard == null) {
|
||||
throw new IllegalArgumentException("未找到该设备ID对应的SIM卡信息");
|
||||
GnssDevice device = gnssDeviceMapper.queryByDeviceId(content.trim());
|
||||
if (device == null || device.getIccid() == null || device.getIccid().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("未找到该设备ID或设备没有ICCID信息");
|
||||
}
|
||||
params.put("card", simCard.getIccid());
|
||||
params.put("card", device.getIccid());
|
||||
break;
|
||||
case "simNumber":
|
||||
params.put("card", content);
|
||||
@ -176,6 +260,166 @@ public class SimCardController extends BasicController {
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/sim/traffic-records")
|
||||
@ResponseBody
|
||||
public JSONObject getTrafficRecords(HttpSession session,
|
||||
int page,
|
||||
int limit,
|
||||
String searchType,
|
||||
String searchContent) {
|
||||
try {
|
||||
Page<TrafficRecord> pageable = new Page<>(page, limit);
|
||||
QueryWrapper<TrafficRecord> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (!StringUtils.isEmpty(searchContent)) {
|
||||
switch(searchType) {
|
||||
case "deviceId":
|
||||
GnssDevice device = gnssDeviceMapper.queryByDeviceId(searchContent.trim());
|
||||
if (device != null && device.getIccid() != null && !device.getIccid().trim().isEmpty()) {
|
||||
queryWrapper.eq("iccid", device.getIccid());
|
||||
} else {
|
||||
queryWrapper.eq("iccid", "");
|
||||
}
|
||||
break;
|
||||
case "iccid":
|
||||
queryWrapper.like("iccid", searchContent.trim());
|
||||
break;
|
||||
case "simNumber":
|
||||
TrafficCard cardByMsisdn = trafficCardMapper.selectOne(
|
||||
new QueryWrapper<TrafficCard>().like("msisdn", searchContent.trim())
|
||||
);
|
||||
if (cardByMsisdn != null) {
|
||||
queryWrapper.eq("iccid", cardByMsisdn.getIccid());
|
||||
} else {
|
||||
queryWrapper.eq("iccid", "");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("record_time");
|
||||
IPage<TrafficRecord> records = trafficRecordMapper.selectPage(pageable, queryWrapper);
|
||||
|
||||
List<Map<String, Object>> enrichedData = new ArrayList<>();
|
||||
for (TrafficRecord record : records.getRecords()) {
|
||||
Map<String, Object> recordData = new HashMap<>();
|
||||
recordData.put("id", record.getId());
|
||||
recordData.put("iccid", record.getIccid());
|
||||
recordData.put("recordTime", record.getRecordTime());
|
||||
recordData.put("remaining", record.getRemaining());
|
||||
recordData.put("total", record.getTotal());
|
||||
recordData.put("used", record.getUsed());
|
||||
|
||||
GnssDevice device = gnssDeviceMapper.selectOne(
|
||||
new QueryWrapper<GnssDevice>().eq("iccid", record.getIccid())
|
||||
);
|
||||
if (device != null) {
|
||||
recordData.put("deviceid", device.getDeviceid());
|
||||
} else {
|
||||
recordData.put("deviceid", "无绑定设备");
|
||||
}
|
||||
|
||||
TrafficCard trafficCard = trafficCardMapper.findByIccid(record.getIccid());
|
||||
if (trafficCard != null) {
|
||||
recordData.put("msisdn", trafficCard.getMsisdn());
|
||||
} else {
|
||||
recordData.put("msisdn", "-");
|
||||
}
|
||||
|
||||
enrichedData.add(recordData);
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", 0);
|
||||
jsonObject.put("msg", "");
|
||||
jsonObject.put("count", records.getTotal());
|
||||
jsonObject.put("data", enrichedData);
|
||||
return jsonObject;
|
||||
|
||||
} catch (Exception e) {
|
||||
JSONObject errorResponse = new JSONObject();
|
||||
errorResponse.put("code", 1);
|
||||
errorResponse.put("msg", "查询失败: " + e.getMessage());
|
||||
errorResponse.put("count", 0);
|
||||
errorResponse.put("data", new ArrayList<>());
|
||||
return errorResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/sim/device-mapping")
|
||||
@ResponseBody
|
||||
public JSONObject getDeviceMapping(HttpSession session,
|
||||
int page,
|
||||
int limit,
|
||||
String searchType,
|
||||
String searchContent) {
|
||||
try {
|
||||
Page<TrafficDeviceMapping> pageable = new Page<>(page, limit);
|
||||
QueryWrapper<TrafficDeviceMapping> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (!StringUtils.isEmpty(searchContent)) {
|
||||
switch(searchType) {
|
||||
case "deviceId":
|
||||
queryWrapper.like("deviceid", searchContent.trim());
|
||||
break;
|
||||
case "iccid":
|
||||
queryWrapper.like("iccid", searchContent.trim());
|
||||
break;
|
||||
case "simNumber":
|
||||
TrafficCard cardByMsisdn = trafficCardMapper.selectOne(
|
||||
new QueryWrapper<TrafficCard>().like("msisdn", searchContent.trim())
|
||||
);
|
||||
if (cardByMsisdn != null) {
|
||||
queryWrapper.eq("iccid", cardByMsisdn.getIccid());
|
||||
} else {
|
||||
queryWrapper.eq("iccid", "");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("start_time");
|
||||
IPage<TrafficDeviceMapping> mappings = trafficDeviceMappingMapper.selectPage(pageable, queryWrapper);
|
||||
|
||||
List<Map<String, Object>> enrichedData = new ArrayList<>();
|
||||
for (TrafficDeviceMapping mapping : mappings.getRecords()) {
|
||||
Map<String, Object> mappingData = new HashMap<>();
|
||||
mappingData.put("id", mapping.getId());
|
||||
mappingData.put("deviceId", mapping.getDeviceid());
|
||||
mappingData.put("iccid", mapping.getIccid());
|
||||
mappingData.put("startTime", mapping.getStartTime());
|
||||
mappingData.put("endTime", mapping.getEndTime());
|
||||
|
||||
mappingData.put("isActive", mapping.getEndTime() == null);
|
||||
|
||||
// 查找对应的流量卡信息
|
||||
TrafficCard trafficCard = trafficCardMapper.findByIccid(mapping.getIccid());
|
||||
if (trafficCard != null) {
|
||||
mappingData.put("msisdn", trafficCard.getMsisdn());
|
||||
} else {
|
||||
mappingData.put("msisdn", "-");
|
||||
}
|
||||
|
||||
enrichedData.add(mappingData);
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", 0);
|
||||
jsonObject.put("msg", "");
|
||||
jsonObject.put("count", mappings.getTotal());
|
||||
jsonObject.put("data", enrichedData);
|
||||
return jsonObject;
|
||||
|
||||
} catch (Exception e) {
|
||||
JSONObject errorResponse = new JSONObject();
|
||||
errorResponse.put("code", 1);
|
||||
errorResponse.put("msg", "查询失败: " + e.getMessage());
|
||||
errorResponse.put("count", 0);
|
||||
errorResponse.put("data", new ArrayList<>());
|
||||
return errorResponse;
|
||||
}
|
||||
}
|
||||
|
||||
private String calculateSignature(Map<String, String> params) {
|
||||
try {
|
||||
List<String> paramList = new ArrayList<>();
|
||||
|
||||
@ -396,3 +396,52 @@ CREATE TABLE IF NOT EXISTS `ehmconfig` (
|
||||
`calcstathours` int DEFAULT NULL COMMENT '数据分析周期',
|
||||
PRIMARY KEY (`updatetime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
/***
|
||||
流量卡信息表
|
||||
*/
|
||||
CREATE TABLE IF NOT EXISTS `traffic_cards` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,唯一',
|
||||
`iccid` VARCHAR(20) NOT NULL COMMENT 'ICCID号,唯一标识SIM卡',
|
||||
`msisdn` VARCHAR(20) NOT NULL COMMENT '物联卡号码',
|
||||
`status` INT DEFAULT -1 COMMENT 'SIM卡状态(-1:未知, 1:待激活, 2:已激活, 3:停机, 4:注销, 5:库存, 6:可测试, 7:失效, 99:号码不存在)',
|
||||
`remaining` INT DEFAULT 0 COMMENT '剩余流量,单位MB,为避免小数,存储值为实际值的1000倍',
|
||||
`total` INT DEFAULT 0 COMMENT '总流量,单位MB,同上',
|
||||
`used` INT DEFAULT 0 COMMENT '已用流量,单位MB,同上',
|
||||
`update_time` DATETIME DEFAULT NULL COMMENT '最后更新时间',
|
||||
`query_status` TINYINT DEFAULT 0 COMMENT 'SIM卡查询状态:0=正常,1=非当前卡商,2=其他错误',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_iccid` (`iccid`),
|
||||
INDEX `idx_status` (`status`),
|
||||
INDEX `idx_query_status` (`query_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流量卡信息表';
|
||||
|
||||
/***
|
||||
设备-SIM卡映射表
|
||||
*/
|
||||
CREATE TABLE IF NOT EXISTS `traffic_device_mappings` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,唯一',
|
||||
`deviceid` VARCHAR(20) NOT NULL COMMENT '设备ID',
|
||||
`iccid` VARCHAR(20) NOT NULL COMMENT 'SIM卡ICCID',
|
||||
`start_time` DATETIME NOT NULL COMMENT '开始使用时间',
|
||||
`end_time` DATETIME DEFAULT NULL COMMENT '结束使用时间,NULL表示当前正在使用',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_deviceid` (`deviceid`),
|
||||
KEY `idx_iccid` (`iccid`),
|
||||
KEY `idx_start_time` (`start_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备-SIM卡映射表';
|
||||
|
||||
/***
|
||||
流量使用记录表
|
||||
*/
|
||||
CREATE TABLE IF NOT EXISTS `traffic_records` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,唯一',
|
||||
`iccid` VARCHAR(20) NOT NULL COMMENT 'SIM卡ICCID',
|
||||
`record_time` DATETIME NOT NULL COMMENT '记录时间',
|
||||
`remaining` INT DEFAULT NULL COMMENT '剩余流量,单位MB,为避免小数,存储值为实际值的1000倍',
|
||||
`used` INT DEFAULT NULL COMMENT '已用流量,单位MB,为避免小数,存储值为实际值的1000倍',
|
||||
`total` INT DEFAULT NULL COMMENT '总流量,单位MB,为避免小数,存储值为实际值的1000倍',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_iccid` (`iccid`),
|
||||
KEY `idx_record_time` (`record_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流量使用记录表';
|
||||
|
||||
@ -130,7 +130,19 @@
|
||||
"target": "_self"
|
||||
},
|
||||
{
|
||||
"title": "卡信息查询",
|
||||
"title": "流量使用记录",
|
||||
"href": "page/sim_traffic_records",
|
||||
"icon": "fa fa-minus",
|
||||
"target": "_self"
|
||||
},
|
||||
{
|
||||
"title": "设备映射记录",
|
||||
"href": "page/sim_device_mapping",
|
||||
"icon": "fa fa-minus",
|
||||
"target": "_self"
|
||||
},
|
||||
{
|
||||
"title": "卡信息聚合查询",
|
||||
"href": "page/sim_traffic_query",
|
||||
"icon": "fa fa-minus",
|
||||
"target": "_self"
|
||||
|
||||
@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>设备SIM卡映射记录</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="stylesheet" href="../lib/layui-v2.6.3/css/layui.css" media="all">
|
||||
<link rel="stylesheet" href="../css/public.css" media="all">
|
||||
</head>
|
||||
<body>
|
||||
<div class="layuimini-container">
|
||||
<div class="layuimini-main">
|
||||
<fieldset class="table-search-fieldset">
|
||||
<legend>搜索信息</legend>
|
||||
<div style="margin: 10px 10px 10px 10px">
|
||||
<form class="layui-form layui-form-pane" action="" id="searchForm">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">查询类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="searchType" lay-verify="required">
|
||||
<option value="deviceId">设备号</option>
|
||||
<option value="iccid">ICCID</option>
|
||||
<option value="simNumber">SIM卡号</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">搜索内容</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="searchContent" id="searchInput" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="searchSubmit"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="layui-tab layui-tab-card" lay-filter="data-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">设备映射记录</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<table class="layui-hide" id="deviceMappingTable" lay-filter="deviceMappingFilter"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="statusTpl">
|
||||
{{# if(d.isActive){ }}
|
||||
<span class="layui-badge layui-bg-green">使用中</span>
|
||||
{{# } else { }}
|
||||
<span class="layui-badge">已结束</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
|
||||
<script th:inline="javascript">
|
||||
layui.use(['form', 'table','layer','element'], function () {
|
||||
var table = layui.table
|
||||
,form = layui.form
|
||||
,layer = layui.layer
|
||||
,element = layui.element;
|
||||
|
||||
var data_cols = [
|
||||
{field: 'deviceId', title: '设备号'},
|
||||
{field: 'iccid', title: 'ICCID'},
|
||||
{field: 'msisdn', title: 'SIM卡号'},
|
||||
{field: 'startTime', title: '开始时间', templet: "<div>{{layui.util.toDateString(d.startTime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'endTime', title: '结束时间', templet: function(d) {
|
||||
return d.endTime ? layui.util.toDateString(d.endTime, 'yyyy-MM-dd HH:mm:ss') : '-';
|
||||
}},
|
||||
{field: 'isActive', title: '状态', templet: '#statusTpl'},
|
||||
{field: 'duration', title: '使用时长', templet: function(d) {
|
||||
if (!d.startTime) return '-';
|
||||
|
||||
var start = new Date(d.startTime);
|
||||
var end = d.endTime ? new Date(d.endTime) : new Date();
|
||||
var diffMs = end - start;
|
||||
|
||||
if (diffMs < 0) return '-';
|
||||
|
||||
var days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||
var hours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
|
||||
if (days > 0) {
|
||||
return days + '天' + hours + '小时';
|
||||
} else if (hours > 0) {
|
||||
return hours + '小时';
|
||||
} else {
|
||||
var minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
return minutes + '分钟';
|
||||
}
|
||||
}}
|
||||
];
|
||||
|
||||
form.render();
|
||||
table.render({
|
||||
elem: '#deviceMappingTable',
|
||||
url: '/sim/device-mapping',
|
||||
defaultToolbar: ['filter'],
|
||||
cols: [
|
||||
data_cols
|
||||
],
|
||||
limits: [20, 50, 100, 200, 300],
|
||||
limit: 20,
|
||||
page: true,
|
||||
skin: 'line',
|
||||
done: function(res) {
|
||||
if(res.code !== 0) {
|
||||
layer.msg(res.msg || '查询失败', {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.verify({
|
||||
searchContent: function(value, item) {
|
||||
var type = $('select[name="searchType"]').val();
|
||||
if(!value) {
|
||||
return '请输入搜索内容';
|
||||
}
|
||||
if(type === 'iccid' && !/^\d{19,20}$/.test(value)) {
|
||||
return 'ICCID必须是19-20位数字';
|
||||
}
|
||||
if(type === 'simNumber' && !/^\d{11,13}$/.test(value)) {
|
||||
return 'SIM卡号必须是11-13位数字';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(searchSubmit)', function(data){
|
||||
var loadIndex = layer.load(1);
|
||||
table.reload('deviceMappingTable', {
|
||||
page: {
|
||||
curr: 1
|
||||
}
|
||||
,where: {
|
||||
searchType: data.field.searchType,
|
||||
searchContent: data.field.searchContent
|
||||
}
|
||||
,done: function(res) {
|
||||
layer.close(loadIndex);
|
||||
if(res.code !== 0) {
|
||||
layer.msg(res.msg || '查询失败', {icon: 2});
|
||||
} else if(res.count === 0) {
|
||||
layer.msg('未找到符合条件的数据', {icon: 0});
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -110,9 +110,9 @@
|
||||
function updateFlowChart(simData) {
|
||||
initEcharts();
|
||||
|
||||
var used = parseFloat(simData.used) || 0;
|
||||
var remaining = parseFloat(simData.remaining) || 0;
|
||||
var total = parseFloat(simData.total) || (used + remaining);
|
||||
var used = simData.used ? (simData.used / 1000) : 0;
|
||||
var remaining = simData.remaining ? (simData.remaining / 1000) : 0;
|
||||
var total = simData.total ? (simData.total / 1000) : (used + remaining);
|
||||
|
||||
var option = {
|
||||
title: {
|
||||
@ -191,11 +191,17 @@
|
||||
{field: 'deviceid', title: '设备号'},
|
||||
{field: 'iccid', title: 'ICCID'},
|
||||
{field: 'msisdn', title: 'SIM 卡号'},
|
||||
{field: 'updatetime', title: '更新时间',templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'updateTime', title: '更新时间',templet: "<div>{{layui.util.toDateString(d.updateTime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'status', title: '状态',templet: '#statusTpl'},
|
||||
{field: 'remaining', title: '剩余流量(MB)'},
|
||||
{field: 'used', title: '已使用流量(MB)'},
|
||||
{field: 'total', title: '总流量(MB)'}
|
||||
{field: 'remaining', title: '剩余流量(MB)', templet: function(d) {
|
||||
return d.remaining ? (d.remaining / 1000).toFixed(2) : '0';
|
||||
}},
|
||||
{field: 'used', title: '已使用流量(MB)', templet: function(d) {
|
||||
return d.used ? (d.used / 1000).toFixed(2) : '0';
|
||||
}},
|
||||
{field: 'total', title: '总流量(MB)', templet: function(d) {
|
||||
return d.total ? (d.total / 1000).toFixed(2) : '0';
|
||||
}}
|
||||
|
||||
];
|
||||
|
||||
|
||||
@ -0,0 +1,140 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>SIM卡流量使用记录</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="stylesheet" href="../lib/layui-v2.6.3/css/layui.css" media="all">
|
||||
<link rel="stylesheet" href="../css/public.css" media="all">
|
||||
</head>
|
||||
<body>
|
||||
<div class="layuimini-container">
|
||||
<div class="layuimini-main">
|
||||
<fieldset class="table-search-fieldset">
|
||||
<legend>搜索信息</legend>
|
||||
<div style="margin: 10px 10px 10px 10px">
|
||||
<form class="layui-form layui-form-pane" action="" id="searchForm">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">查询类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="searchType" lay-verify="required">
|
||||
<option value="deviceId">设备号</option>
|
||||
<option value="iccid">ICCID</option>
|
||||
<option value="simNumber">SIM卡号</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">搜索内容</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="searchContent" id="searchInput" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="searchSubmit"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="layui-tab layui-tab-card" lay-filter="data-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">流量使用记录</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<table class="layui-hide" id="trafficRecordsTable" lay-filter="trafficRecordsFilter"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
|
||||
<script th:inline="javascript">
|
||||
layui.use(['form', 'table','layer','element'], function () {
|
||||
var table = layui.table
|
||||
,form = layui.form
|
||||
,layer = layui.layer
|
||||
,element = layui.element;
|
||||
|
||||
var data_cols = [
|
||||
{field: 'deviceid', title: '设备号'},
|
||||
{field: 'iccid', title: 'ICCID'},
|
||||
{field: 'msisdn', title: 'SIM卡号'},
|
||||
{field: 'recordTime', title: '记录时间', templet: "<div>{{layui.util.toDateString(d.recordTime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'remaining', title: '剩余流量(MB)', templet: function(d) {
|
||||
return d.remaining ? (d.remaining / 1000).toFixed(2) : '0';
|
||||
}},
|
||||
{field: 'used', title: '已使用流量(MB)', templet: function(d) {
|
||||
return d.used ? (d.used / 1000).toFixed(2) : '0';
|
||||
}},
|
||||
{field: 'total', title: '总流量(MB)', templet: function(d) {
|
||||
return d.total ? (d.total / 1000).toFixed(2) : '0';
|
||||
}}
|
||||
];
|
||||
|
||||
form.render();
|
||||
table.render({
|
||||
elem: '#trafficRecordsTable',
|
||||
url: '/sim/traffic-records',
|
||||
defaultToolbar: ['filter'],
|
||||
cols: [
|
||||
data_cols
|
||||
],
|
||||
limits: [20, 50, 100, 200, 300],
|
||||
limit: 20,
|
||||
page: true,
|
||||
skin: 'line',
|
||||
done: function(res) {
|
||||
if(res.code !== 0) {
|
||||
layer.msg(res.msg || '查询失败', {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.verify({
|
||||
searchContent: function(value, item) {
|
||||
var type = $('select[name="searchType"]').val();
|
||||
if(!value) {
|
||||
return '请输入搜索内容';
|
||||
}
|
||||
if(type === 'iccid' && !/^\d{19,20}$/.test(value)) {
|
||||
return 'ICCID必须是19-20位数字';
|
||||
}
|
||||
if(type === 'simNumber' && !/^\d{11,13}$/.test(value)) {
|
||||
return 'SIM卡号必须是11-13位数字';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(searchSubmit)', function(data){
|
||||
var loadIndex = layer.load(1);
|
||||
table.reload('trafficRecordsTable', {
|
||||
page: {
|
||||
curr: 1
|
||||
}
|
||||
,where: {
|
||||
searchType: data.field.searchType,
|
||||
searchContent: data.field.searchContent
|
||||
}
|
||||
,done: function(res) {
|
||||
layer.close(loadIndex);
|
||||
if(res.code !== 0) {
|
||||
layer.msg(res.msg || '查询失败', {icon: 2});
|
||||
} else if(res.count === 0) {
|
||||
layer.msg('未找到符合条件的数据', {icon: 0});
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user