This commit is contained in:
2026-08-26 13:58:33 +08:00
parent fc0a6ea7ca
commit 4398e8b347
78 changed files with 920 additions and 1549 deletions
@@ -13,6 +13,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -74,6 +75,20 @@ public class InternalBusinessFileController {
.body(value.content());
}
@DeleteMapping("/{tenantId}/{businessType}/{businessId}/{fileId}")
public ApiResponse<Void> delete(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@RequestHeader("X-Operator-Id") long operatorId,
@PathVariable long tenantId,
@PathVariable @NotBlank String businessType,
@PathVariable @NotBlank String businessId,
@PathVariable @Positive long fileId) {
check(token);
checkTenantId(tenantId);
service.delete(tenantId, businessType, businessId, fileId, operatorId);
return ApiResponse.success(null);
}
private void check(String token) {
if (token == null || !MessageDigest.isEqual(
token.getBytes(StandardCharsets.UTF_8), expected.getBytes(StandardCharsets.UTF_8))) {
@@ -3,7 +3,16 @@ package com.baiyue.file.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.file.entity.FileObjectEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface FileObjectMapper extends BaseMapper<FileObjectEntity> {
@Update("UPDATE file_object SET status='VOID',deleted=1,updated_by=#{operatorId},"
+ "updated_at=CURRENT_TIMESTAMP(3),version=version+1 WHERE id=#{fileId} AND tenant_id=#{tenantId} "
+ "AND business_type=#{businessType} AND business_id=#{businessId} AND status='ACTIVE' "
+ "AND deleted=0 AND version=#{version}")
int voidOwned(@Param("tenantId") long tenantId, @Param("businessType") String businessType,
@Param("businessId") String businessId, @Param("fileId") long fileId,
@Param("version") int version, @Param("operatorId") long operatorId);
}
@@ -65,6 +65,16 @@ public class BusinessFileStorage {
}
}
boolean delete(String objectKey) {
Path target = root.resolve(objectKey).normalize();
if (!target.startsWith(root)) return false;
try {
return Files.deleteIfExists(target);
} catch (IOException exception) {
return false;
}
}
private void verifyContent(MultipartFile file, String type) {
try (InputStream input = file.getInputStream()) {
byte[] prefix = input.readNBytes(4096);
@@ -7,6 +7,7 @@ import com.baiyue.file.entity.FileObjectEntity;
import com.baiyue.file.mapper.FileObjectMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
@@ -20,7 +21,7 @@ public class InternalBusinessFileService {
"CUSTOMS_RECEIPT",
"FINANCE_VOUCHER",
"CONTAINER_CLEARANCE_DOCUMENT",
"LOADING_CUSTOMS_DOCUMENT",
"LOADING_CUSTOMS_DECLARATION",
"CONTAINER_DELIVERY_ORDER",
"CONTAINER_PICKUP_ORDER",
"OVERSEAS_UNLOAD_PHOTO",
@@ -70,6 +71,16 @@ public class InternalBusinessFileService {
return new Download(value.getOriginalName(), value.getContentType(), storage.read(value.getObjectKey()));
}
@Transactional
public void delete(long tenantId, String businessType, String businessId, long fileId, long operatorId) {
if (operatorId <= 0) throw new BizException(ResultCode.BAD_REQUEST, "操作人必须有效");
FileObjectEntity value = required(tenantId, businessType, businessId, fileId);
int changed = files.voidOwned(tenantId, businessType, businessId, fileId,
value.getVersion(), operatorId);
if (changed != 1) throw new BizException(ResultCode.CONFLICT, "业务附件已变化,请刷新后重试");
storage.delete(value.getObjectKey());
}
private FileObjectEntity required(long tenantId, String businessType, String businessId, long fileId) {
validateScope(tenantId, businessType, businessId, fileId);
FileObjectEntity value = files.selectOne(new LambdaQueryWrapper<FileObjectEntity>()