From fd043bb9f2c25e0fe78e39aa116b8a7598f05b07 Mon Sep 17 00:00:00 2001 From: xiaowang Date: Tue, 29 Jul 2025 14:51:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E7=89=88=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- na-core/pom.xml | 6 + .../java/com/nailart/config/MinioConfig.java | 47 + .../controller/FileStorageController.java | 170 +++ .../controller/MainInfoController.java | 54 + .../controller/PortfolioController.java | 66 +- .../SampleCollectionController.java | 156 +++ .../nailart/controller/TagInfoController.java | 120 ++ .../java/com/nailart/dataobj/MainInfoDO.java | 72 ++ .../java/com/nailart/dataobj/PortfolioDO.java | 9 +- .../nailart/dataobj/SampleCollectionDO.java | 46 + .../main/java/com/nailart/dataobj/TagDO.java | 19 + .../com/nailart/mapper/MainInfoMapper.java | 9 + .../com/nailart/mapper/PortfolioMapper.java | 1 + .../mapper/SampleCollectionMapper.java | 9 + .../com/nailart/mapper/TagInfoMapper.java | 17 + .../nailart/service/FileStorageService.java | 91 ++ .../com/nailart/service/MainInfoService.java | 10 + .../com/nailart/service/PortfolioService.java | 4 + .../service/SampleCollectionService.java | 17 + .../com/nailart/service/TagInfoService.java | 55 + .../service/impl/FileStorageServiceImpl.java | 202 +++ .../service/impl/MainInfoServiceImpl.java | 45 + .../service/impl/PortfolioServiceImpl.java | 18 +- .../impl/SampleCollectionServiceImpl.java | 66 + .../service/impl/TagInfoServiceImpl.java | 60 + .../java/com/nailart/utils/MinioUtils.java | 303 +++++ .../src/main/java/com/nailart/vo/RespVO.java | 25 + na-core/src/main/resources/application.yml | 19 +- na-frontend/src/App.vue | 24 +- na-frontend/src/components/ManTabBar.vue | 47 + na-frontend/src/components/NavBar.vue | 6 +- na-frontend/src/components/TabBar.vue | 4 +- na-frontend/src/main.js | 34 + na-frontend/src/pages/DetailPage.vue | 22 + na-frontend/src/pages/HomePage.vue | 53 +- na-frontend/src/pages/PortfolioPage.vue | 271 +++-- .../src/pages/SampleCollectionPage.vue | 703 +++++++---- na-frontend/src/pages/man/HomeManPage.vue | 358 ++++++ .../src/pages/man/PortfolioManPage.vue | 1083 ++++++++++++++++ .../src/pages/man/SampleCollectionManPage.vue | 1084 +++++++++++++++++ na-frontend/src/pages/man/TagManPage.vue | 31 + na-frontend/src/router/index.js | 27 +- na-frontend/src/utils/file.js | 227 ++++ na-frontend/src/utils/request.js | 53 +- 44 files changed, 5376 insertions(+), 367 deletions(-) create mode 100644 na-core/src/main/java/com/nailart/config/MinioConfig.java create mode 100644 na-core/src/main/java/com/nailart/controller/FileStorageController.java create mode 100644 na-core/src/main/java/com/nailart/controller/MainInfoController.java create mode 100644 na-core/src/main/java/com/nailart/controller/SampleCollectionController.java create mode 100644 na-core/src/main/java/com/nailart/controller/TagInfoController.java create mode 100644 na-core/src/main/java/com/nailart/dataobj/MainInfoDO.java create mode 100644 na-core/src/main/java/com/nailart/dataobj/SampleCollectionDO.java create mode 100644 na-core/src/main/java/com/nailart/dataobj/TagDO.java create mode 100644 na-core/src/main/java/com/nailart/mapper/MainInfoMapper.java create mode 100644 na-core/src/main/java/com/nailart/mapper/SampleCollectionMapper.java create mode 100644 na-core/src/main/java/com/nailart/mapper/TagInfoMapper.java create mode 100644 na-core/src/main/java/com/nailart/service/FileStorageService.java create mode 100644 na-core/src/main/java/com/nailart/service/MainInfoService.java create mode 100644 na-core/src/main/java/com/nailart/service/SampleCollectionService.java create mode 100644 na-core/src/main/java/com/nailart/service/TagInfoService.java create mode 100644 na-core/src/main/java/com/nailart/service/impl/FileStorageServiceImpl.java create mode 100644 na-core/src/main/java/com/nailart/service/impl/MainInfoServiceImpl.java create mode 100644 na-core/src/main/java/com/nailart/service/impl/SampleCollectionServiceImpl.java create mode 100644 na-core/src/main/java/com/nailart/service/impl/TagInfoServiceImpl.java create mode 100644 na-core/src/main/java/com/nailart/utils/MinioUtils.java create mode 100644 na-frontend/src/components/ManTabBar.vue create mode 100644 na-frontend/src/pages/DetailPage.vue create mode 100644 na-frontend/src/pages/man/HomeManPage.vue create mode 100644 na-frontend/src/pages/man/PortfolioManPage.vue create mode 100644 na-frontend/src/pages/man/SampleCollectionManPage.vue create mode 100644 na-frontend/src/pages/man/TagManPage.vue create mode 100644 na-frontend/src/utils/file.js diff --git a/na-core/pom.xml b/na-core/pom.xml index d525d65..1096593 100644 --- a/na-core/pom.xml +++ b/na-core/pom.xml @@ -137,6 +137,12 @@ 6.0.0 provided + + + io.minio + minio + 8.5.6 + diff --git a/na-core/src/main/java/com/nailart/config/MinioConfig.java b/na-core/src/main/java/com/nailart/config/MinioConfig.java new file mode 100644 index 0000000..7ec4782 --- /dev/null +++ b/na-core/src/main/java/com/nailart/config/MinioConfig.java @@ -0,0 +1,47 @@ +package com.nailart.config; + + +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * @Description MinIO配置类,用于创建MinIO客户端实例 + * @Classname MinioConfig + * @Date 2025/7/16 10:13 + * @Created by 21616 + */ +@Configuration +public class MinioConfig { + + @Value("${minio.endpoint}") + private String endpoint; + + @Value("${minio.accessKey}") + private String accessKey; + + @Value("${minio.secretKey}") + private String secretKey; + + @Value("${minio.region:}") + private String region; + + @Value("${minio.connectTimeout:30}") + private long connectTimeout; + + @Value("${minio.writeTimeout:60}") + private long writeTimeout; + + @Value("${minio.readTimeout:60}") + private long readTimeout; + + @Bean + public MinioClient minioClient() { + return MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + } +} \ No newline at end of file diff --git a/na-core/src/main/java/com/nailart/controller/FileStorageController.java b/na-core/src/main/java/com/nailart/controller/FileStorageController.java new file mode 100644 index 0000000..c0ac45d --- /dev/null +++ b/na-core/src/main/java/com/nailart/controller/FileStorageController.java @@ -0,0 +1,170 @@ +package com.nailart.controller; + + +import com.nailart.service.FileStorageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * @Description 文件服务控制类 + * @Classname FileStorageController + * @Date 2025/7/19 15:47 + * @Created by 21616 + */ +@RestController +@RequestMapping("/file") +public class FileStorageController { + + private final FileStorageService fileStorageService; + + @Autowired + public FileStorageController(FileStorageService fileStorageService) { + this.fileStorageService = fileStorageService; + } + + /** + * 上传文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @param file 文件内容 + * @return 上传成功的文件路径 + */ + @PostMapping("/upload") + public ResponseEntity uploadFile( + @RequestParam("bucketName") String bucketName, + @RequestParam("filePath") String filePath, + @RequestParam("file") MultipartFile file) { + + String uploadedFilePath = fileStorageService.uploadFile(bucketName, filePath, file); + return ResponseEntity.ok(uploadedFilePath); + } + + /** + * 下载文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @return 文件内容流 + */ + @GetMapping("/download") + public ResponseEntity downloadFile( + @RequestParam("bucketName") String bucketName, + @RequestParam("filePath") String filePath) { + + try { + InputStream fileStream = fileStorageService.downloadFile(bucketName, filePath); + + // 设置响应头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + + // 处理中文文件名编码 + String encodedFileName = URLEncoder.encode(filePath.substring(filePath.lastIndexOf("/") + 1), + StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + headers.setContentDispositionFormData("attachment", encodedFileName); + + return new ResponseEntity<>(fileStream, headers, HttpStatus.OK); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); + } + } + + /** + * 删除文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @return 操作结果 + */ + @DeleteMapping("/delete") + public ResponseEntity deleteFile( + @RequestParam("bucketName") String bucketName, + @RequestParam("filePath") String filePath) { + + fileStorageService.deleteFile(bucketName, filePath); + return ResponseEntity.noContent().build(); + } + + /** + * 检查文件是否存在 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @return 文件是否存在 + */ + @GetMapping("/exists") + public ResponseEntity fileExists( + @RequestParam("bucketName") String bucketName, + @RequestParam("filePath") String filePath) { + + boolean exists = fileStorageService.fileExists(bucketName, filePath); + return ResponseEntity.ok(exists); + } + + /** + * 获取文件的预签名URL + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @param expirySeconds 过期时间(秒) + * @return 预签名URL + */ + @GetMapping("/url") + public ResponseEntity getFileUrl( + @RequestParam("bucketName") String bucketName, + @RequestParam("filePath") String filePath, + @RequestParam(value = "expirySeconds", defaultValue = "3600") int expirySeconds) { + + String url = fileStorageService.getFileUrl(bucketName, filePath, expirySeconds); + return ResponseEntity.ok(url); + } + + /** + * 获取所有存储桶列表 + * @return 存储桶名称列表 + */ + @GetMapping("/buckets") + public ResponseEntity> listBuckets() { + List buckets = fileStorageService.listBuckets(); + return ResponseEntity.ok(buckets); + } + + /** + * 创建存储桶 + * @param bucketName 存储桶名称 + * @return 操作结果 + */ + @PostMapping("/buckets") + public ResponseEntity createBucket(@RequestParam("bucketName") String bucketName) { + fileStorageService.createBucket(bucketName); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + /** + * 删除存储桶 + * @param bucketName 存储桶名称 + * @return 操作结果 + */ + @DeleteMapping("/buckets") + public ResponseEntity deleteBucket(@RequestParam("bucketName") String bucketName) { + fileStorageService.deleteBucket(bucketName); + return ResponseEntity.noContent().build(); + } + + /** + * 检查存储桶是否存在 + * @param bucketName 存储桶名称 + * @return 存储桶是否存在 + */ + @GetMapping("/buckets/exists") + public ResponseEntity bucketExists(@RequestParam("bucketName") String bucketName) { + boolean exists = fileStorageService.bucketExists(bucketName); + return ResponseEntity.ok(exists); + } +} diff --git a/na-core/src/main/java/com/nailart/controller/MainInfoController.java b/na-core/src/main/java/com/nailart/controller/MainInfoController.java new file mode 100644 index 0000000..fbc9a60 --- /dev/null +++ b/na-core/src/main/java/com/nailart/controller/MainInfoController.java @@ -0,0 +1,54 @@ +package com.nailart.controller; + +import com.nailart.dataobj.MainInfoDO; +import com.nailart.enums.RespCodeEnum; +import com.nailart.service.MainInfoService; +import com.nailart.service.impl.FileStorageServiceImpl; +import com.nailart.vo.RespVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Description 主页信息控制类 + * @Classname MainInfoController + * @Date 2025/7/27 23:16 + * @Created by 21616 + */ +@RestController +@RequestMapping("/mainInfo") +public class MainInfoController { + private final MainInfoService mainInfoService; + private static final Logger logger = LoggerFactory.getLogger(MainInfoController.class); + + + public MainInfoController(MainInfoService mainInfoService) { + + this.mainInfoService = mainInfoService; + } + + @RequestMapping("/getMainInfo") + public RespVO getMainInfo() { + try { + MainInfoDO mainInfo = mainInfoService.getMainInfo(); + return RespVO.success(mainInfo); + } catch (Exception e) { + logger.error("获取主页信息失败", e); + return RespVO.error(RespCodeEnum.DATABASE_ERROR, RespCodeEnum.DATABASE_ERROR.getMessage()); + } + } + + @RequestMapping("/updateMainInfo") + public RespVO updateMainInfo(@RequestBody MainInfoDO mainInfoDO) { + try { + mainInfoService.updateMainInfo(mainInfoDO); + return RespVO.success(); + } catch (Exception e) { + logger.error("更新主信息失败", e); + return RespVO.error(RespCodeEnum.DATABASE_ERROR, RespCodeEnum.DATABASE_ERROR.getMessage()); + } + } +} diff --git a/na-core/src/main/java/com/nailart/controller/PortfolioController.java b/na-core/src/main/java/com/nailart/controller/PortfolioController.java index 91f5935..20ab2bb 100644 --- a/na-core/src/main/java/com/nailart/controller/PortfolioController.java +++ b/na-core/src/main/java/com/nailart/controller/PortfolioController.java @@ -26,8 +26,15 @@ public class PortfolioController { this.portfolioService = portfolioService; } + /** + * 获取作品集列表 + * + * @param page 页码 + * @param size 每页数量 + * @return RespVO + */ @GetMapping("/list") - public RespVO getPortfolioList(@RequestParam("page") int page, @RequestParam("size") int size) { + public RespVO getPortfolioList(@RequestParam("page") Integer page, @RequestParam("size") Integer size) { RespVO respVO = new RespVO(); if (page <= 0 || size <= 0) { respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); @@ -47,8 +54,14 @@ public class PortfolioController { } + /** + * 添加作品集 + * + * @param portfolioDO 作品集信息 + * @return RespVO + */ @PostMapping("/add") - public RespVO addPortfolio(PortfolioDO portfolioDO) { + public RespVO addPortfolio(@RequestBody PortfolioDO portfolioDO) { RespVO respVO = new RespVO(); if (portfolioDO == null) { respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); @@ -69,8 +82,14 @@ public class PortfolioController { } + /** + * 更新作品集 + * + * @param portfolioDO 作品集信息 + * @return RespVO + */ @PostMapping("/update") - public RespVO updatePortfolio(PortfolioDO portfolioDO) { + public RespVO updatePortfolio(@RequestBody PortfolioDO portfolioDO) { RespVO respVO = new RespVO(); if (portfolioDO == null) { respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); @@ -89,6 +108,12 @@ public class PortfolioController { return respVO; } + /** + * 删除作品集 + * + * @param id 作品集id + * @return RespVO + */ @PostMapping("/delete") public RespVO deletePortfolio(@RequestParam("id") Integer id) { RespVO respVO = new RespVO(); @@ -109,8 +134,15 @@ public class PortfolioController { return respVO; } + /** + * 添加作品集 + * + * @param id 作品集id + * @param changes 点赞数 + * @return RespVO + */ @PostMapping("/addLoves") - public RespVO addLoves(@RequestParam("id") Integer id,@RequestParam Integer changes) { + public RespVO addLoves(@RequestParam("id") Integer id, @RequestParam Integer changes) { RespVO respVO = new RespVO(); if (id == null) { respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); @@ -125,7 +157,31 @@ public class PortfolioController { } catch (Exception e) { respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); - } + } return respVO; } + + @GetMapping("/getPortfolioByTag") + public RespVO getPortfolioByTag(@RequestParam("tagID") Long tagID, + @RequestParam("page") Integer page, + @RequestParam("size") Integer size) { + RespVO respVO = new RespVO(); + if (tagID == null) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + IPage portfolioByTag = portfolioService.getPortfolioByTag(tagID, page, size); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(portfolioByTag); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + } + + } diff --git a/na-core/src/main/java/com/nailart/controller/SampleCollectionController.java b/na-core/src/main/java/com/nailart/controller/SampleCollectionController.java new file mode 100644 index 0000000..36c57c9 --- /dev/null +++ b/na-core/src/main/java/com/nailart/controller/SampleCollectionController.java @@ -0,0 +1,156 @@ +package com.nailart.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.nailart.dataobj.SampleCollectionDO; +import com.nailart.enums.RespCodeEnum; +import com.nailart.service.SampleCollectionService; +import com.nailart.vo.RespVO; +import org.springframework.web.bind.annotation.*; + +/** + * @Classname SampleCollectionController + * @Description 样品集控制类 + * @Date 2025/7/10 18:48 + * @Created by 21616 + */ +@RestController +@RequestMapping("/sampleCollection") +public class SampleCollectionController { + private final SampleCollectionService SampleCollectionService; + + public SampleCollectionController(SampleCollectionService SampleCollectionService) { + this.SampleCollectionService = SampleCollectionService; + } + + /** + * 获取样品集列表 + * + * @param page 页码 + * @param size 每页数量 + * @return RespVO + */ + @GetMapping("/list") + public RespVO getSampleCollectionList(@RequestParam("page") Integer page, @RequestParam("size") Integer size) { + RespVO respVO = new RespVO(); + if (page <= 0 || size <= 0) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + IPage SampleCollectionDOIPage = SampleCollectionService.listByPage(page, size); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(SampleCollectionDOIPage); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + + } + + /** + * 添加样品集 + * + * @param SampleCollectionDO 样品集信息 + * @return RespVO + */ + @PostMapping("/add") + public RespVO addSampleCollection(@RequestBody SampleCollectionDO SampleCollectionDO) { + RespVO respVO = new RespVO(); + if (SampleCollectionDO == null) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + Integer i = SampleCollectionService.addSampleCollection(SampleCollectionDO); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(i); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + + + } + + /** + * 更新样品集 + * + * @param SampleCollectionDO 样品集信息 + * @return RespVO + */ + @PostMapping("/update") + public RespVO updateSampleCollection(@RequestBody SampleCollectionDO SampleCollectionDO) { + RespVO respVO = new RespVO(); + if (SampleCollectionDO == null) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + Integer i = SampleCollectionService.updateSampleCollection(SampleCollectionDO); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(i); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + } + + /** + * 删除样品集 + * + * @param id 样品集id + * @return RespVO + */ + @PostMapping("/delete") + public RespVO deleteSampleCollection(@RequestParam("id") Integer id) { + RespVO respVO = new RespVO(); + if (id == null) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + Integer i = SampleCollectionService.deleteSampleCollection(id); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(i); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + } + + @GetMapping("/getSampleCollectionByTag") + public RespVO getSampleCollectionByTag(@RequestParam("tagID") Long tagID, + @RequestParam("page") Integer page, + @RequestParam("size") Integer size) { + RespVO respVO = new RespVO(); + if (tagID == null) { + respVO.setCode(RespCodeEnum.BAD_REQUEST.getCode()); + respVO.setMsg(RespCodeEnum.BAD_REQUEST.getMessage()); + return respVO; + } + try { + IPage SampleCollectionByTag = SampleCollectionService.getSampleCollectionByTag(tagID, page, size); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + respVO.setData(SampleCollectionByTag); + } catch (Exception e) { + respVO.setCode(RespCodeEnum.INTERNAL_SERVER_ERROR.getCode()); + respVO.setMsg(RespCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); + } + return respVO; + } + + +} diff --git a/na-core/src/main/java/com/nailart/controller/TagInfoController.java b/na-core/src/main/java/com/nailart/controller/TagInfoController.java new file mode 100644 index 0000000..8371308 --- /dev/null +++ b/na-core/src/main/java/com/nailart/controller/TagInfoController.java @@ -0,0 +1,120 @@ +package com.nailart.controller; + +import com.nailart.dataobj.TagDO; +import com.nailart.service.TagInfoService; +import com.nailart.vo.RespVO; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @Description 标签信息控制类 + * @Classname TagInfoController + * @Date 2025/7/14 23:44 + * @Created by 21616 + */ +@RestController +@RequestMapping("/tagInfo") +public class TagInfoController { + private final TagInfoService tagInfoService; + + TagInfoController(TagInfoService tagInfoService) { + this.tagInfoService = tagInfoService; + } + + @PostMapping("/save") + public RespVO saveTagInfo(@RequestBody TagDO tagDO) { + RespVO respVO = new RespVO(); + if (tagDO == null) { + respVO.setCode(500); + respVO.setMsg("标签信息不能为空"); + return respVO; + } + if (tagInfoService.saveTagInfo(tagDO) == 0) { + respVO.setCode(500); + respVO.setMsg("保存标签信息失败"); + return respVO; + } + respVO.setCode(200); + respVO.setData(tagInfoService.saveTagInfo(tagDO)); + return respVO; + } + + @GetMapping("/getById") + public RespVO getTagInfoById(@RequestParam Long id) { + RespVO respVO = new RespVO(); + if (id == null) { + respVO.setCode(500); + respVO.setMsg("标签id不能为空"); + return respVO; + } + TagDO tagInfoById = tagInfoService.getTagInfoById(id); + if (tagInfoById == null) { + respVO.setCode(500); + respVO.setMsg("查询标签信息失败"); + return respVO; + } + respVO.setCode(200); + respVO.setData(tagInfoById); + return respVO; + + + } + + @PostMapping("/deleteById") + public RespVO deleteTagInfoById(@RequestBody Long id) { + + RespVO respVO = new RespVO(); + if (id == null) { + respVO.setCode(500); + respVO.setMsg("标签id不能为空"); + return respVO; + } + TagDO tagInfoById = tagInfoService.getTagInfoById(id); + if (tagInfoById == null) { + respVO.setCode(500); + respVO.setMsg("查询标签信息失败"); + return respVO; + } + + respVO.setCode(200); + respVO.setData(tagInfoById); + return respVO; + } + + + @PostMapping("/updateById") + public RespVO updateTagInfoById(@RequestBody TagDO tagDO) { + RespVO respVO = new RespVO(); + if (tagDO == null) { + respVO.setCode(500); + respVO.setMsg("标签信息不能为空"); + return respVO; + } + TagDO tagInfoById = tagInfoService.getTagInfoById(tagDO.getId()); + if (tagInfoById == null) { + respVO.setCode(500); + respVO.setMsg("查询标签信息失败"); + return respVO; + } + respVO.setCode(200); + respVO.setData(tagInfoById); + + return respVO; + } + + @GetMapping("/getAll") + public RespVO getAllTagInfo() { + RespVO respVO = new RespVO(); + List allTagInfo = tagInfoService.getAllTagInfo(); + if (allTagInfo == null || allTagInfo.isEmpty()) { + respVO.setCode(500); + respVO.setMsg("获取标签信息失败"); + return respVO; + + } + respVO.setCode(200); + respVO.setData(allTagInfo); + return respVO; + } +} diff --git a/na-core/src/main/java/com/nailart/dataobj/MainInfoDO.java b/na-core/src/main/java/com/nailart/dataobj/MainInfoDO.java new file mode 100644 index 0000000..c3a0225 --- /dev/null +++ b/na-core/src/main/java/com/nailart/dataobj/MainInfoDO.java @@ -0,0 +1,72 @@ +package com.nailart.dataobj; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @Description 主页信息表 + * @Classname MainInfoDO + * @Date 2025/7/23 23:38 + * @Created by 21616 + */ +@Data +@TableName("main_info") +public class MainInfoDO { + /** + * id + */ + private Integer id; + /** + * 主页图 + */ + private String headImg; + /** + * 店名 + */ + private String name; + /** + * 介绍 + */ + private String introduce; + /** + * 状态 + * + */ + private Integer status; + /** + * 工作时间 + */ + private String worktime; + /** + * 地址 + */ + private String address; + /** + * 广告图,&&分开 + */ + private String publicityImg; + /** + * 新品图列表,&&分开 + */ + private String newPortfolioImg; + /** + * 弹窗图 + */ + private String popcard; + /** + * 指引图 + */ + private String locationImg; + /** + * 环境图 + */ + private String environmentImg; + /** + * 微信 + */ + private String wechat; + /** + * 电话 + */ + private String phone; +} diff --git a/na-core/src/main/java/com/nailart/dataobj/PortfolioDO.java b/na-core/src/main/java/com/nailart/dataobj/PortfolioDO.java index 416e5e8..17d99e9 100644 --- a/na-core/src/main/java/com/nailart/dataobj/PortfolioDO.java +++ b/na-core/src/main/java/com/nailart/dataobj/PortfolioDO.java @@ -32,13 +32,10 @@ public class PortfolioDO { */ private String imgFilename; /** - * 标签 + * 标签ID */ - private String tag; - /** - * 标签样式 - */ - private String tagStyle; + private String tagId; + /** * 创建时间 */ diff --git a/na-core/src/main/java/com/nailart/dataobj/SampleCollectionDO.java b/na-core/src/main/java/com/nailart/dataobj/SampleCollectionDO.java new file mode 100644 index 0000000..50117ed --- /dev/null +++ b/na-core/src/main/java/com/nailart/dataobj/SampleCollectionDO.java @@ -0,0 +1,46 @@ +package com.nailart.dataobj; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@Data +@TableName("sample_collection_info") +public class SampleCollectionDO { + /** + * id + */ + private Long id; + /** + * 标题 + */ + private String title; + /** + * 描述 + */ + private String description; + /** + * 图片地址 + */ + private String imgUrl; + /** + * 图片文件名 + */ + private String imgFilename; + /** + * 标签ID + */ + private String tagId; + + /** + * 创建时间 + */ + private String createTime; + /** + * 状态 + */ + private Integer status; + /** + * 价格 + */ + private Integer amt; +} diff --git a/na-core/src/main/java/com/nailart/dataobj/TagDO.java b/na-core/src/main/java/com/nailart/dataobj/TagDO.java new file mode 100644 index 0000000..c0f9ad1 --- /dev/null +++ b/na-core/src/main/java/com/nailart/dataobj/TagDO.java @@ -0,0 +1,19 @@ +package com.nailart.dataobj; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @Description 标签信息实体类 + * @Classname TagDO + * @Date 2025/7/14 23:18 + * @Created by 21616 + */ +@Data +@TableName("tag_info") +public class TagDO { + private Long id; + private String tagName; + private String tagColor; + private String tagBgColor; +} diff --git a/na-core/src/main/java/com/nailart/mapper/MainInfoMapper.java b/na-core/src/main/java/com/nailart/mapper/MainInfoMapper.java new file mode 100644 index 0000000..917a3f9 --- /dev/null +++ b/na-core/src/main/java/com/nailart/mapper/MainInfoMapper.java @@ -0,0 +1,9 @@ +package com.nailart.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.nailart.dataobj.MainInfoDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MainInfoMapper extends BaseMapper { +} diff --git a/na-core/src/main/java/com/nailart/mapper/PortfolioMapper.java b/na-core/src/main/java/com/nailart/mapper/PortfolioMapper.java index 712d819..f7d1520 100644 --- a/na-core/src/main/java/com/nailart/mapper/PortfolioMapper.java +++ b/na-core/src/main/java/com/nailart/mapper/PortfolioMapper.java @@ -1,5 +1,6 @@ package com.nailart.mapper; +import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.nailart.dataobj.PortfolioDO; import org.apache.ibatis.annotations.Mapper; diff --git a/na-core/src/main/java/com/nailart/mapper/SampleCollectionMapper.java b/na-core/src/main/java/com/nailart/mapper/SampleCollectionMapper.java new file mode 100644 index 0000000..5e9a151 --- /dev/null +++ b/na-core/src/main/java/com/nailart/mapper/SampleCollectionMapper.java @@ -0,0 +1,9 @@ +package com.nailart.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.nailart.dataobj.SampleCollectionDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SampleCollectionMapper extends BaseMapper { +} diff --git a/na-core/src/main/java/com/nailart/mapper/TagInfoMapper.java b/na-core/src/main/java/com/nailart/mapper/TagInfoMapper.java new file mode 100644 index 0000000..ddfdf8b --- /dev/null +++ b/na-core/src/main/java/com/nailart/mapper/TagInfoMapper.java @@ -0,0 +1,17 @@ +package com.nailart.mapper; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.nailart.dataobj.TagDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Description 标签信息表 + * @Classname TagInfoMapper + * @Date 2025/7/14 23:22 + * @Created by 21616 + */ +@Mapper +public interface TagInfoMapper extends BaseMapper { + +} diff --git a/na-core/src/main/java/com/nailart/service/FileStorageService.java b/na-core/src/main/java/com/nailart/service/FileStorageService.java new file mode 100644 index 0000000..8099ac8 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/FileStorageService.java @@ -0,0 +1,91 @@ +package com.nailart.service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * @Description 文件存储服务接口,定义文件管理操作 + * @Classname FileStorageService + * @Date 2025/7/19 17:19 + * @Created by 21616 + */ +public interface FileStorageService { + + /** + * 上传文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @param file 文件内容 + * @return 文件存储路径 + */ + String uploadFile(String bucketName, String filePath, MultipartFile file); + + /** + * 上传文件(从输入流) + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @param inputStream 文件输入流 + * @param contentType 文件内容类型 + * @return 文件存储路径 + */ + String uploadFile(String bucketName, String filePath, InputStream inputStream, String contentType); + + /** + * 下载文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @return 文件输入流 + */ + InputStream downloadFile(String bucketName, String filePath); + + /** + * 删除文件 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + */ + void deleteFile(String bucketName, String filePath); + + /** + * 检查文件是否存在 + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @return 文件是否存在 + */ + boolean fileExists(String bucketName, String filePath); + + /** + * 获取文件的预签名URL + * @param bucketName 存储桶名称 + * @param filePath 文件路径 + * @param expirySeconds 过期时间(秒) + * @return 预签名URL + */ + String getFileUrl(String bucketName, String filePath, int expirySeconds); + + /** + * 获取存储桶列表 + * @return 存储桶名称列表 + */ + List listBuckets(); + + /** + * 检查存储桶是否存在 + * @param bucketName 存储桶名称 + * @return 存储桶是否存在 + */ + boolean bucketExists(String bucketName); + + /** + * 创建存储桶 + * @param bucketName 存储桶名称 + */ + void createBucket(String bucketName); + + /** + * 删除存储桶(必须为空) + * @param bucketName 存储桶名称 + */ + void deleteBucket(String bucketName); +} diff --git a/na-core/src/main/java/com/nailart/service/MainInfoService.java b/na-core/src/main/java/com/nailart/service/MainInfoService.java new file mode 100644 index 0000000..55c7972 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/MainInfoService.java @@ -0,0 +1,10 @@ +package com.nailart.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.nailart.dataobj.MainInfoDO; + + +public interface MainInfoService extends IService { + public MainInfoDO getMainInfo(); + public Integer updateMainInfo(MainInfoDO mainInfoDO); +} diff --git a/na-core/src/main/java/com/nailart/service/PortfolioService.java b/na-core/src/main/java/com/nailart/service/PortfolioService.java index da696ed..a414e54 100644 --- a/na-core/src/main/java/com/nailart/service/PortfolioService.java +++ b/na-core/src/main/java/com/nailart/service/PortfolioService.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.nailart.dataobj.PortfolioDO; +import java.util.List; + public interface PortfolioService extends IService { IPage listByPage(int page, int size); @@ -14,4 +16,6 @@ public interface PortfolioService extends IService { Integer deletePortfolio(Integer id); Integer addLoves(Integer id, Integer changes); + + IPage getPortfolioByTag(Long tagID,int page, int size); } diff --git a/na-core/src/main/java/com/nailart/service/SampleCollectionService.java b/na-core/src/main/java/com/nailart/service/SampleCollectionService.java new file mode 100644 index 0000000..14d22a6 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/SampleCollectionService.java @@ -0,0 +1,17 @@ +package com.nailart.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.nailart.dataobj.SampleCollectionDO; + + +public interface SampleCollectionService extends IService { + IPage listByPage(int page, int size); + Integer addSampleCollection(SampleCollectionDO SampleCollectionDO); + + Integer updateSampleCollection(SampleCollectionDO SampleCollectionDO); + + Integer deleteSampleCollection(Integer id); + + IPage getSampleCollectionByTag(Long tagID,int page, int size); +} diff --git a/na-core/src/main/java/com/nailart/service/TagInfoService.java b/na-core/src/main/java/com/nailart/service/TagInfoService.java new file mode 100644 index 0000000..32609a9 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/TagInfoService.java @@ -0,0 +1,55 @@ +package com.nailart.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.nailart.dataobj.TagDO; +import org.springframework.data.relational.core.sql.In; + +import java.util.List; + +/** + * @Description 标签信息服务类 + * @Classname TagInfoService + * @Date 2025/7/14 23:24 + * @Created by 21616 + */ +public interface TagInfoService extends IService { + /** + * 保存标签信息 + * + * @param tagDO 标签信息 + * @return 是否成功 + */ + public Integer saveTagInfo(TagDO tagDO); + + /** + * 根据id查询标签信息 + * + * @param id 标签id + * @return 标签信息 + */ + public TagDO getTagInfoById(Long id); + + /** + * 根据id删除标签信息 + * + * @param id 标签id + * @return 是否成功 + */ + public Integer deleteTagInfoById(Long id); + + /** + * 根据id更新标签信息 + * + * @param tagDO 标签信息 + * @return 是否成功 + */ + public Integer updateTagInfoById(TagDO tagDO); + + /** + * 查询所有标签信息 + * + * @return 标签信息列表 + */ + public List getAllTagInfo(); + +} diff --git a/na-core/src/main/java/com/nailart/service/impl/FileStorageServiceImpl.java b/na-core/src/main/java/com/nailart/service/impl/FileStorageServiceImpl.java new file mode 100644 index 0000000..5f33d76 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/impl/FileStorageServiceImpl.java @@ -0,0 +1,202 @@ +package com.nailart.service.impl; +import com.nailart.service.FileStorageService; +import com.nailart.utils.MinioUtils; +import io.minio.Result; +import io.minio.messages.Bucket; +import io.minio.messages.Item; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +/** + * @Description MinIO 文件存储服务实现 + * @Classname FileStorageServiceImpl + * @Date 2025/7/19 17:20 + * @Created by 21616 + */ +@Service +public class FileStorageServiceImpl implements FileStorageService { + + private static final Logger logger = LoggerFactory.getLogger(FileStorageServiceImpl.class); + private final MinioUtils minioUtils; + + @Autowired + public FileStorageServiceImpl(MinioUtils minioUtils) { + this.minioUtils = minioUtils; + } + + @Override + public String uploadFile(String bucketName, String filePath, MultipartFile file) { + try { + ensureBucketExists(bucketName); + minioUtils.uploadFile(bucketName, filePath, file); + logger.info("文件上传成功: bucket={}, path={}", bucketName, filePath); + return filePath; + } catch (Exception e) { + logger.error("文件上传失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("文件上传失败", e); + } + } + + @Override + public String uploadFile(String bucketName, String filePath, InputStream inputStream, String contentType) { + try { + ensureBucketExists(bucketName); + minioUtils.uploadFile(bucketName, filePath, inputStream, contentType); + logger.info("文件上传成功: bucket={}, path={}", bucketName, filePath); + return filePath; + } catch (Exception e) { + logger.error("文件上传失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("文件上传失败", e); + } + } + + @Override + public InputStream downloadFile(String bucketName, String filePath) { + try { + if (!fileExists(bucketName, filePath)) { + logger.warn("文件不存在: bucket={}, path={}", bucketName, filePath); + throw new RuntimeException("文件不存在"); + } + return minioUtils.getObject(bucketName, filePath); + } catch (Exception e) { + logger.error("文件下载失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("文件下载失败", e); + } + } + + @Override + public void deleteFile(String bucketName, String filePath) { + try { + if (!fileExists(bucketName, filePath)) { + logger.warn("文件不存在,无需删除: bucket={}, path={}", bucketName, filePath); + return; + } + minioUtils.removeObject(bucketName, filePath); + logger.info("文件删除成功: bucket={}, path={}", bucketName, filePath); + } catch (Exception e) { + logger.error("文件删除失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("文件删除失败", e); + } + } + + @Override + public boolean fileExists(String bucketName, String filePath) { + try { + if (!bucketExists(bucketName)) { + return false; + } + // 通过列出对象检查文件是否存在 + Iterable> results = minioUtils.listObjects(bucketName); + return StreamSupport.stream(results.spliterator(), false) + .anyMatch(itemResult -> { + try { + return itemResult.get().objectName().equals(filePath); + } catch (Exception e) { + logger.error("检查文件存在时出错", e); + return false; + } + }); + } catch (Exception e) { + logger.error("检查文件存在失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("检查文件存在失败", e); + } + } + + @Override + public String getFileUrl(String bucketName, String filePath, int expirySeconds) { + try { + if (!fileExists(bucketName, filePath)) { + logger.warn("文件不存在,无法生成URL: bucket={}, path={}", bucketName, filePath); + throw new RuntimeException("文件不存在"); + } + return minioUtils.getPresignedUrl(bucketName, filePath, expirySeconds); + } catch (Exception e) { + logger.error("生成文件URL失败: bucket={}, path={}", bucketName, filePath, e); + throw new RuntimeException("生成文件URL失败", e); + } + } + + @Override + public List listBuckets() { + try { + List buckets = minioUtils.listBuckets(); + return buckets.stream().map(Bucket::name).collect(Collectors.toList()); + } catch (Exception e) { + logger.error("获取存储桶列表失败", e); + throw new RuntimeException("获取存储桶列表失败", e); + } + } + + @Override + public boolean bucketExists(String bucketName) { + try { + return minioUtils.bucketExists(bucketName); + } catch (Exception e) { + logger.error("检查存储桶存在失败: bucket={}", bucketName, e); + throw new RuntimeException("检查存储桶存在失败", e); + } + } + + @Override + public void createBucket(String bucketName) { + try { + if (!bucketExists(bucketName)) { + minioUtils.makeBucket(bucketName); + logger.info("创建存储桶成功: bucket={}", bucketName); + } else { + logger.info("存储桶已存在: bucket={}", bucketName); + } + } catch (Exception e) { + logger.error("创建存储桶失败: bucket={}", bucketName, e); + throw new RuntimeException("创建存储桶失败", e); + } + } + + @Override + public void deleteBucket(String bucketName) { + try { + if (bucketExists(bucketName)) { + // 检查存储桶是否为空 + Iterable> results = minioUtils.listObjects(bucketName); + boolean isEmpty = !results.iterator().hasNext(); + + if (isEmpty) { + minioUtils.removeBucket(bucketName); + logger.info("删除存储桶成功: bucket={}", bucketName); + } else { + logger.warn("无法删除非空存储桶: bucket={}", bucketName); + throw new RuntimeException("无法删除非空存储桶"); + } + } else { + logger.info("存储桶不存在,无需删除: bucket={}", bucketName); + } + } catch (Exception e) { + logger.error("删除存储桶失败: bucket={}", bucketName, e); + throw new RuntimeException("删除存储桶失败", e); + } + } + + /** + * 确保存储桶存在,如果不存在则创建 + */ + private void ensureBucketExists(String bucketName) { + try { + if (!minioUtils.bucketExists(bucketName)) { + minioUtils.makeBucket(bucketName); + logger.info("自动创建存储桶: {}", bucketName); + } + } catch (Exception e) { + logger.error("确保存储桶存在失败: bucket={}", bucketName, e); + throw new RuntimeException("初始化存储桶失败", e); + } + } +} diff --git a/na-core/src/main/java/com/nailart/service/impl/MainInfoServiceImpl.java b/na-core/src/main/java/com/nailart/service/impl/MainInfoServiceImpl.java new file mode 100644 index 0000000..af9f8f8 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/impl/MainInfoServiceImpl.java @@ -0,0 +1,45 @@ +package com.nailart.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.nailart.dataobj.MainInfoDO; +import com.nailart.dataobj.PortfolioDO; +import com.nailart.enums.RespCodeEnum; +import com.nailart.mapper.MainInfoMapper; +import com.nailart.mapper.PortfolioMapper; +import com.nailart.service.MainInfoService; +import com.nailart.service.PortfolioService; +import com.nailart.utils.DateUtils; +import com.nailart.vo.RespVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * @Author: xiaowang + * @Date: 2021/5/10 14:48 + */ +@Service +public class MainInfoServiceImpl extends ServiceImpl implements MainInfoService { + + private final MainInfoMapper mainInfoMapper; + private static final Logger logger = LoggerFactory.getLogger(MainInfoServiceImpl.class); + + public MainInfoServiceImpl(MainInfoMapper mainInfoMapper) { + + this.mainInfoMapper = mainInfoMapper; + } + + + @Override + public MainInfoDO getMainInfo() { + return mainInfoMapper.selectById(1); + } + + @Override + public Integer updateMainInfo(MainInfoDO mainInfoDO) { + return mainInfoMapper.updateById(mainInfoDO); + } +} diff --git a/na-core/src/main/java/com/nailart/service/impl/PortfolioServiceImpl.java b/na-core/src/main/java/com/nailart/service/impl/PortfolioServiceImpl.java index e068a47..6894577 100644 --- a/na-core/src/main/java/com/nailart/service/impl/PortfolioServiceImpl.java +++ b/na-core/src/main/java/com/nailart/service/impl/PortfolioServiceImpl.java @@ -1,11 +1,15 @@ package com.nailart.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.nailart.dataobj.PortfolioDO; +import com.nailart.dataobj.TagDO; import com.nailart.mapper.PortfolioMapper; import com.nailart.service.PortfolioService; +import com.nailart.service.TagInfoService; +import com.nailart.utils.DateUtils; import org.springframework.stereotype.Service; /** @@ -18,7 +22,8 @@ public class PortfolioServiceImpl extends ServiceImpl getPortfolioByTag(Long tagID,int page, int size) { + Page portfolioDOPage = new Page<>(page, size); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.like(PortfolioDO::getTagId, tagID); + return page(portfolioDOPage, wrapper); + } } diff --git a/na-core/src/main/java/com/nailart/service/impl/SampleCollectionServiceImpl.java b/na-core/src/main/java/com/nailart/service/impl/SampleCollectionServiceImpl.java new file mode 100644 index 0000000..168cb15 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/impl/SampleCollectionServiceImpl.java @@ -0,0 +1,66 @@ +package com.nailart.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.nailart.dataobj.SampleCollectionDO; +import com.nailart.mapper.SampleCollectionMapper; +import com.nailart.service.SampleCollectionService; +import com.nailart.utils.DateUtils; +import org.springframework.stereotype.Service; + +/** + * + * @Author: xiaowang + * @Date: 2021/5/10 14:48 + */ +@Service +public class SampleCollectionServiceImpl extends ServiceImpl implements SampleCollectionService { + + private final SampleCollectionMapper SampleCollectionMapper; + + public SampleCollectionServiceImpl(SampleCollectionMapper SampleCollectionMapper ) { + + this.SampleCollectionMapper = SampleCollectionMapper; + } + + /** + * + * @param page + * @param size + * @return + */ + @Override + public IPage listByPage(int page, int size) { + Page objectPage = new Page<>(page, size); + return SampleCollectionMapper.selectPage(objectPage, null); + } + + @Override + public Integer addSampleCollection(SampleCollectionDO SampleCollectionDO) { + if (SampleCollectionDO.getCreateTime()==null){ + SampleCollectionDO.setCreateTime(DateUtils.getCurrentDateTime()); + } + return SampleCollectionMapper.insert(SampleCollectionDO); + } + + @Override + public Integer updateSampleCollection(SampleCollectionDO SampleCollectionDO) { + return SampleCollectionMapper.updateById(SampleCollectionDO); + } + + @Override + public Integer deleteSampleCollection(Integer id) { + return SampleCollectionMapper.deleteById(id); + } + + + @Override + public IPage getSampleCollectionByTag(Long tagID,int page, int size) { + Page SampleCollectionDOPage = new Page<>(page, size); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.like(SampleCollectionDO::getTagId, tagID); + return page(SampleCollectionDOPage, wrapper); + } +} diff --git a/na-core/src/main/java/com/nailart/service/impl/TagInfoServiceImpl.java b/na-core/src/main/java/com/nailart/service/impl/TagInfoServiceImpl.java new file mode 100644 index 0000000..a1ed523 --- /dev/null +++ b/na-core/src/main/java/com/nailart/service/impl/TagInfoServiceImpl.java @@ -0,0 +1,60 @@ +package com.nailart.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.nailart.dataobj.TagDO; +import com.nailart.mapper.TagInfoMapper; +import com.nailart.service.TagInfoService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Description 标签信息服务实现类 + * @Classname TagInfoServiceImpl + * @Date 2025/7/14 23:25 + * @Created by 21616 + */ +@Service +public class TagInfoServiceImpl extends ServiceImpl implements TagInfoService { + @Override + public Integer saveTagInfo(TagDO tagDO) { + if (tagDO == null) { + return 0; + } + try { + + return this.saveTagInfo(tagDO); + } catch (Exception e) { + return 0; + } + } + + @Override + public TagDO getTagInfoById(Long id) { + if (id == null) { + return null; + } + return this.getTagInfoById(id); + } + + @Override + public Integer deleteTagInfoById(Long id) { + if (id == null) { + return 0; + } + return this.deleteTagInfoById(id); + } + + @Override + public Integer updateTagInfoById(TagDO tagDO) { + if (tagDO == null) { + return 0; + } + return this.updateTagInfoById(tagDO); + } + + @Override + public List getAllTagInfo() { + return this.list(); + } +} diff --git a/na-core/src/main/java/com/nailart/utils/MinioUtils.java b/na-core/src/main/java/com/nailart/utils/MinioUtils.java new file mode 100644 index 0000000..c788f7a --- /dev/null +++ b/na-core/src/main/java/com/nailart/utils/MinioUtils.java @@ -0,0 +1,303 @@ +package com.nailart.utils; +import io.minio.*; +import io.minio.errors.*; +import io.minio.http.Method; +import io.minio.messages.Bucket; +import io.minio.messages.Item; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +/** + * @Description MinIO工具类,提供文件存储操作的封装 + * @Classname MinioUtils + * @Date 2025/7/16 10:15 + * @Created by 21616 + */ +@Component +public class MinioUtils { + + private final MinioClient minioClient; + + @Autowired + public MinioUtils(MinioClient minioClient) { + this.minioClient = minioClient; + } + + /** + * 检查存储桶是否存在 + * @param bucketName 存储桶名称 + * @return 存在返回true,否则返回false + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public boolean bucketExists(String bucketName) throws ServerException, InsufficientDataException, + ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, + InvalidResponseException, XmlParserException, InternalException { + return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } + + /** + * 创建存储桶 + * @param bucketName 存储桶名称 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void makeBucket(String bucketName) throws ServerException, InsufficientDataException, + ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, + InvalidResponseException, XmlParserException, InternalException { + if (!bucketExists(bucketName)) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + } + + /** + * 列出所有存储桶 + * @return 存储桶列表 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public List listBuckets() throws ServerException, InsufficientDataException, + ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, + InvalidResponseException, XmlParserException, InternalException { + return minioClient.listBuckets(); + } + + /** + * 删除存储桶(必须为空桶) + * @param bucketName 存储桶名称 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void removeBucket(String bucketName) throws ServerException, InsufficientDataException, + ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, + InvalidResponseException, XmlParserException, InternalException { + if (bucketExists(bucketName)) { + minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } + + /** + * 上传本地文件到MinIO + * @param bucketName 存储桶名称 + * @param objectName 对象名称(文件路径) + * @param filePath 本地文件路径 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void uploadFile(String bucketName, String objectName, String filePath) throws ServerException, + InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + minioClient.uploadObject(UploadObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .filename(filePath) + .build()); + } + + /** + * 通过InputStream上传文件 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @param inputStream 文件输入流 + * @param contentType 文件内容类型 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void uploadFile(String bucketName, String objectName, InputStream inputStream, String contentType) + throws ServerException, InsufficientDataException, ErrorResponseException, IOException, + NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, + InternalException { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .stream(inputStream, inputStream.available(), -1) + .contentType(contentType) + .build()); + } + + /** + * 通过Spring MultipartFile上传文件 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @param file Spring MultipartFile文件对象 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void uploadFile(String bucketName, String objectName, MultipartFile file) + throws ServerException, InsufficientDataException, ErrorResponseException, IOException, + NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, + InternalException { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .stream(file.getInputStream(), file.getSize(), -1) + .contentType(file.getContentType()) + .build()); + } + + /** + * 下载文件到本地 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @param filePath 本地文件路径 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void downloadFile(String bucketName, String objectName, String filePath) throws ServerException, + InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + minioClient.downloadObject(DownloadObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .filename(filePath) + .build()); + } + + /** + * 获取文件输入流 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @return 文件输入流 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public InputStream getObject(String bucketName, String objectName) throws ServerException, + InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + return minioClient.getObject(GetObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .build()); + } + + /** + * 删除对象 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public void removeObject(String bucketName, String objectName) throws ServerException, + InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + minioClient.removeObject(RemoveObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .build()); + } + + /** + * 列出存储桶中的对象 + * @param bucketName 存储桶名称 + * @return 对象迭代器 + */ + public Iterable> listObjects(String bucketName) { + return minioClient.listObjects(ListObjectsArgs.builder() + .bucket(bucketName) + .build()); + } + + /** + * 生成预签名URL(用于临时访问私有对象) + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @param expiry 过期时间(秒) + * @return 预签名URL + * @throws ServerException 服务端异常 + * @throws InsufficientDataException 数据不足异常 + * @throws ErrorResponseException 错误响应异常 + * @throws IOException IO异常 + * @throws NoSuchAlgorithmException 算法不存在异常 + * @throws InvalidKeyException 无效密钥异常 + * @throws InvalidResponseException 无效响应异常 + * @throws XmlParserException XML解析异常 + * @throws InternalException 内部异常 + */ + public String getPresignedUrl(String bucketName, String objectName, int expiry) throws ServerException, + InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket(bucketName) + .object(objectName) + .expiry(expiry) + .build()); + } +} \ No newline at end of file diff --git a/na-core/src/main/java/com/nailart/vo/RespVO.java b/na-core/src/main/java/com/nailart/vo/RespVO.java index 6fcb209..d232221 100644 --- a/na-core/src/main/java/com/nailart/vo/RespVO.java +++ b/na-core/src/main/java/com/nailart/vo/RespVO.java @@ -1,5 +1,6 @@ package com.nailart.vo; +import com.nailart.enums.RespCodeEnum; import lombok.Data; /** @@ -13,4 +14,28 @@ public class RespVO { private Integer code; private String msg; private Object data; + + public static RespVO success() { + RespVO respVO = new RespVO(); + respVO.setCode(RespCodeEnum.SUCCESS.getCode()); + respVO.setMsg(RespCodeEnum.SUCCESS.getMessage()); + return respVO; + } + public static RespVO success(Object data) { + RespVO respVO = success(); + respVO.setData(data); + return respVO; + } + public static RespVO error(RespCodeEnum respCodeEnum) { + RespVO respVO = new RespVO(); + respVO.setCode(respCodeEnum.getCode()); + respVO.setMsg(respCodeEnum.getMessage()); + return respVO; + } + public static RespVO error(RespCodeEnum respCodeEnum, Object data) { + RespVO respVO = error(respCodeEnum); + respVO.setData(data); + return respVO; + } + } diff --git a/na-core/src/main/resources/application.yml b/na-core/src/main/resources/application.yml index 28535d3..074ae41 100644 --- a/na-core/src/main/resources/application.yml +++ b/na-core/src/main/resources/application.yml @@ -3,8 +3,15 @@ server: servlet: context-path: /api # 应用访问路径前缀 + # 数据源配置 spring: + servlet: + multipart: + max-file-size: 2GB + max-request-size: 5GB + file-size-threshold: 1024MB # 超过10MB的文件直接写入磁盘 + location: /data/temp # 指定临时存储目录(确保有足够空间) datasource: type: com.alibaba.druid.pool.DruidDataSource # 使用Druid连接池 driver-class-name: com.mysql.cj.jdbc.Driver # MySQL驱动类 @@ -73,4 +80,14 @@ logging: root: info # 根日志级别 com.example.mapper: debug # Mapper接口日志级别(调试用) file: - name: logs/application.log # 日志文件存储路径 \ No newline at end of file + name: logs/application.log # 日志文件存储路径 + +minio: + endpoint: http://xiaowangnas.com:9000 + accessKey: 3RDAaP9M8HT4RDWszNSr # 访问密钥ID + secretKey: uM7jdTIF3Xk77rwbPq0vaQWXR16zgXHqYwSRiHez # 访问密钥Secret + region: # 存储桶所在区域 + # 超时配置(秒) + connectTimeout: 30 + writeTimeout: 60 + readTimeout: 60 \ No newline at end of file diff --git a/na-frontend/src/App.vue b/na-frontend/src/App.vue index afeebda..b7fa13b 100644 --- a/na-frontend/src/App.vue +++ b/na-frontend/src/App.vue @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/na-frontend/src/components/NavBar.vue b/na-frontend/src/components/NavBar.vue index c0f5b83..edd50e9 100644 --- a/na-frontend/src/components/NavBar.vue +++ b/na-frontend/src/components/NavBar.vue @@ -1,9 +1,7 @@ diff --git a/na-frontend/src/components/TabBar.vue b/na-frontend/src/components/TabBar.vue index aac4e78..e97608b 100644 --- a/na-frontend/src/components/TabBar.vue +++ b/na-frontend/src/components/TabBar.vue @@ -7,11 +7,11 @@ - 作品集 + 作品集 - 样品集 + 样品集 diff --git a/na-frontend/src/main.js b/na-frontend/src/main.js index 510f13a..10b75a3 100644 --- a/na-frontend/src/main.js +++ b/na-frontend/src/main.js @@ -3,6 +3,11 @@ import Vuex from "vuex"; import App from "./App.vue"; import router from "./router"; import axios from './utils/request' +import { FileServicePlugin } from './utils/file'; +// 注册插件 +Vue.use(FileServicePlugin , { + baseUrl: 'http://wcy111.top:35001' // 可选,API基础URL +}); import { Tabbar, TabbarItem } from "vant"; import { NavBar } from "vant"; import { Toast } from "vant"; @@ -17,7 +22,36 @@ import { Swipe, SwipeItem } from 'vant'; import { Loading } from 'vant'; import { NoticeBar } from 'vant'; import { Sticky } from 'vant'; +import { Tab, Tabs } from 'vant'; +import { List } from 'vant'; +import { Overlay } from 'vant'; +import { Form } from 'vant'; +import { Field } from 'vant'; +import { Uploader } from 'vant'; +import { Dialog } from 'vant'; +import { Picker } from 'vant'; +import { DropdownMenu, DropdownItem } from 'vant'; +import { Checkbox, CheckboxGroup } from 'vant'; +import { Cell, CellGroup } from 'vant'; +import { Divider } from 'vant'; +Vue.use(Divider); +Vue.use(Cell); +Vue.use(CellGroup); +Vue.use(Checkbox); +Vue.use(CheckboxGroup); +Vue.use(DropdownMenu); +Vue.use(DropdownItem); +Vue.use(Picker); +// 全局注册 +Vue.use(Dialog); +Vue.use(Uploader); +Vue.use(Form); +Vue.use(Field); +Vue.use(Overlay); +Vue.use(List); +Vue.use(Tab); +Vue.use(Tabs); Vue.use(Sticky); Vue.use(NoticeBar); Vue.use(Loading); diff --git a/na-frontend/src/pages/DetailPage.vue b/na-frontend/src/pages/DetailPage.vue new file mode 100644 index 0000000..5a3f748 --- /dev/null +++ b/na-frontend/src/pages/DetailPage.vue @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/na-frontend/src/pages/HomePage.vue b/na-frontend/src/pages/HomePage.vue index 8fdaece..67af7df 100644 --- a/na-frontend/src/pages/HomePage.vue +++ b/na-frontend/src/pages/HomePage.vue @@ -1,36 +1,69 @@ \ No newline at end of file diff --git a/na-frontend/src/pages/PortfolioPage.vue b/na-frontend/src/pages/PortfolioPage.vue index 14d6274..e3627f0 100644 --- a/na-frontend/src/pages/PortfolioPage.vue +++ b/na-frontend/src/pages/PortfolioPage.vue @@ -1,5 +1,11 @@ - \ No newline at end of file diff --git a/na-frontend/src/pages/man/HomeManPage.vue b/na-frontend/src/pages/man/HomeManPage.vue new file mode 100644 index 0000000..eca9d58 --- /dev/null +++ b/na-frontend/src/pages/man/HomeManPage.vue @@ -0,0 +1,358 @@ + + + + \ No newline at end of file diff --git a/na-frontend/src/pages/man/PortfolioManPage.vue b/na-frontend/src/pages/man/PortfolioManPage.vue new file mode 100644 index 0000000..3c4e755 --- /dev/null +++ b/na-frontend/src/pages/man/PortfolioManPage.vue @@ -0,0 +1,1083 @@ + + + + + \ No newline at end of file diff --git a/na-frontend/src/pages/man/SampleCollectionManPage.vue b/na-frontend/src/pages/man/SampleCollectionManPage.vue new file mode 100644 index 0000000..cdc6cfa --- /dev/null +++ b/na-frontend/src/pages/man/SampleCollectionManPage.vue @@ -0,0 +1,1084 @@ + + + + \ No newline at end of file diff --git a/na-frontend/src/pages/man/TagManPage.vue b/na-frontend/src/pages/man/TagManPage.vue new file mode 100644 index 0000000..552f802 --- /dev/null +++ b/na-frontend/src/pages/man/TagManPage.vue @@ -0,0 +1,31 @@ + + + + \ No newline at end of file diff --git a/na-frontend/src/router/index.js b/na-frontend/src/router/index.js index 3c36009..e6f6b17 100644 --- a/na-frontend/src/router/index.js +++ b/na-frontend/src/router/index.js @@ -3,6 +3,10 @@ import VueRouter from "vue-router"; import HomePage from "@/pages/HomePage.vue"; import PortfolioPage from "@/pages/PortfolioPage.vue"; import SampleCollectionPage from "@/pages/SampleCollectionPage.vue"; +import PortfolioMan from "@/pages/man/PortfolioManPage.vue"; +import SampleCollectionMan from "@/pages/man/SampleCollectionManPage.vue"; +import HomeManPage from "@/pages/man/HomeManPage.vue"; +import TagManPage from "@/pages/man/TagManPage.vue"; Vue.use(VueRouter); const routes = [ @@ -10,20 +14,37 @@ const routes = [ path: "/", name: "HomePage", component: HomePage, - meta: { direction: "back" }, }, { path: "/portfolio", name: "PortfolioPage", component: PortfolioPage, - meta: { direction: "forward" }, }, { path: "/sampleCollection", name: "SampleCollectionPage", component: SampleCollectionPage, - meta: { direction: "forward" }, }, + { + path: "/yumi", + name: "Yumi", + component: HomeManPage, + }, + { + path: "/sampleCollectionman", + name: "SampleCollectionMan", + component: SampleCollectionMan, + }, + { + path: "/portfolioMan", + name: "PortfolioMan", + component: PortfolioMan, + }, + { + path: "/tagManPage", + name: "tagManPage", + component: TagManPage, + } ]; const router = new VueRouter({ diff --git a/na-frontend/src/utils/file.js b/na-frontend/src/utils/file.js new file mode 100644 index 0000000..dbe6c65 --- /dev/null +++ b/na-frontend/src/utils/file.js @@ -0,0 +1,227 @@ +import axios from 'axios'; +/** + * 文件操作工具类,支持上传、下载和删除文件 + */ + +export class FileService { + constructor(baseUrl = '') { + this.baseUrl = baseUrl; + } + + /** + * 上传文件到MinIO + * @param {Object} options - 上传参数 + * @param {string} options.bucketName - 存储桶名称 + * @param {string} options.filePath - 文件路径 + * @param {File} options.file - 文件对象 + * @param {Function} [options.onProgress] - 进度回调函数 + * @returns {Promise} - 上传成功后的文件路径 + */ + uploadFile(options) { + const { + bucketName, + filePath, + file, + } = options; + + if (!bucketName || !filePath || !file) { + return Promise.reject(new Error('缺少必要的上传参数')); + } + + const formData = new FormData(); + formData.append('bucketName', bucketName); + formData.append('filePath', filePath); + formData.append('file', file); + + return axios({ + url: `${this.baseUrl}/api/file/upload`, + method: 'post', + data: formData, + headers: { 'Accept': 'application/json' }, + timeout: 60000 + }) + .then(response => { + if (response.status === 200) { + return response.data; + } + throw new Error(`上传失败,状态码:${response.status}`); + }) + .catch(this._handleError); + } + + /** + * 下载文件 + * @param {Object} options - 下载参数 + * @param {string} options.bucketName - 存储桶名称 + * @param {string} options.filePath - 文件路径 + * @param {string} [options.fileName] - 下载时使用的文件名 + * @returns {Promise} + */ + downloadFile(options) { + const { bucketName, filePath, fileName } = options; + + if (!bucketName || !filePath) { + return Promise.reject(new Error('缺少必要的下载参数')); + } + + return axios({ + url: `${this.baseUrl}/api/file/download`, + method: 'get', + params: { bucketName, filePath }, + responseType: 'blob', + timeout: 60000 + }) + .then(response => { + if (response.status === 200) { + this._saveFile(response.data, fileName || filePath.split('/').pop()); + return; + } + throw new Error(`下载失败,状态码:${response.status}`); + }) + .catch(this._handleError); + } + + /** + * 删除文件 + * @param {Object} options - 删除参数 + * @param {string} options.bucketName - 存储桶名称 + * @param {string} options.filePath - 文件路径 + * @returns {Promise} + */ + deleteFile(options) { + const { bucketName, filePath } = options; + + if (!bucketName || !filePath) { + return Promise.reject(new Error('缺少必要的删除参数')); + } + + return axios({ + url: `${this.baseUrl}/api/file/delete`, + method: 'delete', + params: { bucketName, filePath }, + timeout: 30000 + }) + .then(response => { + if (response.status === 204) { + return; + } + throw new Error(`删除失败,状态码:${response.status}`); + }) + .catch(this._handleError); + } + + /** + * 检查文件是否存在 + * @param {Object} options - 参数 + * @param {string} options.bucketName - 存储桶名称 + * @param {string} options.filePath - 文件路径 + * @returns {Promise} - 文件是否存在 + */ + fileExists(options) { + const { bucketName, filePath } = options; + + if (!bucketName || !filePath) { + return Promise.reject(new Error('缺少必要的参数')); + } + + return axios({ + url: `${this.baseUrl}/api/file/exists`, + method: 'get', + params: { bucketName, filePath }, + timeout: 30000 + }) + .then(response => { + if (response.status === 200) { + return response.data; + } + throw new Error(`检查失败,状态码:${response.status}`); + }) + .catch(this._handleError); + } + + /** + * 获取文件URL + * @param {Object} options - 参数 + * @param {string} options.bucketName - 存储桶名称 + * @param {string} options.filePath - 文件路径 + * @param {number} [options.expirySeconds=3600] - URL有效期(秒) + * @returns {Promise} - 文件URL + */ + getFileUrl(options) { + const { bucketName, filePath, expirySeconds = 3600 } = options; + + if (!bucketName || !filePath) { + return Promise.reject(new Error('缺少必要的参数')); + } + + return axios({ + url: `${this.baseUrl}/api/file/url`, + method: 'get', + params: { bucketName, filePath, expirySeconds }, + timeout: 30000 + }) + .then(response => { + if (response.status === 200) { + return response.data; + } + throw new Error(`获取URL失败,状态码:${response.status}`); + }) + .catch(this._handleError); + } + + /** + * 保存文件到本地 + * @param {Blob} blob - 文件内容 + * @param {string} fileName - 文件名 + */ + _saveFile(blob, fileName) { + // 处理中文文件名 + const encodedFileName = encodeURIComponent(fileName); + + if (navigator.msSaveBlob) { + // 兼容IE + navigator.msSaveBlob(blob, fileName); + } else { + // 现代浏览器 + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.setAttribute('download', encodedFileName); + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(link.href); + } + } + + /** + * 统一处理错误 + * @param {Error} error - 错误对象 + */ + _handleError(error) { + let errorMsg = '操作失败'; + if (error.response) { + errorMsg += `,服务器错误:${error.response.status} ${error.response.statusText}`; + } else if (error.request) { + errorMsg += ',未收到服务器响应'; + } else { + errorMsg += `,${error.message}`; + } + console.error(errorMsg, error); + throw new Error(errorMsg); + } + } + + // 创建Vue插件 +export const FileServicePlugin = { + install(Vue, options = {}) { + // 创建全局实例 + const fileService = new FileService(options.baseUrl); + + // 方式1:添加到Vue原型,全局可用 + Vue.prototype.$fileService = fileService; + + // 方式2:注册为全局组件 + Vue.fileService = fileService; + } + }; \ No newline at end of file diff --git a/na-frontend/src/utils/request.js b/na-frontend/src/utils/request.js index d364fc4..f51d09d 100644 --- a/na-frontend/src/utils/request.js +++ b/na-frontend/src/utils/request.js @@ -2,7 +2,7 @@ import axios from 'axios' // 创建 axios 实例 const service = axios.create({ - baseURL: "http://127.0.0.1:8090/api", + baseURL: "http://wcy111.top:35001/api", timeout: 10000 }) @@ -19,31 +19,11 @@ const getRequestKey = (config) => { const dataStr = data ? JSON.stringify(data) : ''; return `${method}:${url}:${paramsStr}:${dataStr}`; }; -// 请求拦截器:添加防抖逻辑 + +// 请求拦截器:仅保留防抖逻辑 service.interceptors.request.use( config => { - // 1. 检查POST请求体和参数是否都为空 - if (config.method === 'post') { - // 检查请求体是否为空 - const bodyEmpty = - config.data === undefined || - config.data === null || - (typeof config.data === 'object' && Object.keys(config.data).length === 0) || - (typeof config.data === 'string' && config.data.trim().length === 0); - - // 检查URL参数是否为空 - const paramsEmpty = - config.params === undefined || - config.params === null || - (typeof config.params === 'object' && Object.keys(config.params).length === 0); - - // 如果请求体和参数都为空,则打印提示 - if (bodyEmpty && paramsEmpty) { - console.log('提示:POST请求的请求体和参数均为空'); - } - } - - // 2. 防抖逻辑 + // 防抖逻辑 return new Promise((resolve) => { const requestKey = getRequestKey(config); // 清除之前的定时器 @@ -63,4 +43,29 @@ service.interceptors.request.use( return Promise.reject(error); } ); + +// 响应拦截器保持不变 +service.interceptors.response.use( + response => { + return response.data; + }, + error => { + let errorMessage = '请求失败'; + + if (error.response) { + errorMessage = `请求失败,状态码: ${error.response.status}`; + if (error.response.data && error.response.data.message) { + errorMessage += `,原因: ${error.response.data.message}`; + } + } else if (error.request) { + errorMessage = '请求已发送,但没有收到响应'; + } else { + errorMessage = `请求错误: ${error.message}`; + } + + console.error(errorMessage); + return Promise.reject(errorMessage); + } +); + export default service; \ No newline at end of file