feat: 优化

This commit is contained in:
2026-03-03 04:16:48 +08:00
parent fdc159de7e
commit b0fa4279b0
7 changed files with 289 additions and 354 deletions

View File

@@ -100,12 +100,14 @@ public class AppTikUserFileController {
@Parameter(name = "fileCategory", description = "文件分类video/audio/image", required = true, example = "video")
@Parameter(name = "groupId", description = "分组编号(可选)")
@Parameter(name = "fileSize", description = "文件大小(字节)")
@Parameter(name = "contentType", description = "文件 MIME 类型(可选,如 video/mp4")
public CommonResult<Object> getPresignedUrl(
@RequestParam("fileName") String fileName,
@RequestParam("fileCategory") String fileCategory,
@RequestParam(value = "groupId", required = false) Long groupId,
@RequestParam(value = "fileSize", required = false) Long fileSize) {
return success(userFileService.getPresignedUrl(fileName, fileCategory, groupId, fileSize));
@RequestParam(value = "fileSize", required = false) Long fileSize,
@RequestParam(value = "contentType", required = false) String contentType) {
return success(userFileService.getPresignedUrl(fileName, fileCategory, groupId, fileSize, contentType));
}
@PostMapping("/complete-upload")

View File

@@ -82,9 +82,10 @@ public interface TikUserFileService {
* @param fileCategory 文件分类
* @param groupId 分组编号(可选)
* @param fileSize 文件大小(可选)
* @param contentType 文件 MIME 类型(可选)
* @return 预签名URL信息
*/
Object getPresignedUrl(String fileName, String fileCategory, Long groupId, Long fileSize);
Object getPresignedUrl(String fileName, String fileCategory, Long groupId, Long fileSize, String contentType);
/**
* 确认上传(直传模式)

View File

@@ -53,24 +53,8 @@ import static cn.iocoder.yudao.module.tik.enums.ErrorCodeConstants.*;
@Slf4j
public class TikUserFileServiceImpl implements TikUserFileService {
/** 预签名URL过期时间24小时单位 */
private static final int PRESIGN_URL_EXPIRATION_SECONDS = 24 * 60 * 60;
/**
* 上传文件的前缀,是否包含日期
*
* 目的:按照日期,进行分目录
*/
private static boolean PATH_PREFIX_DATE_ENABLE = true;
/**
* 上传文件的后缀,是否包含时间戳
*
* 目的:保证文件的唯一性,避免覆盖
* 定制:可按需调整成 UUID、或者其他方式
*/
private static boolean PATH_SUFFIX_TIMESTAMP_ENABLE = true;
@Resource
private TikUserFileMapper userFileMapper;
@@ -93,25 +77,18 @@ public class TikUserFileServiceImpl implements TikUserFileService {
public Long uploadFile(MultipartFile file, String fileCategory, String coverBase64, Integer duration, Long groupId) {
Long userId = SecurityFrameworkUtils.getLoginUserId();
// ========== 第一阶段:校验和准备 ==========
// 1. 校验文件分类和配额
// 校验文件分类和配额
validateUploadRequest(fileCategory, file.getSize());
// 2. 获取OSS基础目录路径
// 获取OSS基础目录路径
String baseDirectory = ossInitService.getOssDirectoryByCategory(userId, fileCategory);
// ========== 第二阶段:处理文件和上传 ==========
// 处理文件和上传
UploadFileContext context = processFileAndUpload(file, baseDirectory);
// ========== 第三阶段:保存数据库 ==========
try {
return saveFileRecord(userId, file, fileCategory, context.fileUrl, context.filePath,
coverBase64, baseDirectory, context.infraFileId, duration, groupId);
} catch (Exception e) {
log.error("[uploadFile][保存数据库失败]", e);
deleteOssFile(context.infraFileId, context.filePath, context.fileUrl);
throw e;
}
// 保存数据库
return saveFileRecord(userId, file, fileCategory, context.fileUrl, context.filePath,
coverBase64, baseDirectory, context.infraFileId, duration, groupId);
}
/**
@@ -143,12 +120,11 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
/**
* 统一处理文件和上传逻辑
* 处理文件和上传逻辑
*/
private UploadFileContext processFileAndUpload(MultipartFile file, String baseDirectory) {
UploadFileContext context = new UploadFileContext();
// 1. 读取文件内容
byte[] fileContent;
try {
fileContent = IoUtil.readBytes(file.getInputStream());
@@ -158,12 +134,10 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
try {
// 2. 处理文件名和类型
String fileName = file.getOriginalFilename();
String fileType = file.getContentType();
if (StrUtil.isEmpty(fileType)) {
fileType = FileTypeUtils.getMineType(fileContent, fileName);
}
String fileType = StrUtil.isEmpty(file.getContentType())
? FileTypeUtils.getMineType(fileContent, fileName)
: file.getContentType();
if (StrUtil.isEmpty(fileName)) {
fileName = DigestUtil.sha256Hex(fileContent);
}
@@ -174,15 +148,12 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
}
// 3. 生成上传路径
String filePath = generateUploadPath(fileName, baseDirectory);
// 4. 上传到OSS
FileClient client = fileConfigService.getMasterFileClient();
Assert.notNull(client, "客户端(master) 不能为空");
String presignedUrl = client.upload(fileContent, filePath, fileType);
// 5. 保存到 infra_file 表
String fileUrl = HttpUtils.removeUrlQuery(presignedUrl);
FileDO infraFile = new FileDO()
.setConfigId(client.getId())
@@ -193,7 +164,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
.setSize((int) file.getSize());
fileMapper.insert(infraFile);
// 6. 设置上下文
context.fileUrl = fileUrl;
context.filePath = filePath;
context.infraFileId = infraFile.getId();
@@ -210,13 +180,12 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
/**
* 统一处理视频封面上传
* 处理视频封面上传
*/
private String handleCoverUpload(String coverBase64, String fileName, String fileType, String baseDirectory) {
if (StrUtil.isBlank(coverBase64) || !StrUtil.containsIgnoreCase(fileType, "video")) {
return null;
}
try {
String base64Data = coverBase64.contains(",")
? coverBase64.substring(coverBase64.indexOf(",") + 1)
@@ -234,102 +203,62 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
/**
* 保存文件记录到数据库(在事务中执行)
* 保存文件记录到数据库
*/
@Transactional(rollbackFor = Exception.class)
public Long saveFileRecord(Long userId, MultipartFile file, String fileCategory,
String fileUrl, String filePath, String coverBase64, String baseDirectory, Long infraFileId, Integer duration, Long groupId) {
// 验证 infraFileId 不为空
String fileUrl, String filePath, String coverBase64,
String baseDirectory, Long infraFileId, Integer duration, Long groupId) {
if (infraFileId == null) {
log.error("[saveFileRecord][infra_file.id 为空,无法保存文件记录,用户({})URL({})]", userId, fileUrl);
log.error("[saveFileRecord][infra_file.id 为空,用户({})URL({})]", userId, fileUrl);
throw exception(FILE_NOT_EXISTS, "文件记录保存失败无法获取文件ID");
}
// 处理视频封面和文件名
String fileName = file.getOriginalFilename();
String fileType = file.getContentType();
String coverUrl = handleCoverUpload(coverBase64, fileName, fileType, baseDirectory);
// 处理显示名称(去除文件后缀)
String displayName = FileUtil.mainName(fileName);
// 9. 创建文件记录保存完整路径包含封面URL和Base64
TikUserFileDO userFile = new TikUserFileDO()
.setUserId(userId)
.setFileId(infraFileId) // 关联infra_file表用于后续通过FileService管理文件
.setFileName(fileName) // 保留原始文件名(系统标识)
.setDisplayName(displayName) // 设置显示名称(无后缀,用户可编辑)
.setFileId(infraFileId)
.setFileName(fileName)
.setDisplayName(displayName)
.setFileType(fileType)
.setFileCategory(fileCategory)
.setFileSize(file.getSize())
.setFileUrl(fileUrl)
.setFilePath(filePath) // 保存完整的OSS路径由FileService生成
.setCoverUrl(coverUrl) // 设置封面URL如果有
.setCoverBase64(StrUtil.isNotBlank(coverBase64) ? coverBase64 : null) // 保存原始base64数据如果有
.setDuration(duration) // 设置视频时长(如果有)
.setGroupId(groupId); // 设置分组编号(如果有)
.setFilePath(filePath)
.setCoverUrl(coverUrl)
.setCoverBase64(StrUtil.isNotBlank(coverBase64) ? coverBase64 : null)
.setDuration(duration)
.setGroupId(groupId);
userFileMapper.insert(userFile);
// 10. 更新配额
memberUserProfileService.increaseUsedStorage(String.valueOf(userId), file.getSize());
log.info("[saveFileRecord][用户({})保存文件记录成功,文件编号({})infra文件编号({})]",
log.info("[saveFileRecord][用户({})保存文件成功,userFileId({})infraFileId({})]",
userId, userFile.getId(), infraFileId);
// 返回 infra_file.id保持与现有配音功能的兼容性
return infraFileId;
}
/**
* 删除OSS文件当数据库保存失败时调用
*/
private void deleteOssFile(Long infraFileId, String filePath, String fileUrl) {
try {
if (infraFileId != null) {
// 优先通过 infra_file.id 删除(更准确)
// 注意:这里需要调用 FileService.deleteFile但 FileApi 没有提供删除方法
// 暂时通过 filePath 删除
log.warn("[deleteOssFile][通过 infra_file.id 删除文件,但 FileApi 未提供删除方法id({})]", infraFileId);
}
// 通过 filePath 删除(需要获取 FileClient
// TODO: 实现通过 filePath 删除OSS文件的逻辑
// 可以通过 FileService 或 FileClient 删除
log.warn("[deleteOssFile][准备删除OSS文件路径({})URL({})]", filePath, fileUrl);
} catch (Exception e) {
// 删除OSS文件失败不影响主流程只记录日志
log.error("[deleteOssFile][删除OSS文件失败路径({})URL({})]", filePath, fileUrl, e);
}
}
/**
* 生成上传路径(与 FileService 保持一致)
* 生成上传路径
* 格式:{directory}/{yyyyMMdd}/{filename}_{timestamp}.ext
*/
private String generateUploadPath(String name, String directory) {
// 1. 生成前缀、后缀
String prefix = null;
if (PATH_PREFIX_DATE_ENABLE) {
prefix = LocalDateTimeUtil.format(LocalDateTimeUtil.now(), PURE_DATE_PATTERN);
}
String suffix = null;
if (PATH_SUFFIX_TIMESTAMP_ENABLE) {
suffix = String.valueOf(System.currentTimeMillis());
}
String prefix = LocalDateTimeUtil.format(LocalDateTimeUtil.now(), PURE_DATE_PATTERN);
String suffix = String.valueOf(System.currentTimeMillis());
// 2.1 先拼接 suffix 后缀
if (StrUtil.isNotEmpty(suffix)) {
String ext = FileUtil.extName(name);
if (StrUtil.isNotEmpty(ext)) {
name = FileUtil.mainName(name) + "_" + suffix + "." + ext;
} else {
name = name + "_" + suffix;
}
// 拼接后缀
String ext = FileUtil.extName(name);
if (StrUtil.isNotEmpty(ext)) {
name = FileUtil.mainName(name) + "_" + suffix + "." + ext;
} else {
name = name + "_" + suffix;
}
// 2.2 再拼接 prefix 前缀
if (StrUtil.isNotEmpty(prefix)) {
name = prefix + "/" + name;
}
// 2.3 最后拼接 directory 目录
// 拼接前缀和目录
name = prefix + "/" + name;
if (StrUtil.isNotEmpty(directory)) {
name = directory + "/" + name;
}
@@ -338,51 +267,36 @@ public class TikUserFileServiceImpl implements TikUserFileService {
@Override
public PageResult<AppTikUserFileRespVO> getFilePage(AppTikUserFilePageReqVO pageReqVO) {
// 自动填充当前登录用户ID
Long userId = SecurityFrameworkUtils.getLoginUserId();
pageReqVO.setUserId(userId);
pageReqVO.setUserId(SecurityFrameworkUtils.getLoginUserId());
// 查询文件列表
PageResult<TikUserFileDO> pageResult = userFileMapper.selectPage(pageReqVO);
// 转换为VO并生成预览URL
return CollectionUtils.convertPage(pageResult, file -> {
AppTikUserFileRespVO vo = BeanUtils.toBean(file, AppTikUserFileRespVO.class);
// 判断文件类型
boolean isVideo = StrUtil.containsIgnoreCase(file.getFileType(), "video");
boolean isImage = FileTypeUtils.isImage(file.getFileType());
vo.setIsVideo(isVideo);
vo.setIsImage(isImage);
// 视频文件不使用任何OSS预签名URL包括coverUrl和previewUrl
// 只返回coverBase64如果有前端从本地缓存获取
String coverUrlPresigned = null;
String thumbnailUrlPresigned = null;
String previewUrl = null;
// 非视频文件才生成预签名URL
if (!isVideo) {
// 图片文件生成缩略图URL
thumbnailUrlPresigned = StrUtil.isNotBlank(file.getThumbnailUrl())
? getCachedPresignUrl(file.getThumbnailUrl(), PRESIGN_URL_EXPIRATION_SECONDS)
: null;
// 生成预览URL
if (isImage) {
// 图片:优先使用缩略图,没有缩略图时使用原图
previewUrl = thumbnailUrlPresigned != null
? thumbnailUrlPresigned
: getCachedPresignUrl(file.getFileUrl(), PRESIGN_URL_EXPIRATION_SECONDS);
}
// 视频文件不生成OSS预签名URL前端使用coverBase64缓存
if (isVideo) {
vo.setCoverUrl(null);
vo.setThumbnailUrl(null);
vo.setPreviewUrl(null);
return vo;
}
// 设置封面和缩略图URL
vo.setCoverUrl(coverUrlPresigned);
vo.setThumbnailUrl(thumbnailUrlPresigned);
// 非视频文件生成预签名URL
String thumbnailUrl = StrUtil.isNotBlank(file.getThumbnailUrl())
? getCachedPresignUrl(file.getThumbnailUrl(), PRESIGN_URL_EXPIRATION_SECONDS)
: null;
vo.setCoverUrl(null);
vo.setThumbnailUrl(thumbnailUrl);
// 生成预览URL
vo.setPreviewUrl(previewUrl);
// 图片预览URL:优先缩略图,否则原图
vo.setPreviewUrl(isImage
? (thumbnailUrl != null ? thumbnailUrl : getCachedPresignUrl(file.getFileUrl(), PRESIGN_URL_EXPIRATION_SECONDS))
: null);
return vo;
});
@@ -396,43 +310,15 @@ public class TikUserFileServiceImpl implements TikUserFileService {
}
Long userId = SecurityFrameworkUtils.getLoginUserId();
// 查询文件列表
List<TikUserFileDO> files = userFileMapper.selectBatchIds(fileIds);
if (files.isEmpty()) {
return;
}
// 校验文件属于当前用户
for (TikUserFileDO file : files) {
if (!file.getUserId().equals(userId)) {
throw exception(FILE_NOT_EXISTS, "文件不属于当前用户");
}
}
// 物理删除OSS文件使用 file_path如果为空则使用 file_url
for (TikUserFileDO file : files) {
try {
// 优先使用 file_path如果没有则从 file_url 提取路径
String pathToDelete = file.getFilePath();
if (StrUtil.isBlank(pathToDelete)) {
// 如果 file_path 为空,尝试从 file_url 提取路径
// file_url 格式可能是https://domain.com/path/to/file 或 /path/to/file
pathToDelete = extractPathFromUrl(file.getFileUrl());
}
if (StrUtil.isNotBlank(pathToDelete)) {
// 通过 FileApi 删除文件(需要先获取 FileDO然后删除
// 注意:这里需要关联 infra_file 表获取 configId 和 path
// 如果 file_id 存在,可以通过 FileService 删除
// 否则需要直接调用 FileClient 删除
// TODO: 实现物理删除OSS文件的逻辑
log.debug("[deleteFiles][准备删除OSS文件路径({})]", pathToDelete);
}
} catch (Exception e) {
// 删除OSS文件失败不影响逻辑删除只记录日志
log.warn("[deleteFiles][删除OSS文件失败文件编号({}),路径({})]",
file.getId(), file.getFilePath(), e);
}
boolean allOwnedByUser = files.stream().allMatch(file -> file.getUserId().equals(userId));
if (!allOwnedByUser) {
throw exception(FILE_NOT_EXISTS, "文件不属于当前用户");
}
// 逻辑删除文件
@@ -442,7 +328,7 @@ public class TikUserFileServiceImpl implements TikUserFileService {
long totalSize = files.stream().mapToLong(TikUserFileDO::getFileSize).sum();
memberUserProfileService.decreaseUsedStorage(String.valueOf(userId), totalSize);
log.info("[deleteFiles][用户({})删除文件成功,文件数量({})]", userId, fileIds.size());
log.info("[deleteFiles][用户({})删除文件成功,数量({})]", userId, fileIds.size());
}
@Override
@@ -514,37 +400,25 @@ public class TikUserFileServiceImpl implements TikUserFileService {
/**
* 确定预览URL
*
* @param file 文件对象
* @param type 预览类型cover/thumbnail可选
* @return 预览URL
*/
private String determinePreviewUrl(TikUserFileDO file, String type) {
// 视频文件不返回任何OSS URL前端使用coverBase64缓存
boolean isVideo = StrUtil.containsIgnoreCase(file.getFileType(), "video");
if (isVideo) {
if (StrUtil.containsIgnoreCase(file.getFileType(), "video")) {
return null;
}
// 明确指定封面类型(非视频文件)
// 明确指定类型
if (StrUtil.equals(type, "cover") && StrUtil.isNotBlank(file.getCoverUrl())) {
return file.getCoverUrl();
}
// 明确指定缩略图类型
if (StrUtil.equals(type, "thumbnail") && StrUtil.isNotBlank(file.getThumbnailUrl())) {
return file.getThumbnailUrl();
}
// 根据文件类型自动选择
boolean isImage = FileTypeUtils.isImage(file.getFileType());
if (isImage) {
return StrUtil.isNotBlank(file.getThumbnailUrl())
? file.getThumbnailUrl()
: file.getFileUrl();
if (FileTypeUtils.isImage(file.getFileType())) {
return StrUtil.isNotBlank(file.getThumbnailUrl()) ? file.getThumbnailUrl() : file.getFileUrl();
}
// 默认返回原文件URL
return file.getFileUrl();
}
@@ -558,92 +432,41 @@ public class TikUserFileServiceImpl implements TikUserFileService {
return fileApi.presignGetUrl(url, expirationSeconds != null ? expirationSeconds : 86400);
}
/**
* 从URL中提取文件路径
*
* @param url 文件URL
* @return 文件路径
*/
private String extractPathFromUrl(String url) {
if (StrUtil.isBlank(url)) {
return null;
}
try {
// 移除URL中的查询参数签名参数等
String cleanUrl = url;
if (url.contains("?")) {
cleanUrl = url.substring(0, url.indexOf("?"));
}
// 如果URL包含域名提取路径部分
if (cleanUrl.contains("://")) {
int pathStart = cleanUrl.indexOf("/", cleanUrl.indexOf("://") + 3);
if (pathStart > 0) {
String fullPath = cleanUrl.substring(pathStart);
// 路径可能包含 bucket 名称,需要提取实际的文件路径
// 例如:/bucket-name/user-id/tenant-id/voice/20251117/file.wav
// 实际 path 可能是user-id/tenant-id/voice/20251117/file.wav
// 但数据库中的 path 格式是voice/20251117/file_timestamp.wav
// 所以我们需要找到包含日期格式的部分yyyyMMdd
return fullPath;
}
}
// 如果已经是路径格式,直接返回(去除查询参数)
if (cleanUrl.startsWith("/")) {
return cleanUrl;
}
} catch (Exception e) {
log.warn("[extractPathFromUrl][从URL提取路径失败URL({})]", url, e);
}
return null;
}
@Override
public Object getPresignedUrl(String fileName, String fileCategory, Long groupId, Long fileSize) {
public Object getPresignedUrl(String fileName, String fileCategory, Long groupId, Long fileSize, String contentType) {
Long userId = SecurityFrameworkUtils.getLoginUserId();
// 1. 校验请求
validateUploadRequest(fileCategory, fileSize);
// 2. 获取OSS基础目录路径
String baseDirectory = ossInitService.getOssDirectoryByCategory(userId, fileCategory);
// 3. 生成文件路径
String filePath = generateUploadPath(fileName, baseDirectory);
// 4. 生成预签名URL
FileClient client = fileConfigService.getMasterFileClient();
Assert.notNull(client, "客户端(master) 不能为空");
String presignedUrl = client.presignPutUrl(filePath);
String effectiveContentType = StrUtil.isNotBlank(contentType)
? contentType
: FileTypeUtils.getMineType(fileName);
String presignedUrl = client.presignPutUrl(filePath, effectiveContentType);
String visitUrl = client.presignGetUrl(filePath, null);
// 5. 替换为自定义域名
String domain = getFileClientDomain(client.getId());
if (StrUtil.isNotBlank(domain)) {
presignedUrl = replaceUrlDomain(presignedUrl, domain);
visitUrl = replaceUrlDomain(visitUrl, domain);
}
// 6. 构建返回结果
return Map.of(
"presignedUrl", presignedUrl,
"uploadUrl", HttpUtils.removeUrlQuery(visitUrl),
"fileKey", filePath,
"expiresIn", PRESIGN_URL_EXPIRATION_SECONDS,
"method", "PUT",
"headers", Map.of(
"Content-Type", FileTypeUtils.getMineType(fileName)
)
"headers", Map.of("Content-Type", effectiveContentType)
);
}
/**
* 获取文件客户端的自定义域名
* @param configId 配置编号
* @return 自定义域名,如果没有配置则返回 null
*/
private String getFileClientDomain(Long configId) {
if (configId == null) {
return null;
@@ -661,26 +484,18 @@ public class TikUserFileServiceImpl implements TikUserFileService {
/**
* 替换URL的域名部分
* @param url 原始URL
* @param newDomain 新域名
* @return 替换后的URL
*/
private String replaceUrlDomain(String url, String newDomain) {
if (StrUtil.isBlank(url) || StrUtil.isBlank(newDomain)) {
return url;
}
try {
// 提取原始URL的协议和主机名
int schemeEnd = url.indexOf("://");
if (schemeEnd > 0) {
int pathStart = url.indexOf("/", schemeEnd + 3);
String pathAndQuery = pathStart > 0 ? url.substring(pathStart) : "";
return newDomain + pathAndQuery;
}
} catch (Exception e) {
log.warn("[replaceUrlDomain][替换域名失败url({})]", url, e);
int schemeEnd = url.indexOf("://");
if (schemeEnd <= 0) {
return url;
}
return url;
int pathStart = url.indexOf("/", schemeEnd + 3);
String pathAndQuery = pathStart > 0 ? url.substring(pathStart) : "";
return newDomain + pathAndQuery;
}
@Override
@@ -689,7 +504,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
public Object completeUpload(Object request) {
Long userId = SecurityFrameworkUtils.getLoginUserId();
// 解析请求参数
Map<String, Object> params = (Map<String, Object>) request;
String fileKey = (String) params.get("fileKey");
String fileName = (String) params.get("fileName");
@@ -700,16 +514,12 @@ public class TikUserFileServiceImpl implements TikUserFileService {
String coverBase64 = (String) params.get("coverBase64");
Integer duration = params.get("duration") != null ? ((Number) params.get("duration")).intValue() : null;
// 1. 校验请求
validateUploadRequest(fileCategory, fileSize);
// 2. 获取文件访问URL
FileClient client = fileConfigService.getMasterFileClient();
Assert.notNull(client, "客户端(master) 不能为空");
String fileUrl = client.presignGetUrl(fileKey, null);
fileUrl = HttpUtils.removeUrlQuery(fileUrl);
String fileUrl = HttpUtils.removeUrlQuery(client.presignGetUrl(fileKey, null));
// 3. 保存到 infra_file 表
FileDO infraFile = new FileDO()
.setConfigId(client.getId())
.setName(fileName)
@@ -720,18 +530,15 @@ public class TikUserFileServiceImpl implements TikUserFileService {
fileMapper.insert(infraFile);
Long infraFileId = infraFile.getId();
// 4. 处理视频封面
String baseDirectory = ossInitService.getOssDirectoryByCategory(userId, fileCategory);
String coverUrl = handleCoverUpload(coverBase64, fileName, fileType, baseDirectory);
// 处理显示名称(去除文件后缀)
String displayName = FileUtil.mainName(fileName);
// 5. 保存到 tik_user_file 表
TikUserFileDO userFile = new TikUserFileDO()
.setUserId(userId)
.setFileId(infraFileId)
.setFileName(fileName) // 保留原始文件名(系统标识)
.setDisplayName(displayName) // 设置显示名称(无后缀,用户可编辑)
.setFileName(fileName)
.setDisplayName(displayName)
.setFileType(fileType)
.setFileCategory(fileCategory)
.setFileSize(fileSize)
@@ -743,13 +550,11 @@ public class TikUserFileServiceImpl implements TikUserFileService {
.setGroupId(groupId);
userFileMapper.insert(userFile);
// 6. 更新配额
memberUserProfileService.increaseUsedStorage(String.valueOf(userId), fileSize);
log.info("[completeUpload][用户({})直传上传完成infraFileId({})userFileId({})]",
userId, infraFileId, userFile.getId());
// 7. 返回结果
return Map.of(
"infraFileId", infraFileId,
"userFileId", userFile.getId(),