diff --git a/njcn-common/src/main/java/com/njcn/common/utils/BinaryDataConverter.java b/njcn-common/src/main/java/com/njcn/common/utils/BinaryDataConverter.java new file mode 100644 index 0000000..9170829 --- /dev/null +++ b/njcn-common/src/main/java/com/njcn/common/utils/BinaryDataConverter.java @@ -0,0 +1,72 @@ +package com.njcn.common.utils; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + + +/** + * @author hongawen + * @version 1.0 + * @data 2025/6/17 15:17 + */ +public class BinaryDataConverter { + + /** + * 使用Base64编码转换二进制数据为字符串 + * 推荐使用,最安全可靠的方式 + */ + public static String toBase64String(byte[] binaryData) { + return Base64.getEncoder().encodeToString(binaryData); + } + + /** + * 使用Base64解码字符串为二进制数据 + */ + public static byte[] fromBase64String(String base64String) { + return Base64.getDecoder().decode(base64String); + } + + /** + * 使用UTF-8编码转换二进制数据为字符串 + * 注意:仅适用于文本数据,不适用于二进制数据 + */ + public static String toUtf8String(byte[] binaryData) { + return new String(binaryData, StandardCharsets.UTF_8); + } + + /** + * 使用UTF-8编码转换字符串为二进制数据 + */ + public static byte[] fromUtf8String(String text) { + return text.getBytes(StandardCharsets.UTF_8); + } + + /** + * 使用十六进制字符串表示二进制数据 + * 适用于需要可读性的场景 + */ + public static String toHexString(byte[] binaryData) { + StringBuilder hexString = new StringBuilder(); + for (byte b : binaryData) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * 从十六进制字符串转换为二进制数据 + */ + public static byte[] fromHexString(String hexString) { + int len = hexString.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/njcn-common/src/main/java/com/njcn/common/utils/images/BinFileReader.java b/njcn-common/src/main/java/com/njcn/common/utils/images/BinFileReader.java new file mode 100644 index 0000000..a9054cb --- /dev/null +++ b/njcn-common/src/main/java/com/njcn/common/utils/images/BinFileReader.java @@ -0,0 +1,58 @@ +package com.njcn.common.utils.images; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author hongawen + * @version 1.0 + * @data 2025/6/18 11:17 + */ +public class BinFileReader { + + public static void main(String[] args) { + String filePath = "C:\\Users\\hongawen\\Desktop\\temp\\qrc.bin"; + try (InputStream inputStream = new FileInputStream(filePath)) { + printHexDump(inputStream); + } catch (IOException e) { + System.err.println("读取文件时发生错误: " + e.getMessage()); + } + } + + public static void printHexDump(InputStream stream) throws IOException { + int bytesPerLine = 16; + byte[] buffer = new byte[bytesPerLine]; + int bytesRead; + long offset = 0; + + while ((bytesRead = stream.read(buffer)) != -1) { + // 打印偏移量 (地址) + System.out.printf("%08X: ", offset); + + // 打印十六进制值 + StringBuilder hexString = new StringBuilder(); + StringBuilder asciiString = new StringBuilder(); + for (int i = 0; i < bytesPerLine; i++) { + if (i < bytesRead) { + byte b = buffer[i]; + hexString.append(String.format("%02X ", b)); + // 将非打印字符替换为 '.' + asciiString.append(isPrintableChar(b) ? (char) b : '.'); + } else { + // 用空格填充,如果最后一行不满 + hexString.append(" "); + } + if (i == 7) { // 在中间加一个空格,方便阅读 + hexString.append(" "); + } + } + System.out.printf("%-50s %s%n", hexString.toString(), asciiString.toString()); + offset += bytesRead; + } + } + + private static boolean isPrintableChar(byte b) { + return b >= 32 && b <= 126; + } +} diff --git a/njcn-common/src/main/java/com/njcn/common/utils/images/ImageConverter.java b/njcn-common/src/main/java/com/njcn/common/utils/images/ImageConverter.java index 013a208..4e06b9a 100644 --- a/njcn-common/src/main/java/com/njcn/common/utils/images/ImageConverter.java +++ b/njcn-common/src/main/java/com/njcn/common/utils/images/ImageConverter.java @@ -1,6 +1,7 @@ package com.njcn.common.utils.images; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.nio.ByteBuffer; @@ -15,188 +16,81 @@ import java.nio.ByteOrder; */ public class ImageConverter { + /** - * 将BufferedImage转换为BMP格式的字节数组 - * @param image BufferedImage对象 - * @return BMP格式的字节数组 - * @throws IOException 如果转换过程中发生IO错误 + * 将一个 BufferedImage 对象转换为我们自定义的 BMPF .bin 格式。 */ - public static byte[] convertToBMP(BufferedImage image) throws IOException { + public static byte[] convertToBinFormat(BufferedImage image) throws IOException { + final int targetWidth = 148; + final int targetHeight = 148; + + // 1. 创建一个新的、与目标格式(16-bpp)匹配的、固定尺寸的画布 + BufferedImage newImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_USHORT_GRAY); + Graphics2D g2d = newImage.createGraphics(); + + // 2. 用白色(0xFFFF)填充背景 + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, targetWidth, targetHeight); + + // 3. 将原始图像居中绘制到新画布上 + // Graphics2D 会自动处理颜色转换 + int x = (targetWidth - image.getWidth()) / 2; + int y = (targetHeight - image.getHeight()) / 2; + g2d.drawImage(image, x, y, null); + g2d.dispose(); + + // 4. 后续操作基于新的、尺寸和位深度都正确的图像 + final int bpp = 16; + // Stride: 每行像素数据的字节数。对于无填充的16bpp图像,等于 宽度 * 2 + final int stride = targetWidth * (bpp / 8); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream out = new DataOutputStream(baos)) { + // --- 写入文件头 (严格44字节) --- + // 根据C代码 qrc_bitmap.pData = ...[44/4], 我们确认头部为44字节 + ByteBuffer headerBuffer = ByteBuffer.allocate(44); + // 头部使用小端序 + headerBuffer.order(ByteOrder.LITTLE_ENDIAN); - // 写入BMP文件头 - writeBMPHeader(baos, image.getWidth(), image.getHeight()); + // 0-3: 魔术字 + headerBuffer.put(new byte[]{'b', 'm', 'p', 'f'}); + // 4-5: 宽度 + headerBuffer.putShort((short) targetWidth); + // 6-7: 高度 + headerBuffer.putShort((short) targetHeight); + // 8-9: 行字节数 + headerBuffer.putShort((short) stride); + // 10-11: 位深度 + headerBuffer.putShort((short) bpp); - // 写入像素数据 - for (int y = image.getHeight() - 1; y >= 0; y--) { - for (int x = 0; x < image.getWidth(); x++) { - int rgb = image.getRGB(x, y); - // 写入BGR格式(BMP格式要求) - // B - baos.write(rgb & 0xFF); - // G - baos.write((rgb >> 8) & 0xFF); - // R - baos.write((rgb >> 16) & 0xFF); - } - // 行对齐(每行必须是4字节的倍数) - int padding = (4 - (image.getWidth() * 3) % 4) % 4; - for (int i = 0; i < padding; i++) { - baos.write(0); + // 12-43: 剩余32字节,严格参照样本文件(out.txt)填充 + headerBuffer.put(new byte[]{ + // 12 bytes of zeros + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 20 bytes of specific data from sample + (byte)0x44, (byte)0x3A, (byte)0x5C, (byte)0xD7, (byte)0xD4, (byte)0xB6, (byte)0xAF, (byte)0xBC, + (byte)0xEC, (byte)0xB2, (byte)0xE2, (byte)0xC6, (byte)0xBD, (byte)0xCC, (byte)0xA8, (byte)0x5C, + (byte)0x4C, (byte)0xAB, 0x00, 0x00 + }); + + // 将44字节头部一次性写入 + out.write(headerBuffer.array()); + + // --- 写入像素数据 --- + // 为确保数据布局的绝对正确,放弃直接访问数据缓冲区(getDataBuffer), + // 改为使用逐点扫描(getSample)的方式。这可以避免任何因Java内部内存对齐 + // 或行填充(padding)导致的图像数据错位问题。 + for (int r = 0; r < targetHeight; r++) { + for (int c = 0; c < targetWidth; c++) { + // getSample(x, y, band) 返回的是一个int, 但其值就是16位的ushort值 + int pixelValue = newImage.getRaster().getSample(c, r, 0); + // 依旧使用大端序写入像素数据,这是上次测试得出的结论 + out.writeShort((short) pixelValue); + } } } - return baos.toByteArray(); } - /** - * 将BMP格式的字节数组转换为BIN格式 - * @param bmpData BMP格式的字节数组 - * @return BIN格式的字节数组 - */ - public static byte[] convertBMPToBIN(byte[] bmpData) { - // 跳过BMP文件头(54字节) - int offset = 54; - int width = ByteBuffer.wrap(bmpData, 18, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); - int height = ByteBuffer.wrap(bmpData, 22, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); - // 计算每行的字节数(包括对齐) - int rowSize = ((width * 3 + 3) / 4) * 4; - - // 创建BIN数据数组 - byte[] binData = new byte[width * height]; - int binIndex = 0; - - // 转换像素数据 - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int bmpIndex = offset + (height - 1 - y) * rowSize + x * 3; - // 将BGR转换为灰度值 - int b = bmpData[bmpIndex] & 0xFF; - int g = bmpData[bmpIndex + 1] & 0xFF; - int r = bmpData[bmpIndex + 2] & 0xFF; - // 使用加权平均计算灰度值 - int gray = (int) (0.299 * r + 0.587 * g + 0.114 * b); - // 二值化(阈值设为128) - binData[binIndex++] = (byte) (gray > 128 ? 1 : 0); - } - } - - return binData; - } - - /** - * 将BufferedImage直接转换为BIN格式 - * @param image BufferedImage对象 - * @return BIN格式的字节数组 - * @throws IOException 如果转换过程中发生IO错误 - */ - public static byte[] convertToBIN(BufferedImage image) throws IOException { - byte[] bmpData = convertToBMP(image); - return convertBMPToBIN(bmpData); - } - - /** - * 写入BMP文件头 - * @param out 输出流 - * @param width 图片宽度 - * @param height 图片高度 - * @throws IOException 如果写入过程中发生IO错误 - */ - private static void writeBMPHeader(OutputStream out, int width, int height) throws IOException { - // 文件头(14字节) - out.write('B'); - out.write('M'); - writeInt(out, 54 + width * height * 3); // 文件大小 - writeInt(out, 0); // 保留 - writeInt(out, 54); // 数据偏移量 - - // 信息头(40字节) - writeInt(out, 40); // 信息头大小 - writeInt(out, width); // 宽度 - writeInt(out, height); // 高度 - writeShort(out, 1); // 颜色平面数 - writeShort(out, 24); // 每像素位数 - writeInt(out, 0); // 压缩方式 - writeInt(out, width * height * 3); // 图像数据大小 - writeInt(out, 0); // 水平分辨率 - writeInt(out, 0); // 垂直分辨率 - writeInt(out, 0); // 使用的颜色数 - writeInt(out, 0); // 重要颜色数 - } - - /** - * 写入一个整数(小端序) - * @param out 输出流 - * @param value 要写入的整数 - * @throws IOException 如果写入过程中发生IO错误 - */ - private static void writeInt(OutputStream out, int value) throws IOException { - out.write(value & 0xFF); - out.write((value >> 8) & 0xFF); - out.write((value >> 16) & 0xFF); - out.write((value >> 24) & 0xFF); - } - - /** - * 写入一个短整数(小端序) - * @param out 输出流 - * @param value 要写入的短整数 - * @throws IOException 如果写入过程中发生IO错误 - */ - private static void writeShort(OutputStream out, int value) throws IOException { - out.write(value & 0xFF); - out.write((value >> 8) & 0xFF); - } - - - /** - * 将BufferedImage保存为BMP文件 - * @param image BufferedImage对象 - * @param filePath 保存路径 - * @throws IOException 如果保存过程中发生IO错误 - */ - public static void saveAsBMP(BufferedImage image, String filePath) throws IOException { - byte[] bmpData = convertToBMP(image); - try (FileOutputStream fos = new FileOutputStream(filePath)) { - fos.write(bmpData); - } - } - - /** - * 将BufferedImage保存为BIN文件 - * @param image BufferedImage对象 - * @param filePath 保存路径 - * @throws IOException 如果保存过程中发生IO错误 - */ - public static void saveAsBIN(BufferedImage image, String filePath) throws IOException { - byte[] binData = convertToBIN(image); - try (FileOutputStream fos = new FileOutputStream(filePath)) { - fos.write(binData); - } - } - - /** - * 将BMP数据保存为文件 - * @param bmpData BMP格式的字节数组 - * @param filePath 保存路径 - * @throws IOException 如果保存过程中发生IO错误 - */ - public static void saveBMPToFile(byte[] bmpData, String filePath) throws IOException { - try (FileOutputStream fos = new FileOutputStream(filePath)) { - fos.write(bmpData); - } - } - - /** - * 将BIN数据保存为文件 - * @param binData BIN格式的字节数组 - * @param filePath 保存路径 - * @throws IOException 如果保存过程中发生IO错误 - */ - public static void saveBINToFile(byte[] binData, String filePath) throws IOException { - try (FileOutputStream fos = new FileOutputStream(filePath)) { - fos.write(binData); - } - } } diff --git a/njcn-db/mybatis-plus/pom.xml b/njcn-db/mybatis-plus/pom.xml index ebfb424..18af2da 100644 --- a/njcn-db/mybatis-plus/pom.xml +++ b/njcn-db/mybatis-plus/pom.xml @@ -74,6 +74,13 @@ ${druid.version} + + + + + + + mysql diff --git a/njcn-plugin/README.md b/njcn-plugin/README.md new file mode 100644 index 0000000..ebde1cc --- /dev/null +++ b/njcn-plugin/README.md @@ -0,0 +1,4 @@ +# 本模块用于收录各种插件工具 +## HttpClient模块 + + 封装RestTemplate用于远程接口访问; \ No newline at end of file diff --git a/njcn-plugin/RestTemplate-plugin/pom.xml b/njcn-plugin/RestTemplate-plugin/pom.xml new file mode 100644 index 0000000..3e7f8d8 --- /dev/null +++ b/njcn-plugin/RestTemplate-plugin/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + com.njcn + njcn-plugin + 1.0.0 + + + RestTemplate-plugin + + + 8 + 8 + UTF-8 + + + + + + com.njcn + njcn-common + 0.0.1 + + + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + + \ No newline at end of file diff --git a/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/config/RestTemplateConfig.java b/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/config/RestTemplateConfig.java new file mode 100644 index 0000000..743d203 --- /dev/null +++ b/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/config/RestTemplateConfig.java @@ -0,0 +1,53 @@ +package com.njcn.http.config; + + +import com.njcn.http.util.RestTemplateUtil; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author hongawen + * @version 1.0 + * @data 2025/6/17 11:15 + */ +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + // 创建连接池管理器 + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + // 设置最大连接数 + connectionManager.setMaxTotal(200); + // 设置每个主机的最大连接数 + connectionManager.setDefaultMaxPerRoute(20); + + // 创建HttpClient + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .build(); + + // 创建HttpComponentsClientHttpRequestFactory + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + // 设置连接超时时间(毫秒) + factory.setConnectTimeout(5000); + // 设置读取超时时间(毫秒) + factory.setReadTimeout(5000); + // 设置连接不够用的等待时间(毫秒) + factory.setConnectionRequestTimeout(2000); + + return new RestTemplate(factory); + } + + @Bean + @ConditionalOnMissingBean + public RestTemplateUtil restTemplateUtil(RestTemplate restTemplate) { + return new RestTemplateUtil(restTemplate); + } +} diff --git a/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/util/RestTemplateUtil.java b/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/util/RestTemplateUtil.java new file mode 100644 index 0000000..16f122e --- /dev/null +++ b/njcn-plugin/RestTemplate-plugin/src/main/java/com/njcn/http/util/RestTemplateUtil.java @@ -0,0 +1,291 @@ +package com.njcn.http.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.*; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.File; +import java.util.Map; + +/** + * @author hongawen + * @version 1.0 + * @data 2025/6/17 11:18 + */ +@Slf4j +public class RestTemplateUtil { + + private final RestTemplate restTemplate; + + public RestTemplateUtil(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + /** + * GET请求 + */ + public T get(String url, Class responseType) { + return get(url, null, null, responseType); + } + + /** + * GET请求(带参数) + */ + public T get(String url, Map params, Class responseType) { + return get(url, params, null, responseType); + } + + /** + * GET请求(带参数和请求头) + */ + public T get(String url, Map params, HttpHeaders headers, Class responseType) { + try { + // 构建URL(带参数) + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); + if (params != null) { + params.forEach(builder::queryParam); + } + + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + if (headers != null) { + requestHeaders.addAll(headers); + } + + // 发送请求 + HttpEntity requestEntity = new HttpEntity<>(requestHeaders); + ResponseEntity response = restTemplate.exchange( + builder.build().encode().toUri(), + HttpMethod.GET, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("GET请求异常: {}", e.getMessage(), e); + throw new RuntimeException("GET请求异常", e); + } + } + + /** + * POST请求(JSON数据) + */ + public T postJson(String url, Object body, Class responseType) { + return postJson(url, body, null, responseType); + } + + /** + * POST请求(JSON数据,带请求头) + */ + public T postJson(String url, Object body, HttpHeaders headers, Class responseType) { + try { + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.APPLICATION_JSON); + if (headers != null) { + requestHeaders.addAll(headers); + } + + // 发送请求 + HttpEntity requestEntity = new HttpEntity<>(body, requestHeaders); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("POST请求异常: {}", e.getMessage(), e); + throw new RuntimeException("POST请求异常", e); + } + } + + /** + * POST请求(表单数据) + */ + public T postForm(String url, Map formData, Class responseType) { + return postForm(url, formData, null, responseType); + } + + /** + * POST请求(表单数据,带请求头) + */ + public T postForm(String url, Map formData, HttpHeaders headers, Class responseType) { + try { + // 构建表单数据 + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + if (formData != null) { + formData.forEach((key, value) -> requestBody.add(key, value)); + } + + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + if (headers != null) { + requestHeaders.addAll(headers); + } + + // 发送请求 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, requestHeaders); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("POST表单请求异常: {}", e.getMessage(), e); + throw new RuntimeException("POST表单请求异常", e); + } + } + + /** + * PUT请求 + */ + public T put(String url, Object body, Class responseType) { + return put(url, body, null, responseType); + } + + /** + * PUT请求(带请求头) + */ + public T put(String url, Object body, HttpHeaders headers, Class responseType) { + try { + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.APPLICATION_JSON); + if (headers != null) { + requestHeaders.addAll(headers); + } + + // 发送请求 + HttpEntity requestEntity = new HttpEntity<>(body, requestHeaders); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.PUT, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("PUT请求异常: {}", e.getMessage(), e); + throw new RuntimeException("PUT请求异常", e); + } + } + + /** + * DELETE请求 + */ + public T delete(String url, Class responseType) { + return delete(url, null, responseType); + } + + /** + * DELETE请求(带请求头) + */ + public T delete(String url, HttpHeaders headers, Class responseType) { + try { + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + if (headers != null) { + requestHeaders.addAll(headers); + } + + // 发送请求 + HttpEntity requestEntity = new HttpEntity<>(requestHeaders); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.DELETE, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("DELETE请求异常: {}", e.getMessage(), e); + throw new RuntimeException("DELETE请求异常", e); + } + } + + /** + * 文件上传 + */ + public T uploadFile(String url, File file, String fileParamName, Map params, Class responseType) { + try { + // 构建表单数据 + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add(fileParamName, new FileSystemResource(file)); + if (params != null) { + params.forEach(requestBody::add); + } + + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + + // 发送请求 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, requestHeaders); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + responseType + ); + + return response.getBody(); + } catch (Exception e) { + log.error("文件上传异常: {}", e.getMessage(), e); + throw new RuntimeException("文件上传异常", e); + } + } + + /** + * 文件上传 + */ + public ResponseEntity uploadFile(String url, File file) { + try { + // 构建表单数据 + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add("file", new FileSystemResource(file)); + + // 构建请求头 + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + + // 发送请求 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, requestHeaders); + return restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + String.class + ); + } catch (Exception e) { + log.error("文件上传异常: {}", e.getMessage(), e); + throw new RuntimeException("文件上传异常", e); + } + } + + /** + * 文件下载 + */ + public byte[] downloadFile(String url) { + try { + ResponseEntity response = restTemplate.getForEntity(url, byte[].class); + return response.getBody(); + } catch (Exception e) { + log.error("文件下载异常: {}", e.getMessage(), e); + throw new RuntimeException("文件下载异常", e); + } + } +} \ No newline at end of file diff --git a/njcn-plugin/RestTemplate-plugin/src/main/resources/META-INF/spring.factories b/njcn-plugin/RestTemplate-plugin/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..8503a87 --- /dev/null +++ b/njcn-plugin/RestTemplate-plugin/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.njcn.http.config.RestTemplateConfig \ No newline at end of file diff --git a/njcn-plugin/pom.xml b/njcn-plugin/pom.xml new file mode 100644 index 0000000..3dc1a65 --- /dev/null +++ b/njcn-plugin/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + com.njcn + BasicDependVersion + 1.0.0 + + pom + njcn-plugin + + RestTemplate-plugin + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/njcn-springboot/readMe.md b/njcn-springboot/README.md similarity index 100% rename from njcn-springboot/readMe.md rename to njcn-springboot/README.md diff --git a/njcn-springboot/spingboot2.3.12/src/main/java/com/njcn/web/utils/ThreePhaseUnbalance.java b/njcn-springboot/spingboot2.3.12/src/main/java/com/njcn/web/utils/ThreePhaseUnbalance.java new file mode 100644 index 0000000..53b7df0 --- /dev/null +++ b/njcn-springboot/spingboot2.3.12/src/main/java/com/njcn/web/utils/ThreePhaseUnbalance.java @@ -0,0 +1,140 @@ +package com.njcn.web.utils; + +/** + * @author hongawen + * @version 1.0 + * @data 2025/2/25 13:24 + */ +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; + +public class ThreePhaseUnbalance { + + // 定义复数类 + static class Complex { + BigDecimal real; // 复数的实部 + BigDecimal imag; // 复数的虚部 + + // 构造函数,初始化复数的实部和虚部 + public Complex(BigDecimal real, BigDecimal imag) { + this.real = real; + this.imag = imag; + } + + // 复数加法 + public Complex add(Complex other) { + return new Complex(this.real.add(other.real), this.imag.add(other.imag)); + } + + // 复数乘法 + public Complex multiply(Complex other) { + BigDecimal real = this.real.multiply(other.real).subtract(this.imag.multiply(other.imag)); + BigDecimal imag = this.real.multiply(other.imag).add(this.imag.multiply(other.real)); + return new Complex(real, imag); + } + + // 复数除以一个实数 + public Complex divide(BigDecimal divisor) { + return new Complex(this.real.divide(divisor, MathContext.DECIMAL128), + this.imag.divide(divisor, MathContext.DECIMAL128)); + } + + // 计算复数的模(幅值) + public BigDecimal magnitude() { + return BigDecimal.valueOf(Math.sqrt(real.multiply(real).add(imag.multiply(imag)).doubleValue())); + } + } + + // 将幅值和角度(角度制)转换为复数 + public static Complex fromPolar(BigDecimal magnitude, BigDecimal angleDegrees) { + double angleRadians = Math.toRadians(angleDegrees.doubleValue()); // 将角度转换为弧度 + BigDecimal real = magnitude.multiply(BigDecimal.valueOf(Math.cos(angleRadians))); // 计算实部 + BigDecimal imag = magnitude.multiply(BigDecimal.valueOf(Math.sin(angleRadians))); // 计算虚部 + return new Complex(real, imag); + } + + // 计算三相电压的对称分量(零序、正序、负序) + public static Complex[] calculateSymmetricalComponents(Complex va, Complex vb, Complex vc) { + // 120度相位因子 a = e^(j120°) = -0.5 + j * sqrt(3)/2 + Complex a = new Complex(BigDecimal.valueOf(-0.5), BigDecimal.valueOf(Math.sqrt(3) / 2)); + // 240度相位因子 a^2 = e^(j240°) = -0.5 - j * sqrt(3)/2 + Complex a2 = a.multiply(a); + + // 零序分量 V0 = (Va + Vb + Vc) / 3 + Complex v0 = va.add(vb).add(vc).divide(BigDecimal.valueOf(3)); + + // 正序分量 V1 = (Va + a * Vb + a^2 * Vc) / 3 + Complex v1 = va.add(a.multiply(vb)).add(a2.multiply(vc)).divide(BigDecimal.valueOf(3)); + + // 负序分量 V2 = (Va + a^2 * Vb + a * Vc) / 3 + Complex v2 = va.add(a2.multiply(vb)).add(a.multiply(vc)).divide(BigDecimal.valueOf(3)); + + // 返回零序、正序、负序分量 + return new Complex[]{v0, v1, v2}; + } + + // 计算三相电压的不平衡度 + public static void calculateUnbalance(Complex v0, Complex v1, Complex v2) { + // 计算零序、正序、负序分量的幅值 + BigDecimal v0Mag = v0.magnitude(); // 零序分量幅值 + BigDecimal v1Mag = v1.magnitude(); // 正序分量幅值 + BigDecimal v2Mag = v2.magnitude(); // 负序分量幅值 + + // 计算不平衡度 + BigDecimal zeroSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ? + v0Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO; + + BigDecimal negativeSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ? + v2Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO; + + BigDecimal positiveSequenceUnbalance = BigDecimal.valueOf(100); // 正序电压不平衡度通常为100% + + // 输出结果,保留2位小数 + System.out.printf("零序电压不平衡度: %.2f%%\n", zeroSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue()); + System.out.printf("负序电压不平衡度: %.2f%%\n", negativeSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue()); + System.out.printf("正序电压不平衡度: %.2f%%\n", positiveSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + + public static void main(String[] args) { + + // 输入三相电压的幅值和相位角(角度制) + System.out.println("请输入A相电压的幅值(单位:V):"); + BigDecimal vaMagnitude = new BigDecimal(57.74); + System.out.println("请输入A相电压的相位角(单位:度):"); + BigDecimal vaAngle = new BigDecimal(0); + + System.out.println("请输入B相电压的幅值(单位:V):"); + BigDecimal vbMagnitude =new BigDecimal(57.74*0.9); + System.out.println("请输入B相电压的相位角(单位:度):"); + BigDecimal vbAngle = new BigDecimal(-122); + + System.out.println("请输入C相电压的幅值(单位:V):"); + BigDecimal vcMagnitude =new BigDecimal(57.74); + System.out.println("请输入C相电压的相位角(单位:度):"); + BigDecimal vcAngle = new BigDecimal(118); + + // 将幅值和角度转换为复数 + Complex va = fromPolar(vaMagnitude, vaAngle); + Complex vb = fromPolar(vbMagnitude, vbAngle); + Complex vc = fromPolar(vcMagnitude, vcAngle); + + // 计算对称分量 + Complex[] symmetricalComponents = calculateSymmetricalComponents(va, vb, vc); + Complex v0 = symmetricalComponents[0]; // 零序分量 + Complex v1 = symmetricalComponents[1]; // 正序分量 + Complex v2 = symmetricalComponents[2]; // 负序分量 + + // 输出对称分量 + System.out.printf("零序分量: %.2f + j%.2f\n", v0.real.setScale(2, RoundingMode.HALF_UP).doubleValue(), + v0.imag.setScale(2, RoundingMode.HALF_UP).doubleValue()); + System.out.printf("正序分量: %.2f + j%.2f\n", v1.real.setScale(2, RoundingMode.HALF_UP).doubleValue(), + v1.imag.setScale(2, RoundingMode.HALF_UP).doubleValue()); + System.out.printf("负序分量: %.2f + j%.2f\n", v2.real.setScale(2, RoundingMode.HALF_UP).doubleValue(), + v2.imag.setScale(2, RoundingMode.HALF_UP).doubleValue()); + + // 计算并输出不平衡度 + calculateUnbalance(v0, v1, v2); + + } +} diff --git a/pom.xml b/pom.xml index f1711d7..cd32a18 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ njcn-common njcn-springboot njcn-db + njcn-plugin pom 版本控制项目