From 7b619aa4cb6e7b39abf1d452fd2226057ed988a2 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Fri, 16 Jan 2026 16:37:03 +0800 Subject: [PATCH] =?UTF-8?q?GlobalAward=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/controller/GlobalAwardController.java | 43 ++++ .../da/mapper/primary/ContestantMapper.java | 12 ++ .../da/mapper/primary/entity/Contestant.java | 72 +++++++ .../com/ai/da/model/dto/ContestantDTO.java | 29 +++ .../com/ai/da/service/GlobalAwardService.java | 15 ++ .../service/impl/GlobalAwardServiceImpl.java | 190 ++++++++++++++++++ 6 files changed, 361 insertions(+) create mode 100644 src/main/java/com/ai/da/controller/GlobalAwardController.java create mode 100644 src/main/java/com/ai/da/mapper/primary/ContestantMapper.java create mode 100644 src/main/java/com/ai/da/mapper/primary/entity/Contestant.java create mode 100644 src/main/java/com/ai/da/model/dto/ContestantDTO.java create mode 100644 src/main/java/com/ai/da/service/GlobalAwardService.java create mode 100644 src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java diff --git a/src/main/java/com/ai/da/controller/GlobalAwardController.java b/src/main/java/com/ai/da/controller/GlobalAwardController.java new file mode 100644 index 00000000..9d59d3d5 --- /dev/null +++ b/src/main/java/com/ai/da/controller/GlobalAwardController.java @@ -0,0 +1,43 @@ +package com.ai.da.controller; + +import com.ai.da.common.response.Response; +import com.ai.da.model.dto.ContestantDTO; +import com.ai.da.service.GlobalAwardService; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +@RestController +@RequestMapping("/api/global-award") +public class GlobalAwardController { + + @Resource + private GlobalAwardService globalAwardService; + + @PostMapping("/uploads/pdf") + public Response uploadPdf(@RequestParam("file") MultipartFile file, + @RequestParam(value = "email", required = false) String email) throws Exception { + return Response.success(globalAwardService.uploadPdf(file, email)); + } + + @PostMapping("/uploads/video") + public Response uploadVideo(@RequestParam("file") MultipartFile file, + @RequestParam(value = "email", required = false) String email) throws Exception { + return Response.success(globalAwardService.uploadVideo(file, email)); + } + + @PostMapping("/contestants/save") + public Response> submit(@RequestBody ContestantDTO request) { + return Response.success(globalAwardService.saveContestant(request)); + } + + @GetMapping("/contestants/by-email") + public Response getContestantByEmail(@RequestParam("email") String email) { + ContestantDTO dto = globalAwardService.getContestantByEmail(email); + return Response.success(dto); + } +} + + diff --git a/src/main/java/com/ai/da/mapper/primary/ContestantMapper.java b/src/main/java/com/ai/da/mapper/primary/ContestantMapper.java new file mode 100644 index 00000000..2349f058 --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/ContestantMapper.java @@ -0,0 +1,12 @@ +package com.ai.da.mapper.primary; + +import com.ai.da.mapper.primary.entity.Contestant; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ContestantMapper extends BaseMapper { + +} + + diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Contestant.java b/src/main/java/com/ai/da/mapper/primary/entity/Contestant.java new file mode 100644 index 00000000..353c6dda --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/entity/Contestant.java @@ -0,0 +1,72 @@ +package com.ai.da.mapper.primary.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * submissions 表对应实体 — 参赛选手信息 (Contestant) + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName("submissions") +public class Contestant { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + private String email; + + @TableField("first_name") + private String firstName; + + @TableField("last_name") + private String lastName; + + private String gender; + + private String occupation; + + private Integer age; + + @TableField("country_region_city") + private String countryRegionCity; + + @TableField("phone_number") + private String phoneNumber; + + @TableField("portfolio_url") + private String portfolioUrl; + + @TableField("design_title") + private String designTitle; + + @TableField("design_description") + private String designDescription; + + @TableField("pdf_path") + private String pdfPath; + + @TableField("video_path") + private String videoPath; + + @TableField("created_at") + private LocalDateTime createdAt; + + @TableField("updated_at") + private LocalDateTime updatedAt; + + @TableField("created_by") + private Long createdBy; +} + + diff --git a/src/main/java/com/ai/da/model/dto/ContestantDTO.java b/src/main/java/com/ai/da/model/dto/ContestantDTO.java new file mode 100644 index 00000000..342be7a7 --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/ContestantDTO.java @@ -0,0 +1,29 @@ +package com.ai.da.model.dto; + +import lombok.Data; + +/** + * Contestant request DTO for Global Award + */ +@Data +public class ContestantDTO { + private String email; + private String firstName; + private String lastName; + private String gender; + private String occupation; + private Integer age; + private String countryRegionCity; + private String phoneNumber; + private String portfolioUrl; + private String designTitle; + private String designDescription; + private String pdfPath; + private String videoPath; + /** + * 是否确认覆盖已存在记录(false 表示发现已有记录时仅返回 existingRecord,不覆盖) + */ + private Boolean confirm = false; +} + + diff --git a/src/main/java/com/ai/da/service/GlobalAwardService.java b/src/main/java/com/ai/da/service/GlobalAwardService.java new file mode 100644 index 00000000..212a23e7 --- /dev/null +++ b/src/main/java/com/ai/da/service/GlobalAwardService.java @@ -0,0 +1,15 @@ +package com.ai.da.service; + +import com.ai.da.model.dto.ContestantDTO; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +public interface GlobalAwardService { + String uploadPdf(MultipartFile file, String email) throws Exception; + String uploadVideo(MultipartFile file, String email) throws Exception; + Map saveContestant(ContestantDTO request); + com.ai.da.model.dto.ContestantDTO getContestantByEmail(String email); +} + + diff --git a/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java b/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java new file mode 100644 index 00000000..d1bac9bf --- /dev/null +++ b/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java @@ -0,0 +1,190 @@ +package com.ai.da.service.impl; + +import com.ai.da.common.config.exception.BusinessException; +import com.ai.da.mapper.primary.ContestantMapper; +import com.ai.da.mapper.primary.entity.Contestant; +import com.ai.da.model.dto.ContestantDTO; +import com.ai.da.service.GlobalAwardService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import com.ai.da.common.utils.MinioUtil; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Service +@Slf4j +public class GlobalAwardServiceImpl implements GlobalAwardService { + + @Resource + private ContestantMapper contestantMapper; + + @Value("${file.upload.dir:uploads}") + private String uploadDir; + + private static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM"); + @Value("${minio.bucket:contestants}") + private String minioBucket; + + @Resource + private MinioUtil minioUtil; + + @Override + public String uploadPdf(MultipartFile file, String email) throws Exception { + validatePdf(file); + String path = storeFile(file, email, "pdf"); + return path; + } + + @Override + public String uploadVideo(MultipartFile file, String email) throws Exception { + validateVideo(file); + String path = storeFile(file, email, "video"); + return path; + } + + private void validatePdf(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("file is empty"); + } + String ct = file.getContentType(); + if (ct == null || !ct.toLowerCase().contains("pdf")) { + throw new BusinessException("only pdf allowed"); + } + // size limit example 20MB + if (file.getSize() > 20L * 1024 * 1024) { + throw new BusinessException("pdf too large"); + } + } + + private void validateVideo(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("file is empty"); + } + String ct = file.getContentType(); + if (ct == null || !(ct.toLowerCase().contains("mp4") || ct.toLowerCase().contains("video") )) { + throw new BusinessException("invalid video type"); + } + // size limit example 100MB + if (file.getSize() > 100L * 1024 * 1024) { + throw new BusinessException("video too large"); + } + } + + private String normalizeEmail(String email) { + if (email == null) { + return "anonymous"; + } + return email.replaceAll("[^a-zA-Z0-9]", "_"); + } + + private String storeFile(MultipartFile file, String email, String kind) throws IOException { + String normalized = normalizeEmail(email); + String datePart = LocalDateTime.now().format(YYYY_MM_DD); + String ext = ""; + String original = file.getOriginalFilename(); + if (original != null && original.contains(".")) { + ext = original.substring(original.lastIndexOf('.')); + } + String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + ext; + String relativePath = "contestants/" + normalized + "/" + datePart + "/" + filename; + + String uploadedPath = minioUtil.upload(minioBucket, relativePath, file, null); + log.info("uploaded via MinioUtil: {}", uploadedPath); + return uploadedPath; + } + + @Override + public Map saveContestant(ContestantDTO request) { + Map resp = new HashMap<>(); + if (request.getEmail() == null) { + throw new IllegalArgumentException("email required"); + } + + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("email", request.getEmail()); + Contestant existing = contestantMapper.selectOne(qw); + + LocalDateTime now = LocalDateTime.now(); + if (existing == null) { + Contestant toInsert = Contestant.builder() + .email(request.getEmail()) + .firstName(request.getFirstName()) + .lastName(request.getLastName()) + .gender(request.getGender()) + .occupation(request.getOccupation()) + .age(request.getAge()) + .countryRegionCity(request.getCountryRegionCity()) + .phoneNumber(request.getPhoneNumber()) + .portfolioUrl(request.getPortfolioUrl()) + .designTitle(request.getDesignTitle()) + .designDescription(request.getDesignDescription()) + .pdfPath(request.getPdfPath()) + .videoPath(request.getVideoPath()) + .createdAt(now) + .updatedAt(now) + .build(); + contestantMapper.insert(toInsert); + resp.put("success", true); + return resp; + } else { + // update existing contestant + existing.setFirstName(request.getFirstName()); + existing.setLastName(request.getLastName()); + existing.setGender(request.getGender()); + existing.setOccupation(request.getOccupation()); + existing.setAge(request.getAge()); + existing.setCountryRegionCity(request.getCountryRegionCity()); + existing.setPhoneNumber(request.getPhoneNumber()); + existing.setPortfolioUrl(request.getPortfolioUrl()); + existing.setDesignTitle(request.getDesignTitle()); + existing.setDesignDescription(request.getDesignDescription()); + existing.setPdfPath(request.getPdfPath()); + existing.setVideoPath(request.getVideoPath()); + existing.setUpdatedAt(now); + contestantMapper.updateById(existing); + resp.put("success", true); + return resp; + } + } + + @Override + public ContestantDTO getContestantByEmail(String email) { + if (email == null) { + throw new BusinessException("email required"); + } + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("email", email); + Contestant existing = contestantMapper.selectOne(qw); + if (existing == null) { + return null; + } + ContestantDTO dto = new ContestantDTO(); + dto.setEmail(existing.getEmail()); + dto.setFirstName(existing.getFirstName()); + dto.setLastName(existing.getLastName()); + dto.setGender(existing.getGender()); + dto.setOccupation(existing.getOccupation()); + dto.setAge(existing.getAge()); + dto.setCountryRegionCity(existing.getCountryRegionCity()); + dto.setPhoneNumber(existing.getPhoneNumber()); + dto.setPortfolioUrl(existing.getPortfolioUrl()); + dto.setDesignTitle(existing.getDesignTitle()); + dto.setDesignDescription(existing.getDesignDescription()); + dto.setPdfPath(existing.getPdfPath()); + dto.setVideoPath(existing.getVideoPath()); + return dto; + } +} + +