基于SpringBoot实现的抽奖系统(一)

项目背景和介绍

抽奖系统类似于直播中的抽奖活动,在日常生活中也是比较常见,于是基于SpringBoot提供一个安全,可靠,易于维护的抽奖平台,本次项目采用了以下的策略:

1.集成多种技术组件:项目中集成了MySql,redis,RabbitMQ等常见的中间件.

2.允许管理员创建配置抽奖活动,管理人员和奖品信息.

3.使用了哈希加密,对称加密等加密方法保障用户的隐私数据.

4.提供了比较全面的日志处理与异常处理机制,简化维护过程.

项目结构

展示层:使用web的方式展示前端界面.

通信层:使用HTTP协议进行通信.

服务层:包含日志模块,业务模块,中间件.

数据层:使用mysql存储用户的数据和活动信息

本次项目的结构中,common层表示存放的一些全局使用例如错误码,统一返回结果,拦截器等

controller层中主要是与前端交互的接口,service层中就是接收controller层中任务处理.dao层是提供数据库中的查询等操作.

数据库设计

⽤户表:存储⽤⼾信息,如⽤户名、密码、邮箱等。

活动表:存储活动信息,如活动名称、描述、活动状态等。

奖品表:存储奖品信息,如奖品名称、奖品图等。

活动奖品关联表:存储⼀个活动下关联了哪些奖品。

活动⽤户关联表:存储⼀个活动下设置的参与⼈员。

中奖记录表:存储⼀个活动的中奖名单,如活动id,奖品id,中奖者id等。

项目启动

定义错误码

错误码是一种明确的表示错误信息的方式,与模糊的异常相比可以准确的指出错误所在,而错误码是由数字组成,也方便在日志中寻找.

本次项目的错误码主要包含两个信息,错误码与错误信息,方便区分错误的类别.

也定义了多模块中的错误码,将controller层与service层中的不同错误码分开,同时设计了全局的错误码,来对付意外的错误.

自定义异常类

自定义异常类作用是为了在可能抛出异常的地方,希望抛出我们可控的异常,这样可以方便定位到问题所在,异常类可以和错误码一起使用.

统一返回结果

为了方便返回,我们定义了CommonResult<T>来表示统一的返回结果,包括成功信息,错误信息,状态码.

这样做的好处是将无论调用哪个接口,都确保客户端收到的响应具有一至的结构.

错误码和错误消息也可以让客户端快速识别和处理错误.

在实际应⽤中,CommonResult<T>作为控制器层⽅法的返回类型,可以被Springboot框架等⾃动转换为JSON或其他格式的响应体,发送给客⼾端,这种⽅式提⾼了API的可⽤性和可维护性.

序列化工具

序列化工具本次使用的是Jackson,序列化工具的使用场景例如在日志打映的时候,将传入的JSON字符转为字符串.

我们自己实现的工具类中,也是参考了SpringBoot中源码的设计思路.

当我们调用parseMap时,就会调用tryparse来传入表示序列化的lamda表达式,而在t方法中,又调用了call方法,相当于执行那一段lamda表达式,此时又会捕捉异常,这样的做法可以防止每一次调用序列化时都需要捕捉异常,是一种很巧妙的做法.

日志处理

日志处理采用的是SLF4J+logback的处理机制,在本地测试时会将日志打映在控制台上,而在运行环境下又会分文件处理.

此处提供配置文件:

application文件配置和新增的logback.xml

复制代码
logging.config=classpath:logback-spring.xml
spring.profiles.active=dev

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <springProfile name="dev">
        <!--输出到控制台-->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n%ex</pattern>
            </encoder>
        </appender>
        <root level="info">
            <appender-ref ref="console" />
        </root>
    </springProfile>

    <springProfile name="prod,test">
        <!--ERROR级别的日志放在logErrorDir目录下,INFO级别的日志放在logInfoDir目录下-->
        <property name="logback.logErrorDir" value="/root/lottery-system/logs/error"/>
        <property name="logback.logInfoDir" value="/root/lottery-system/logs/info"/>
        <property name="logback.appName" value="lotterySystem"/>
        <contextName>${logback.appName}</contextName>

        <!--ERROR级别的日志配置如下-->
        <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
                如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
                的日志改名为今天的日期。即,<File> 的日志都是当天的。
            -->
            <File>${logback.logErrorDir}/error.log</File>
            <!-- 日志level过滤器,保证error.***.log中只记录ERROR级别的日志-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--文件路径,定义了日志的切分方式------把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
                <FileNamePattern>${logback.logErrorDir}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--只保留最近14天的日志-->
                <maxHistory>14</maxHistory>
                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
                <!--<totalSizeCap>1GB</totalSizeCap>-->
            </rollingPolicy>
            <!--日志输出编码格式化-->
            <encoder>
                <charset>UTF-8</charset>
                <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n%ex</pattern>
            </encoder>
        </appender>

        <!--INFO级别的日志配置如下-->
        <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
                如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
                的日志改名为今天的日期。即,<File> 的日志都是当天的。
            -->
            <File>${logback.logInfoDir}/info.log</File>
            <!--自定义过滤器,保证info.***.log中只打印INFO级别的日志, 填写全限定路径-->
            <filter class="com.example.lotterysystem.common.filter"/>
            <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--文件路径,定义了日志的切分方式------把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
                <FileNamePattern>${logback.logInfoDir}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--只保留最近14天的日志-->
                <maxHistory>14</maxHistory>
                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
                <!--<totalSizeCap>1GB</totalSizeCap>-->
            </rollingPolicy>
            <!--日志输出编码格式化-->
            <encoder>
                <charset>UTF-8</charset>
                <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n%ex</pattern>
            </encoder>
        </appender>
        <root level="info">
            <appender-ref ref="fileErrorLog" />
            <appender-ref ref="fileInfoLog"/>
        </root>
    </springProfile>
</configuration>
相关推荐
落魄实习生1 小时前
Agent Scope Java 2.x 系列【1】核心架构
java·开发语言·架构
鬼手点金2 小时前
Claude Code示范案例-常用快捷命令
java·服务器·前端·计算机视觉·前向传播
温暖小土2 小时前
Spring AI 接入通义千问向量模型
java·人工智能·spring
Sarvartha2 小时前
数组基础知识
java·开发语言
泡海椒2 小时前
金融报表开发:jquick-pdf K 线图 PDF 可视化解决方案
java·开发语言·金融·pdf
余槐i2 小时前
rollbackFor 不设置,Checked 异常不回滚:Spring 默认规则与两行修复的取舍
java·spring boot·后端·spring·事务管理
孙启超2 小时前
【AI开发之Rust】第 17 课:项目总览与核心架构 —— AI 助手 Rust 核心从 0 到 1
开发语言·后端·rust
❀͜͡傀儡师2 小时前
Spring AI 集成 TypeSafe:用判断模型处理工单分流与链路决策
java·人工智能·spring
bro_Java6662 小时前
《栈与队列:数据结构的“双生花”》
java·数据结构·编辑器