From fcf095728e928bdc449a1551d9601a792011ffff Mon Sep 17 00:00:00 2001 From: RichZDS <3388214266@qq.com> Date: Wed, 14 Jan 2026 22:57:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(storage):=20=E9=9B=86=E6=88=90=E8=85=BE?= =?UTF-8?q?=E8=AE=AF=E4=BA=91COS=E5=AE=9E=E7=8E=B0=E7=AE=80=E5=8E=86PDF?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 配置腾讯云COS相关参数和密钥设置 - 新增CosConfig配置类和CosService对象存储服务 - 实现文件上传、删除、存在性检查等功能 - 修改简历上传接口使用COS存储替代本地存储 - 限制文件格式仅支持PDF并更新前端验证逻辑 - 添加删除COS文件的后端接口和前端调用 - 移除前端MyResumes页面中的旧PDF管理组件 - 更新上传和删除操作的安全验证和权限检查 --- BOSSBackEnd/.gitignore | 1 + BOSSBackEnd/pom.xml | 6 + .../java/com/zds/boss/config/CosConfig.java | 58 +++++++ .../zds/boss/controller/ResumeController.java | 141 ++++++++++++---- .../java/com/zds/boss/service/CosService.java | 159 ++++++++++++++++++ .../boss/service/ResumeAddressService.java | 37 ++++ .../impl/ResumeAddressServiceImpl.java | 56 ++++++ .../src/main/resources/application.yml | 13 ++ BOSSFrontEnd/src/api/api/resumeController.ts | 16 +- BOSSFrontEnd/src/views/MyResumes.vue | 42 ++--- BOSSFrontEnd/src/views/ResumeEdit.vue | 51 ++++-- 11 files changed, 513 insertions(+), 67 deletions(-) create mode 100644 BOSSBackEnd/src/main/java/com/zds/boss/config/CosConfig.java create mode 100644 BOSSBackEnd/src/main/java/com/zds/boss/service/CosService.java create mode 100644 BOSSBackEnd/src/main/java/com/zds/boss/service/ResumeAddressService.java create mode 100644 BOSSBackEnd/src/main/java/com/zds/boss/service/impl/ResumeAddressServiceImpl.java diff --git a/BOSSBackEnd/.gitignore b/BOSSBackEnd/.gitignore index 667aaef..d57193e 100644 --- a/BOSSBackEnd/.gitignore +++ b/BOSSBackEnd/.gitignore @@ -31,3 +31,4 @@ build/ ### VS Code ### .vscode/ +/src/main/resources/application-dev.yml diff --git a/BOSSBackEnd/pom.xml b/BOSSBackEnd/pom.xml index 5aa9c65..35390fc 100644 --- a/BOSSBackEnd/pom.xml +++ b/BOSSBackEnd/pom.xml @@ -123,6 +123,12 @@ 6.0.0 provided + + + com.qcloud + cos_api + 5.6.227 + diff --git a/BOSSBackEnd/src/main/java/com/zds/boss/config/CosConfig.java b/BOSSBackEnd/src/main/java/com/zds/boss/config/CosConfig.java new file mode 100644 index 0000000..e604f17 --- /dev/null +++ b/BOSSBackEnd/src/main/java/com/zds/boss/config/CosConfig.java @@ -0,0 +1,58 @@ +package com.zds.boss.config; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.region.Region; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 腾讯云COS配置类 + */ +@Configuration +@ConfigurationProperties(prefix = "cos") +@Data +public class CosConfig { + + /** + * 密钥ID + */ + private String secretId; + + /** + * 密钥Key + */ + private String secretKey; + + /** + * 地域 + */ + private String region; + + /** + * 存储桶名称 + */ + private String bucketName; + + /** + * 访问域名前缀 + */ + private String urlPrefix; + + /** + * 创建COS客户端Bean + */ + @Bean + public COSClient cosClient() { + // 初始化用户身份信息 + COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); + // 设置bucket的区域 + ClientConfig clientConfig = new ClientConfig(new Region(region)); + // 生成cos客户端 + return new COSClient(cred, clientConfig); + } +} diff --git a/BOSSBackEnd/src/main/java/com/zds/boss/controller/ResumeController.java b/BOSSBackEnd/src/main/java/com/zds/boss/controller/ResumeController.java index 80403f9..834ea2f 100644 --- a/BOSSBackEnd/src/main/java/com/zds/boss/controller/ResumeController.java +++ b/BOSSBackEnd/src/main/java/com/zds/boss/controller/ResumeController.java @@ -14,7 +14,9 @@ import com.zds.boss.model.entity.Resume; import com.zds.boss.model.entity.User; import com.zds.boss.model.enums.UserRoleEnum; import com.zds.boss.model.vo.ResumeVO; +import com.zds.boss.service.CosService; import com.zds.boss.service.FileService; +import com.zds.boss.service.ResumeAddressService; import com.zds.boss.service.ResumeService; import com.zds.boss.service.UserService; import jakarta.annotation.Resource; @@ -24,7 +26,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.util.UUID; /** @@ -44,6 +45,12 @@ public class ResumeController { @Resource private FileService fileService; + @Resource + private CosService cosService; + + @Resource + private ResumeAddressService resumeAddressService; + @Value("${file.max-size-mb:10}") private long maxSizeMb; @@ -158,14 +165,18 @@ public class ResumeController { } /** - * 上传简历附件文件 + * 上传简历附件文件到腾讯云COS * - * @param file 文件 - * @param request HTTP请求 + * @param file 文件 + * @param resumeId 简历ID(可选,如果传入则同时更新resume的attachment_url) + * @param request HTTP请求 * @return 文件访问URL */ @PostMapping("/upload") - public BaseResponse uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) { + public BaseResponse uploadFile( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "resumeId", required = false) Long resumeId, + HttpServletRequest request) { if (file == null || file.isEmpty()) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不能为空"); } @@ -192,36 +203,104 @@ public class ResumeController { extension = originalFilename.substring(lastDotIndex).toLowerCase(); } - // 验证文件类型(允许PDF和Word文档) + // 验证文件类型(仅允许PDF) String contentType = file.getContentType(); - boolean isAllowedContentType = "application/pdf".equals(contentType) - || "application/msword".equals(contentType) - || "application/vnd.openxmlformats-officedocument.wordprocessingml.document".equals(contentType); - boolean isAllowedExtension = ".pdf".equals(extension) || ".doc".equals(extension) || ".docx".equals(extension); + boolean isAllowedContentType = "application/pdf".equals(contentType); + boolean isAllowedExtension = ".pdf".equals(extension); if (!isAllowedContentType && !isAllowedExtension) { - throw new BusinessException(ErrorCode.PARAMS_ERROR, "仅支持PDF和Word文档格式"); + throw new BusinessException(ErrorCode.PARAMS_ERROR, "仅支持PDF格式"); } - try { - // 生成唯一文件名:用户ID_时间戳_UUID.扩展名 - String timestamp = String.valueOf(System.currentTimeMillis()); - String uniqueFileName = String.format("%d_%s_%s%s", - loginUser.getId(), timestamp, UUID.randomUUID().toString().replace("-", ""), extension); - - // 构建相对路径:resume/{userId}/{filename} - String relativePath = String.format("resume/%d/%s", loginUser.getId(), uniqueFileName); - - // 上传文件 - fileService.upload(relativePath, file); - - // 构建文件访问URL - String fileUrl = fileService.buildUrl(relativePath); - - log.info("用户 {} 上传文件成功: {}, URL: {}", loginUser.getId(), originalFilename, fileUrl); - return ResultUtils.success(fileUrl); - } catch (IOException e) { - log.error("文件上传失败: {}", e.getMessage(), e); - throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败: " + e.getMessage()); + // 如果传入了resumeId,检查是否有权限操作该简历 + if (resumeId != null && resumeId > 0) { + Resume resume = resumeService.getById(resumeId); + if (resume == null) { + throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "简历不存在"); + } + if (!resume.getUserId().equals(loginUser.getId())) { + throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权操作该简历"); + } } + + // 生成随机文件Key(用于resume_address表) + String fileKey = UUID.randomUUID().toString().replace("-", ""); + + // 生成唯一文件名:用户ID_时间戳_UUID + String timestamp = String.valueOf(System.currentTimeMillis()); + String customFileName = String.format("%d_%s_%s", + loginUser.getId(), timestamp, fileKey); + + // 构建存储目录:resume/{userId} + String directory = String.format("resume/%d", loginUser.getId()); + + // 上传文件到腾讯云COS + String fileUrl = cosService.uploadFile(file, directory, customFileName); + + // 如果传入了resumeId,更新resume的attachment_url + if (resumeId != null && resumeId > 0) { + Resume resume = new Resume(); + resume.setId(resumeId); + resume.setAttachmentUrl(fileUrl); + resumeService.updateById(resume); + log.info("已更新简历 {} 的附件URL", resumeId); + } + + // 保存或更新resume_address记录 + resumeAddressService.saveOrUpdateByUserId(loginUser.getId(), resumeId, fileUrl, fileKey); + log.info("已保存简历地址记录,userId: {}, fileKey: {}", loginUser.getId(), fileKey); + + log.info("用户 {} 上传简历PDF成功: {}, URL: {}", loginUser.getId(), originalFilename, fileUrl); + return ResultUtils.success(fileUrl); + } + + /** + * 删除COS中的简历附件文件 + * + * @param fileUrl 文件URL + * @param resumeId 简历ID(可选,如果传入则同时清空resume的attachment_url) + * @param request HTTP请求 + * @return 是否删除成功 + */ + @PostMapping("/delete-file") + public BaseResponse deleteFile( + @RequestParam("fileUrl") String fileUrl, + @RequestParam(value = "resumeId", required = false) Long resumeId, + HttpServletRequest request) { + if (fileUrl == null || fileUrl.isEmpty()) { + throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件URL不能为空"); + } + + User loginUser = userService.getLoginUser(request); + + // 验证URL是否属于当前用户(安全检查) + String userDirectory = String.format("resume/%d/", loginUser.getId()); + if (!fileUrl.contains(userDirectory)) { + throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权删除该文件"); + } + + // 删除COS中的文件 + boolean result = cosService.deleteFile(fileUrl); + + if (result) { + log.info("用户 {} 删除文件成功: {}", loginUser.getId(), fileUrl); + + // 如果传入了resumeId,清空resume的attachment_url + if (resumeId != null && resumeId > 0) { + Resume resume = resumeService.getById(resumeId); + if (resume != null && resume.getUserId().equals(loginUser.getId())) { + resume.setAttachmentUrl(""); + resumeService.updateById(resume); + log.info("已清空简历 {} 的附件URL", resumeId); + } + } + + // 删除resume_address记录 + resumeAddressService.deleteByUserId(loginUser.getId()); + log.info("已删除用户 {} 的简历地址记录", loginUser.getId()); + } else { + log.warn("用户 {} 删除文件失败: {}", loginUser.getId(), fileUrl); + } + + return ResultUtils.success(result); } } diff --git a/BOSSBackEnd/src/main/java/com/zds/boss/service/CosService.java b/BOSSBackEnd/src/main/java/com/zds/boss/service/CosService.java new file mode 100644 index 0000000..9113876 --- /dev/null +++ b/BOSSBackEnd/src/main/java/com/zds/boss/service/CosService.java @@ -0,0 +1,159 @@ +package com.zds.boss.service; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.model.*; +import com.zds.boss.config.CosConfig; +import com.zds.boss.exception.BusinessException; +import com.zds.boss.exception.ErrorCode; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +/** + * 腾讯云COS对象存储服务 + */ +@Service +@Slf4j +public class CosService { + + @Resource + private COSClient cosClient; + + @Resource + private CosConfig cosConfig; + + /** + * 上传文件到COS + * + * @param file 要上传的文件 + * @param directory 存储目录(如:resume/123) + * @param customName 自定义文件名(不含扩展名),为null时使用UUID + * @return 文件的访问URL + */ + public String uploadFile(MultipartFile file, String directory, String customName) { + // 获取原始文件名和扩展名 + String originalFilename = file.getOriginalFilename(); + String extension = ""; + if (originalFilename != null && originalFilename.contains(".")) { + extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase(); + } + + // 生成文件名 + String fileName; + if (customName != null && !customName.isEmpty()) { + fileName = customName + extension; + } else { + fileName = UUID.randomUUID().toString().replace("-", "") + extension; + } + + // 构建完整的对象Key(路径) + String key = directory + "/" + fileName; + // 移除开头的斜杠(如果有) + if (key.startsWith("/")) { + key = key.substring(1); + } + + try (InputStream inputStream = file.getInputStream()) { + // 设置对象元数据 + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(file.getSize()); + metadata.setContentType(file.getContentType()); + + // 创建上传请求 + PutObjectRequest putObjectRequest = new PutObjectRequest( + cosConfig.getBucketName(), + key, + inputStream, + metadata + ); + + // 执行上传 + PutObjectResult result = cosClient.putObject(putObjectRequest); + log.info("文件上传成功,Key: {}, ETag: {}", key, result.getETag()); + + // 返回文件访问URL + return cosConfig.getUrlPrefix() + "/" + key; + } catch (IOException e) { + log.error("文件上传失败: {}", e.getMessage(), e); + throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败: " + e.getMessage()); + } + } + + /** + * 上传文件到COS(使用默认UUID文件名) + * + * @param file 要上传的文件 + * @param directory 存储目录 + * @return 文件的访问URL + */ + public String uploadFile(MultipartFile file, String directory) { + return uploadFile(file, directory, null); + } + + /** + * 删除COS中的文件 + * + * @param fileUrl 文件的完整URL + * @return 是否删除成功 + */ + public boolean deleteFile(String fileUrl) { + if (fileUrl == null || fileUrl.isEmpty()) { + return false; + } + + try { + // 从URL中提取对象Key + String key = extractKeyFromUrl(fileUrl); + if (key == null) { + log.warn("无法从URL中提取Key: {}", fileUrl); + return false; + } + + // 删除对象 + cosClient.deleteObject(cosConfig.getBucketName(), key); + log.info("文件删除成功,Key: {}", key); + return true; + } catch (Exception e) { + log.error("文件删除失败: {}", e.getMessage(), e); + return false; + } + } + + /** + * 从URL中提取对象Key + * + * @param fileUrl 文件URL + * @return 对象Key + */ + private String extractKeyFromUrl(String fileUrl) { + String urlPrefix = cosConfig.getUrlPrefix(); + if (fileUrl.startsWith(urlPrefix)) { + String key = fileUrl.substring(urlPrefix.length()); + if (key.startsWith("/")) { + key = key.substring(1); + } + return key; + } + return null; + } + + /** + * 检查文件是否存在 + * + * @param key 对象Key + * @return 是否存在 + */ + public boolean doesObjectExist(String key) { + try { + return cosClient.doesObjectExist(cosConfig.getBucketName(), key); + } catch (Exception e) { + log.error("检查文件是否存在失败: {}", e.getMessage(), e); + return false; + } + } +} diff --git a/BOSSBackEnd/src/main/java/com/zds/boss/service/ResumeAddressService.java b/BOSSBackEnd/src/main/java/com/zds/boss/service/ResumeAddressService.java new file mode 100644 index 0000000..76e0662 --- /dev/null +++ b/BOSSBackEnd/src/main/java/com/zds/boss/service/ResumeAddressService.java @@ -0,0 +1,37 @@ +package com.zds.boss.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zds.boss.model.entity.ResumeAddress; + +/** + * 简历地址服务 + */ +public interface ResumeAddressService extends IService { + + /** + * 根据用户ID获取简历地址 + * + * @param userId 用户ID + * @return 简历地址 + */ + ResumeAddress getByUserId(Long userId); + + /** + * 保存或更新简历地址 + * + * @param userId 用户ID + * @param resumeId 简历ID(可为空) + * @param address 文件URL + * @param fileKey 文件Key + * @return 简历地址 + */ + ResumeAddress saveOrUpdateByUserId(Long userId, Long resumeId, String address, String fileKey); + + /** + * 根据用户ID删除简历地址 + * + * @param userId 用户ID + * @return 是否成功 + */ + boolean deleteByUserId(Long userId); +} diff --git a/BOSSBackEnd/src/main/java/com/zds/boss/service/impl/ResumeAddressServiceImpl.java b/BOSSBackEnd/src/main/java/com/zds/boss/service/impl/ResumeAddressServiceImpl.java new file mode 100644 index 0000000..36c06cc --- /dev/null +++ b/BOSSBackEnd/src/main/java/com/zds/boss/service/impl/ResumeAddressServiceImpl.java @@ -0,0 +1,56 @@ +package com.zds.boss.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zds.boss.mapper.ResumeAddressMapper; +import com.zds.boss.model.entity.ResumeAddress; +import com.zds.boss.service.ResumeAddressService; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * 简历地址服务实现 + */ +@Service +public class ResumeAddressServiceImpl extends ServiceImpl + implements ResumeAddressService { + + @Override + public ResumeAddress getByUserId(Long userId) { + return this.getOne(new LambdaQueryWrapper() + .eq(ResumeAddress::getUserId, userId)); + } + + @Override + public ResumeAddress saveOrUpdateByUserId(Long userId, Long resumeId, String address, String fileKey) { + ResumeAddress existing = getByUserId(userId); + + if (existing != null) { + // 更新现有记录 + existing.setResumeId(resumeId); + existing.setAddress(address); + existing.setFileKey(fileKey); + existing.setUpdatedAt(new Date()); + this.updateById(existing); + return existing; + } else { + // 创建新记录 + ResumeAddress resumeAddress = new ResumeAddress(); + resumeAddress.setUserId(userId); + resumeAddress.setResumeId(resumeId); + resumeAddress.setAddress(address); + resumeAddress.setFileKey(fileKey); + resumeAddress.setCreatedAt(new Date()); + resumeAddress.setUpdatedAt(new Date()); + this.save(resumeAddress); + return resumeAddress; + } + } + + @Override + public boolean deleteByUserId(Long userId) { + return this.remove(new LambdaQueryWrapper() + .eq(ResumeAddress::getUserId, userId)); + } +} diff --git a/BOSSBackEnd/src/main/resources/application.yml b/BOSSBackEnd/src/main/resources/application.yml index bd98e05..9021d7c 100644 --- a/BOSSBackEnd/src/main/resources/application.yml +++ b/BOSSBackEnd/src/main/resources/application.yml @@ -71,3 +71,16 @@ file: path: src/main/resources/static # 最大文件大小(MB) max-size-mb: 10 + +# 腾讯云COS配置 +cos: + # 密钥ID + secret-id: + # 密钥Key(生产环境请使用环境变量或配置中心) + secret-key: + # 地域 + region: ap-shanghai + # 存储桶名称 + bucket-name: bosss-1336488383 + # 访问域名前缀 + url-prefix: https://bosss-1336488383.cos.ap-shanghai.myqcloud.com \ No newline at end of file diff --git a/BOSSFrontEnd/src/api/api/resumeController.ts b/BOSSFrontEnd/src/api/api/resumeController.ts index 4ccfc5a..94d0f25 100644 --- a/BOSSFrontEnd/src/api/api/resumeController.ts +++ b/BOSSFrontEnd/src/api/api/resumeController.ts @@ -71,7 +71,7 @@ export async function updateResume( }) } -/** 此处后端没有提供注释 POST /resume/upload */ +/** 上传简历PDF文件到腾讯云COS POST /resume/upload */ export async function uploadFile(body: {}, options?: { [key: string]: any }) { return request('/resume/upload', { method: 'POST', @@ -82,3 +82,17 @@ export async function uploadFile(body: {}, options?: { [key: string]: any }) { ...(options || {}), }) } + +/** 删除COS中的简历附件文件 POST /resume/delete-file */ +export async function deleteResumeFile( + params: { fileUrl: string; resumeId?: number }, + options?: { [key: string]: any } +) { + return request('/resume/delete-file', { + method: 'POST', + params: { + ...params, + }, + ...(options || {}), + }) +} diff --git a/BOSSFrontEnd/src/views/MyResumes.vue b/BOSSFrontEnd/src/views/MyResumes.vue index 8d8c056..2d25ce2 100644 --- a/BOSSFrontEnd/src/views/MyResumes.vue +++ b/BOSSFrontEnd/src/views/MyResumes.vue @@ -35,26 +35,26 @@ - -
- -
-
-
- - 查看PDF - - 删除 - - -
-
- - 上传PDF - -
-
-
+ + + + + + + + + + + + + + + + + + + + @@ -62,7 +62,7 @@ import NavBar from '@/components/NavBar.vue'; import { ref, onMounted, reactive } from 'vue'; import { listResumeVoByPage, deleteResume } from '@/api/api/resumeController'; -import { uploadResumePdf, getMyResumeAddress, deleteMyResumeAddress } from '@/api/api/resumeAddressController'; +import { } from '@/api/api/resumeController'; import { message } from 'ant-design-vue'; import { useRouter } from 'vue-router'; import { useUserStore } from '@/stores/user'; diff --git a/BOSSFrontEnd/src/views/ResumeEdit.vue b/BOSSFrontEnd/src/views/ResumeEdit.vue index 6fba74b..ee343fc 100644 --- a/BOSSFrontEnd/src/views/ResumeEdit.vue +++ b/BOSSFrontEnd/src/views/ResumeEdit.vue @@ -31,11 +31,11 @@ :before-upload="beforeUpload" :show-upload-list="false" :custom-request="handleUpload" - accept=".pdf,.doc,.docx" + accept=".pdf" > - 上传简历附件(PDF/Word) + 上传简历PDF
@@ -50,15 +50,17 @@
- 支持PDF和Word格式,最大10MB + 仅支持PDF格式,最大10MB(文件将上传至腾讯云COS)
@@ -80,9 +82,9 @@ import NavBar from '@/components/NavBar.vue'; import { reactive, ref, onMounted, computed } from 'vue'; import { useRoute, useRouter } from 'vue-router'; -import { addResume, updateResume, getResumeVoById } from '@/api/api/resumeController'; +import { addResume, updateResume, getResumeVoById, deleteResumeFile } from '@/api/api/resumeController'; import { message, Upload } from 'ant-design-vue'; -import { UploadOutlined, FileTextOutlined } from '@ant-design/icons-vue'; +import { UploadOutlined, FilePdfOutlined } from '@ant-design/icons-vue'; const route = useRoute(); const router = useRouter(); @@ -132,14 +134,12 @@ const extractFileName = (url: string) => { const beforeUpload = (file: File) => { const isPdf = file.type === 'application/pdf'; - const isWord = file.type === 'application/msword' || - file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; const extension = file.name.split('.').pop()?.toLowerCase(); - const isAllowedExtension = extension === 'pdf' || extension === 'doc' || extension === 'docx'; + const isAllowedExtension = extension === 'pdf'; const isLt10M = file.size / 1024 / 1024 < 10; - if (!isPdf && !isWord && !isAllowedExtension) { - message.error('仅支持PDF和Word文档格式'); + if (!isPdf && !isAllowedExtension) { + message.error('仅支持PDF格式'); return Upload.LIST_IGNORE; } if (!isLt10M) { @@ -156,6 +156,11 @@ const handleUpload = async (options: any) => { try { const formData = new FormData(); formData.append('file', file); + + // 如果是编辑模式,传入resumeId以便同时更新resume的attachment_url + if (isEdit.value && route.params.id) { + formData.append('resumeId', String(route.params.id)); + } const baseUrl = import.meta.env.VITE_API_BASE_URL || '/api'; const xhr = new XMLHttpRequest(); @@ -201,7 +206,25 @@ const handleUpload = async (options: any) => { } }; -const clearAttachment = () => { +const clearAttachment = async () => { + if (formState.attachmentUrl) { + try { + // 调用后端接口删除COS中的文件,如果是编辑模式传入resumeId + const params: { fileUrl: string; resumeId?: number } = { fileUrl: formState.attachmentUrl }; + if (isEdit.value && route.params.id) { + params.resumeId = Number(route.params.id); + } + const res = await deleteResumeFile(params); + if (res.code === 0) { + message.success('文件删除成功'); + } else { + message.warning('文件删除可能失败,请手动确认'); + } + } catch (error) { + console.error('删除文件失败:', error); + message.warning('文件删除失败'); + } + } formState.attachmentUrl = ''; uploadFileName.value = ''; };