推送
This commit is contained in:
14
pom.xml
14
pom.xml
@ -194,6 +194,20 @@
|
||||
<artifactId>gdal</artifactId>
|
||||
<version>3.11.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Excel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- CSV -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-csv</artifactId>
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
226
src/main/java/com/yj/earth/FastFileEncryptor.java
Normal file
226
src/main/java/com/yj/earth/FastFileEncryptor.java
Normal file
@ -0,0 +1,226 @@
|
||||
package com.yj.earth;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.*;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 文件快速加密解密工具类(仅加密文件开头部分,兼顾保护与效率)
|
||||
*/
|
||||
public class FastFileEncryptor {
|
||||
// 加密算法参数
|
||||
private static final String ALGORITHM = "AES";
|
||||
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
|
||||
private static final int KEY_SIZE = 256; // 需JCE无限制权限包
|
||||
private static final int IV_LENGTH = 16; // AES块大小
|
||||
private static final int ITERATION_COUNT = 65536;
|
||||
private static final int SALT_LENGTH = 16;
|
||||
// 只加密文件开头的1MB(可根据需求调整,建议512KB~2MB)
|
||||
private static final int ENCRYPTED_LENGTH = 1024 * 1024; // 1MB
|
||||
|
||||
|
||||
/**
|
||||
* 加密文件(仅加密开头部分)
|
||||
* @param sourceFilePath 源文件路径
|
||||
* @param encryptedFilePath 加密后文件路径
|
||||
* @param password 加密密码
|
||||
* @throws Exception 加密过程异常
|
||||
*/
|
||||
public static void encryptFile(String sourceFilePath, String encryptedFilePath, String password) throws Exception {
|
||||
File sourceFile = new File(sourceFilePath);
|
||||
File encryptedFile = new File(encryptedFilePath);
|
||||
|
||||
// 验证源文件
|
||||
if (!sourceFile.exists() || !sourceFile.isFile()) {
|
||||
throw new FileNotFoundException("源文件不存在或不是文件: " + sourceFilePath);
|
||||
}
|
||||
// 检查磁盘空间
|
||||
if (!checkDiskSpace(encryptedFilePath, sourceFile.length())) {
|
||||
throw new IOException("目标路径磁盘空间不足,至少需要 " + sourceFile.length() + " 字节");
|
||||
}
|
||||
|
||||
// 生成盐值和IV向量
|
||||
byte[] salt = generateRandomBytes(SALT_LENGTH);
|
||||
byte[] iv = generateRandomBytes(IV_LENGTH);
|
||||
SecretKey secretKey = generateSecretKey(password, salt);
|
||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
||||
|
||||
try (
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
|
||||
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(encryptedFile))
|
||||
) {
|
||||
// 写入元数据:盐值(16) + IV(16) + 加密长度(4)
|
||||
bos.write(salt);
|
||||
bos.write(iv);
|
||||
bos.write(intToBytes(ENCRYPTED_LENGTH));
|
||||
bos.flush();
|
||||
|
||||
// 加密开头指定长度的字节
|
||||
byte[] encryptBuffer = new byte[ENCRYPTED_LENGTH];
|
||||
int actualRead = bis.read(encryptBuffer); // 实际读取的字节数(可能文件小于1MB)
|
||||
if (actualRead > 0) {
|
||||
byte[] encryptedBytes = cipher.doFinal(encryptBuffer, 0, actualRead);
|
||||
bos.write(encryptedBytes);
|
||||
}
|
||||
|
||||
// 直接复制剩余未加密的字节
|
||||
byte[] copyBuffer = new byte[1024 * 1024]; // 1MB缓冲区加速复制
|
||||
int bytesRead;
|
||||
while ((bytesRead = bis.read(copyBuffer)) != -1) {
|
||||
bos.write(copyBuffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解密文件(仅解密开头部分)
|
||||
* @param encryptedFilePath 加密文件路径
|
||||
* @param decryptedFilePath 解密后文件路径
|
||||
* @param password 解密密码
|
||||
* @throws Exception 解密过程异常
|
||||
*/
|
||||
public static void decryptFile(String encryptedFilePath, String decryptedFilePath, String password) throws Exception {
|
||||
File encryptedFile = new File(encryptedFilePath);
|
||||
File decryptedFile = new File(decryptedFilePath);
|
||||
|
||||
// 验证加密文件
|
||||
if (!encryptedFile.exists() || !encryptedFile.isFile()) {
|
||||
throw new FileNotFoundException("加密文件不存在或不是文件: " + encryptedFilePath);
|
||||
}
|
||||
// 检查磁盘空间(减去元数据字节数)
|
||||
long requiredSize = encryptedFile.length() - SALT_LENGTH - IV_LENGTH - 4;
|
||||
if (!checkDiskSpace(decryptedFilePath, requiredSize)) {
|
||||
throw new IOException("目标路径磁盘空间不足,至少需要 " + requiredSize + " 字节");
|
||||
}
|
||||
|
||||
try (
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(encryptedFile));
|
||||
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(decryptedFile))
|
||||
) {
|
||||
// 读取元数据:盐值、IV、加密长度
|
||||
byte[] salt = new byte[SALT_LENGTH];
|
||||
byte[] iv = new byte[IV_LENGTH];
|
||||
byte[] lengthBytes = new byte[4];
|
||||
if (bis.read(salt) != SALT_LENGTH || bis.read(iv) != IV_LENGTH || bis.read(lengthBytes) != 4) {
|
||||
throw new IOException("加密文件格式错误,元数据不完整");
|
||||
}
|
||||
int encryptedLength = bytesToInt(lengthBytes);
|
||||
|
||||
// 生成密钥并解密开头部分
|
||||
SecretKey secretKey = generateSecretKey(password, salt);
|
||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
||||
|
||||
// 读取加密的开头数据(预留填充空间)
|
||||
byte[] encryptedBytes = new byte[encryptedLength + 16]; // 最多额外16字节填充
|
||||
int actualEncryptedRead = bis.read(encryptedBytes);
|
||||
if (actualEncryptedRead <= 0) {
|
||||
throw new IOException("加密文件内容不完整");
|
||||
}
|
||||
// 解密并写入(只取原始加密长度的字节,去除填充)
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedBytes, 0, actualEncryptedRead);
|
||||
bos.write(decryptedBytes, 0, Math.min(decryptedBytes.length, encryptedLength));
|
||||
|
||||
// 直接复制剩余未加密的字节
|
||||
byte[] copyBuffer = new byte[1024 * 1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = bis.read(copyBuffer)) != -1) {
|
||||
bos.write(copyBuffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成随机字节数组(盐值或IV)
|
||||
*/
|
||||
private static byte[] generateRandomBytes(int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
new Random().nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 基于密码和盐值生成AES密钥
|
||||
*/
|
||||
private static SecretKey generateSecretKey(String password, byte[] salt) throws Exception {
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_SIZE);
|
||||
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), ALGORITHM);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查目标路径磁盘空间
|
||||
*/
|
||||
private static boolean checkDiskSpace(String targetPath, long requiredSize) {
|
||||
File file = new File(targetPath);
|
||||
File parent = file.getParentFile();
|
||||
if (parent == null) {
|
||||
parent = new File(System.getProperty("user.dir"));
|
||||
}
|
||||
return parent.getFreeSpace() >= requiredSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* int转4字节数组(大端序)
|
||||
*/
|
||||
private static byte[] intToBytes(int value) {
|
||||
return new byte[]{
|
||||
(byte) (value >> 24),
|
||||
(byte) (value >> 16),
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 4字节数组转int(大端序)
|
||||
*/
|
||||
private static int bytesToInt(byte[] bytes) {
|
||||
return ((bytes[0] & 0xFF) << 24) |
|
||||
((bytes[1] & 0xFF) << 16) |
|
||||
((bytes[2] & 0xFF) << 8) |
|
||||
(bytes[3] & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
// 测试示例
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 测试参数(替换为实际文件路径)
|
||||
String sourceFile = "E:\\yjearth\\poi\\poi.db"; // 原始大文件
|
||||
String encryptedFile = "E:\\yjearth\\poi\\poi_enc.db"; // 加密后文件
|
||||
String decryptedFile = "E:\\yjearth\\poi\\poi_dec.db"; // 解密后文件
|
||||
String password = "MySecretPassword123!"; // 密码
|
||||
|
||||
// 加密(仅加密开头1MB,速度极快)
|
||||
System.out.println("开始加密文件...");
|
||||
long encryptStart = System.currentTimeMillis();
|
||||
encryptFile(sourceFile, encryptedFile, password);
|
||||
long encryptEnd = System.currentTimeMillis();
|
||||
System.out.println("加密完成!耗时:" + (encryptEnd - encryptStart) + "ms,加密文件:" + encryptedFile);
|
||||
|
||||
// 解密(仅解密开头1MB,速度极快)
|
||||
System.out.println("开始解密文件...");
|
||||
long decryptStart = System.currentTimeMillis();
|
||||
decryptFile(encryptedFile, decryptedFile, password);
|
||||
long decryptEnd = System.currentTimeMillis();
|
||||
System.out.println("解密完成!耗时:" + (decryptEnd - decryptStart) + "ms,解密文件:" + decryptedFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.vo.CsvField;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "CSV文件解析")
|
||||
@RestController
|
||||
@RequestMapping("/csv")
|
||||
public class CsvController {
|
||||
@GetMapping("/parseCsv")
|
||||
@Operation(summary = "解析CSV文件")
|
||||
public ApiResponse parseCsv(@Parameter(description = "文件路径") @RequestParam String filePath) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
List<CsvField> fieldList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "GBK");
|
||||
CSVParser parser = new CSVParser(reader, CSVFormat.DEFAULT)) {
|
||||
// 遍历所有行(CSVRecord即一行数据)
|
||||
for (CSVRecord record : parser) {
|
||||
// 确保每行至少有3列(A、B、C列)
|
||||
if (record.size() >= 3) {
|
||||
String label = record.get(1).trim();
|
||||
String key = record.get(2).trim();
|
||||
fieldList.add(new CsvField(key, label));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ApiResponse.success(fieldList);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,279 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.business.domain.Device;
|
||||
import com.yj.earth.business.service.DeviceService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.vo.AddDeviceDto;
|
||||
import com.yj.earth.dto.device.ImportDeviceDto;
|
||||
import com.yj.earth.dto.device.UpdateDeviceDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "设备信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/device")
|
||||
public class DeviceController {
|
||||
@Resource
|
||||
private DeviceService deviceService;
|
||||
public static final String TYPE_DAHUA = "大华";
|
||||
public static final String TYPE_HIKVISION = "海康";
|
||||
// FLV地址拼接规则、key=设备类型、value=地址模板
|
||||
public static final Map<String, String> FLV_URL_RULES = new HashMap<String, String>() {{
|
||||
put(TYPE_DAHUA, "http://{ip}:{port}/cam/realmonitor?channel={channel}&subtype=0&proto=flv");
|
||||
put(TYPE_HIKVISION, "http://{ip}:{port}/Streaming/Channels/{channel}/flv");
|
||||
}};
|
||||
|
||||
/**
|
||||
* 生成FLV地址
|
||||
*/
|
||||
public static String generateFlvUrl(String type, String ip, Integer port, Integer channel) {
|
||||
return FLV_URL_RULES.get(type)
|
||||
.replace("{ip}", ip)
|
||||
.replace("{port}", port.toString())
|
||||
.replace("{channel}", channel.toString());
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增设备信息")
|
||||
public ApiResponse addDevice(@RequestBody AddDeviceDto addDeviceDto) {
|
||||
Device device = new Device();
|
||||
BeanUtils.copyProperties(addDeviceDto, device);
|
||||
|
||||
// 生成FLV地址
|
||||
String flvUrl = generateFlvUrl(
|
||||
addDeviceDto.getType(),
|
||||
addDeviceDto.getIp(),
|
||||
addDeviceDto.getPort(),
|
||||
addDeviceDto.getChannel()
|
||||
);
|
||||
device.setFlvUrl(flvUrl);
|
||||
|
||||
deviceService.save(device);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询设备信息")
|
||||
public ApiResponse listDevice(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize,@Parameter(description = "设备名称") String cameraName) {
|
||||
LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.isNotBlank(cameraName)) {
|
||||
queryWrapper.like(Device::getCameraName, cameraName);
|
||||
}
|
||||
Page<Device> devicePage = deviceService.page(new Page<>(pageNum, pageSize), queryWrapper);
|
||||
return ApiResponse.success(devicePage);
|
||||
}
|
||||
|
||||
@GetMapping("/delete")
|
||||
@Operation(summary = "删除设备信息")
|
||||
public ApiResponse deleteDevice(@Parameter(description = "设备ID") @RequestParam String id) {
|
||||
deviceService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/getById")
|
||||
@Operation(summary = "查询设备信息")
|
||||
public ApiResponse getDevice(@Parameter(description = "设备ID") @RequestParam String id) {
|
||||
return ApiResponse.success(deviceService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新设备信息")
|
||||
public ApiResponse updateDevice(@RequestBody UpdateDeviceDto updateDeviceDto) {
|
||||
Device device = new Device();
|
||||
BeanUtils.copyProperties(updateDeviceDto, device);
|
||||
|
||||
// 如果更新了影响FLV URL的字段、重新生成FLV URL
|
||||
if (updateDeviceDto.getType() != null || updateDeviceDto.getIp() != null ||
|
||||
updateDeviceDto.getPort() != null || updateDeviceDto.getChannel() != null) {
|
||||
// 如果有任何一个字段为null、从数据库获取原始值
|
||||
Device original = deviceService.getById(updateDeviceDto.getId());
|
||||
String type = updateDeviceDto.getType() != null ? updateDeviceDto.getType() : original.getType();
|
||||
String ip = updateDeviceDto.getIp() != null ? updateDeviceDto.getIp() : original.getIp();
|
||||
Integer port = updateDeviceDto.getPort() != null ? updateDeviceDto.getPort() : original.getPort();
|
||||
Integer channel = updateDeviceDto.getChannel() != null ? updateDeviceDto.getChannel() : original.getChannel();
|
||||
|
||||
device.setFlvUrl(generateFlvUrl(type, ip, port, channel));
|
||||
}
|
||||
|
||||
deviceService.updateById(device);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入设备信息")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApiResponse importDevices(@Parameter(description = "设备Excel文件路径") @RequestParam String filePath) {
|
||||
// 验证文件路径不为空
|
||||
if (StringUtils.isBlank(filePath)) {
|
||||
return ApiResponse.failure("文件路径不能为空");
|
||||
}
|
||||
|
||||
// 验证文件是否存在
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
return ApiResponse.failure("文件不存在或不是有效文件");
|
||||
}
|
||||
|
||||
// 验证文件格式
|
||||
String fileName = file.getName();
|
||||
if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
|
||||
return ApiResponse.failure("请上传Excel格式文件(.xls或.xlsx)");
|
||||
}
|
||||
|
||||
// 验证文件大小
|
||||
long fileSize = file.length();
|
||||
if (fileSize > 10 * 1024 * 1024) {
|
||||
return ApiResponse.failure("文件大小不能超过10MB");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
List<ImportDeviceDto> deviceDtoList = EasyExcel.read(inputStream)
|
||||
.head(ImportDeviceDto.class)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
|
||||
if (CollectionUtils.isEmpty(deviceDtoList)) {
|
||||
return ApiResponse.failure("导入数据为空");
|
||||
}
|
||||
|
||||
List<String> errorMessages = validateImportData(deviceDtoList);
|
||||
if (!errorMessages.isEmpty()) {
|
||||
return ApiResponse.failure("导入数据校验失败:" + String.join(";", errorMessages));
|
||||
}
|
||||
|
||||
List<String> importIps = deviceDtoList.stream()
|
||||
.map(ImportDeviceDto::getIp)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(Device::getIp, importIps);
|
||||
List<Device> existDevices = deviceService.list(queryWrapper);
|
||||
|
||||
Set<String> existIps = existDevices.stream()
|
||||
.map(Device::getIp)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Device> newDevices = deviceDtoList.stream()
|
||||
.filter(dto -> !existIps.contains(dto.getIp()))
|
||||
.map(dto -> {
|
||||
Device device = new Device();
|
||||
BeanUtils.copyProperties(dto, device);
|
||||
// 生成FLV地址
|
||||
String flvUrl = generateFlvUrl(
|
||||
dto.getType(),
|
||||
dto.getIp(),
|
||||
dto.getPort(),
|
||||
dto.getChannel()
|
||||
);
|
||||
device.setFlvUrl(flvUrl);
|
||||
return device;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int total = deviceDtoList.size();
|
||||
int skipped = total - newDevices.size();
|
||||
int saved = 0;
|
||||
|
||||
if (!newDevices.isEmpty()) {
|
||||
boolean saveResult = deviceService.saveBatch(newDevices);
|
||||
saved = saveResult ? newDevices.size() : 0;
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>(3);
|
||||
resultMap.put("total", total);
|
||||
resultMap.put("saved", saved);
|
||||
resultMap.put("skipped", skipped);
|
||||
return ApiResponse.success(resultMap);
|
||||
} catch (IOException e) {
|
||||
return ApiResponse.failure("文件读取失败:" + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("导入异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验导入数据的合法性
|
||||
*/
|
||||
private List<String> validateImportData(List<ImportDeviceDto> deviceList) {
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < deviceList.size(); i++) {
|
||||
ImportDeviceDto dto = deviceList.get(i);
|
||||
int rowNum = i + 2;
|
||||
|
||||
// 校验必填字段
|
||||
if (StringUtils.isBlank(dto.getCameraName())) {
|
||||
errorMessages.add(String.format("第%d行:设备名称不能为空", rowNum));
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getIp())) {
|
||||
errorMessages.add(String.format("第%d行:设备IP不能为空", rowNum));
|
||||
} else if (!isValidIp(dto.getIp())) {
|
||||
errorMessages.add(String.format("第%d行:设备IP格式不正确", rowNum));
|
||||
}
|
||||
if (dto.getPort() == null || dto.getPort() <= 0 || dto.getPort() > 65535) {
|
||||
errorMessages.add(String.format("第%d行:设备端口无效", rowNum));
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getUsername())) {
|
||||
errorMessages.add(String.format("第%d行:用户名不能为空", rowNum));
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getType())) {
|
||||
errorMessages.add(String.format("第%d行:设备类型不能为空", rowNum));
|
||||
} else if (!FLV_URL_RULES.containsKey(dto.getType())) {
|
||||
errorMessages.add(String.format("第%d行:不支持的设备类型", rowNum));
|
||||
}
|
||||
if (dto.getChannel() == null || dto.getChannel() <= 0) {
|
||||
errorMessages.add(String.format("第%d行:设备通道无效", rowNum));
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单校验IP地址格式
|
||||
*/
|
||||
private boolean isValidIp(String ip) {
|
||||
if (ip == null || ip.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
|
||||
+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
|
||||
+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
|
||||
+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
|
||||
return ip.matches(regex);
|
||||
}
|
||||
|
||||
@GetMapping("/import/template")
|
||||
@Operation(summary = "下载导入模板")
|
||||
public void downloadTemplate(HttpServletResponse response) throws IOException {
|
||||
// 设置响应头
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
String fileName = URLEncoder.encode("设备信息导入模板", "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
// 写入模板表头(通过空数据列表触发表头生成)
|
||||
EasyExcel.write(response.getOutputStream(), ImportDeviceDto.class)
|
||||
.sheet("设备信息")
|
||||
.doWrite(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
@ -10,10 +10,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
@ -78,7 +75,7 @@ public class PbfInfoController {
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有地图文件")
|
||||
@PostMapping("/list")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list() {
|
||||
LambdaQueryWrapper<PbfInfo> queryWrapper = new QueryWrapper<PbfInfo>().lambda();
|
||||
// 把启用的排在最前面
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.sql.*;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "全国POI管理")
|
||||
@RestController
|
||||
@RequestMapping("/poi")
|
||||
public class PoiController {
|
||||
private static Connection connection;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
String dbPath = System.getProperty("user.dir") + File.separator + "poi" + File.separator + "poi.db";
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("SQLite连接初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/data")
|
||||
@Operation(summary = "查看数据")
|
||||
public ApiResponse data(@Parameter(description = "分页页码") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize, @Parameter(description = "名称搜索") String name) {
|
||||
int offset = (pageNum - 1) * pageSize;
|
||||
// 构建查询SQL
|
||||
StringBuilder dataSql = new StringBuilder("SELECT id, name, address, lng, lat FROM tbl_pois WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
// 添加名称搜索条件
|
||||
if (name != null && !name.trim().isEmpty()) {
|
||||
dataSql.append(" AND name LIKE ?");
|
||||
params.add("%" + name.trim() + "%");
|
||||
}
|
||||
// 添加分页条件
|
||||
dataSql.append(" LIMIT ? OFFSET ?");
|
||||
try {
|
||||
// 执行数据查询、获取List
|
||||
List<Poi> poiList = new ArrayList<>();
|
||||
long dataStartTime = System.currentTimeMillis();
|
||||
try (PreparedStatement dataPs = connection.prepareStatement(dataSql.toString())) {
|
||||
// 设置参数(搜索条件 + 分页参数)
|
||||
int paramIndex = 1;
|
||||
for (Object param : params) {
|
||||
dataPs.setObject(paramIndex++, param);
|
||||
}
|
||||
dataPs.setInt(paramIndex++, pageSize);
|
||||
dataPs.setInt(paramIndex++, offset);
|
||||
// 处理结果集、填充List
|
||||
try (ResultSet dataRs = dataPs.executeQuery()) {
|
||||
while (dataRs.next()) {
|
||||
Poi poi = new Poi();
|
||||
poi.setId(dataRs.getLong("id"));
|
||||
poi.setName(dataRs.getString("name"));
|
||||
poi.setAddress(dataRs.getString("address"));
|
||||
poi.setLng(dataRs.getString("lng"));
|
||||
poi.setLat(dataRs.getString("lat"));
|
||||
poiList.add(poi);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ApiResponse.success(poiList);
|
||||
} catch (SQLException e) {
|
||||
return ApiResponse.failure("POI数据查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class Poi {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String address;
|
||||
private String lng;
|
||||
private String lat;
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,25 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.datasource.DatabaseManager;
|
||||
import com.yj.earth.dto.system.UpdateSystemServiceDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -23,35 +27,36 @@ import java.util.Map;
|
||||
@RestController
|
||||
@RequestMapping("/systemService")
|
||||
public class SystemController {
|
||||
@Value("${server.host}")
|
||||
private String serverHost;
|
||||
|
||||
@Value("${server.port}")
|
||||
private Integer serverPort;
|
||||
|
||||
// 配置文件路径(jar包所在目录的application.yml)
|
||||
private static final String CONFIG_FILE_PATH = "application.yml";
|
||||
|
||||
// 重启脚本路径(app同级目录)
|
||||
private static final String WINDOWS_REBOOT_SCRIPT = "../reboot.bat";
|
||||
private static final String LINUX_REBOOT_SCRIPT = "../reboot.sh";
|
||||
|
||||
@Operation(summary = "读取系统服务配置")
|
||||
@Operation(summary = "读取系统服务端口配置")
|
||||
@PostMapping("/info")
|
||||
public ApiResponse readSystemServiceConfig() {
|
||||
Map<String, Object> map = Map.of("serverHost", serverHost, "serverPort", serverPort);
|
||||
// 只返回端口信息
|
||||
Map<String, Object> map = Map.of("serverPort", serverPort);
|
||||
return ApiResponse.success(map);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改系统服务配置并重启")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse updateSystemService(@RequestBody UpdateSystemServiceDto updateSystemServiceDto) {
|
||||
@Operation(summary = "修改系统服务端口配置")
|
||||
@PostMapping("/updatePort")
|
||||
public ApiResponse updateSystemPort(@RequestBody UpdateSystemServiceDto updateSystemServiceDto) {
|
||||
try {
|
||||
// 检查端口是否为空
|
||||
if (updateSystemServiceDto.getServerPort() == null) {
|
||||
return ApiResponse.failure("端口号不能为空");
|
||||
}
|
||||
|
||||
// 读取并更新YAML配置
|
||||
Yaml yaml = new Yaml();
|
||||
File configFile = new File(CONFIG_FILE_PATH);
|
||||
if (!configFile.exists()) {
|
||||
return ApiResponse.failure("配置文件不存在");
|
||||
}
|
||||
|
||||
// 解析YAML内容
|
||||
Map<String, Object> configMap;
|
||||
try (InputStream in = Files.newInputStream(Paths.get(CONFIG_FILE_PATH))) {
|
||||
@ -60,15 +65,12 @@ public class SystemController {
|
||||
configMap = new HashMap<>();
|
||||
}
|
||||
}
|
||||
// 更新配置
|
||||
|
||||
// 更新端口配置
|
||||
Map<String, Object> serverMap = (Map<String, Object>) configMap.getOrDefault("server", new HashMap<>());
|
||||
if (updateSystemServiceDto.getServerHost() != null && !updateSystemServiceDto.getServerHost().isEmpty()) {
|
||||
serverMap.put("host", updateSystemServiceDto.getServerHost());
|
||||
}
|
||||
if (updateSystemServiceDto.getServerPort() != null) {
|
||||
serverMap.put("port", updateSystemServiceDto.getServerPort());
|
||||
}
|
||||
serverMap.put("port", updateSystemServiceDto.getServerPort());
|
||||
configMap.put("server", serverMap);
|
||||
|
||||
// 保存配置
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
@ -77,44 +79,71 @@ public class SystemController {
|
||||
try (FileWriter writer = new FileWriter(configFile, StandardCharsets.UTF_8)) {
|
||||
writeYaml.dump(configMap, writer);
|
||||
}
|
||||
// 执行重启脚本
|
||||
executeRebootScript();
|
||||
|
||||
return ApiResponse.success(null);
|
||||
|
||||
} catch (IOException e) {
|
||||
return ApiResponse.failure("更新配置失败:" + e.getMessage());
|
||||
return ApiResponse.failure("更新端口配置失败:" + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("操作失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重启脚本
|
||||
*/
|
||||
private void executeRebootScript() throws IOException, InterruptedException {
|
||||
String scriptPath;
|
||||
ProcessBuilder processBuilder;
|
||||
|
||||
// 判断操作系统类型、选择对应的脚本
|
||||
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
scriptPath = WINDOWS_REBOOT_SCRIPT;
|
||||
// 检查Windows脚本是否存在
|
||||
if (!new File(scriptPath).exists()) {
|
||||
throw new FileNotFoundException("Windows重启脚本不存在: " + scriptPath);
|
||||
}
|
||||
processBuilder = new ProcessBuilder(scriptPath);
|
||||
} else {
|
||||
scriptPath = LINUX_REBOOT_SCRIPT;
|
||||
// 检查Linux脚本是否存在
|
||||
if (!new File(scriptPath).exists()) {
|
||||
throw new FileNotFoundException("Linux重启脚本不存在: " + scriptPath);
|
||||
}
|
||||
// 给脚本添加执行权限
|
||||
new ProcessBuilder("chmod", "+x", scriptPath).start().waitFor();
|
||||
processBuilder = new ProcessBuilder(scriptPath);
|
||||
@Operation(summary = "工程导出")
|
||||
@GetMapping("/export")
|
||||
public void exportProject(HttpServletResponse response) {
|
||||
// 获取SQLite文件绝对路径
|
||||
String sqliteFilePath = DatabaseManager.getSqliteDbFilePath();
|
||||
// 校验路径与文件有效性
|
||||
if (sqliteFilePath == null || sqliteFilePath.isEmpty()) {
|
||||
handleError(response, HttpStatus.BAD_REQUEST.value(), "SQLite数据库未初始化");
|
||||
return;
|
||||
}
|
||||
File sqliteFile = new File(sqliteFilePath);
|
||||
if (!sqliteFile.exists()) {
|
||||
handleError(response, HttpStatus.NOT_FOUND.value(), "SQLite文件不存在");
|
||||
return;
|
||||
}
|
||||
if (!sqliteFile.canRead()) {
|
||||
handleError(response, HttpStatus.FORBIDDEN.value(), "无权限读取SQLite文件");
|
||||
return;
|
||||
}
|
||||
// 配置下载响应头(让浏览器触发下载而非打开)
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setContentLengthLong(sqliteFile.length());
|
||||
// 处理文件名编码
|
||||
String fileName = URLEncoder.encode("app.db", StandardCharsets.UTF_8);
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setBufferSize(1024 * 8); // 设置响应缓冲区(提升传输效率)
|
||||
// 读取文件流并写入响应
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sqliteFile));
|
||||
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
|
||||
byte[] buffer = new byte[1024 * 8];
|
||||
int len;
|
||||
// 循环读取文件内容并写入响应流
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
// 强制刷新缓冲区、确保所有数据发送到前端
|
||||
response.flushBuffer();
|
||||
} catch (IOException e) {
|
||||
handleError(response, HttpStatus.INTERNAL_SERVER_ERROR.value(), "文件下载失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 执行脚本(在新进程中运行、不阻塞当前请求)
|
||||
processBuilder.start();
|
||||
|
||||
|
||||
/**
|
||||
* 返回指定状态码与错误信息
|
||||
*/
|
||||
private void handleError(HttpServletResponse response, int statusCode, String message) {
|
||||
try {
|
||||
response.setStatus(statusCode);
|
||||
response.setContentType("text/plain; charset=utf-8");
|
||||
response.getWriter().write(message);
|
||||
response.getWriter().flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.annotation.CheckAuth;
|
||||
import com.yj.earth.annotation.EncryptResponse;
|
||||
@ -13,10 +14,7 @@ import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.dto.relation.UserBindOrUnBindRoleDto;
|
||||
import com.yj.earth.dto.user.AddUserDto;
|
||||
import com.yj.earth.dto.user.UpdatePasswordDto;
|
||||
import com.yj.earth.dto.user.UpdateUserDto;
|
||||
import com.yj.earth.dto.user.UserLoginDto;
|
||||
import com.yj.earth.dto.user.*;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -38,7 +36,6 @@ public class UserController {
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "新增用户")
|
||||
@PostMapping("/add")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
@ -58,16 +55,6 @@ public class UserController {
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "删除用户")
|
||||
@PostMapping("/delete")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse delete(@Parameter(description = "用户ID") String id) {
|
||||
userService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "更新信息")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse update(@RequestBody UpdateUserDto updateUserDto) {
|
||||
@ -77,7 +64,6 @@ public class UserController {
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "更新密码")
|
||||
@PostMapping("/updatePassword")
|
||||
public ApiResponse updatePassword(@RequestBody UpdatePasswordDto updatePasswordDto) {
|
||||
@ -93,19 +79,29 @@ public class UserController {
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "用户详情")
|
||||
@GetMapping("/getById")
|
||||
public ApiResponse get(@Parameter(description = "用户ID") String id) {
|
||||
return ApiResponse.success(userService.getById(id));
|
||||
}
|
||||
|
||||
@CheckAuth
|
||||
@Operation(summary = "用户列表")
|
||||
@GetMapping("/list")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize) {
|
||||
Page<User> userPage = userService.page(new Page<>(pageNum, pageSize));
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum,
|
||||
@Parameter(description = "分页大小") Integer pageSize,
|
||||
@Parameter(description = "搜索字段") String searchKey,
|
||||
@Parameter(description = "角色ID") String roleId) {
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
|
||||
// 根据用户名或者昵称进行模糊搜索
|
||||
if (StringUtils.isNotBlank(searchKey)) {
|
||||
wrapper.like(User::getUsername, searchKey).or().like(User::getNickname, searchKey);
|
||||
}
|
||||
// 根据角色ID等值搜索
|
||||
if (StringUtils.isNotBlank(roleId)) {
|
||||
wrapper.eq(User::getRoleId, roleId);
|
||||
}
|
||||
Page<User> userPage = userService.page(new Page<>(pageNum, pageSize), wrapper);
|
||||
return ApiResponse.success(userPage);
|
||||
}
|
||||
|
||||
@ -144,4 +140,22 @@ public class UserController {
|
||||
public ApiResponse getCurrentUserInfo() {
|
||||
return ApiResponse.success(userService.getById(StpUtil.getLoginIdAsString()));
|
||||
}
|
||||
|
||||
@Operation(summary = "启用禁用用户数统计")
|
||||
@GetMapping("/getUserStatusCount")
|
||||
public ApiResponse getUserStatusCount() {
|
||||
// 查询状态为1的用户数
|
||||
long useUserCount = userService.count(new LambdaQueryWrapper<User>().eq(User::getStatus, 1));
|
||||
// 查询状态为0的用户数
|
||||
long bindUserCount = userService.count(new LambdaQueryWrapper<User>().eq(User::getStatus, 0));
|
||||
return ApiResponse.success(Map.of("useUserCount", useUserCount, "bindUserCount", bindUserCount));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户")
|
||||
@PostMapping("/deletes")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse deletes(@RequestBody List<String> ids) {
|
||||
userService.removeByIds(ids);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,10 @@ import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.yj.earth.annotation.RoleAccess;
|
||||
import com.yj.earth.business.domain.PbfInfo;
|
||||
import com.yj.earth.business.domain.WebSource;
|
||||
import com.yj.earth.business.service.ModelLibraryService;
|
||||
import com.yj.earth.business.service.PbfInfoService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.business.service.WebSourceService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
@ -39,6 +41,8 @@ public class WebSourceController {
|
||||
private ModelLibraryController modelLibraryController;
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private PbfInfoService pbfInfoService;
|
||||
private static final List<String> SUPPORT_EXTENSIONS = Arrays.asList("clt", "mbtiles", "pak", "pbf", "model");
|
||||
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
@ -102,6 +106,15 @@ public class WebSourceController {
|
||||
if (webSource.getType().equals("model")) {
|
||||
modelLibraryController.importModelLibrary(filePath);
|
||||
}
|
||||
|
||||
// 如果是PBF文件则调用地图服务
|
||||
if (webSource.getType().equals("pbf")) {
|
||||
PbfInfo pbfInfo = new PbfInfo();
|
||||
pbfInfo.setName(FileUtil.getName(filePath));
|
||||
pbfInfo.setPath(filePath);
|
||||
pbfInfo.setIsEnable(0);
|
||||
pbfInfoService.save(pbfInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
45
src/main/java/com/yj/earth/business/domain/Device.java
Normal file
45
src/main/java/com/yj/earth/business/domain/Device.java
Normal file
@ -0,0 +1,45 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class Device implements Serializable {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
@Schema(description = "设备名称")
|
||||
private String cameraName;
|
||||
@Schema(description = "设备类型")
|
||||
private String type;
|
||||
@Schema(description = "设备ip")
|
||||
private String ip;
|
||||
@Schema(description = "设备端口")
|
||||
private Integer port;
|
||||
@Schema(description = "设备用户名")
|
||||
private String username;
|
||||
@Schema(description = "设备密码")
|
||||
private String password;
|
||||
@Schema(description = "设备通道")
|
||||
private Integer channel;
|
||||
@Schema(description = "设备FLV地址")
|
||||
private String flvUrl;
|
||||
@Schema(description = "设备状态")
|
||||
private Integer status;
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -25,6 +25,9 @@ public class Role implements Serializable {
|
||||
@Schema(description = "角色名称")
|
||||
private String roleName;
|
||||
|
||||
@Schema(description = "状态(0未启用、1启用)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
|
||||
@ -41,6 +41,9 @@ public class User implements Serializable {
|
||||
@Schema(description = "所属角色")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "状态(0未启用、1启用)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
18
src/main/java/com/yj/earth/business/mapper/DeviceMapper.java
Normal file
18
src/main/java/com/yj/earth/business/mapper/DeviceMapper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.Device;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-10-14
|
||||
*/
|
||||
@Mapper
|
||||
public interface DeviceMapper extends BaseMapper<Device> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.Device;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-10-14
|
||||
*/
|
||||
public interface DeviceService extends IService<Device> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import com.yj.earth.business.domain.Device;
|
||||
import com.yj.earth.business.mapper.DeviceMapper;
|
||||
import com.yj.earth.business.service.DeviceService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-10-14
|
||||
*/
|
||||
@Service
|
||||
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
|
||||
|
||||
}
|
||||
@ -28,9 +28,10 @@ public class ServerInitService {
|
||||
private RoleService roleService;
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
|
||||
public void init() {
|
||||
// 查询数据库所有需要加载的资源
|
||||
List<Source> list =sourceService.list(new LambdaQueryWrapper<Source>()
|
||||
List<Source> list = sourceService.list(new LambdaQueryWrapper<Source>()
|
||||
.eq(Source::getSourceType, "terrain")
|
||||
.or().eq(Source::getSourceType, "layer")
|
||||
.or().eq(Source::getSourceType, "tileset"));
|
||||
@ -48,18 +49,20 @@ public class ServerInitService {
|
||||
|
||||
public void checkDefaultUserAndRole() {
|
||||
// 查询角色表和用户表是否有数据
|
||||
if(roleService.count() == 0 && userService.count() == 0) {
|
||||
if (roleService.count() == 0 && userService.count() == 0) {
|
||||
log.info("初始化默认用户角色数据");
|
||||
// 新增一个管理员角色
|
||||
Role adminRole = new Role();
|
||||
adminRole.setRoleName("管理员");
|
||||
adminRole.setDescription("系统管理员");
|
||||
adminRole.setStatus(1);
|
||||
adminRole.setIsSuper(1);
|
||||
roleService.save(adminRole);
|
||||
// 新增一个默认角色
|
||||
Role defaultRole = new Role();
|
||||
defaultRole.setRoleName("默认角色");
|
||||
defaultRole.setDescription("系统默认角色");
|
||||
defaultRole.setStatus(1);
|
||||
defaultRole.setIsSuper(0);
|
||||
roleService.save(defaultRole);
|
||||
// 新增一个用户
|
||||
@ -67,6 +70,7 @@ public class ServerInitService {
|
||||
user.setUsername("admin");
|
||||
user.setPassword(BCrypt.hashpw("admin123", BCrypt.gensalt()));
|
||||
user.setNickname("管理员");
|
||||
user.setStatus(1);
|
||||
user.setRoleId(adminRole.getId());
|
||||
user.setPhone("13888888888");
|
||||
userService.save(user);
|
||||
|
||||
@ -34,7 +34,7 @@ public class CodeUtil {
|
||||
}
|
||||
|
||||
// 传入需要生成代码的表名
|
||||
Generation("pbf_info");
|
||||
Generation("device");
|
||||
}
|
||||
|
||||
public static void Generation(String... tableName) {
|
||||
|
||||
@ -8,6 +8,7 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -15,34 +16,22 @@ import java.util.Map;
|
||||
|
||||
import static org.gdal.ogr.ogrConstants.*;
|
||||
|
||||
/**
|
||||
* GDAL地理数据转JSON工具类
|
||||
*/
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.*;
|
||||
import org.gdal.osr.SpatialReference;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* GDAL地理数据转JSON工具类
|
||||
*/
|
||||
public class GdalJsonConverter {
|
||||
|
||||
static {
|
||||
// 初始化GDAL
|
||||
// 动态获取项目根目录下的lib文件夹路径
|
||||
String projectRoot = System.getProperty("user.dir");
|
||||
|
||||
// 拼接lib目录路径(项目根目录/lib)
|
||||
String projLibPath = projectRoot + File.separator + "lib";
|
||||
|
||||
// 设置PROJ库路径(指向包含proj.db的lib目录)
|
||||
gdal.SetConfigOption("PROJ_LIB", projLibPath);
|
||||
gdal.AllRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地理数据文件转换为指定结构的JSON
|
||||
* @param filePath 文件绝对路径
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String convertToJson(String filePath) {
|
||||
return convertToJson(filePath, "");
|
||||
@ -50,11 +39,8 @@ public class GdalJsonConverter {
|
||||
|
||||
/**
|
||||
* 将地理数据文件转换为指定结构的JSON
|
||||
* @param filePath 文件绝对路径
|
||||
* @param targetCrs 目标坐标系(如:"EPSG:4326"),为空则不转换
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String convertToJson(String filePath, String targetCrs) {
|
||||
private static String convertToJson(String filePath, String targetCrs) {
|
||||
try {
|
||||
// 打开数据源
|
||||
DataSource dataSource = ogr.Open(filePath, 0);
|
||||
@ -80,7 +66,7 @@ public class GdalJsonConverter {
|
||||
result.put("list", listArray);
|
||||
dataSource.delete();
|
||||
|
||||
return result.toString(2); // 缩进2个空格,美化输出
|
||||
return result.toString(2);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("转换失败: " + e.getMessage(), e);
|
||||
@ -88,7 +74,7 @@ public class GdalJsonConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FeatureCollection对象
|
||||
* 创建 FeatureCollection 对象
|
||||
*/
|
||||
private static JSONObject createFeatureCollection(Layer layer, String layerName, String targetCrs) throws JSONException {
|
||||
JSONObject featureCollection = new JSONObject();
|
||||
@ -108,10 +94,10 @@ public class GdalJsonConverter {
|
||||
// 重置图层读取位置
|
||||
layer.ResetReading();
|
||||
|
||||
// 获取图层定义,用于字段信息
|
||||
// 获取图层定义、用于字段信息
|
||||
FeatureDefn layerDefn = layer.GetLayerDefn();
|
||||
|
||||
// 如果需要坐标转换,创建坐标转换对象
|
||||
// 如果需要坐标转换、创建坐标转换对象
|
||||
SpatialReference sourceSrs = layer.GetSpatialRef();
|
||||
SpatialReference targetSrs = null;
|
||||
CoordinateTransformation coordTrans = null;
|
||||
@ -164,7 +150,7 @@ public class GdalJsonConverter {
|
||||
return proj4.trim();
|
||||
}
|
||||
|
||||
// 如果PROJ4为空,尝试获取WKT
|
||||
// 如果PROJ4为空、尝试获取WKT
|
||||
String wkt = srs.ExportToWkt();
|
||||
if (wkt != null && !wkt.trim().isEmpty()) {
|
||||
return wkt.trim();
|
||||
@ -186,8 +172,7 @@ public class GdalJsonConverter {
|
||||
/**
|
||||
* 创建单个Feature对象
|
||||
*/
|
||||
private static JSONObject createFeature(Feature feature, FeatureDefn layerDefn,
|
||||
CoordinateTransformation coordTrans, boolean transformCoords) throws JSONException {
|
||||
private static JSONObject createFeature(Feature feature, FeatureDefn layerDefn, CoordinateTransformation coordTrans, boolean transformCoords) throws JSONException {
|
||||
JSONObject featureObj = new JSONObject();
|
||||
featureObj.put("type", "Feature");
|
||||
|
||||
@ -220,7 +205,7 @@ public class GdalJsonConverter {
|
||||
geomToUse = transformedGeom;
|
||||
} catch (Exception e) {
|
||||
System.err.println("坐标转换失败: " + e.getMessage());
|
||||
// 如果转换失败,使用原始几何
|
||||
// 如果转换失败、使用原始几何
|
||||
geomToUse = geometry;
|
||||
}
|
||||
}
|
||||
@ -233,7 +218,7 @@ public class GdalJsonConverter {
|
||||
geomToUse.delete();
|
||||
}
|
||||
} else {
|
||||
// 如果没有几何信息,创建空的MultiPolygon
|
||||
// 如果没有几何信息、创建空的MultiPolygon
|
||||
featureObj.put("geometry", createEmptyMultiPolygon());
|
||||
}
|
||||
|
||||
@ -452,11 +437,9 @@ public class GdalJsonConverter {
|
||||
layerInfo.put("name", layer.GetName());
|
||||
layerInfo.put("featureCount", layer.GetFeatureCount());
|
||||
layerInfo.put("geometryType", ogr.GeometryTypeToName(layer.GetGeomType()));
|
||||
|
||||
// 添加坐标系信息
|
||||
String crs = getLayerCrs(layer);
|
||||
layerInfo.put("crs", crs != null ? crs : "未知");
|
||||
|
||||
layers.add(layerInfo);
|
||||
}
|
||||
info.put("layers", layers);
|
||||
|
||||
@ -171,11 +171,11 @@ public class SdkUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
log.error("sdk配置文件中未配置server.port");
|
||||
log.error("sdk 配置文件中未配置 server.port");
|
||||
} catch (IOException e) {
|
||||
log.error("读取sdk配置文件失败", e);
|
||||
log.error("读取 sdk 配置文件失败", e);
|
||||
} catch (ClassCastException e) {
|
||||
log.error("sdk配置文件中server.port格式错误、应为数字类型", e);
|
||||
log.error("sdk 配置文件中 server.port 格式错误、应为数字类型", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ public class DatabaseManager {
|
||||
classes.add(BusinessConfig.class);
|
||||
classes.add(WebSource.class);
|
||||
classes.add(PbfInfo.class);
|
||||
classes.add(Device.class);
|
||||
ENTITY_CLASSES = Collections.unmodifiableList(classes);
|
||||
}
|
||||
|
||||
@ -192,10 +193,6 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void createTablesForEntities(DatabaseType dbType, Class entityClass, Connection connection) throws SQLException {
|
||||
createTableIfNotExists(connection, dbType, entityClass);
|
||||
}
|
||||
|
||||
private static void createTableIfNotExists(Connection connection, DatabaseType dbType, Class<?> entityClass) throws SQLException {
|
||||
String tableName = getUnderlineName(entityClass.getSimpleName());
|
||||
|
||||
|
||||
34
src/main/java/com/yj/earth/design/Device.java
Normal file
34
src/main/java/com/yj/earth/design/Device.java
Normal file
@ -0,0 +1,34 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class Device {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
@Schema(description = "设备名称")
|
||||
private String cameraName;
|
||||
@Schema(description = "设备类型")
|
||||
private String type;
|
||||
@Schema(description = "设备ip")
|
||||
private String ip;
|
||||
@Schema(description = "设备端口")
|
||||
private Integer port;
|
||||
@Schema(description = "设备用户名")
|
||||
private String username;
|
||||
@Schema(description = "设备密码")
|
||||
private String password;
|
||||
@Schema(description = "设备通道")
|
||||
private Integer channel;
|
||||
@Schema(description = "设备FLV地址")
|
||||
private String flvUrl;
|
||||
@Schema(description = "设备状态")
|
||||
private Integer status;
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -13,6 +13,8 @@ public class Role {
|
||||
private String roleName;
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
@Schema(description = "状态(0未启用、1启用)")
|
||||
private Integer status;
|
||||
@Schema(description = "是否超级管理员")
|
||||
private Integer isSuper;
|
||||
@Schema(description = "创建时间")
|
||||
|
||||
@ -6,7 +6,7 @@ import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class User{
|
||||
public class User {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
@Schema(description = "用户名")
|
||||
@ -19,6 +19,8 @@ public class User{
|
||||
private String nickname;
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
@Schema(description = "状态(0未启用、1启用)")
|
||||
private Integer status;
|
||||
@Schema(description = "所属角色")
|
||||
private String roleId;
|
||||
@Schema(description = "创建时间")
|
||||
|
||||
23
src/main/java/com/yj/earth/dto/device/ImportDeviceDto.java
Normal file
23
src/main/java/com/yj/earth/dto/device/ImportDeviceDto.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.yj.earth.dto.device;
|
||||
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ImportDeviceDto {
|
||||
@ExcelProperty(value = "名称", index = 0)
|
||||
private String cameraName;
|
||||
@ExcelProperty(value = "设备IP", index = 1)
|
||||
private String ip;
|
||||
@ExcelProperty(value = "设备端口", index = 2)
|
||||
private Integer port;
|
||||
@ExcelProperty(value = "用户名", index = 3)
|
||||
private String username;
|
||||
@ExcelProperty(value = "密码", index = 4)
|
||||
private String password;
|
||||
@ExcelProperty(value = "设备类型", index = 5)
|
||||
private String type;
|
||||
@ExcelProperty(value = "通道号", index = 6)
|
||||
private Integer channel;
|
||||
}
|
||||
24
src/main/java/com/yj/earth/dto/device/UpdateDeviceDto.java
Normal file
24
src/main/java/com/yj/earth/dto/device/UpdateDeviceDto.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.yj.earth.dto.device;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateDeviceDto {
|
||||
@Schema(description = "设备ID")
|
||||
private String id;
|
||||
@Schema(description = "设备名称")
|
||||
private String cameraName;
|
||||
@Schema(description = "设备类型")
|
||||
private String type;
|
||||
@Schema(description = "设备ip")
|
||||
private String ip;
|
||||
@Schema(description = "设备端口")
|
||||
private Integer port;
|
||||
@Schema(description = "设备用户名")
|
||||
private String username;
|
||||
@Schema(description = "设备密码")
|
||||
private String password;
|
||||
@Schema(description = "设备通道")
|
||||
private Integer channel;
|
||||
}
|
||||
@ -5,8 +5,6 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateSystemServiceDto {
|
||||
@Schema(description = "服务地址")
|
||||
private String serverHost;
|
||||
@Schema(description = "服务端口")
|
||||
private Integer serverPort;
|
||||
}
|
||||
|
||||
@ -8,13 +8,12 @@ import lombok.Data;
|
||||
public class UpdateUserDto {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
@Schema(description = "用户状态")
|
||||
private Integer status;
|
||||
}
|
||||
|
||||
12
src/main/java/com/yj/earth/dto/user/UpdateUserStatusDto.java
Normal file
12
src/main/java/com/yj/earth/dto/user/UpdateUserStatusDto.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.yj.earth.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateUserStatusDto {
|
||||
@Schema(description = "用户ID")
|
||||
private String id;
|
||||
@Schema(description = "用户状态")
|
||||
private Integer status;
|
||||
}
|
||||
11
src/main/java/com/yj/earth/params/Gdb.java
Normal file
11
src/main/java/com/yj/earth/params/Gdb.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@SourceType("gdb")
|
||||
public class Gdb {
|
||||
private String richText;
|
||||
private String sourcePath;
|
||||
}
|
||||
22
src/main/java/com/yj/earth/vo/AddDeviceDto.java
Normal file
22
src/main/java/com/yj/earth/vo/AddDeviceDto.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.yj.earth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddDeviceDto {
|
||||
@Schema(description = "设备名称")
|
||||
private String cameraName;
|
||||
@Schema(description = "设备类型")
|
||||
private String type;
|
||||
@Schema(description = "设备ip")
|
||||
private String ip;
|
||||
@Schema(description = "设备端口")
|
||||
private Integer port;
|
||||
@Schema(description = "设备用户名")
|
||||
private String username;
|
||||
@Schema(description = "设备密码")
|
||||
private String password;
|
||||
@Schema(description = "设备通道")
|
||||
private Integer channel;
|
||||
}
|
||||
14
src/main/java/com/yj/earth/vo/CsvField.java
Normal file
14
src/main/java/com/yj/earth/vo/CsvField.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CsvField {
|
||||
@Schema(description = "字段名称")
|
||||
private String key;
|
||||
@Schema(description = "字段描述")
|
||||
private String label;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
server:
|
||||
host: 192.168.110.25
|
||||
host: 192.168.110.137
|
||||
port: 8848
|
||||
|
||||
sdk:
|
||||
|
||||
25
src/main/resources/mapper/DeviceMapper.xml
Normal file
25
src/main/resources/mapper/DeviceMapper.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?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.yj.earth.business.mapper.DeviceMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.yj.earth.business.domain.Device">
|
||||
<id column="id" property="id" />
|
||||
<result column="camera_name" property="cameraName" />
|
||||
<result column="type" property="type" />
|
||||
<result column="ip" property="ip" />
|
||||
<result column="port" property="port" />
|
||||
<result column="user_name" property="userName" />
|
||||
<result column="password" property="password" />
|
||||
<result column="channel" property="channel" />
|
||||
<result column="flv_url" property="flvUrl" />
|
||||
<result column="created_at" property="createdAt" />
|
||||
<result column="updated_at" property="updatedAt" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, camera_name, type, ip, port, user_name, password, channel, flv_url, created_at, updated_at
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user