🐛 修复 MinIO 预签名走外网 HTTPS,并过滤无效应收应付变更记录
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -95,7 +95,13 @@ public class FileConfig {
|
|||||||
FileStorageProperties.StoreProperties mergedProperties = new FileStorageProperties.StoreProperties();
|
FileStorageProperties.StoreProperties mergedProperties = new FileStorageProperties.StoreProperties();
|
||||||
String fileStoragePrefix = "file.storage." + prefix;
|
String fileStoragePrefix = "file.storage." + prefix;
|
||||||
mergedProperties.setEndpoint(firstNotBlank(properties.getEndpoint(), environment.getProperty(fileStoragePrefix + ".endpoint"), environment.getProperty(prefix + ".endpoint")));
|
mergedProperties.setEndpoint(firstNotBlank(properties.getEndpoint(), environment.getProperty(fileStoragePrefix + ".endpoint"), environment.getProperty(prefix + ".endpoint")));
|
||||||
mergedProperties.setTransformEndpoint(firstNotBlank(properties.getTransformEndpoint(), environment.getProperty(fileStoragePrefix + ".transform-endpoint"), environment.getProperty(prefix + ".transform-endpoint")));
|
mergedProperties.setTransformEndpoint(firstNotBlank(
|
||||||
|
properties.getTransformEndpoint(),
|
||||||
|
environment.getProperty(fileStoragePrefix + ".transform-endpoint"),
|
||||||
|
environment.getProperty(prefix + ".transform-endpoint"),
|
||||||
|
// 兼容 blade-resource / oss 配置,避免仅改对象存储外网地址时分片上传仍走内网 HTTP
|
||||||
|
environment.getProperty("oss.transform-endpoint")
|
||||||
|
));
|
||||||
mergedProperties.setAccessKeyId(firstNotBlank(properties.getAccessKeyId(), environment.getProperty(fileStoragePrefix + ".access-key-id"), environment.getProperty(fileStoragePrefix + ".access-key"), environment.getProperty(prefix + ".access-key-id"), environment.getProperty(prefix + ".access-key")));
|
mergedProperties.setAccessKeyId(firstNotBlank(properties.getAccessKeyId(), environment.getProperty(fileStoragePrefix + ".access-key-id"), environment.getProperty(fileStoragePrefix + ".access-key"), environment.getProperty(prefix + ".access-key-id"), environment.getProperty(prefix + ".access-key")));
|
||||||
mergedProperties.setAccessKeySecret(firstNotBlank(properties.getAccessKeySecret(), environment.getProperty(fileStoragePrefix + ".access-key-secret"), environment.getProperty(fileStoragePrefix + ".secret-key"), environment.getProperty(prefix + ".access-key-secret"), environment.getProperty(prefix + ".secret-key")));
|
mergedProperties.setAccessKeySecret(firstNotBlank(properties.getAccessKeySecret(), environment.getProperty(fileStoragePrefix + ".access-key-secret"), environment.getProperty(fileStoragePrefix + ".secret-key"), environment.getProperty(prefix + ".access-key-secret"), environment.getProperty(prefix + ".secret-key")));
|
||||||
mergedProperties.setBucketName(firstNotBlank(properties.getBucketName(), environment.getProperty(fileStoragePrefix + ".bucket-name"), environment.getProperty(prefix + ".bucket-name")));
|
mergedProperties.setBucketName(firstNotBlank(properties.getBucketName(), environment.getProperty(fileStoragePrefix + ".bucket-name"), environment.getProperty(prefix + ".bucket-name")));
|
||||||
|
|||||||
+84
-6
@@ -25,11 +25,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.file.service.impl;
|
package org.springblade.file.service.impl;
|
||||||
|
|
||||||
|
import io.minio.ComposeObjectArgs;
|
||||||
|
import io.minio.ComposeSource;
|
||||||
import io.minio.GetObjectArgs;
|
import io.minio.GetObjectArgs;
|
||||||
import io.minio.GetPresignedObjectUrlArgs;
|
import io.minio.GetPresignedObjectUrlArgs;
|
||||||
import io.minio.MinioClient;
|
import io.minio.MinioClient;
|
||||||
import io.minio.ComposeObjectArgs;
|
|
||||||
import io.minio.ComposeSource;
|
|
||||||
import io.minio.PutObjectArgs;
|
import io.minio.PutObjectArgs;
|
||||||
import io.minio.RemoveObjectArgs;
|
import io.minio.RemoveObjectArgs;
|
||||||
import io.minio.UploadObjectArgs;
|
import io.minio.UploadObjectArgs;
|
||||||
@@ -42,6 +42,7 @@ import org.springblade.file.config.FileStorageProperties;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
@@ -64,6 +65,7 @@ public class MinioFileServiceImpl extends AbstractFileService {
|
|||||||
|
|
||||||
private final FileStorageProperties.StoreProperties properties;
|
private final FileStorageProperties.StoreProperties properties;
|
||||||
private final MinioClient minioClient;
|
private final MinioClient minioClient;
|
||||||
|
private volatile MinioClient transformPresignClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String initiateMultipartUpload(String objectKey) {
|
public String initiateMultipartUpload(String objectKey) {
|
||||||
@@ -73,9 +75,13 @@ public class MinioFileServiceImpl extends AbstractFileService {
|
|||||||
@Override
|
@Override
|
||||||
public String getUploadPartUrl(String objectKey, String uploadId, Integer partNumber, Long expires) {
|
public String getUploadPartUrl(String objectKey, String uploadId, Integer partNumber, Long expires) {
|
||||||
try {
|
try {
|
||||||
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
String presignedUrl = resolvePresignClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||||
.method(Method.PUT).bucket(properties.getBucketName()).object(partObjectKey(objectKey, uploadId, partNumber))
|
.method(Method.PUT)
|
||||||
.expiry(Math.toIntExact(resolveExpires(expires))).build());
|
.bucket(properties.getBucketName())
|
||||||
|
.object(partObjectKey(objectKey, uploadId, partNumber))
|
||||||
|
.expiry(Math.toIntExact(resolveExpires(expires)))
|
||||||
|
.build());
|
||||||
|
return applyTransformPathPrefix(presignedUrl);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
log.error("MinIO获取分片上传链接异常 objectKey:{}", objectKey, exception);
|
log.error("MinIO获取分片上传链接异常 objectKey:{}", objectKey, exception);
|
||||||
throw new ServiceException("获取分片上传链接异常");
|
throw new ServiceException("获取分片上传链接异常");
|
||||||
@@ -143,12 +149,13 @@ public class MinioFileServiceImpl extends AbstractFileService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
String presignedUrl = resolvePresignClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||||
.method(Method.GET)
|
.method(Method.GET)
|
||||||
.bucket(properties.getBucketName())
|
.bucket(properties.getBucketName())
|
||||||
.object(resolveObjectKey(objectKey))
|
.object(resolveObjectKey(objectKey))
|
||||||
.expiry(Math.toIntExact(resolveExpires(expires)))
|
.expiry(Math.toIntExact(resolveExpires(expires)))
|
||||||
.build());
|
.build());
|
||||||
|
return applyTransformPathPrefix(presignedUrl);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
log.error("MinIO获取文件链接异常 objectKey:{}", objectKey, exception);
|
log.error("MinIO获取文件链接异常 objectKey:{}", objectKey, exception);
|
||||||
throw new ServiceException("MinIO获取文件链接异常");
|
throw new ServiceException("MinIO获取文件链接异常");
|
||||||
@@ -170,6 +177,77 @@ public class MinioFileServiceImpl extends AbstractFileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器侧预签名优先使用外网地址,避免 HTTPS 页面访问 HTTP MinIO 被 Mixed Content 拦截。
|
||||||
|
* MinIO SDK 不允许 endpoint 带 path,因此外网地址中的 /oss 等前缀在签名后单独拼回。
|
||||||
|
*/
|
||||||
|
private MinioClient resolvePresignClient() {
|
||||||
|
String transformEndpoint = properties.getTransformEndpoint();
|
||||||
|
if (StringUtil.isBlank(transformEndpoint)) {
|
||||||
|
return minioClient;
|
||||||
|
}
|
||||||
|
MinioClient cachedClient = transformPresignClient;
|
||||||
|
if (cachedClient != null) {
|
||||||
|
return cachedClient;
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
if (transformPresignClient == null) {
|
||||||
|
transformPresignClient = MinioClient.builder()
|
||||||
|
.endpoint(resolveTransformOrigin(transformEndpoint))
|
||||||
|
.credentials(properties.getAccessKeyId(), properties.getAccessKeySecret())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return transformPresignClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTransformOrigin(String transformEndpoint) {
|
||||||
|
URI transformUri = URI.create(transformEndpoint.trim());
|
||||||
|
if (StringUtil.isBlank(transformUri.getScheme()) || StringUtil.isBlank(transformUri.getHost())) {
|
||||||
|
throw new ServiceException("MinIO外网地址配置不正确");
|
||||||
|
}
|
||||||
|
StringBuilder origin = new StringBuilder();
|
||||||
|
origin.append(transformUri.getScheme()).append("://").append(transformUri.getHost());
|
||||||
|
if (transformUri.getPort() > 0) {
|
||||||
|
origin.append(':').append(transformUri.getPort());
|
||||||
|
}
|
||||||
|
return origin.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String applyTransformPathPrefix(String presignedUrl) {
|
||||||
|
String transformEndpoint = properties.getTransformEndpoint();
|
||||||
|
if (StringUtil.isBlank(transformEndpoint) || StringUtil.isBlank(presignedUrl)) {
|
||||||
|
return presignedUrl;
|
||||||
|
}
|
||||||
|
String pathPrefix = normalizePathPrefix(URI.create(transformEndpoint.trim()).getPath());
|
||||||
|
if (StringUtil.isBlank(pathPrefix)) {
|
||||||
|
return presignedUrl;
|
||||||
|
}
|
||||||
|
URI url = URI.create(presignedUrl);
|
||||||
|
String path = url.getRawPath() == null ? "" : url.getRawPath();
|
||||||
|
if (path.equals(pathPrefix) || path.startsWith(pathPrefix + "/")) {
|
||||||
|
return presignedUrl;
|
||||||
|
}
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append(url.getScheme()).append("://").append(url.getHost());
|
||||||
|
if (url.getPort() > 0) {
|
||||||
|
result.append(':').append(url.getPort());
|
||||||
|
}
|
||||||
|
result.append(pathPrefix).append(path);
|
||||||
|
if (StringUtil.isNotBlank(url.getRawQuery())) {
|
||||||
|
result.append('?').append(url.getRawQuery());
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizePathPrefix(String path) {
|
||||||
|
if (StringUtil.isBlank(path) || "/".equals(path)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String normalized = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
|
||||||
|
return normalized.startsWith("/") ? normalized : "/" + normalized;
|
||||||
|
}
|
||||||
|
|
||||||
private String resolveObjectKey(String objectKey) {
|
private String resolveObjectKey(String objectKey) {
|
||||||
if (StringUtil.isBlank(properties.getRootDirectory())) {
|
if (StringUtil.isBlank(properties.getRootDirectory())) {
|
||||||
return objectKey;
|
return objectKey;
|
||||||
|
|||||||
+67
-16
@@ -432,13 +432,23 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<ReceivablePayableChangeRecordVO> changeRecords(IPage<?> page, Long detailId) {
|
public IPage<ReceivablePayableChangeRecordVO> changeRecords(IPage<?> page, Long detailId) {
|
||||||
LambdaQueryWrapper<ReceivablePayableChangeRecord> wrapper = Wrappers.<ReceivablePayableChangeRecord>lambdaQuery()
|
List<ReceivablePayableChangeRecord> meaningfulRecords = changeRecordMapper.selectList(
|
||||||
|
Wrappers.<ReceivablePayableChangeRecord>lambdaQuery()
|
||||||
.eq(ReceivablePayableChangeRecord::getDetailId, detailId)
|
.eq(ReceivablePayableChangeRecord::getDetailId, detailId)
|
||||||
.eq(ReceivablePayableChangeRecord::getIsDeleted, 0)
|
.eq(ReceivablePayableChangeRecord::getIsDeleted, 0)
|
||||||
.orderByDesc(ReceivablePayableChangeRecord::getAdjustTime);
|
.orderByDesc(ReceivablePayableChangeRecord::getAdjustTime))
|
||||||
IPage<ReceivablePayableChangeRecord> entityPage = changeRecordMapper.selectPage(new Page<>(page.getCurrent(), page.getSize()), wrapper);
|
.stream()
|
||||||
Page<ReceivablePayableChangeRecordVO> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
|
.filter(record -> hasMeaningfulChange(record.getChangeContent()))
|
||||||
voPage.setRecords(entityPage.getRecords().stream().map(record -> Objects.requireNonNull(
|
.toList();
|
||||||
|
long current = page.getCurrent();
|
||||||
|
long size = page.getSize();
|
||||||
|
int fromIndex = (int) Math.max(0L, (current - 1L) * size);
|
||||||
|
int toIndex = (int) Math.min(meaningfulRecords.size(), fromIndex + size);
|
||||||
|
List<ReceivablePayableChangeRecord> pageRecords = fromIndex >= meaningfulRecords.size()
|
||||||
|
? List.of()
|
||||||
|
: meaningfulRecords.subList(fromIndex, toIndex);
|
||||||
|
Page<ReceivablePayableChangeRecordVO> voPage = new Page<>(current, size, meaningfulRecords.size());
|
||||||
|
voPage.setRecords(pageRecords.stream().map(record -> Objects.requireNonNull(
|
||||||
BeanUtil.copyProperties(record, ReceivablePayableChangeRecordVO.class))).toList());
|
BeanUtil.copyProperties(record, ReceivablePayableChangeRecordVO.class))).toList());
|
||||||
return voPage;
|
return voPage;
|
||||||
}
|
}
|
||||||
@@ -613,14 +623,8 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
appendChange(changes, "运输单价", existing.getUnitPrice(), adjusted.getUnitPrice());
|
appendChange(changes, "运输单价", existing.getUnitPrice(), adjusted.getUnitPrice());
|
||||||
appendChange(changes, "里程", existing.getMileage(), mileage);
|
appendChange(changes, "里程", existing.getMileage(), mileage);
|
||||||
appendChange(changes, "运输费", existing.getFreightAmount(), freightAmount);
|
appendChange(changes, "运输费", existing.getFreightAmount(), freightAmount);
|
||||||
if (!Objects.equals(existing.getRemark(), adjusted.getRemark())) {
|
appendChange(changes, "备注", existing.getRemark(), adjusted.getRemark());
|
||||||
changes.add("【备注】从[" + Objects.toString(existing.getRemark(), "") + "]调整为["
|
appendChange(changes, "变更原因", existing.getChangeReason(), adjusted.getChangeReason());
|
||||||
+ Objects.toString(adjusted.getRemark(), "") + "]");
|
|
||||||
}
|
|
||||||
if (!Objects.equals(existing.getChangeReason(), adjusted.getChangeReason())) {
|
|
||||||
changes.add("【变更原因】从[" + Objects.toString(existing.getChangeReason(), "") + "]调整为["
|
|
||||||
+ Objects.toString(adjusted.getChangeReason(), "") + "]");
|
|
||||||
}
|
|
||||||
Map<String, Object> oldFeeItems = parseMap(existing.getFeeItemsJson());
|
Map<String, Object> oldFeeItems = parseMap(existing.getFeeItemsJson());
|
||||||
for (String name : allowedFeeItems) {
|
for (String name : allowedFeeItems) {
|
||||||
appendChange(changes, name, decimal(oldFeeItems.get(name)), money(effectiveFeeItems.get(name)));
|
appendChange(changes, name, decimal(oldFeeItems.get(name)), money(effectiveFeeItems.get(name)));
|
||||||
@@ -2367,12 +2371,56 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void appendChange(List<String> changes, String field, String before, String after) {
|
private void appendChange(List<String> changes, String field, String before, String after) {
|
||||||
if (!Objects.equals(before, after)) {
|
String oldValue = normalizeChangeText(before);
|
||||||
changes.add("【" + field + "】从[" + Objects.toString(before, "") + "]调整为["
|
String newValue = normalizeChangeText(after);
|
||||||
+ Objects.toString(after, "") + "]");
|
if (!Objects.equals(oldValue, newValue)) {
|
||||||
|
changes.add("【" + field + "】从[" + oldValue + "]调整为[" + newValue + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变更文本归一化:null / 空白视为相同,避免出现「从[]调整为[]」无效记录
|
||||||
|
*/
|
||||||
|
private String normalizeChangeText(String value) {
|
||||||
|
return value == null ? "" : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否存在实际前后差异;整段内容均无变化时不展示
|
||||||
|
*/
|
||||||
|
private boolean hasMeaningfulChange(String content) {
|
||||||
|
if (Func.isBlank(content)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String text = content.trim();
|
||||||
|
int suffixStart = text.lastIndexOf('(');
|
||||||
|
if (suffixStart > 0 && text.endsWith(")")) {
|
||||||
|
text = text.substring(0, suffixStart);
|
||||||
|
}
|
||||||
|
boolean hasPart = false;
|
||||||
|
for (String part : text.split(";")) {
|
||||||
|
if (Func.isBlank(part)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hasPart = true;
|
||||||
|
if (isMeaningfulChangePart(part.trim())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !hasPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMeaningfulChangePart(String part) {
|
||||||
|
int fromIdx = part.indexOf("从[");
|
||||||
|
int toIdx = part.indexOf("]调整为[");
|
||||||
|
if (fromIdx < 0 || toIdx < fromIdx || !part.endsWith("]")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String before = part.substring(fromIdx + 2, toIdx);
|
||||||
|
String after = part.substring(toIdx + 5, part.length() - 1);
|
||||||
|
return !Objects.equals(before, after);
|
||||||
|
}
|
||||||
|
|
||||||
private String formatValue(BigDecimal value) {
|
private String formatValue(BigDecimal value) {
|
||||||
return money(value).stripTrailingZeros().toPlainString();
|
return money(value).stripTrailingZeros().toPlainString();
|
||||||
}
|
}
|
||||||
@@ -2617,6 +2665,9 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void saveChangeRecord(ReceivablePayableDetail detail, String content, String reason, String lineNo) {
|
private void saveChangeRecord(ReceivablePayableDetail detail, String content, String reason, String lineNo) {
|
||||||
|
if (!hasMeaningfulChange(content)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ReceivablePayableChangeRecord record = new ReceivablePayableChangeRecord();
|
ReceivablePayableChangeRecord record = new ReceivablePayableChangeRecord();
|
||||||
record.setDetailId(detail.getId());
|
record.setDetailId(detail.getId());
|
||||||
record.setLineNo(lineNo);
|
record.setLineNo(lineNo);
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ baidu:
|
|||||||
request-timeout: 30s
|
request-timeout: 30s
|
||||||
token-refresh-advance: 1m
|
token-refresh-advance: 1m
|
||||||
|
|
||||||
|
# 凭证分片上传等走 blade-file,外网地址需单独配置(与 /resource/oss 的 transformEndpoint 保持一致)
|
||||||
|
file:
|
||||||
|
storage:
|
||||||
|
minio:
|
||||||
|
transform-endpoint: ${MINIO_TRANSFORM_ENDPOINT:https://tms-prod.xinghy56.com/oss}
|
||||||
|
|
||||||
powerjob:
|
powerjob:
|
||||||
worker:
|
worker:
|
||||||
server-address: 172.16.203.228:7700
|
server-address: 172.16.203.228:7700
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* BladeX Commercial License Agreement
|
|
||||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
|
||||||
* <p>
|
|
||||||
* Use of this software is governed by the Commercial License Agreement
|
|
||||||
* obtained after purchasing a license from BladeX.
|
|
||||||
* <p>
|
|
||||||
* 1. This software is for development use only under a valid license
|
|
||||||
* from BladeX.
|
|
||||||
* <p>
|
|
||||||
* 2. Redistribution of this software's source code to any third party
|
|
||||||
* without a commercial license is strictly prohibited.
|
|
||||||
* <p>
|
|
||||||
* 3. Licensees may copyright their own code but cannot use segments
|
|
||||||
* from this software for such purposes. Copyright of this software
|
|
||||||
* remains with BladeX.
|
|
||||||
* <p>
|
|
||||||
* Using this software signifies agreement to this License, and the software
|
|
||||||
* must not be used for illegal purposes.
|
|
||||||
* <p>
|
|
||||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
|
||||||
* not liable for any claims arising from secondary or illegal development.
|
|
||||||
* <p>
|
|
||||||
* Author: Chill Zhuang (bladejava@qq.com)
|
|
||||||
*/
|
|
||||||
package org.springblade.core.tool.node;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springblade.core.tool.utils.Func;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 树型节点类
|
|
||||||
*
|
|
||||||
* @author smallchill
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class TreeNode extends BaseNode<TreeNode> {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@JsonSerialize(using = ToStringSerializer.class)
|
|
||||||
private Long key;
|
|
||||||
|
|
||||||
@JsonSerialize(using = ToStringSerializer.class)
|
|
||||||
private Long value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
TreeNode other = (TreeNode) obj;
|
|
||||||
return Func.equals(this.getId(), other.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(id, parentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user