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
版本控制项目