阶段项目 -- 智能学习辅助系统

3 篇文章 0 订阅
2 篇文章 0 订阅

阶段项目 – 智能学习辅助系统

1. 开发流程

  1. 查看页面原型,明确需求;

  2. 阅读接口文档;

  3. 思路分析;

  4. 接口开发;

    代码实现,要严格遵守接口文档

    如:接口文档规定的请求路径、请求方式…

  5. 接口测试;

    先通过postman这样的接口测试工具进行测试

  6. 前后端联调

    启动前后端,访问前端工程,通过前端工程访问服务端接口。

    *. 打开nginx(占用90端口),找到部署的前端工程;

    ​ 打开要测试的后端部分,触发功能(即发送请求);

    ​ nginx接收请求后,会将请求发送给8080端口的tomcat,由tomcat处理请求,将数据返回给前端;

    此处tomcat处理请求流程即为下方“功能实现流程”。

    ​ 前端解析后将数据渲染展示。

2. 功能实现流程

  1. 前端发送请求后会请求到controller的方法;

  2. controller(控制层)中先调用service(业务逻辑层)获取数据;

  3. service(业务逻辑层)中调用mapper(数据访问层)接口中的方法;

  4. mapper(数据访问层)调用实现类中的方法向数据库发送sql语句,并将结果封装,最后返回给service,service再返回给controller,controller拿到数据后再返回给前端。

    在这里,数据访问层为“mapper”,使用Mybatis这一款优秀的持久层框架。

3. 工具使用

  1. lombak,简化了JavaBean类的方法书写,使代码更加简洁;

    导入依赖:

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1EdGugm-1682127453605)(D:\a05_JavaWeb\我的笔记\图\lombak使用.png)]

  2. pageHelper,进行分页条件查询

4. 登录校验 - 令牌技术

通过如下步骤可以完成登录校验的操作(JWT令牌技术+过滤器Filter):

  1. 客户端向服务端发送请求,服务端生成令牌下发给客户端;
  2. 客户端每次请求中携带令牌至服务端,服务端对请求进行统一拦截,并获取请求中的令牌进行检验。
4.1 会话跟踪方案

传统会话跟踪技术:

  • 客户端会话跟踪方案:Cookie;
  • 服务端会话跟踪方案:Session;

主流方案:

  • 令牌技术。
4.2 令牌技术

​ 为达到登录校验的目的,我们对主流的令牌技术进行使用。

​ JWT( JSON Web Token):将原始的JSON格式进行了封装,实现了加密的效果。

组成:

第一部分:Header(头),记录令牌类型、签名算法等;

第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等;

第三部分:Signature(签名):防止Token被篡改,确保安全性。

*前两部分使用Base64编码(基于26个字母的大小写、0-9、+、/来表示二进制数据的编码方式)。

  1. 引入依赖;

    <!--JWT令牌-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
    
  2. 在工具类中书写生成和解析JWT令牌的方法;

    //生成JWT令牌
    private static String signKey = "offer888";
    private static Long expire = 43200000L;
    
    public static String generateJwt(Map<String, Object> claims){
           String jwt = Jwts.builder()
                    .signWith(SignatureAlgorithm.HS256, signKey)        //签名算法和密钥
                    .addClaims(claims)                                  //自定义内容(载荷)
                    .setExpiration(new Date(System.currentTimeMillis() + expire))   //设置有效期
                    .compact();
            return jwt;
        }
    //解析JWT令牌
        public static Claims parseJWT(String jwt){
            Claims claims = Jwts.parser()
                    .setSigningKey(signKey)		//生成令牌时使用的密钥
                    .parseClaimsJws(jwt)		//令牌
                    .getBody();
            return claims;						//返回自定义信息
        }
    
  3. Controller层:利用用户名和密码条件查询表中是否存在该用户进行登录验证;

    返回用户对象,若返回对象非空,代表该用户存在且密码正确,下发令牌;

    使用Map集合存储自定义信息,调用方法生成令牌并返回。

        @PostMapping("/login")
        public Result login(@RequestBody Emp emp){
            log.info("用户登录 -> 账号:{};密码:{}", emp.getUsername(), emp.getPassword());
            Emp e = empService.login(emp);
    
            //登录成功,生成令牌,下发令牌
            if(e != null){
                Map<String, Object> claims = new HashMap<>();
                claims.put("id", e.getId());
                claims.put("name", e.getName());
                claims.put("username", e.getUsername());
    
                String jwt = JwtUtils.generateJwt(claims);
                return Result.success(jwt);
            }
            //登陆失败,返回错误信息
            return Result.error("用户名或密码错误");
        }
    
  4. Service层:书写方法调用mapper层的方法;

  5. Mapper层:根据用户名和密码查询员工。

     @Select("select * from emp where username = #{username} and password = #{password}")
        Emp getByUsernameAndPassword(Emp emp);
    

5. 登录校验 - 过滤器Filter

JavaWeb三大组件(Servelet、Fileter、Listener)之一。

过滤器一般完成一些通用的操作,如:登录校验、统一编码处理、敏感字符处理等。

*Servelet、Listener目前使用较少。

  1. 创建一个类实现Fileter接口;

  2. 类上添加注解@WebFileter(***)配置拦截路径;

    配置方式举例
    拦截具体路径/login
    目录拦截/emps/*
    拦截所有/*
  3. Fileter并不是SpringBoot中的组件,使用需要在启动类上添加注解@ServeletComponentScan。

    代表开启了对servlet组件的支持

  4. 下面是登陆检验使用过滤器的代码实现:

     @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) servletRequest;
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            // 1.获取请求url
            String url = req.getRequestURL().toString();
            log.info("请求的url:{}", url);
    
            // 2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
            if(url.contains("login")){
                log.info("登陆操作,放行...");
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
    
            // 3.获取请求头中的令牌(token)
            String jwt = req.getHeader("token");
    
            // 4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
            if(!StringUtils.hasLength(jwt)){
                log.info("请求头token为空,返回未登陆的信息");
                Result error = Result.error("NOT_LOGIN");
                //手动转换 对象->JSON ----->使用阿里巴巴fastjson
                String notLogin = JSONObject.toJSONString(error);
                resp.getWriter().write(notLogin);
                //调用resp中的输出流getWriter(),再调用write()将信息响应回去
                return;
            }
            // 5.解析token,如果解析失败,返回错误结果(未登录)
            try {
                JwtUtils.parseJWT(jwt);
            } catch (Exception e) {
                e.printStackTrace();
                log.info("解析令牌失败,返回未登录错误信息");
                Result error = Result.error("NOT_LOGIN");
                //在Controller中可以自动转换为JSON格式
                //在这里需要手动转换 对象->JSON -----> 使用阿里巴巴fastjson
                String notLogin = JSONObject.toJSONString(error);
                resp.getWriter().write(notLogin);
                return;
            }
    
            // 6.放行
            log.info("令牌合法,放行");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    

6. 登录校验 – 补充

​ 在登录校验使用过滤器时,因为不是在Controller层中,获取到的对象不会自动转换为JSON格式,因此我们引入了阿里巴巴fastjson进行手动转换。

阿里巴巴fastjson使用:
  1. 在pom文件中引入阿里巴巴fastjson的依赖;

    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>fastjson</artifactId>
       <version>1.2.76</version>
    </dependency>
    
  2. 使用时调用方法即可。

7. 文件上传

7.1 简介
7.1.1 文件上传的前端页面三要素
  1. 表单项 type=“file”;

    设置此表单项在页面中就出现了”选择文件“的选项,可以选择文件上传。

  2. 表单提交方式 post;

    使用post提交方式,因为上传的文件一般都比较大

  3. 表单的enctype属性(默认方式提交的只是文件名–url编码后的格式)。

    使用enctype属性指定上传方式为multipart/form-data,因为普通默认的编码格式不适合上传大型的二进制数据

7.1.2 服务端接收数据

​ 在服务端定义方法接收即可,在方法中声明形参;对于文件,Spring提供了一个API – MultipartFile,使用该API就可以接收上传的文件。

要成功接收上传的文件,要保证表单项的名称和方法形参的名称保持一致;若不一致可通过@RequestParam注解指定Value属性。

7.2 本地存储

在服务端,接收到上传上来的文件之后,将文件存储在本地服务器的磁盘中。

构造唯一的文件名,调用transferTo方法

@PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) throws IOException {
        log.info("文件上传:{}, {}, {}", username, age, image);

        //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //构造唯一的文件名(文件名不能够重复)
        int index = originalFilename.lastIndexOf(".");
        String exname = originalFilename.substring(index);
        String newFileName = UUID.randomUUID().toString() + exname;
        log.info("新的文件名:{}", newFileName);

        //将文件存储在服务器的磁盘目录中 D:\图集\学习案例
        image.transferTo(new File("D:\\图集\\学习案例\\"+newFileName));

        return Result.success();
    }
7.3 补充

​ SpringBoot中,默认上传单个文件大小为1M,我们可以在配置文件中进行大小的设置:

#文件上传大小配置
servlet:
  multipart:
    max-file-size: 10MB			#对单个文件上传大小的设置
    max-request-size: 100MB		#对单次文件上传大小的设置

8. 文件上传 – 使用阿里云OSS

​ 起初使用本地存储的形式进行文件上传,本地存储有几项缺点:无法直接访问,磁盘空间限制,受磁盘状态影响(如磁盘损坏)。

​ 现使用阿里云OSS存储文件。

​ PS.篇幅较长,在另一篇文章中做详细分析。

9. 记录操作日志

9.1 需求

​ 将增、删、改相关接口的操作日志记录到数据库表中。

9.2 实现思路

​ 对所有业务类中增、删、改方法添加统一功能,使用AOP技术最方便。

​ 增、删、改方法没有规律,故可使用自定义注解的方式完成目标方法的匹配。

@Retention(RetentionPolicy.RUNTIME)		//注解作用场景
@Target(ElementType.METHOD)				//注解作用位置
public @interface Log {
}
9.3 步骤
  1. 在数据库中添加日志表;

    IDEA中要刷新数据库连接,连接到新表。

  2. 创建OperateLog类;

  3. 定义注解@Log;

  4. 定义OperateLogMapper接口,写入插入日志数据的代码;

10. 出现过的错误

  1. 不该出现的错误:使用预编译sql,语句中‘#{}’中没有填写参数;

    不知道为什么对其他方法进行了报错,排查方向出现问题,耗费两个半小时!

    *知道为什么对其他方法进行报错了,在其他模块中有本模块相同的方法名,导致idea红色报错,但是这种报错不影响SpringBoot程序的启动。

*细节

其实很多都是注解的使用。

  1. 控制层、业务逻辑层、数据访问层的class或接口实现类的文件类名上要标注注解;

    controller(控制层):@RestController

    //@RestController=@Controller+@ResponseBody
    //@Controller: 表明该类是一个控制类
    //@ReponseBody: 会将返回值的对象转为json再响应回来
    

    service(业务逻辑层):@Service

    mapper(数据访问层):@Mapper

  2. 控制层、业务逻辑层内要添加使用依赖注入的注解@Autowired;

    控制层要调用业务逻辑层的方法,故在控制层内创建业务逻辑层接口名的对象,并添加依赖注入的注解;

    业务逻辑层要调用数据访问层的方法获取数据库的数据,故在业务逻辑层创建数据访问层的对象,并添加依赖注入的注解。

    *上述注解使用的前提是各层已将bean对象交给IOC容器管理。

  3. 在控制层类名上添加注解@Slf4j,该注解即使用日志,可以在方法内直接调用log.info(),在括号内书写日志;

    使用日志书写相比于”sout“更加专业。

  4. 请求路径:添加注解@Mapping,写于方法体之上,在之后书写请求路径,用于表示调用什么方法完成什么操作;

    可在注解前加上请求方式,如:

    // 查询数据
    @GetMapping("/depts")  //限定了请求方式为GET,请求路径为"/depts"
    public Result list() {
         log.info("查询全部部门数据");   //使用日志,更加专业
         List<Dept> deptList = deptService.list();
        							  //获取业务逻辑层返回的数据
         return Result.success(deptList);
     }
    
    // 删除数据  
    @DeleteMapping("/depts/{id}") 
    					   //限定了请求方式为DELETE,请求路径为"/depts/{id}"
    public Result delete(@PathVariable Integer id){
         log.info("根据id删除部门:{}", id);
         //调用service删除部门
         deptService.delete(id);
         return Result.success();
     }
    			/*此处一个注意点,使用了根据id删除数据的方式,请求路径添加了“/{id}”。
    			因此在书写方法形参时,要在形参前添加注解@PathVariable,由此会将方法的			 实参传递给请求路径,替换掉占位符,获得完整的请求路径。*/
    

*小细节

  1. 程序错误,修改后要重新启动程序,再去试验是否修改正确;

  2. 报错404:请求资源不存在(URL有误或资源删除);

    修改错误:

    1. 可能是自己程序书写错误,如:sql语句属性名书写错误等;
  3. 在mapper中书写较长的SQL时,可以创建一个XML映射文件:

    下面对XML映射文件举例:

    <!--书写配置-->
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <!--此处注意点:namespace、id、resultType-->
    <mapper namespace = "com.itheima.mapper.EmpMapper">
        <select id="list" resultType="com.itheima.pojo.Emp">
            select *
            from emp
            <where>
                <if test="name != null and name != ''">
                    name like concat('%', #{name}, '%')
                </if>
                <if test="gender != null">
                    and gender = #{gender}
                </if>
                <if test="begin != null and end != null">
                    and entrydate between #{begin} and #{end}
                </if>
            </where>
            order by update_time desc
        </select>
        
    <!--在这里,对多个id对应的数据进行删除操作,使用了foreach进行遍历集合-->
        <delete id="delete">
            delete from emp where id in
                   <foreach collection="ids" item="id" separator="," open="(" close=")">
                            #{id}
                   </foreach>
        </delete>
    </mapper>
    

    有几点注意事项:

    • 创建于”resources“包,与对应的Mapper接口同包同名

    • 首先书写配置,在Mabatis中文网的”入门“标签下可找到;

    • XML映射文件中的namespace属性与Mapper接口全类名保持一致;

      namespace => 接口全类名

    • XML映射文件中SQL语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

      id => Mapper接口中的方法名

      resultType => 方法的返回类型(返回类型为单条记录所封装的类型

reach collection=“ids” item=“id” separator=“,” open=“(” close=“)”>

                    #{id}
           </foreach>
</delete>
```

有几点注意事项:

  • 创建于”resources“包,与对应的Mapper接口同包同名

  • 首先书写配置,在Mabatis中文网的”入门“标签下可找到;

  • XML映射文件中的namespace属性与Mapper接口全类名保持一致;

    namespace => 接口全类名

  • XML映射文件中SQL语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

    id => Mapper接口中的方法名

    resultType => 方法的返回类型(返回类型为单条记录所封装的类型

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值