Ver Fonte

feat: aliyun

mjh há 4 anos atrás
pai
commit
672d70e64d

+ 0 - 2
pom.xml

@@ -74,7 +74,6 @@
             <artifactId>judgement-common</artifactId>
             <version>0.2.0</version>
         </dependency>
-
         <dependency>
             <groupId>cn.seecoder</groupId>
             <artifactId>judgement-client</artifactId>
@@ -129,7 +128,6 @@
                 <version>2.4.4</version>
                 <scope>import</scope>
             </dependency>
-
             <dependency>
                 <groupId>io.jsonwebtoken</groupId>
                 <artifactId>jjwt-api</artifactId>

+ 15 - 6
seecoder-portal-server/pom.xml

@@ -88,11 +88,6 @@
             <artifactId>postgresql</artifactId>
             <scope>runtime</scope>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-configuration-processor</artifactId>
-            <optional>true</optional>
-        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
@@ -102,7 +97,6 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
-
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-api</artifactId>
@@ -121,6 +115,21 @@
             <groupId>com.aliyun</groupId>
             <artifactId>aliyun-java-sdk-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+            <version>2.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.10.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 
 </project>

+ 2 - 2
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/SeecoderPortalServerApplication.java

@@ -1,16 +1,16 @@
 package cn.seecoder.seecoderportalserver;
 
 import cn.seecoder.seecoderportalserver.config.property.AliProperties;
+import cn.seecoder.seecoderportalserver.utility.OssUtil;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 
 /**
  * @author Kunduin
  */
 @SpringBootApplication
-@EnableConfigurationProperties({AliProperties.class})
+@EnableConfigurationProperties({AliProperties.class, OssUtil.class})
 public class SeecoderPortalServerApplication {
     public static void main(String[] args) {
         SpringApplication.run(SeecoderPortalServerApplication.class, args);

+ 2 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/config/SpringSecurityConfiguration.java

@@ -51,6 +51,7 @@ public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
                 .and()
                 .authorizeRequests()
                 .antMatchers(
+                        "/api/test/upload",
                         "/api/user/sendMessage",
                         "/api/user/login",
                         "/api/user/register",
@@ -74,6 +75,7 @@ public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
                         "/api/course/selection/confirm",
                         "/api/course/selection/cancel",
                         "/api/course/checkName",
+                        "/api/course/uploadImage",
                         "/api/eval/exam/student",
                         "/api/eval/exam/student/groupByCourse",
                         "/api/eval/exam/teacher",

+ 9 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/service/CourseService.java

@@ -2,6 +2,7 @@ package cn.seecoder.seecoderportalserver.service;
 
 import cn.seecoder.seecoderportalcommon.vo.*;
 import cn.seecoder.seecoderportalserver.domain.key.CourseSelectionKey;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -38,4 +39,12 @@ public interface CourseService {
     CourseVO findCourseByEvalId(int evalId) throws Exception;
 
     Boolean isCourseNameExisted(String name);
+
+    /**
+     * 上传课程图片
+     * @param name 课程名称
+     * @param file 课程图片文件
+     * @return 图片上传到阿里云oss后返回的url
+     */
+    String uploadCourseImage(String name, MultipartFile file);
 }

+ 36 - 7
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/serviceImpl/CourseServiceImpl.java

@@ -14,10 +14,13 @@ import cn.seecoder.seecoderportalserver.service.UserService;
 import cn.seecoder.seecoderportalserver.serviceImpl.subsystem.CoderService;
 import cn.seecoder.seecoderportalserver.serviceImpl.subsystem.EvalService;
 import cn.seecoder.seecoderportalserver.serviceImpl.subsystem.HelperService;
+import cn.seecoder.seecoderportalserver.utility.OssUtil;
 import cn.seecoder.seecoderportalserver.web.rest.errors.CustomErrorException;
 import com.alibaba.fastjson.JSONObject;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -30,8 +33,9 @@ public class CourseServiceImpl implements CourseService {
     private final CoderService coderService;
     private final HelperService helperService;
     private final EvalService evalService;
+    private final OssUtil ossUtil;
 
-    public CourseServiceImpl(CourseRepository courseRepository, CourseMapper courseMapper, CourseSelectionRepository courseSelectionRepository, CoderService coderService, HelperService helperService, EvalService evalService, UserService userService) {
+    public CourseServiceImpl(CourseRepository courseRepository, CourseMapper courseMapper, CourseSelectionRepository courseSelectionRepository, CoderService coderService, HelperService helperService, EvalService evalService, UserService userService, OssUtil ossUtil) {
         this.courseRepository = courseRepository;
         this.courseMapper = courseMapper;
         this.courseSelectionRepository = courseSelectionRepository;
@@ -39,8 +43,10 @@ public class CourseServiceImpl implements CourseService {
         this.coderService = coderService;
         this.helperService = helperService;
         this.evalService = evalService;
+        this.ossUtil = ossUtil;
     }
 
+    @Override
     public CourseVO createCourse(CourseVO courseVO, Long userId) {
         // 确认授课教师
         UserVO teacher = userService.findUserById(userId);
@@ -48,9 +54,9 @@ public class CourseServiceImpl implements CourseService {
             throw new CustomErrorException("当前用户无权创建课程");
         }
 
+        // 子系统:eval、coder
         Integer coderId;
         Integer evalId;
-
         // 创建Coder课程
         if (courseVO.getCoderId() != null) {
             JSONObject coderRes = coderService.createCourse(courseVO);
@@ -73,12 +79,14 @@ public class CourseServiceImpl implements CourseService {
                 courseVO.setEvalId(evalId);
             }
         }
+
         Course course = courseMapper.courseVOToCourse(courseVO);
         course.setState(ObjectState.NORMAL);
         Course result = courseRepository.save(course);
         return courseMapper.courseToCourseVO(result);
     }
 
+    @Override
     public CourseVO updateCourse(CourseVO courseVO, Long userId) {
         CourseVO course = this.findCourseById(courseVO.getId());
         if (!userId.equals(course.getTeacher().getId())) {
@@ -97,6 +105,7 @@ public class CourseServiceImpl implements CourseService {
         return courseMapper.courseToCourseVO(result);
     }
 
+    @Override
     public String deleteCourse(Long courseId, Long userId) {
         CourseVO course = this.findCourseById(courseId);
         if (!userId.equals(course.getTeacher().getId())) {
@@ -109,6 +118,7 @@ public class CourseServiceImpl implements CourseService {
         return "课程删除成功";
     }
 
+    @Override
     public List<CourseVO> findAllCourses() {
         List<Course> courses = courseRepository.findAll();
         return courses.stream()
@@ -117,6 +127,7 @@ public class CourseServiceImpl implements CourseService {
                 .collect(Collectors.toList());
     }
 
+    @Override
     public List<CourseVO> findTeacherCourses(Long teacherId) {
         List<Course> courses = courseRepository.findCoursesByTeacherId(teacherId);
         return courses.stream()
@@ -125,6 +136,7 @@ public class CourseServiceImpl implements CourseService {
                 .collect(Collectors.toList());
     }
 
+    @Override
     public List<CourseVO> findStudentCourses(Long studentId) {
         List<CourseSelection> courseSelections = courseSelectionRepository.findCourseSelectionsByUserId(studentId);
         return courseSelections.stream()
@@ -133,6 +145,7 @@ public class CourseServiceImpl implements CourseService {
                 .collect(Collectors.toList());
     }
 
+    @Override
     public List<CourseVO> findRecommendCourses() {
         List<Course> courses = courseRepository.findCoursesByState(ObjectState.EXAMPLE);
         return courses.stream()
@@ -140,24 +153,29 @@ public class CourseServiceImpl implements CourseService {
                 .collect(Collectors.toList());
     }
 
+    @Override
     public CourseVO findCourseByCoderId(Integer coderId) {
         Course course = courseRepository.findCourseByCoderId(coderId).orElseThrow(CustomErrorException::courseNotFound);
         return course.getState() == ObjectState.DELETED ? null : courseMapper.courseToCourseVO(course);
     }
 
+    @Override
     public Boolean checkCourseNameUnique(Long id, String name) {
         Course course = courseRepository.findCourseByName(name).orElse(null);
         return course == null || course.getId().equals(id);
     }
 
+    @Override
     public CourseVO findCourseById(Long id) {
         Course course = courseRepository.findById(id)
                 .orElseThrow(CustomErrorException::courseNotFound);
-        if (course.getState().equals(ObjectState.DELETED))
+        if (course.getState() == ObjectState.DELETED) {
             throw CustomErrorException.courseNotFound();
+        }
         return courseMapper.courseToCourseVO(course);
     }
 
+    @Override
     public String confirmCourseSelection(Long courseId, Long userId) {
         UserVO userVO = userService.findUserById(userId);
         assert userService.isStudent(userVO.getRole());
@@ -166,9 +184,6 @@ public class CourseServiceImpl implements CourseService {
         // coder系统选课
         if (course.getCoderId() != null) {
             JSONObject coderRes = coderService.chooseCourse(course.getInvitationCode());
-//            if (!coderRes.containsKey("res")) {
-//                throw new CustomErrorException("Coder: " + coderRes.get("msg"));
-//            }
         }
         // eval系统选课
         if (course.getEvalId() != null) {
@@ -184,6 +199,7 @@ public class CourseServiceImpl implements CourseService {
         return "选课成功";
     }
 
+    @Override
     public String cancelCourseSelection(CourseSelectionKey courseSelectionKey, Long userId) {
         UserVO user = userService.findUserById(userId);
         if (!user.getId().equals(courseSelectionKey.getUserId())) {
@@ -199,24 +215,37 @@ public class CourseServiceImpl implements CourseService {
         }
     }
 
+    @Override
     public Boolean checkSelection(Long userId, Long courseId) {
         CourseSelectionKey key = new CourseSelectionKey(userId, courseId);
         CourseSelection record = courseSelectionRepository.findById(key).orElse(null);
         return record != null && record.getState();
     }
 
+    @Override
     public Boolean checkCourseCodeUnique(Long id, String code) {
         Course course = courseRepository.findCourseByInvitationCode(code).orElse(null);
         return course == null || course.getId().equals(id);
     }
 
-    public CourseVO findCourseByEvalId(int evalId){
+    @Override
+    public CourseVO findCourseByEvalId(int evalId) {
         Course course = courseRepository.findCourseByEvalId(evalId).orElseThrow(CustomErrorException::courseNotFound);
         return course.getState() == ObjectState.DELETED ? null : courseMapper.courseToCourseVO(course);
     }
 
+    @Override
     public Boolean isCourseNameExisted(String name) {
         Course course = courseRepository.findCourseByName(name).orElse(null);
         return course != null;
     }
+
+    @Override
+    public String uploadCourseImage(String name, MultipartFile file) {
+        try {
+            return ossUtil.upload(name + ".jpg", file.getInputStream());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }

+ 104 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/utility/OssUtil.java

@@ -0,0 +1,104 @@
+package cn.seecoder.seecoderportalserver.utility;
+
+import cn.seecoder.seecoderportalserver.web.rest.errors.CustomErrorException;
+import com.aliyun.oss.*;
+import com.aliyun.oss.model.Bucket;
+import com.aliyun.oss.model.PutObjectRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author wind
+ */
+@Component
+@ConfigurationProperties("aliyun.oss")
+public class OssUtil {
+    private static final Logger log = LoggerFactory.getLogger(OssUtil.class);
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String bucketName;
+
+    public String upload(String objectName, InputStream inputStream) {
+        OSS ossClient = getOss();
+        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
+        try {
+            ossClient.putObject(putObjectRequest);
+        } catch (OSSException | ClientException e) {
+            log.error(e.getMessage());
+            throw new CustomErrorException("文件上传失败");
+        } finally {
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+        }
+        return ossClient.generatePresignedUrl(bucketName, objectName, new Date()).toString().split("\\?Expires")[0];
+    }
+
+    @Async
+    public void delete(String objectName) {
+        OSS ossClient = getOss();
+        try {
+            ossClient.deleteObject(bucketName, objectName);
+        } catch (OSSException | ClientException e) {
+            log.error(e.getMessage());
+            throw new CustomErrorException("文件删除失败");
+        } finally {
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+        }
+    }
+
+    private OSS getOss() {
+        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getAccessKeyId() {
+        return accessKeyId;
+    }
+
+    public void setAccessKeyId(String accessKeyId) {
+        this.accessKeyId = accessKeyId;
+    }
+
+    public String getAccessKeySecret() {
+        return accessKeySecret;
+    }
+
+    public void setAccessKeySecret(String accessKeySecret) {
+        this.accessKeySecret = accessKeySecret;
+    }
+
+    public String getBucketName() {
+        return bucketName;
+    }
+
+    public void setBucketName(String bucketName) {
+        this.bucketName = bucketName;
+    }
+
+//    public static void main(String[] args) {
+//        OssUtil ossUtil = new OssUtil();
+//        ossUtil.setEndpoint("https://oss-cn-hangzhou.aliyuncs.com");
+//        ossUtil.setAccessKeyId("LTAI4Fi1qmL4iuhH8t7G7r2H");
+//        ossUtil.setAccessKeySecret("7tJWyQqa0cMkQGaPMMvtH6c0VLiOO2");
+//        OSS oss = ossUtil.getOss();
+//        Bucket bucket = oss.createBucket("seec-portal");
+//    }
+}

+ 9 - 2
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/CourseController.java

@@ -6,6 +6,7 @@ import cn.seecoder.seecoderportalserver.security.SecurityUtils;
 import cn.seecoder.seecoderportalserver.service.CourseService;
 import cn.seecoder.seecoderportalserver.utility.OkResponse;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -82,16 +83,22 @@ public class CourseController {
     }
 
     @GetMapping("/checkCode")
-    public OkResponse<Boolean> checkCourseCodeUnique(@RequestParam Long id, @RequestParam String code){
+    public OkResponse<Boolean> checkCourseCodeUnique(@RequestParam Long id, @RequestParam String code) {
         return OkResponse.of(courseService.checkCourseCodeUnique(id, code));
     }
 
     @GetMapping("/getCourseByEvalId")
-    public OkResponse<Long> findCourseByEvalId(@RequestParam int evalId){
+    public OkResponse<Long> findCourseByEvalId(@RequestParam int evalId) {
         try {
             return OkResponse.of(courseService.findCourseByEvalId(evalId).getId());
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
+
+    @PostMapping("/uploadImage")
+    public OkResponse<String> uploadCourseImage(@RequestPart("name") String name,
+                                                @RequestPart("file") MultipartFile file) {
+        return OkResponse.of(courseService.uploadCourseImage(name, file));
+    }
 }

+ 17 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/TestController.java

@@ -0,0 +1,17 @@
+package cn.seecoder.seecoderportalserver.web.rest;
+
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 临时接口测试
+ * @author wind
+ */
+@RestController
+@RequestMapping("/api/test")
+public class TestController {
+    @PostMapping("/upload")
+    public void upload(@RequestPart("file") MultipartFile file){
+        System.out.println(file.getSize());
+    }
+}

+ 1 - 1
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/errors/CustomErrorException.java

@@ -64,6 +64,6 @@ public class CustomErrorException extends RuntimeException {
      * @return 抛出异常
      */
     public static CustomErrorException userPhoneNotFound(String phone){
-        return new CustomErrorException("手机号 \"" + phone + " \"不存在!");
+        return new CustomErrorException("手机号 \"" + phone + "\" 不存在!");
     }
 }

+ 16 - 4
seecoder-portal-server/src/main/resources/application-dev.yml

@@ -5,7 +5,19 @@ spring:
     show-sql: true
   datasource:
     url: jdbc:postgresql://localhost:5432/portal
-    username: root
-    password: 123456
-#    username: postgres
-#    password: postgres
+#    username: root
+#    password: 123456
+    username: postgres
+    password: postgres
+
+aliyun:
+  oss:
+    endpoint: https://oss-cn-hangzhou.aliyuncs.com
+    accessKeyId: LTAI5t8r92pu9SjG1vz9fCKT
+    accessKeySecret: NPGHba96kBK0PdL65QwQNyMBz0PCfq
+    bucketName: mjh1
+#    endpoint: https://oss-cn-hangzhou.aliyuncs.com
+#    accessKeyId: LTAI4Fi1qmL4iuhH8t7G7r2H
+#    accessKeySecret: 7tJWyQqa0cMkQGaPMMvtH6c0VLiOO2
+#    bucketName: seec-portal
+