作业及课程管理系统(一)

本篇主要讲解的是后端多个模块中的common部分,也就是大众通用部分。

以后每篇讲解会说明一下开发中遇到的坑以及使用的技术,同时会贴上部分的代码,以便了解。

common部分主要承载了记录用户的登录信息,文件上传,论坛功能。其重要技术有阿里云oss上传代码、feign组件的使用,以及springcloud各服务之间的文件上传功能。其中各服务之间文件传递是难点以及坑最多的部分,这部分放在最后详细说明一下。

如果在阅读中发现任何问题,欢迎联系我,帮助我改正!谢谢各位阅读的大佬!

记录用户的登录信息

这里使用了一个aop切面技术,在后台打印日志记录用户登录,这个只是学习aop的时候做的一部分,实际记录是存储在控制台中。aop具体的代码为:
1.配置注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author wangyb
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLog {
    String value() default "";
}

2.实例化注解

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * @author wangyb
 */
@Aspect
@Component
@Slf4j
public class UserLogCut {

    @Pointcut("@annotation(com.bysj.wyb.common.annotation.UserLog)")
    public void UserLog(){

    }

    @Before("UserLog()")
    public void BeforeLog(JoinPoint point){
        log.info("----------开始记录用户登录信息---------");
        log.info("用户名信息:" +  Arrays.toString(point.getArgs()));
        log.info("登录时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

    @Around("UserLog()")
    public void methodAround(ProceedingJoinPoint point){
        log.info("----------用户登录成功!---------");
        try {
            Object o=point.proceed();
        }catch (Throwable t){
            log.error(t.getMessage(),t);
        }
    }

    @After("UserLog()")
    public void returnLog(){
        log.info("-----------记录用户登录信息结束------------");
    }
}

3.使用注解
使用aop切面技术给方法添加注解时要注意,业务逻辑不能放在注解中,一旦注解失效业务逻辑就会失效,造成bug。aop切面应该是进行一些辅助性操作,例如日志记录等

/**
     * 登录信息登记
     * @param uid
     * @param request
     */
    @UserLog
    @RequestMapping(value = "/logCounter")
    public void logCounter(@RequestParam String uid, HttpServletRequest request){
        sysService.logCount(uid);
    }

论坛功能

论坛部分没有突出的地方,就是普通的CRUD(虽然其他的部分也是CRUD哈哈哈),就不在这里做详细的展开了。

文件上传

第一部分是阿里云oss文件上传的java SDK,这个在阿里云的官方文档中就能找到教程,这里具体讲一下我怎么做的。

首先是引入依赖:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.4.2</version>
</dependency>

引入依赖以后便可以使用java代码对oss服务器进行操作,其他的操作这里就不展示了,只展示项目中使用到的文件上传部分:

public Result uplodToOSS(MultipartFile multipartFile,String uploadCatalogAndName){
        HandleResult hr=new HandleResult();
        // Endpoint以杭州为例,其它Region请按实际情况填写。
        String endpoint = "http://oss-cn-chengdu.aliyuncs.com";
        // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
        String accessKeyId = "123";
        String accessKeySecret = "456";
        String bucketName = "bysj";
        URL url = null;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件后缀
        String prefix = fileName.substring(fileName.lastIndexOf("."));
        // 若需要防止生成的临时文件重复,可以在文件名后添加随机码

        try {
            File file = File.createTempFile(fileName, prefix);
            multipartFile.transferTo(file);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, uploadCatalogAndName, file);
            ossClient.putObject(putObjectRequest);
            Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
            url=ossClient.generatePresignedUrl(bucketName,uploadCatalogAndName,expiration);
            System.out.println(url.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(url!=null){
                ossClient.shutdown();
                return hr.outResultWithData("0","上传成功",url.toString());
            }else{
                ossClient.shutdown();
                return hr.outResultWithoutData("1","上传失败");
            }
        }
    }

我们分段看一下上面的代码

第一部分是配置基础属性,也就是你的oss地址,你的bucket各项属性,例如授权id,授权秘钥,bucketname等等,同时对文件名进行了处理,然后开启上传链接

// Endpoint以杭州为例,其它Region请按实际情况填写。
        String endpoint = "http://oss-cn-chengdu.aliyuncs.com";
        // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
        String accessKeyId = "123";
        String accessKeySecret = "456";
        String bucketName = "bysj";
        URL url = null;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件后缀
        String prefix = fileName.substring(fileName.lastIndexOf("."));

随后开始上传文件,这里没有什么技术含量,按照官方文档撰写代码即可
上传完文件后,就要获取到文件的读取地址,以便对文件的调用或者查看

try {
            File file = File.createTempFile(fileName, prefix);
            multipartFile.transferTo(file);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, uploadCatalogAndName, file);
            ossClient.putObject(putObjectRequest);
            Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
			//获取文件可读取的url地址
            url=ossClient.generatePresignedUrl(bucketName,uploadCatalogAndName,expiration);
            System.out.println(url.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(url!=null){
                ossClient.shutdown();
                return hr.outResultWithData("0","上传成功",url.toString());
            }else{
                ossClient.shutdown();
                return hr.outResultWithoutData("1","上传失败");
            }
        }

这里值得要注意的是url的过期时间,按照官方文档以及查阅的资料,过期时间是按照毫秒换算,但是不论怎么设置最后获取的url只有三分钟时间,三分钟后即为过期,所以这个小bug对于图像文件来说不是很友好,下载文件也不友好,只能做个演示用,如果有需要的同学可以研究一下这个bug

同时在获取时间时,官方文档给出的是

new Date().getTime()

但是不知道是IDEA还是阿里巴巴代码规范检测会给你的代码标红,让你改成

System.currentTimeMillis()

这里建议使用第二种,别问,问就是不知道。

因为是公用的文件上传,所以各服务之间使用的feign组件进行服务调用,common对外提供接口。
这里的接口使用注意点有几个

1.对外提供接口时,需要指定为POST方法,或者直接使用@PostMapping注解
2.接口参数中的文件,必须添加注解@RequestPart

/**
     * 上传文件接口
     * @param file
     * @param uploadCatalogAndName
     * @return
     */
    @RequestMapping(value = "/upload",method = RequestMethod.POST)
    public Result uploadToOss(@RequestPart("file") MultipartFile file, @RequestParam String uploadCatalogAndName){
        return sysService.uplodToOSS(file,uploadCatalogAndName);
    }

3.添加依赖,以解决FeignClient使用服务名无法调用其他服务只能指定url的问题,尚不清楚这个依赖会在服务者或者消费者那个里面其效果,保险起见二者都谈加了这个依赖

<dependency>
    <groupId>com.netflix.archaius</groupId>
    <artifactId>archaius-core</artifactId>
    <version>0.7.6</version>
    <exclusions>
        <exclusion>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </exclusion>
    </exclusions>
</dependency>

4.消费者调用接口写成mapper的形式,但不添加mapper注解,添加@FeignClient注解:

/**
 * @author wangyb
 */
@FeignClient(name = "pd-common",configuration = MultipartSupportConfig.class)
public interface CommonFeign {

    @RequestMapping(value = "/system/logCounter")
    void logCounter(@RequestParam String uid);

    @RequestMapping(value = "/system/upload",method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    Result upload(@RequestPart("file") MultipartFile file, @RequestParam String uploadCatalogAndName);
}

5.添加服务之间可以传递文件的配置文件,配置注入方式查看上面的代码:

import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

@Configuration
public class MultipartSupportConfig {
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
}

这里调用使用了发现注册中心的服务名进行feign调用服务者,还有另一种方式为绝对url的方式,但是这样违背了feign的初衷,和访问url没有什么区别,可扩展性也不高,所以遇到无法调用服务名的情况要想办法解决

如果在使用feign调用上出现什么问题,要多看几篇大佬的文章,解决方式挺多的,总有一款适合你

common部分中大家可能看到有个类是HandleResult,这个是自己写的结果返回处理,分为带数据返回和不带数据返回,类如下:


/**
 * @author wangyb
 */
public class HandleResult {

    public Result outResultWithData(String status, String message, Object data){
        Result re=new Result();
        if("1".equals(status)){
            re.setStatus("1");
            re.setMessage(message);
            re.setData(data);
        }
        else {
            re.setStatus("0");
            re.setMessage(message);
            re.setData(data);
        }

        return re;
    }

    public Result outResultWithoutData(String status, String message){
        Result re=new Result();
        if("1".equals(status)){
            re.setStatus("1");
            re.setMessage(message);
        }
        else {
            re.setStatus("0");
            re.setMessage(message);
        }

        return re;
    }

}

配套使用的是result对象:



/**
 * @author admin
 */
public class Result {

    private String status;

    private String message;

    private Object data;

    @Override
    public String toString() {
        return "Result{" +
                "status='" + status + '\'' +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

同时还有一些其他的算法,会在后面做介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值