对接mq和模型上传管理和绑定构件,构建树形
This commit is contained in:
24
src/main/java/com/bim/api/config/FileConvertMqConfig.java
Normal file
24
src/main/java/com/bim/api/config/FileConvertMqConfig.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.bim.api.config;
|
||||
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FileConvertMqConfig {
|
||||
private final FileConvertProperties properties;
|
||||
|
||||
public FileConvertMqConfig(FileConvertProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue fileConvertModelQueue() {
|
||||
return new Queue(properties.getModelQueueName(), true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue fileConvertCallbackQueue() {
|
||||
return new Queue(properties.getCallbackQueueName(), true);
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/bim/api/config/FileConvertProperties.java
Normal file
15
src/main/java/com/bim/api/config/FileConvertProperties.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.bim.api.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "file-convert")
|
||||
public class FileConvertProperties {
|
||||
private String secretKey;
|
||||
private String modelQueueName = "bim_engine_queue_file_convert";
|
||||
private String callbackQueueName = "system_file_convert_callback_queue";
|
||||
private String currentLanguage = "zh-CN";
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.bim.api.controller;
|
||||
|
||||
import com.bim.api.entity.ModelManagement;
|
||||
import com.bim.api.service.ModelManagementService;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
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.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/model-management")
|
||||
public class ModelManagementController {
|
||||
private final ModelManagementService modelManagementService;
|
||||
|
||||
public ModelManagementController(ModelManagementService modelManagementService) {
|
||||
this.modelManagementService = modelManagementService;
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public Map<String, Object> list(ModelManagement query) {
|
||||
return ok(modelManagementService.findList(query));
|
||||
}
|
||||
|
||||
@GetMapping("/{modelId}")
|
||||
public Map<String, Object> getInfo(@PathVariable Long modelId) {
|
||||
return ok(modelManagementService.getById(modelId));
|
||||
}
|
||||
|
||||
@PostMapping("/upload")
|
||||
public Map<String, Object> upload(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("modelName") String modelName,
|
||||
@RequestParam("modelType") String modelType) {
|
||||
try {
|
||||
return ok(modelManagementService.upload(file, modelName, modelType));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public Map<String, Object> edit(@RequestBody ModelManagement modelManagement) {
|
||||
try {
|
||||
return ok(modelManagementService.updateById(modelManagement));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{modelId}/switch")
|
||||
public Map<String, Object> switchCurrent(@PathVariable Long modelId) {
|
||||
try {
|
||||
return ok(modelManagementService.switchCurrent(modelId));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{modelIds}")
|
||||
public Map<String, Object> remove(@PathVariable String modelIds) {
|
||||
try {
|
||||
return ok(modelManagementService.deleteWithFiles(modelManagementService.parseIds(modelIds)));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> ok(Object data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("data", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> error(String message) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 500);
|
||||
result.put("message", message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.bim.api.controller;
|
||||
|
||||
import com.bim.api.entity.SysOssConfig;
|
||||
import com.bim.api.service.SysOssConfigService;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/resource/oss/config")
|
||||
public class SysOssConfigController {
|
||||
private final SysOssConfigService ossConfigService;
|
||||
|
||||
public SysOssConfigController(SysOssConfigService ossConfigService) {
|
||||
this.ossConfigService = ossConfigService;
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public Map<String, Object> list(SysOssConfig query) {
|
||||
return ok(ossConfigService.findList(query));
|
||||
}
|
||||
|
||||
@GetMapping("/{ossConfigId}")
|
||||
public Map<String, Object> getInfo(@PathVariable Long ossConfigId) {
|
||||
return ok(ossConfigService.getById(ossConfigId));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Map<String, Object> add(@RequestBody SysOssConfig config) {
|
||||
try {
|
||||
return ok(ossConfigService.save(config));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public Map<String, Object> edit(@RequestBody SysOssConfig config) {
|
||||
try {
|
||||
return ok(ossConfigService.updateById(config));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/changeStatus")
|
||||
public Map<String, Object> changeStatus(@RequestBody SysOssConfig config) {
|
||||
try {
|
||||
return ok(ossConfigService.changeStatus(config.getOssConfigId()));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{ossConfigIds}")
|
||||
public Map<String, Object> remove(@PathVariable String ossConfigIds) {
|
||||
try {
|
||||
return ok(ossConfigService.removeByIds(parseIds(ossConfigIds)));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> parseIds(String ids) {
|
||||
return Arrays.stream(ids.split(","))
|
||||
.filter(id -> !id.isBlank())
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> ok(Object data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("data", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> error(String message) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 500);
|
||||
result.put("message", message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
92
src/main/java/com/bim/api/controller/SysOssController.java
Normal file
92
src/main/java/com/bim/api/controller/SysOssController.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.bim.api.controller;
|
||||
|
||||
import com.bim.api.entity.SysOss;
|
||||
import com.bim.api.service.SysOssService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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.multipart.MultipartFile;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/resource/oss")
|
||||
public class SysOssController {
|
||||
private final SysOssService ossService;
|
||||
|
||||
public SysOssController(SysOssService ossService) {
|
||||
this.ossService = ossService;
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public Map<String, Object> list(SysOss query) {
|
||||
return ok(ossService.findList(query));
|
||||
}
|
||||
|
||||
@GetMapping("/listByIds/{ossIds}")
|
||||
public Map<String, Object> listByIds(@PathVariable String ossIds) {
|
||||
List<Long> ids = parseIds(ossIds);
|
||||
return ok(ossService.listByIds(ids));
|
||||
}
|
||||
|
||||
@PostMapping({"/upload", "/ossUpload"})
|
||||
public Map<String, Object> upload(@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
return ok(ossService.upload(file));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/download/{ossId}")
|
||||
public void download(@PathVariable Long ossId, HttpServletResponse response) throws Exception {
|
||||
ossService.download(ossId, response);
|
||||
}
|
||||
|
||||
@GetMapping("/preview/{ossId}")
|
||||
public Map<String, Object> preview(@PathVariable Long ossId) {
|
||||
try {
|
||||
return ok(ossService.preview(ossId));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{ossIds}")
|
||||
public Map<String, Object> remove(@PathVariable String ossIds) {
|
||||
try {
|
||||
return ok(ossService.deleteWithFiles(parseIds(ossIds)));
|
||||
} catch (Exception e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> parseIds(String ids) {
|
||||
return Arrays.stream(ids.split(","))
|
||||
.filter(id -> !id.isBlank())
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> ok(Object data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("data", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> error(String message) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 500);
|
||||
result.put("message", message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/bim/api/entity/ModelManagement.java
Normal file
32
src/main/java/com/bim/api/entity/ModelManagement.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.bim.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("model_management")
|
||||
public class ModelManagement {
|
||||
@TableId(value = "model_id", type = IdType.ASSIGN_ID)
|
||||
private Long modelId;
|
||||
private String modelName;
|
||||
private String modelType;
|
||||
private Long ossId;
|
||||
private String fileName;
|
||||
private String originalName;
|
||||
private String fileSuffix;
|
||||
private String fileUrl;
|
||||
private Long fileSize;
|
||||
private String convertedUrl;
|
||||
private String convertStatus;
|
||||
private String convertMessage;
|
||||
private String keyCode;
|
||||
private String sheetStr;
|
||||
private String codeData;
|
||||
private String status;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
27
src/main/java/com/bim/api/entity/SysOss.java
Normal file
27
src/main/java/com/bim/api/entity/SysOss.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.bim.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_oss")
|
||||
public class SysOss {
|
||||
@TableId(value = "oss_id", type = IdType.ASSIGN_ID)
|
||||
private Long ossId;
|
||||
private String tenantId;
|
||||
private String fileName;
|
||||
private String originalName;
|
||||
private String fileSuffix;
|
||||
private String url;
|
||||
private String ext1;
|
||||
private Long createDept;
|
||||
private LocalDateTime createTime;
|
||||
private Long createBy;
|
||||
private LocalDateTime updateTime;
|
||||
private Long updateBy;
|
||||
private String service;
|
||||
}
|
||||
34
src/main/java/com/bim/api/entity/SysOssConfig.java
Normal file
34
src/main/java/com/bim/api/entity/SysOssConfig.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.bim.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_oss_config")
|
||||
public class SysOssConfig {
|
||||
@TableId(value = "oss_config_id", type = IdType.AUTO)
|
||||
private Long ossConfigId;
|
||||
private String tenantId;
|
||||
private String configKey;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucketName;
|
||||
private String prefix;
|
||||
private String endpoint;
|
||||
private String domain;
|
||||
private String isHttps;
|
||||
private String region;
|
||||
private String accessPolicy;
|
||||
private String status;
|
||||
private String ext1;
|
||||
private Long createDept;
|
||||
private Long createBy;
|
||||
private LocalDateTime createTime;
|
||||
private Long updateBy;
|
||||
private LocalDateTime updateTime;
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.bim.api.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.bim.api.entity.ModelManagement;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ModelManagementMapper extends BaseMapper<ModelManagement> {
|
||||
}
|
||||
9
src/main/java/com/bim/api/mapper/SysOssConfigMapper.java
Normal file
9
src/main/java/com/bim/api/mapper/SysOssConfigMapper.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.bim.api.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.bim.api.entity.SysOssConfig;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SysOssConfigMapper extends BaseMapper<SysOssConfig> {
|
||||
}
|
||||
9
src/main/java/com/bim/api/mapper/SysOssMapper.java
Normal file
9
src/main/java/com/bim/api/mapper/SysOssMapper.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.bim.api.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.bim.api.entity.SysOss;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SysOssMapper extends BaseMapper<SysOss> {
|
||||
}
|
||||
187
src/main/java/com/bim/api/oss/OssClient.java
Normal file
187
src/main/java/com/bim/api/oss/OssClient.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package com.bim.api.oss;
|
||||
|
||||
import com.bim.api.entity.SysOssConfig;
|
||||
import org.springframework.util.StringUtils;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.ResponseInputStream;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.S3Configuration;
|
||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
|
||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OssClient implements AutoCloseable {
|
||||
private static final String[] CLOUD_SERVICE = {"aliyun", "qcloud", "qiniu", "obs"};
|
||||
|
||||
private final SysOssConfig properties;
|
||||
private final S3Client client;
|
||||
private final S3Presigner presigner;
|
||||
|
||||
public OssClient(SysOssConfig properties) {
|
||||
this.properties = properties;
|
||||
try {
|
||||
StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
|
||||
S3Configuration config = S3Configuration.builder()
|
||||
.chunkedEncodingEnabled(false)
|
||||
.pathStyleAccessEnabled(!containsCloudService(properties.getEndpoint()))
|
||||
.build();
|
||||
|
||||
this.client = S3Client.builder()
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.endpointOverride(URI.create(getEndpoint()))
|
||||
.region(region())
|
||||
.serviceConfiguration(config)
|
||||
.build();
|
||||
this.presigner = S3Presigner.builder()
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.endpointOverride(URI.create(getDomain()))
|
||||
.region(region())
|
||||
.serviceConfiguration(config)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
throw new OssException("OSS配置错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public UploadResult upload(InputStream inputStream, long size, String suffix, String contentType) {
|
||||
String key = nextObjectKey(suffix);
|
||||
try {
|
||||
PutObjectResponse response = client.putObject(PutObjectRequest.builder()
|
||||
.bucket(properties.getBucketName())
|
||||
.key(key)
|
||||
.contentType(StringUtils.hasText(contentType) ? contentType : mimeType(suffix))
|
||||
.build(), RequestBody.fromInputStream(inputStream, size));
|
||||
return UploadResult.builder()
|
||||
.url(getUrl() + "/" + key)
|
||||
.filename(key)
|
||||
.eTag(response.eTag())
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
throw new OssException("上传文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void download(String key, OutputStream outputStream) {
|
||||
try (ResponseInputStream<GetObjectResponse> inputStream = client.getObject(GetObjectRequest.builder()
|
||||
.bucket(properties.getBucketName())
|
||||
.key(removeBaseUrl(key))
|
||||
.build())) {
|
||||
inputStream.transferTo(outputStream);
|
||||
} catch (IOException e) {
|
||||
throw new OssException("写出文件失败: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
throw new OssException("下载文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(String key) {
|
||||
try {
|
||||
client.deleteObject(DeleteObjectRequest.builder()
|
||||
.bucket(properties.getBucketName())
|
||||
.key(removeBaseUrl(key))
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
throw new OssException("删除文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrivateUrl(String key, Duration expiredTime) {
|
||||
return presigner.presignGetObject(builder -> builder
|
||||
.signatureDuration(expiredTime)
|
||||
.getObjectRequest(request -> request.bucket(properties.getBucketName()).key(removeBaseUrl(key)).build())
|
||||
.build()).url().toString();
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
return "0".equals(properties.getAccessPolicy());
|
||||
}
|
||||
|
||||
public String getConfigKey() {
|
||||
return properties.getConfigKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
client.close();
|
||||
presigner.close();
|
||||
}
|
||||
|
||||
private String nextObjectKey(String suffix) {
|
||||
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
String fileName = UUID.randomUUID().toString().replace("-", "") + (suffix == null ? "" : suffix);
|
||||
return StringUtils.hasText(properties.getPrefix())
|
||||
? properties.getPrefix() + "/" + datePath + "/" + fileName
|
||||
: datePath + "/" + fileName;
|
||||
}
|
||||
|
||||
private String getEndpoint() {
|
||||
return protocol() + properties.getEndpoint();
|
||||
}
|
||||
|
||||
private String getDomain() {
|
||||
if (StringUtils.hasText(properties.getDomain())) {
|
||||
String domain = properties.getDomain();
|
||||
return domain.startsWith("http://") || domain.startsWith("https://") ? domain : protocol() + domain;
|
||||
}
|
||||
return getEndpoint();
|
||||
}
|
||||
|
||||
private String getUrl() {
|
||||
String endpoint = properties.getEndpoint();
|
||||
String domain = properties.getDomain();
|
||||
if (containsCloudService(endpoint)) {
|
||||
return protocol() + (StringUtils.hasText(domain) ? domain : properties.getBucketName() + "." + endpoint);
|
||||
}
|
||||
if (StringUtils.hasText(domain)) {
|
||||
String base = domain.startsWith("http://") || domain.startsWith("https://") ? domain : protocol() + domain;
|
||||
return base + "/" + properties.getBucketName();
|
||||
}
|
||||
return protocol() + endpoint + "/" + properties.getBucketName();
|
||||
}
|
||||
|
||||
private String removeBaseUrl(String path) {
|
||||
return path == null ? null : path.replace(getUrl() + "/", "");
|
||||
}
|
||||
|
||||
private Region region() {
|
||||
return StringUtils.hasText(properties.getRegion()) ? Region.of(properties.getRegion()) : Region.US_EAST_1;
|
||||
}
|
||||
|
||||
private String protocol() {
|
||||
return "Y".equalsIgnoreCase(properties.getIsHttps()) ? "https://" : "http://";
|
||||
}
|
||||
|
||||
private boolean containsCloudService(String endpoint) {
|
||||
if (endpoint == null) {
|
||||
return false;
|
||||
}
|
||||
for (String cloudService : CLOUD_SERVICE) {
|
||||
if (endpoint.contains(cloudService)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String mimeType(String suffix) {
|
||||
String mimeType = URLConnection.guessContentTypeFromName("file" + suffix);
|
||||
return StringUtils.hasText(mimeType) ? mimeType : "application/octet-stream";
|
||||
}
|
||||
}
|
||||
7
src/main/java/com/bim/api/oss/OssException.java
Normal file
7
src/main/java/com/bim/api/oss/OssException.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package com.bim.api.oss;
|
||||
|
||||
public class OssException extends RuntimeException {
|
||||
public OssException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
11
src/main/java/com/bim/api/oss/SysOssUploadVo.java
Normal file
11
src/main/java/com/bim/api/oss/SysOssUploadVo.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.bim.api.oss;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SysOssUploadVo {
|
||||
private String url;
|
||||
private String fileName;
|
||||
private Long ossId;
|
||||
private String fileSuffix;
|
||||
}
|
||||
12
src/main/java/com/bim/api/oss/UploadResult.java
Normal file
12
src/main/java/com/bim/api/oss/UploadResult.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.bim.api.oss;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class UploadResult {
|
||||
private String url;
|
||||
private String filename;
|
||||
private String eTag;
|
||||
}
|
||||
144
src/main/java/com/bim/api/service/ModelManagementService.java
Normal file
144
src/main/java/com/bim/api/service/ModelManagementService.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.bim.api.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bim.api.entity.ModelManagement;
|
||||
import com.bim.api.entity.SysOss;
|
||||
import com.bim.api.mapper.ModelManagementMapper;
|
||||
import com.bim.api.oss.OssException;
|
||||
import com.bim.api.service.convert.FileConvertMessageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class ModelManagementService extends ServiceImpl<ModelManagementMapper, ModelManagement> {
|
||||
private static final String STATUS_CURRENT = "0";
|
||||
private static final String STATUS_AVAILABLE = "1";
|
||||
private static final Set<String> SUPPORT_SUFFIXES = Set.of(".ifc", ".rvt", ".nwd", ".fbx");
|
||||
|
||||
private final SysOssService ossService;
|
||||
private final FileConvertMessageService fileConvertMessageService;
|
||||
|
||||
public ModelManagementService(SysOssService ossService, FileConvertMessageService fileConvertMessageService) {
|
||||
this.ossService = ossService;
|
||||
this.fileConvertMessageService = fileConvertMessageService;
|
||||
}
|
||||
|
||||
public List<ModelManagement> findList(ModelManagement query) {
|
||||
return list(new LambdaQueryWrapper<ModelManagement>()
|
||||
.like(StringUtils.hasText(query.getModelName()), ModelManagement::getModelName, query.getModelName())
|
||||
.eq(StringUtils.hasText(query.getModelType()), ModelManagement::getModelType, query.getModelType())
|
||||
.eq(StringUtils.hasText(query.getStatus()), ModelManagement::getStatus, query.getStatus())
|
||||
.orderByAsc(ModelManagement::getStatus)
|
||||
.orderByDesc(ModelManagement::getCreateTime));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ModelManagement upload(MultipartFile file, String modelName, String modelType) {
|
||||
validateUpload(file, modelName, modelType);
|
||||
SysOss oss = ossService.upload(file);
|
||||
ModelManagement model = new ModelManagement();
|
||||
model.setModelName(modelName.trim());
|
||||
model.setModelType(modelType.trim());
|
||||
model.setOssId(oss.getOssId());
|
||||
model.setFileName(oss.getFileName());
|
||||
model.setOriginalName(oss.getOriginalName());
|
||||
model.setFileSuffix(oss.getFileSuffix());
|
||||
model.setFileUrl(oss.getUrl());
|
||||
model.setFileSize(file.getSize());
|
||||
model.setConvertStatus("PENDING");
|
||||
model.setConvertMessage("转换任务已发送");
|
||||
model.setStatus(STATUS_AVAILABLE);
|
||||
model.setCreateTime(LocalDateTime.now());
|
||||
save(model);
|
||||
fileConvertMessageService.sendModelConvertTask(model);
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateById(ModelManagement entity) {
|
||||
ModelManagement old = getById(entity.getModelId());
|
||||
if (old == null) {
|
||||
throw new OssException("模型不存在: " + entity.getModelId());
|
||||
}
|
||||
if (!StringUtils.hasText(entity.getModelName())) {
|
||||
entity.setModelName(old.getModelName());
|
||||
}
|
||||
if (!StringUtils.hasText(entity.getModelType())) {
|
||||
entity.setModelType(old.getModelType());
|
||||
}
|
||||
entity.setOssId(old.getOssId());
|
||||
entity.setFileName(old.getFileName());
|
||||
entity.setOriginalName(old.getOriginalName());
|
||||
entity.setFileSuffix(old.getFileSuffix());
|
||||
entity.setFileUrl(old.getFileUrl());
|
||||
entity.setFileSize(old.getFileSize());
|
||||
entity.setConvertedUrl(old.getConvertedUrl());
|
||||
entity.setConvertStatus(old.getConvertStatus());
|
||||
entity.setConvertMessage(old.getConvertMessage());
|
||||
entity.setKeyCode(old.getKeyCode());
|
||||
entity.setSheetStr(old.getSheetStr());
|
||||
entity.setCodeData(old.getCodeData());
|
||||
entity.setStatus(old.getStatus());
|
||||
entity.setUpdateTime(LocalDateTime.now());
|
||||
return super.updateById(entity);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean switchCurrent(Long modelId) {
|
||||
ModelManagement model = getById(modelId);
|
||||
if (model == null) {
|
||||
throw new OssException("模型不存在: " + modelId);
|
||||
}
|
||||
lambdaUpdate().set(ModelManagement::getStatus, STATUS_AVAILABLE).update();
|
||||
model.setStatus(STATUS_CURRENT);
|
||||
model.setUpdateTime(LocalDateTime.now());
|
||||
return super.updateById(model);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteWithFiles(Collection<Long> modelIds) {
|
||||
List<ModelManagement> models = listByIds(modelIds);
|
||||
List<Long> ossIds = models.stream()
|
||||
.map(ModelManagement::getOssId)
|
||||
.filter(id -> id != null)
|
||||
.toList();
|
||||
if (!ossIds.isEmpty()) {
|
||||
ossService.deleteWithFiles(ossIds);
|
||||
}
|
||||
return removeByIds(modelIds);
|
||||
}
|
||||
|
||||
public List<Long> parseIds(String ids) {
|
||||
return Arrays.stream(ids.split(","))
|
||||
.filter(id -> !id.isBlank())
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private void validateUpload(MultipartFile file, String modelName, String modelType) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new OssException("上传模型文件不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(modelName)) {
|
||||
throw new OssException("模型名称不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(modelType)) {
|
||||
throw new OssException("模型类型不能为空");
|
||||
}
|
||||
String originalName = StringUtils.hasText(file.getOriginalFilename()) ? file.getOriginalFilename() : "";
|
||||
String suffix = originalName.contains(".") ? originalName.substring(originalName.lastIndexOf('.')).toLowerCase(Locale.ROOT) : "";
|
||||
if (!SUPPORT_SUFFIXES.contains(suffix)) {
|
||||
throw new OssException("仅支持 IFC / RVT / NWD / FBX 模型文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/main/java/com/bim/api/service/SysOssConfigService.java
Normal file
103
src/main/java/com/bim/api/service/SysOssConfigService.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.bim.api.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bim.api.entity.SysOssConfig;
|
||||
import com.bim.api.mapper.SysOssConfigMapper;
|
||||
import com.bim.api.oss.OssException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class SysOssConfigService extends ServiceImpl<SysOssConfigMapper, SysOssConfig> {
|
||||
|
||||
public List<SysOssConfig> findList(SysOssConfig query) {
|
||||
LambdaQueryWrapper<SysOssConfig> wrapper = new LambdaQueryWrapper<SysOssConfig>()
|
||||
.eq(StringUtils.hasText(query.getConfigKey()), SysOssConfig::getConfigKey, query.getConfigKey())
|
||||
.like(StringUtils.hasText(query.getBucketName()), SysOssConfig::getBucketName, query.getBucketName())
|
||||
.eq(StringUtils.hasText(query.getStatus()), SysOssConfig::getStatus, query.getStatus())
|
||||
.orderByAsc(SysOssConfig::getOssConfigId);
|
||||
return list(wrapper);
|
||||
}
|
||||
|
||||
public SysOssConfig getDefaultConfig() {
|
||||
SysOssConfig config = getOne(new LambdaQueryWrapper<SysOssConfig>()
|
||||
.eq(SysOssConfig::getStatus, "0")
|
||||
.last("limit 1"));
|
||||
if (config == null) {
|
||||
throw new OssException("未找到默认OSS配置,请先在sys_oss_config中设置status=0的配置");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public SysOssConfig getByConfigKey(String configKey) {
|
||||
SysOssConfig config = getOne(new LambdaQueryWrapper<SysOssConfig>()
|
||||
.eq(SysOssConfig::getConfigKey, configKey)
|
||||
.last("limit 1"));
|
||||
if (config == null) {
|
||||
throw new OssException("OSS配置不存在: " + configKey);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(SysOssConfig entity) {
|
||||
validConfigKey(entity);
|
||||
fillDefaults(entity);
|
||||
entity.setCreateTime(LocalDateTime.now());
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateById(SysOssConfig entity) {
|
||||
validConfigKey(entity);
|
||||
fillDefaults(entity);
|
||||
entity.setUpdateTime(LocalDateTime.now());
|
||||
return super.updateById(entity);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean changeStatus(Long ossConfigId) {
|
||||
SysOssConfig config = getById(ossConfigId);
|
||||
if (config == null) {
|
||||
throw new OssException("OSS配置不存在: " + ossConfigId);
|
||||
}
|
||||
lambdaUpdate().set(SysOssConfig::getStatus, "1").update();
|
||||
config.setStatus("0");
|
||||
config.setUpdateTime(LocalDateTime.now());
|
||||
return super.updateById(config);
|
||||
}
|
||||
|
||||
private void validConfigKey(SysOssConfig entity) {
|
||||
if (!StringUtils.hasText(entity.getConfigKey())) {
|
||||
throw new OssException("配置key不能为空");
|
||||
}
|
||||
SysOssConfig exists = getOne(new LambdaQueryWrapper<SysOssConfig>()
|
||||
.select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey)
|
||||
.eq(SysOssConfig::getConfigKey, entity.getConfigKey())
|
||||
.last("limit 1"));
|
||||
Long id = entity.getOssConfigId();
|
||||
if (exists != null && (id == null || !exists.getOssConfigId().equals(id))) {
|
||||
throw new OssException("配置key已存在: " + entity.getConfigKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDefaults(SysOssConfig entity) {
|
||||
if (!StringUtils.hasText(entity.getTenantId())) {
|
||||
entity.setTenantId("000000");
|
||||
}
|
||||
if (!StringUtils.hasText(entity.getIsHttps())) {
|
||||
entity.setIsHttps("N");
|
||||
}
|
||||
if (!StringUtils.hasText(entity.getAccessPolicy())) {
|
||||
entity.setAccessPolicy("1");
|
||||
}
|
||||
if (!StringUtils.hasText(entity.getStatus())) {
|
||||
entity.setStatus("1");
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/main/java/com/bim/api/service/SysOssService.java
Normal file
128
src/main/java/com/bim/api/service/SysOssService.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package com.bim.api.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bim.api.entity.SysOss;
|
||||
import com.bim.api.entity.SysOssConfig;
|
||||
import com.bim.api.mapper.SysOssMapper;
|
||||
import com.bim.api.oss.OssClient;
|
||||
import com.bim.api.oss.OssException;
|
||||
import com.bim.api.oss.SysOssUploadVo;
|
||||
import com.bim.api.oss.UploadResult;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class SysOssService extends ServiceImpl<SysOssMapper, SysOss> {
|
||||
private final SysOssConfigService ossConfigService;
|
||||
|
||||
public SysOssService(SysOssConfigService ossConfigService) {
|
||||
this.ossConfigService = ossConfigService;
|
||||
}
|
||||
|
||||
public List<SysOss> findList(SysOss query) {
|
||||
List<SysOss> list = list(new LambdaQueryWrapper<SysOss>()
|
||||
.like(StringUtils.hasText(query.getFileName()), SysOss::getFileName, query.getFileName())
|
||||
.like(StringUtils.hasText(query.getOriginalName()), SysOss::getOriginalName, query.getOriginalName())
|
||||
.eq(StringUtils.hasText(query.getFileSuffix()), SysOss::getFileSuffix, query.getFileSuffix())
|
||||
.eq(StringUtils.hasText(query.getService()), SysOss::getService, query.getService())
|
||||
.orderByDesc(SysOss::getOssId));
|
||||
list.forEach(this::matchingUrl);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysOss upload(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new OssException("上传文件不能为空");
|
||||
}
|
||||
String originalName = StringUtils.hasText(file.getOriginalFilename()) ? file.getOriginalFilename() : "file";
|
||||
String suffix = suffix(originalName);
|
||||
SysOssConfig config = ossConfigService.getDefaultConfig();
|
||||
try (OssClient client = new OssClient(config)) {
|
||||
UploadResult uploadResult = client.upload(file.getInputStream(), file.getSize(), suffix, file.getContentType());
|
||||
SysOss oss = new SysOss();
|
||||
oss.setTenantId(StringUtils.hasText(config.getTenantId()) ? config.getTenantId() : "000000");
|
||||
oss.setUrl(uploadResult.getUrl());
|
||||
oss.setFileName(uploadResult.getFilename());
|
||||
oss.setOriginalName(originalName);
|
||||
oss.setFileSuffix(suffix);
|
||||
oss.setService(client.getConfigKey());
|
||||
oss.setCreateTime(LocalDateTime.now());
|
||||
save(oss);
|
||||
return matchingUrl(oss);
|
||||
} catch (IOException e) {
|
||||
throw new OssException("读取上传文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void download(Long ossId, HttpServletResponse response) throws IOException {
|
||||
SysOss oss = getById(ossId);
|
||||
if (oss == null) {
|
||||
throw new OssException("文件数据不存在: " + ossId);
|
||||
}
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
String fileName = URLEncoder.encode(oss.getOriginalName(), StandardCharsets.UTF_8).replace("+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
|
||||
try (OssClient client = new OssClient(ossConfigService.getByConfigKey(oss.getService()))) {
|
||||
client.download(oss.getFileName(), response.getOutputStream());
|
||||
}
|
||||
}
|
||||
|
||||
public SysOssUploadVo preview(Long ossId) {
|
||||
SysOss oss = getById(ossId);
|
||||
if (oss == null) {
|
||||
throw new OssException("文件数据不存在: " + ossId);
|
||||
}
|
||||
SysOssConfig config = ossConfigService.getByConfigKey(oss.getService());
|
||||
try (OssClient client = new OssClient(config)) {
|
||||
SysOssUploadVo vo = new SysOssUploadVo();
|
||||
vo.setOssId(oss.getOssId());
|
||||
vo.setFileName(oss.getOriginalName());
|
||||
vo.setFileSuffix(oss.getFileSuffix());
|
||||
vo.setUrl(client.isPrivate() ? client.getPrivateUrl(oss.getFileName(), Duration.ofSeconds(120)) : oss.getUrl());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteWithFiles(Collection<Long> ossIds) {
|
||||
List<SysOss> list = listByIds(ossIds);
|
||||
for (SysOss oss : list) {
|
||||
try (OssClient client = new OssClient(ossConfigService.getByConfigKey(oss.getService()))) {
|
||||
client.delete(oss.getFileName());
|
||||
}
|
||||
}
|
||||
return removeByIds(ossIds);
|
||||
}
|
||||
|
||||
private SysOss matchingUrl(SysOss oss) {
|
||||
try {
|
||||
try (OssClient client = new OssClient(ossConfigService.getByConfigKey(oss.getService()))) {
|
||||
if (client.isPrivate()) {
|
||||
oss.setUrl(client.getPrivateUrl(oss.getFileName(), Duration.ofSeconds(120)));
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 配置不可用时仍返回数据库中保存的原始URL,便于排查配置问题。
|
||||
}
|
||||
return oss;
|
||||
}
|
||||
|
||||
private String suffix(String fileName) {
|
||||
int index = fileName.lastIndexOf('.');
|
||||
return index >= 0 ? fileName.substring(index) : "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.bim.api.service.convert;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.bim.api.config.FileConvertProperties;
|
||||
import com.bim.api.entity.ModelManagement;
|
||||
import com.bim.api.service.ModelManagementService;
|
||||
import com.bim.api.util.SignUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class FileConvertCallbackListener {
|
||||
private final ObjectMapper objectMapper;
|
||||
private final FileConvertProperties properties;
|
||||
private final ModelManagementService modelManagementService;
|
||||
|
||||
public FileConvertCallbackListener(ObjectMapper objectMapper,
|
||||
FileConvertProperties properties,
|
||||
ModelManagementService modelManagementService) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.properties = properties;
|
||||
this.modelManagementService = modelManagementService;
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "${file-convert.callback-queue-name:system_file_convert_callback_queue}")
|
||||
public void handleCallback(String rawMessage) throws JsonProcessingException {
|
||||
Map<String, Object> message = objectMapper.readValue(rawMessage, new TypeReference<>() {
|
||||
});
|
||||
String sign = safeString(message.get("sign"));
|
||||
boolean signValid = verifyCallbackSign(message, rawMessage, sign);
|
||||
|
||||
Long modelId = parseModelId(message.get("extraData"));
|
||||
if (modelId == null) {
|
||||
throw new IllegalArgumentException("转换回调缺少模型ID");
|
||||
}
|
||||
|
||||
String convertedUrl = firstText(message.get("convertedUrl"), message.get("converterUrl"));
|
||||
String status = normalizeStatus(safeString(message.get("status")));
|
||||
|
||||
modelManagementService.update(new LambdaUpdateWrapper<ModelManagement>()
|
||||
.eq(ModelManagement::getModelId, modelId)
|
||||
.set(ModelManagement::getConvertedUrl, convertedUrl)
|
||||
.set(ModelManagement::getConvertStatus, status)
|
||||
.set(ModelManagement::getConvertMessage, safeString(message.get("message")))
|
||||
.set(ModelManagement::getKeyCode, safeString(message.get("keyCode")))
|
||||
.set(ModelManagement::getSheetStr, safeString(message.get("sheetStr")))
|
||||
.set(ModelManagement::getCodeData, buildCodeData(message, signValid))
|
||||
.setSql("update_time = now()"));
|
||||
}
|
||||
|
||||
private boolean verifyCallbackSign(Map<String, Object> message, String rawMessage, String sign) {
|
||||
if (!StringUtils.hasText(properties.getSecretKey())) {
|
||||
return true;
|
||||
}
|
||||
if (!StringUtils.hasText(sign)) {
|
||||
return false;
|
||||
}
|
||||
if (SignUtil.verifySign(message, properties.getSecretKey(), sign)
|
||||
|| SignUtil.verifySignWithRawStringFields(message, rawMessage, Set.of("extraData"), properties.getSecretKey(), sign)) {
|
||||
return true;
|
||||
}
|
||||
return verifyWithExtraDataAsObject(message, sign) || verifyWithExtraDataAsString(message, sign);
|
||||
}
|
||||
|
||||
private boolean verifyWithExtraDataAsObject(Map<String, Object> message, String sign) {
|
||||
Object extraData = message.get("extraData");
|
||||
if (!(extraData instanceof String extraDataJson) || !StringUtils.hasText(extraDataJson)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Map<String, Object> candidate = new LinkedHashMap<>(message);
|
||||
candidate.put("extraData", objectMapper.readValue(extraDataJson, new TypeReference<Map<String, Object>>() {
|
||||
}));
|
||||
return SignUtil.verifySign(candidate, properties.getSecretKey(), sign);
|
||||
} catch (JsonProcessingException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyWithExtraDataAsString(Map<String, Object> message, String sign) {
|
||||
Object extraData = message.get("extraData");
|
||||
if (!(extraData instanceof Map<?, ?>)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Map<String, Object> candidate = new LinkedHashMap<>(message);
|
||||
candidate.put("extraData", objectMapper.writeValueAsString(extraData));
|
||||
return SignUtil.verifySign(candidate, properties.getSecretKey(), sign);
|
||||
} catch (JsonProcessingException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Long parseModelId(Object extraDataObj) {
|
||||
if (extraDataObj instanceof String extraDataJson) {
|
||||
try {
|
||||
return parseModelId(objectMapper.readValue(extraDataJson, new TypeReference<Map<String, Object>>() {
|
||||
}));
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!(extraDataObj instanceof Map<?, ?> extraData)) {
|
||||
return null;
|
||||
}
|
||||
Object modelId = extraData.get("modelId");
|
||||
if (modelId == null) {
|
||||
modelId = extraData.get("businessId");
|
||||
}
|
||||
if (modelId == null) {
|
||||
return null;
|
||||
}
|
||||
String value = String.valueOf(modelId).replaceFirst("^MODEL_", "");
|
||||
return value.matches("\\d+") ? Long.valueOf(value) : null;
|
||||
}
|
||||
|
||||
private String buildCodeData(Map<String, Object> message, boolean signValid) throws JsonProcessingException {
|
||||
Map<String, Object> codeData = new LinkedHashMap<>();
|
||||
codeData.put("convertedUrl", firstText(message.get("convertedUrl"), message.get("converterUrl")));
|
||||
codeData.put("status", normalizeStatus(safeString(message.get("status"))));
|
||||
codeData.put("message", safeString(message.get("message")));
|
||||
codeData.put("keyCode", safeString(message.get("keyCode")));
|
||||
codeData.put("sheetStr", safeString(message.get("sheetStr")));
|
||||
codeData.put("extraData", message.get("extraData"));
|
||||
codeData.put("signValid", signValid);
|
||||
return objectMapper.writeValueAsString(codeData);
|
||||
}
|
||||
|
||||
private String normalizeStatus(String status) {
|
||||
if (!StringUtils.hasText(status)) {
|
||||
return "FAILED";
|
||||
}
|
||||
return "success".equalsIgnoreCase(status) ? "SUCCESS" : status.toUpperCase();
|
||||
}
|
||||
|
||||
private String firstText(Object first, Object second) {
|
||||
String firstValue = safeString(first);
|
||||
return StringUtils.hasText(firstValue) ? firstValue : safeString(second);
|
||||
}
|
||||
|
||||
private String safeString(Object value) {
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.bim.api.service.convert;
|
||||
|
||||
import com.bim.api.config.FileConvertProperties;
|
||||
import com.bim.api.entity.ModelManagement;
|
||||
import com.bim.api.util.SignUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class FileConvertMessageService {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileConvertMessageService.class);
|
||||
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
private final FileConvertProperties properties;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public FileConvertMessageService(RabbitTemplate rabbitTemplate, FileConvertProperties properties, ObjectMapper objectMapper) {
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
this.properties = properties;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
public void sendModelConvertTask(ModelManagement model) {
|
||||
Map<String, Object> message = new LinkedHashMap<>();
|
||||
message.put("sign", "");
|
||||
message.put("callbackQueueName", properties.getCallbackQueueName());
|
||||
message.put("sourceUrl", model.getFileUrl());
|
||||
message.put("currentLanguage", properties.getCurrentLanguage());
|
||||
message.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
||||
message.put("extraData", buildExtraDataJson(model));
|
||||
message.put("fileName", model.getOriginalName());
|
||||
message.put("fileType", normalizeFileType(model.getFileSuffix()));
|
||||
message.put("fileSize", model.getFileSize());
|
||||
message.put("sign", SignUtil.generateSign(message, properties.getSecretKey()));
|
||||
|
||||
try {
|
||||
String payload = objectMapper.writeValueAsString(message);
|
||||
log.info("发送模型转换任务: {}", payload);
|
||||
rabbitTemplate.convertAndSend(properties.getModelQueueName(), payload);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException("构建模型转换消息失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildExtraDataJson(ModelManagement model) {
|
||||
Map<String, Object> extraData = new LinkedHashMap<>();
|
||||
extraData.put("taskId", "MODEL_" + model.getModelId());
|
||||
extraData.put("businessId", String.valueOf(model.getModelId()));
|
||||
extraData.put("modelId", model.getModelId());
|
||||
try {
|
||||
return objectMapper.writeValueAsString(extraData);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException("构建模型转换扩展数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeFileType(String fileSuffix) {
|
||||
if (fileSuffix == null || fileSuffix.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
return fileSuffix.startsWith(".") ? fileSuffix.substring(1).toLowerCase() : fileSuffix.toLowerCase();
|
||||
}
|
||||
}
|
||||
31
src/main/resources/db/model_management.sql
Normal file
31
src/main/resources/db/model_management.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
-- 模型管理表
|
||||
create table if not exists model_management (
|
||||
model_id bigint(20) not null auto_increment comment '模型主键',
|
||||
model_name varchar(100) not null default '' comment '模型名称',
|
||||
model_type varchar(50) not null default '' comment '模型类型',
|
||||
oss_id bigint(20) default null comment 'OSS文件ID',
|
||||
file_name varchar(255) not null default '' comment '存储文件名',
|
||||
original_name varchar(255) not null default '' comment '原始文件名',
|
||||
file_suffix varchar(20) not null default '' comment '文件后缀名',
|
||||
file_url varchar(500) default null comment '文件访问地址',
|
||||
file_size bigint(20) default null comment '文件大小',
|
||||
converted_url varchar(500) default null comment '转换后文件地址',
|
||||
convert_status varchar(20) not null default 'PENDING' comment '转换状态(PENDING=待转换,SUCCESS=成功,FAILED=失败)',
|
||||
convert_message varchar(500) default null comment '转换状态说明',
|
||||
key_code varchar(255) default null comment '加密code',
|
||||
sheet_str longtext default null comment '蓝图数据',
|
||||
status char(1) not null default '1' comment '状态(0=当前,1=可用)',
|
||||
create_time datetime default null comment '上传时间',
|
||||
update_time datetime default null comment '更新时间',
|
||||
code_data longtext default null comment '构件数据',
|
||||
primary key (model_id),
|
||||
key idx_model_management_type (model_type),
|
||||
key idx_model_management_status (status),
|
||||
key idx_model_management_convert_status (convert_status),
|
||||
key idx_model_management_oss_id (oss_id)
|
||||
) engine=innodb default charset=utf8mb4 comment='模型管理';
|
||||
|
||||
-- 绑定关系表模型维度,旧库需要确认 part_code_relation 已包含 model_id。
|
||||
-- 如未包含,请手工执行:
|
||||
-- alter table part_code_relation add column model_id bigint(20) default null comment '模型ID';
|
||||
-- create index idx_part_code_relation_model_id on part_code_relation (model_id);
|
||||
Reference in New Issue
Block a user