项目练习:在线抽奖系统

在线抽奖系统

项目目标

• 巩固Spring相关知识IOC(Inversion of Control 控制反转)/DI(Dependency Injection)依赖注入。
• 复习 SpringMVC、SpringBoot 的配置以及使用。
• 复习并巩固Mybatis 以及 HttpServlet 异常处理。
• 了解项目开发流程,训练自己的代码调试能力以及在网上的抓包能力。

开发环境&使用技术

• windows
• Spring SpringMVC SpringBoot
• MySQL MyBatis Druid
• Maven
• Lombok

项目实现功能

• 用户的注册、登录
• 用户对奖项进行增加以及增删改查、对人员名单进行增删改查
• 给参加人员抽奖、从抽奖中删除中奖人员

项目演示(动图演示)在这里插入图片描述

在这里插入图片描述
因为不能导入太大的图片,所以在这里总有些限制,所以展现了改项目的主要过程。

数据库设计

在这里插入图片描述
本次项目中用到的user、setting、member、award、record这些表关系如上。
用户操作的人员会记录到设置表当中,设置表记录当前用户操作的人员以及奖项。
每个奖项有多条抽奖记录,一条抽奖记录对应一个抽奖人员。

前后端接口实现

首先我们可以打开F12来看(这里我用的是谷歌浏览器)
在这里插入图片描述
因为点登录进去会换页面不好找,所以我们应该打开我们的调试模式打断点,然后再转到页面看我们抓到的请求(后面抓请求一样步骤我就不搞这么多截图了)
在这里插入图片描述
在这里,我们成功抓到了服务器的请求,是api/user/login(我是写了项目再展示的,所以只能通过这种方式来写过程了)
在这里插入图片描述
Cotroller的匹配规则中,用/api开头,添加前缀
打开UserController
在这里插入图片描述
在这里插入图片描述
这里总的配置是,往后的子路径都在/user之下
在这里插入图片描述
这样就确定了我们的路径/api/user/login
在这里插入图片描述

看到我们的请求体可以知道Post请求(当然这种用户名的怎么可能会是Get请求呢?这也太不安全了。)
接下来我们就按照我们的开发流程
Controller接收处理请求
然后传给Service业务层处理
再从Mapper接口获取数据库数据。
在这里插入图片描述
当然,如果我们的数据不存在我们应当抛出异常。我们抛出异常不能像是我们刚学的时候那样,需要返回我们的错误码和提示,这样,如果有人与我们合作完成这个项目,就更有利于别人的维护以及调试。这一点我认为是我们必备的素质。
进入了页面,页面显示信息会根据我们用户信息查询,接下来我们就需要把查询所需的一些方法写出来。
请求的奖品信息
请求的人员信息

//通过收到的请求来寻找业务层对应的方法
@GetMapping("/query")
    public Object Query(HttpSession session){
        //返回的字段时setting对象属性,缺少的属性先构造好
        //需要setting对象,可以通过user_id从数据库查询
        //user_id从session中的user获取
        User user = (User) session.getAttribute("user");
        Setting setting = settingService.query(user.getId());
        setting.setUser(user);
        return setting;
    }
//这里会返回我们的页面所需的对象信息,用mybatis查找数据
public Setting query(Integer userId) {
        Setting setting = settingMapper.queryByUserId(userId);
        List<Award> awards = awardMapper.queryBySettingId(setting.getId());
        setting.setAwards(awards);
        List<Member> members = memberMapper.queryBySettingId(setting.getId());
        setting.setMembers(members);
        return setting;
    }

做完这些事情后,服务器收到我们反馈的setting并展示信息,如下,我们变获得了我们的人员信息列表。
在这里插入图片描述
同理,我们写下我们通过F12可以找到服务器的请求,然后依此继续写下别的各种请求内容,我们就能够完成实现本页面的一些添加删除等操作
在这里插入图片描述
添加一条记录

Request URL: http://localhost:8080/api/award/add
Request Method: POST
//返回响应
{
  "success" : true,//响应是成功的
  "data" : 4(返回了这条记录的索引)
}

同理,删除

Request URL: http://localhost:8080/api/award/delete/4
Request Method: GET
{name: "添加", count: 10, award: "这次一定"}
award: "这次一定"
count: 10
name: "添加"
//返回响应
{
  "success" : true
}

修改

Request URL: http://localhost:8080/api/award/update
Request Method: POST
{name: "不靠谱大奖", count: 10, award: "未知神秘大奖", id: 5}
award: "未知神秘大奖"
count: 10
id: 5
name: "不靠谱大奖"
//返回响应
{
  "success" : true
}

对于下面的人员操作也一样,添加一个人员

Request URL: http://localhost:8080/api/member/add
Request Method: POST
{name: "我是最帅的人", no: "no90"}
name: "我是最帅的人"
no: "no90"
//返回响应
{
  "success" : true,
  "data" : 97
}

删除一个人员

Request URL: http://localhost:8080/api/member/delete/97
Request Method: GET
//返回响应
{
  "success" : true
}

人员的修改

Request URL: http://localhost:8080/api/member/update
Request Method: POST
{name: "aaad", no: "aaa", id: 98}
id: 98
name: "aaad"
no: "aaa"
//返回响应
{
  "success" : true
}

在这里插入图片描述
对于抽奖是这么的,现在我们操作抽到一个人

Request URL: http://localhost:8080/api/record/add/1
Request Method: POST
[43]
0: 43
//这样就是成功了
{
  "success" : true
}

在这里插入图片描述

去掉抽奖者,继续,网页会发出请求(删除)

Request URL: http://localhost:8080/api/record/delete/member?id=43
Request Method: GET
//删除成功响应
{
  "success" : true
}

在这里插入图片描述
当然,这里我们还可以修改抽奖人数

Request URL: http://localhost:8080/api/setting/update?batchNumber=20
Request Method: GET
//修改成功
{
  "success" : true
}

在这里插入图片描述
这么一次抽奖,抽奖结束直接可以抽到20人

开发环境准备

配置Maven相关的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 默认使用的Spring Framework版本为5.2.10.RELEASE -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>lucky-draw</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!-- spring-boot-starter-web: 基于SpringBoot开发的依赖包,
                                 会再次依赖spring-framework中基本依赖包,aop相关依赖包,web相关依赖包,
                                 还会引入其他如json,tomcat,validation等依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 排除tomcat依赖 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 添加 Undertow 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <!--引入AOP依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!-- mybatis-spring-boot-starter: Mybatis框架在SpringBoot中集成的依赖包,
                                Mybatis是一种数据库对象关系映射Object-Relationl Mapping(ORM)框架,
                                其他还有如Hibernate等 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- Mybatis代码生成工具 -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!-- druid-spring-boot-starter: 阿里Druid数据库连接池,同样的运行时需要 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!-- JDBC:mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
            <scope>runtime</scope>
        </dependency>

        <!-- spring-boot-devtools: SpringBoot的热部署依赖包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <!-- 不能被其它模块继承,如果多个子模块可以去掉 -->
            <optional>true</optional>
        </dependency>

        <!-- lombok: 简化bean代码的框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- spring-boot-starter-test: SpringBoot测试框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- SpringBoot的maven打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- 明确指定一些插件的版本,以免受到 maven 版本的影响 -->
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.8.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-install-plugin</artifactId>
                <version>2.5.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.3</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>

        </plugins>
    </build>

</project>

准备Springboot配置文件

debug=true
# 设置打印日志的级别,及打印sql语句
#日志级别:trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
#扫描的包:druid.sql.Statement类和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.org.example=DEBUG

# 美化JSON数据格式
spring.jackson.serialization.indent-output=true
# 设置JSON数据的日期格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
# JSON数据属性为null时不返回
spring.jackson.default-property-inclusion=non_null
# get请求参数及表单提交数据的日期格式
spring.mvc.format.date=yyyy-MM-dd HH:mm:ss

#servlet上下文路径
#server.servlet.context-path=/lucky-draw
# 自定义属性:用户头像本地保存根路径
user.head.local-path=D:/TMP
user.head.remote-path=http://localhost:8080${server.servlet.context-path:}
#user.head.filename=head.jpg
# 静态资源映射:将路径映射为/,即/static/xxx,映射为/xxx,支持多个字符串,逗号间隔
# 默认为/META-INF/resources/, /resources/, /static/, /public/
# 指定外部web资源文件夹:访问的路径为/
spring.resources.static-locations=classpath:/static/,classpath:/public/,file:${user.head.local-path}

#druid数据库连接池配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/lucky_draw?useUnicode=true&characterEncoding=UTF-8&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true

#指定Mybatis表和实体映射关系xml配置文件,包含表与实体的映射,字段和属性的映射,及各个sql语句
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

准备SpringBoot启动类

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

配置数据库链接

在这里插入图片描述
配置文件里面写的信息

用idea右边的Database链接
在这里插入图片描述
进入设置界面设置数据库:
在这里插入图片描述

代码设计

统一异常处理

public class ExceptionAdvice {

    //自定义异常报错错误码和错误消息
    @ExceptionHandler(AppException.class)
    @ResponseBody
    public Object handle1(AppException e){
        JSONResponse json = new JSONResponse();
        json.setCode(e.getCode());
        json.setMessage(e.getMessage());
        log.debug("自定义异常", e);
        return json;
    }

    //非自定义异常(英文错误信息,堆栈信息,不能给用户看):
    // 指定一个错误码,错误消息(未知错误,请联系管理员)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object handle2(Exception e){
        JSONResponse json = new JSONResponse();
        json.setCode("ERR000");
        json.setMessage("未知错误,请联系管理员");
        log.error("未知错误", e);
        return json;
    }
}

<其中 方法handle1>:对于我们自定义异常,我们自己的应用发生错误的时候,我们通过我们的自定义异常AppException来仔细写明我们的错误信息。

<其中 方法handle2>:捕获到的我们没有定义到的异常,可以说是未知错误。这算是抛出未知的异常。

每次我们自己写的异常都 调用这个我们自己定义的的异常处理来写明我们的异常,说明清除异常编号以及异常的代码。
在这里插入图片描述

专用于本项目定义的异常处理AppException处理本应用在请求中的异常

public class AppException extends RuntimeException {

    private String code;

    public AppException( String code, String message) {
        super(message);
        this.code = code;
    }

    public AppException( String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }
}

拦截器

防止不登陆直接访问管理设置页面。

public class LoginInterceptor implements HandlerInterceptor {

    private ObjectMapper objectMapper;

    public LoginInterceptor(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if(session != null){//获取登录时设置的用户信息
            User user = (User) session.getAttribute("user");
            if(user != null){//登录了,允许访问
                return true;
            }
        }
        //登录失败,不允许访问的业务:区分前后端
        //TODO:前端跳转登录页面,后端返回json
//        new ObjectMapper().writeValueAsString(object);//序列化对象为json字符串
        //请求的服务路径
        String servletPath = request.getServletPath();//   /apiXXX.html
        if(servletPath.startsWith("/api/")){//后端逻辑:返回json
            response.setCharacterEncoding("UTF-8");
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            JSONResponse json = new JSONResponse();
            json.setCode("USR000");
            json.setMessage("用户没有登录,不允许访问");
            String s = objectMapper.writeValueAsString(json);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            PrintWriter pw = response.getWriter();
            pw.println(s);
            pw.flush();
        }else{//前端逻辑:跳转到登录页面 /views/index.html
            //相对路径的写法,一定是请求路径作为相对位置的参照点
            //使用绝对路径来重定向,不建议使用相对路径和转发
            String schema = request.getScheme();//http
            String host = request.getServerName();//ip
            int port = request.getServerPort();//port
            String contextPath = request.getContextPath();//application Context path应用上下文路径
            String basePath = schema+"://"+host+":"+port+contextPath;
            //重定向到登录页面
            response.sendRedirect(basePath+"/index.html");
        }
        return false;
    }
}

设置Mybatis的Mapper基类

其余单独有要求的单独写出来

package org.example.base;

public interface BaseMapper<T> {

    int deleteByPrimaryKey(Integer id);

    int insert(T record);

    int insertSelective(T record);

    T selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(T record);

    int updateByPrimaryKey(T record);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值