SpringBoot 项目——抽奖系统

本项目主要实现的功能是:主要服务于管理员用户,其可圈选奖品,人员来创建抽奖活动,并进行在线抽奖,并可通过短信或邮件的方式通知中奖者,同时普通用户可查看已结束的抽奖活动的中奖结果;

一、项目技术栈及环境

编程语言:后端:Java 17,前端 JavaScript

后端框架:SpringBoot 3

数据库:Mysql

持久层框架:Mybatis,Mybatis-Plus

缓存:Redis

消息队列:RabbitMQ

日志服务:Slf4j

安全校验:JWT

加密解密:hutool

开发工具:lombok

其他:邮箱服务,阿里云短信服务

项目依赖的 pom.xml 文件:点这里

二、项目模块分类

  • 用户模块:管理员注册,登录,普通用户的创建,普通用户中奖状态的管理
  • 奖品模块:奖品的创建,图片的上传,奖品的状态
  • 活动模块:对抽奖活动及活动状态管理
  • 抽奖模块:完成抽奖功能,并展示抽奖结果
  • 中奖通知模块:通过发送邮件和短信来进行中奖通知

三、数据库设计

  • 用户表:存储用户信息,如用户名、密码、邮箱等
  • 奖品表:存储奖品信息,如奖品名称,奖品图片,奖品价值等
  • 活动表:存储活动信息,如活动名称、描述、活动状态等
  • 活动奖品关联表:存储每个活动下关联的奖品,包括奖品数量,奖品等级
  • 活动人员关联表:存储参与每个活动的人员
  • 中奖记录表:存储每个活动的中奖信息名单

sql 文件 点这里 使用source 命令执行 .sql 文件(路径不要存在中文)

数据库表 ER 图

四、统一处理

  • 全局错误码:描述错误的状态,便于更精确的指出问题所在
  • 自定义异常类:更精确地捕获和处理特定类型的错误,提高代码的可读性
  • 统一结果返回:作为控制器层方法的返回类型,封装 HTTP 接口调用的结果,包括成功数据,错误信息和状态码
  • 全局异常捕获:集中管理、统一处理和记录异常信息,提高了系统的健壮性、可维护性
  • 拦截器:验证用户身份,在请求处理的早期阶段进行检查,防止未授权的访问

五、枚举类

  1. GlobalErrorCodeEnum:全局错误码枚举,是错误码中的 code 部分

  2. UserIdentityEnum:用户身份信息枚举,包括普通用户 NORMAL 和 管理员 ADMIN

  3. ActivityStatusEnum:活动状态枚举,包括进行中 RUNNING 和 已完成 COMPLETED

  4. ActivityUserStatusEnum:参与某个活动的用户状态枚举,包括初始化态 INIT 和 已完成 COMPLETED,以此来区分用户是否已经中奖,已经中奖的用户不能参与该活动下的后续抽奖

  5. ActivityPrizeStatusEnum:参与某个活动的奖品状态枚举,包括初始化态 INIT 和 已完成 COMPLETED,以此来区分奖品是否已经被抽取完,被抽取完的奖品不能再次抽取

  6. PrizeGradeEnum:奖品等级枚举,包括一等奖 FIRST_PRIZE、二等奖 SECOND_PRIZE 和 三等奖 THIRED_PRIZE

六、工具类

  1. JacksonUtil:包含静态的序列化和反序列化的方法,实现对象与 json 格式的转化
  2. JWTUtil:包含生成 token 和校验 token 的静态方法,实现用户身份认证
  3. RegexUtil:包含校验邮箱格式,手机号格式是否正确的静态方法,完成参数的校验
  4. MailUtil:封装发送邮件的方法
  5. SMSUtil:封装向手机号发送短信的方法
  6. CaptchaUtil:封装生成随机短信验证码的方法
  7. RedisUtil:封装操作 redis 的方法
  8. DateUtil:包含日期格式化的静态方法

七、业务实现

1. 用户模块

使用 hutool 将敏感字段如(手机号,密码)加密存储

1)注册功能

时序图:

前后端交互接口:

请求 

url: '/register',
type: 'POST',
contentType: 'application/json',
data: {"name": "xxx", "email": "xxx", "phoneNumber": "xxx", "password": "xxx", "identity": "admin"}

响应

"code": 200,
"data": { userId: 11 },
"msg": ""

后端处理

  1. 首先校验用户输入的注册信息,包括邮箱格式,手机号格式等
  2. 校验通过之后,构造 User 对象,并存储在数据库中,同时对用户密码和手机号进行加密
  3. 构造响应,返回 userId

2)发送验证码功能

时序图

前后端交互接口

请求

url: '/verifyCode/send',
type: 'POST',
data: {"phoneNumber": "xxx"}

响应

"code": 200,
"data": true,
"msg": ""

后端实现

首先校验手机号格式是否正确,若正确则通过 CaptchaUtil 类生成随机验证码,并使用 SMSUtil 类的方法发送验证码,并将验证码缓存在 Redis 中,最后构造响应并返回;

3)登录功能

时序图

1. 登录名(可为手机号或邮箱)+ 密码登录

前后端交互接口

请求

url: '/login/password',
type: 'POST',
contentType: 'application/json',
data: { "loginName": "xxx", "password": "xxx", "mandatoryIdentity": "admin" }

响应

"code": 200,
"data": { "token": "xxx", "userName": "xxx", "identity": "xxx" },
"msg": ""

后端处理

根据前端传来的登录名 loginName 判断其格式是邮箱格式还是手机号格式,并根据邮箱/ 手机号在数据库中查找相应的用户,通过 MD5 算法加密用户输入的密码,判断其是否和数据库中的密码是否一致,若一致,则登录成功,构造响应,设置 token 并返回;

2. 手机号 + 验证码登录

请求

url: '/login/verifyCode',
type: 'POST',
contentType: 'application/json',
data: { "phoneNumber": "xxx", "verifyCode": "xxx", "mandatoryIdentity": "admin" }

响应

"code": 200,
"data": { "token": "xxx", "userName": "xxx", "identity": "xxx" },
"msg": ""

后端处理

判断前端传递的手机号格式是否正确,若正确,则根据手机号在数据库中用户表中查询该用户,若查询成功,再判断用户输入的验证码是否正确,若正确则登录成功,构造响应,设置 token 并返回;

4)获取用户列表

时序图

前后端交互接口

请求

url: '/base-user/getUserInfoList',
type: 'GET',
data: {"identity": "admin/noraml/null 查询管理员用户/普通用户/全量用户"}

响应

"code": 200,
"data": [ { "userId": 1, "userName": "xxx", "identity": "admin/normal" },{ "userId": 2, "userName": "xxx", "identity": "admin/normal" },......],
"msg": "" 

后端实现

先根据 identity 确定查询哪部分用户信息,然后执行相应的数据库查询语句,查询出用户信息集合后,最后构造响应并返回

2. 奖品模块

1)创建奖品

时序图

前后端交互接口

请求

url: '/prize/create',
type: 'POST',
contentType: false, // 通过 formData 方式传递
data: {"prizeName": "xxx", "description": "xxx", "price": "xxx", file}

 响应

"code": 200,
"data": { "prizeId": 19 },
"msg": ""

后端处理

首先在数据库奖品表中查询相应奖品名是否已经存在,若存在则不能继续创建,否则构造一个奖品对象,设置前端传来的数据,调用图片上传的方法,保存图片,最后构造响应并返回;

2)查询奖品列表

时序图

前后端交互接口

请求

url: '/prize/getPrizeInfoList',
type: 'POST',
data: { "currentPage": 1, "pageSize": 10 }

响应 

"code": 200,
"data": { "total": 10, "records": [{ "prizeId": 19, "prizeName": "xxx", "description": "xxx", "price": 200,                                 "imageUrl": "xxx" }, { "prizeId": 20, "prizeName": "xxx", "description": "xxx", "price": 500,                                 "imageUrl": "xxx" },  {......}] },
"msg": ""

后端处理

首先查询数据库中的所有奖品的总数,然后根据前端传来的分页参数在数据库中查询相应的记录,构造为一个集合,最后构造响应并返回

3. 活动模块

1)创建活动

时序图

前后端交互接口

请求 

url: '/activity/create',
type: 'POST',
contentType: 'application/json',
data: 
{      "activityName": "xxx", "description": "xxx", "activityPrizeList": [    {"prizeId": 5, "prizeAmount": 2, "prizeTiers": "二等奖"}, {"prizeId": 6, "prizeAmount": 1, "prizeTiers": "一等奖"}, ],"activityUserList": [{"userId": 3, "userName": "xxx"},{"userId": 4, "userName": "xxx"},{"userId": 5, "userName": "xxx"},]
}

响应

"code": 200,
"data": { "activityId": 10 },
"msg": ""

后端处理

首先校验活动信息:包括活动名是否已经存在,活动关联的人员 id 是否在人员表中存在,活动关联的奖品 id 是否在奖品表中存在,并且参加活动的人员数量必须大于等于奖品数量,奖品等级命名是否符合规范等,活动信息通过校验之后,将活动信息保存到数据库中的活动表,活动人员管理表,活动奖品关联表,并将以上信息整合为一个对象,并保存在 redis 中,最后构造响应并返回

2)获取活动列表

时序图

前后端交互接口

请求 

url: '/activity/getActivityList',
type: 'POST',
data: { "currentPage": 1, "pageSize": 10 }

响应

"code": 200,
"data": { "total": 10, "records": [{"activityId": 19, "activityName": "xxx", "description": "xxx", "valid": true},                           {"activityId": 20, "activityName": "xxx", "description": "xxx", "valid": true},                             {......}] },
"msg": ""

后端处理

首先查询数据库活动表中的活动总量,然后根据前端传递的分页参数,查询活动表的相应记录,最后构造响应并返回

3)获取活动详情

时序图

前后端交互接口

请求

url: '/activity/getActivityDetail',
type: 'GET',
data: { "activityId": 10 }

响应

"code": 200,
"data": { "activityId": 10, "activityName": "xxx","description": "xxx", "valid": true,"prizes": [{ "prizeId": 19, "name": "xxx", "description": "xxx", "price": 200,                                 "imageUrl": "xxx", "prizeAmount": 2, "prizeTierName": "xxx", "valid": true}, { "prizeId": 20, "name": "xxx", "description": "xxx", "price": 500,                                 "imageUrl": "xxx", "prizeAmount": 1, "prizeTierName": "xxx", "valid": true},{......}] "users": [{"userId": 10, "userName": "xxx", "valid": true},{"userId": 11, "userName": "xxx", "valid": true},{"userId": 12, "userName": "xxx", "valid": true},{......}]},
"msg": ""

 后端实现

先根据活动 id 在缓存中查询对应的活动,若查到了,则直接返回,否则在数据库中查询,若没有查询到,则说明活动不存在,否则将对应活动信息缓存到 redis 中,最后构造响应并返回

4. 抽奖模块

1)抽奖功能

抽奖是按照奖品来抽取的,每次抽取一种奖品(按照三等奖,二等奖,一等奖的顺序进行),例如:若该奖品有 n 个,则会抽取 n 个用户,该 n 个用户中奖,中奖用户不能再次参与后续抽奖,同时已抽取的奖品不能再次抽取;

抽奖流程

时序图

前后端交互接口

请求

url: '/lottery/prize',
type: 'POST',
contentType: 'application/json',
data: { "activityId": 10, "prizeId": 16, "winningTime": Date, "winnerList": [{ "userId": 8, "userName": "xxx" },{......} ] }

响应

"code": 200,
"data": true,
"msg": ""

后端处理

为了使抽奖过程高效,因此采用异步抽奖来进行,有前端生成抽奖用户,并发送请求到后端,后端收到请求后,往消息队列(RabbitMQ)中发送该抽奖请求,之后直接返回成功,有消息队列的消费者来消费该消息,并完成后端的工作,消费者在消费消息的过程中,如果发生了异常,则会进行消息消费重试 5 次,若重试 5 次都失败了,则会进入到死信队列,等待 bug 处理完之后,再处理该消息,确保消息不会丢失;

消费者处理抽奖请求的具体流程为:

  1. 校验该抽奖请求是否有效,比如校验活动状态是否为正在抽奖中,此次抽取的奖品是否已经抽取过,此次抽取的奖品数量是否等于传来的中奖用户数量等
  2. 进行相应状态的扭转(由于是对多个表进行修改操作,因此要确保以事务的方式执行)(此处使用责任链模式(由于在业务上判断活动是否已完成,需要奖品已完成(存在执行顺序),涉及活动及奖品等多横向维度扭转,不能避免未来不会有其他内容牵扯进活动中)+ 策略模式(各个状态的扭转存在相似性,但又有其各自的条件,使用策略模式让每个状态的扭转都有其各自的策略)):
    1. 判断该奖品的状态是否为 INIT 初始化状态,若为 INIT 状态则该奖品抽取完后,应将该奖品的状态改变为 COPMLETED 已完成状态
    2. 判断该用户的状态是否为 INIT 初始化状态,若为 INIT 状态该用户中奖后,应将该用户的状态改变为 COMPLETED 已完成状态
    3. 判断该活动是否处于 RUNNING 正在进行状态,若为正在进行状态,并且该活动下的奖品已经抽取完毕,则需将该活动的状态修改为 COMPLETED 已完成状态
  3. 保存中奖名单到数据库的中奖记录表和缓存中,将活动信息,奖品信息和中奖人员信息进行整合并存储,在存放在缓存中时,分别已奖品为维度,已活动为维度存储
  4. 通知中奖者(见中奖通知模块)

如果处理上述流程时,发生异常需要进行事务的回滚,即使该次抽奖请求的奖品,活动及相应中奖人员的状态回滚 为 INIT 状态,并且删除相应数据库中奖人员记录表中的数据,并删除对应缓存;

2)查看中奖名单 

时序图

前后端交互接口

请求

url: '/lottery/getWinningRecordList',
type: 'POST',
contentType: 'application/json',
data: { "activityId": 10, "prizeId": 16 }

响应

"code": 200,
"data": [{ "winnerId": 19, "winnerName": "xxx", "prizeName": "xxx", "prizeTier": "xxx",                                 "winningTime": Date }, { "winnerId": 20, "winnerName": "xxx", "prizeName": "xxx", "prizeTier": "xxx",                                 "winningTime": Date },  {......}],
"msg": ""

后端处理

首先根据前端传递的参数判断,活动 id 为空,则直接返回空,否则判断若奖品 id 不为空,则需查询活动下对应奖品的中奖信息,否则需查询活动下所有奖品的中奖信息,查询时,首先在缓存中查询,若查询成功,则直接返回,否则在数据库中查询,如果数据库中也不存在,则返回空,否则写入缓存中,再构造返回

5. 中奖通知模块

该模块直接被后端调用,不涉及前后端交互,使用两个线程并发执行,分别完成短信通知和邮件通知

1)短信通知

后端接口:SMSUtil 类

使用阿里云短信服务进行短信的发送,向所有中奖用户的手机号发送中奖短信模版

2)邮件通知

后端接口:MailUtil 类

使用 QQ 邮箱服务进行邮件的发送,向所有中奖用户的邮件发送中奖邮件模版

七、项目扩展

1. 增加删除奖品和批量删除奖品功能

a. 删除奖品

逻辑:根据奖品 id 删除奖品,首先判断 prizeId 是否为空,不为空时,判断是否有正在进行的活动使用到该奖品,即该奖品还要参与后续抽奖,如果没有进行中的活动使用时,才能删除

前后端交互接口

请求

url: '/prize/deletePrize',
type: 'POST',
data: { "prizeId": 16 }

响应

"code": 200,
"data": true,
"msg": ""

后端代码

    @Overridepublic boolean deletePrizeById(Long prizeId) {if(prizeId == null){throw new GlobalException(GlobalErrorCodeEnum.PRIZE_DELETE_ERROR, "奖品id为空");}List<ActivityPrize> activityPrizeList = activityPrizeMapper.selectList(new QueryWrapper<ActivityPrize>().eq("prize_id", prizeId).eq("status", "INIT"));if(!CollectionUtils.isEmpty(activityPrizeList)){throw new GlobalException(GlobalErrorCodeEnum.PRIZE_DELETE_ERROR, "要删除的奖品在抽奖活动中未抽奖, 请抽奖之后在执行此操作");}int result = prizeMapper.deleteById(prizeId);return result == 1;}

 b. 批量删除奖品 

前后端交互接口

请求

url: '/prize/batchDeletePrizes',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: [16, 17, 18, ...]

 响应

"code": 200,
"data": true,
"msg": ""

后端代码

    @Overridepublic boolean batchDeletePrizesByIds(List<Long> prizeIds) {if(CollectionUtils.isEmpty(prizeIds)){throw new GlobalException(GlobalErrorCodeEnum.PRIZE_DELETE_ERROR);}List<ActivityPrize> activityPrizeList = activityPrizeMapper.selectList(new QueryWrapper<ActivityPrize>().eq("status", "INIT").in("prize_id", prizeIds));if(!CollectionUtils.isEmpty(activityPrizeList)){throw new GlobalException(GlobalErrorCodeEnum.PRIZE_DELETE_ERROR, "要删除的奖品在抽奖活动中未抽奖, 请抽奖之后在执行此操作");}int result = prizeMapper.deleteBatchIds(prizeIds);return result > 0;}

2. 增加查看用户历史中奖信息功能 

逻辑:根据该普通用户的用户 Id 在中奖记录表中查看该用户的历史中奖记录,若没有查到,则说明该用户还没有中奖过,否则返回该用户的所有历史中奖信息

前后端交互接口

请求

url: `/user-history/getUserActivities?userId=10`,
type: 'GET',

 响应

"code": 200,
"data": { [{"userName": "xxx", "activityName": "xxx", "winnerName": "xxx", "prizeName": true, "prizeTier": "一等奖"},                           {"userName": "xxx", "activityName": "xxx", "winnerName": "xxx", "prizeName": true, "prizeTier": "二等奖"},   ] },
"msg": ""

后端代码

    @Overridepublic List<UserHistoryActivityResult> getUserActivities(Long userId) {if(userId == null){throw new GlobalException(GlobalErrorCodeEnum.USER_HISTORY_INFO, "userId 为空");}List<WinningRecord> winningRecords =winningRecordMapper.selectList(new QueryWrapper<WinningRecord>().eq("winner_id", userId));if(CollectionUtils.isEmpty(winningRecords)){throw new GlobalException(GlobalErrorCodeEnum.USER_HISTORY_INFO, "该用户还没有中过奖");}List<UserHistoryActivityResult> list = winningRecords.stream().map(winningRecord -> {UserHistoryActivityResult result = new UserHistoryActivityResult();result.setUserName(winningRecord.getWinnerName());result.setActivityName(winningRecord.getActivityName());result.setWinnerTime(DateUtils.dateFormat(winningRecord.getWinningTime()));result.setPrizeName(winningRecord.getPrizeName());result.setPrizeTier(PrizeGradeEnum.forName(winningRecord.getPrizeTier()).getValue());return result;}).toList();return list;}

八、项目部分页面展示

九、项目原码 gitee

lottery_system: 抽奖系统

十、项目链接

管理员登录页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/52842.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TESSY创建单元测试或集成测试工程

我们以tessy5.1 IDE为例&#xff0c;给大家展示工程的创建过程。 1、打开TESSY5.1软件后&#xff0c;会弹出&#xff1a; 2、点击NEW Project后&#xff0c;会弹出&#xff1a; 3、接下来&#xff0c;就可以打开刚创建的工程&#xff1a; 4、进入到TESSY的主界面后&#xff0c…

Python办公自动化 获取文本数据 支持多种类型文件

学好办公自动化,走遍天下都不怕&#xff01;&#xff01; 前面我们已经学习了&#xff0c;如何用python的下载安装以及入门基础知识&#xff0c;并且也知道如何使用python自动处理Excel文件数据、如何批量生成Word文件、如何对数据分析后生成洞察报告、如何用python实现自动发送…

React中实现antd自定义图标,鼠标悬浮变色

借助 antd 的 tooltip 组件来实现 hover 时变色的效果 1.新建组件 自定义图标一般在iconfont上面获取&#xff0c;复制下来的svg代码&#xff0c;切记要删除 fill 属性后添加到组件中 import { Tooltip } from "antd"; import React from "react";const …

webpack4手动搭建Vue项目

小满视频 很多解释使用通义灵码搜的,通义灵码的搜索结果也是有错误的全程使用pnpm包管理工具&#xff0c;和npm的用法基本一样 学习总结 1. 多看看webpack官网 2. webpack的作用&#xff1a;配置一堆东西&#xff0c;达到运行程序的目的 3. 无论什么东西都转成js&#xff0c;…

理解数据库系统的内部结构

数据库系统在我们的数字世界中扮演着关键角色。本文将介绍数据库系统的内部结构&#xff0c;帮助初学者了解其基本概念。 数据库系统的三级模式 数据库系统内部采用三级模式二级映像结构&#xff0c;包括外模式、模式和内模式。这种结构确保了数据的逻辑独立性和物理独立性。…

如何在Android项目中进行性能优化分析?

引言 在开发过程中&#xff0c;性能优化是一个重要的话题。用户对于应用的性能有着非常高的期望&#xff1a;快速启动、流畅的操作体验、低内存消耗等。那么&#xff0c;作为开发者&#xff0c;我们如何才能在项目中进行性能优化分析呢&#xff1f;今天就来和大家聊聊这个话题…

Linux常见基础命令

Linux基础 初级学习阶段需要了解的知识一、Linux基础命令查阅命令帮助信息1.man2.help Linux命令的基本实用目录操作文件内容操作查看某文件下的用户操作日志压缩和解压缩sudo用户权限操作用户权限操作TOP文件安装 上一篇 VMware安装linux环境 初级学习阶段需要了解的知识 1.…

什么是 AWS CloudWatch?

AWS CloudWatch 是 AWS 提供的一项全面的监控和可观测性服务&#xff0c;使用户能够收集和可视化指标、日志和事件&#xff1b;设置警报&#xff1b;并根据预定义的条件自动执行操作。CloudWatch 提供对 AWS 资源和应用程序的运行状况、性能和运行状态的深入了解&#xff0c;使…

autoware整体架构的分析

autoware framework sensinglidar driver&#xff08;lidar驱动&#xff09;PointCloud Preprocessing&#xff08;点云预处理&#xff09;Detection&#xff08;检测&#xff09;GNSS (全球导航卫星系统)IMU (惯性测量单元) Localization&#xff08;定位&#xff09;Pose Ini…

#Datawhale X 李宏毅苹果书 AI夏令营#1.2了解线性模型

1.2线性模型 什么是线性模型&#xff1f; 初始模型&#xff1a;, 其中y表示观看人数&#xff0c;x1表示前一天的观看人数&#xff0c;这个模型就是在用前一天的观看人数来预测当前的观看人数。 模型改进&#xff1a; 然而真实的数据是有周期性的&#xff0c;每隔7天&#…

这本大模型书一定要读!李开复、周鸿祎都强烈推荐的《实战AI大模型》(附PDF)

《实战AI大模型》是一本旨在填补人工智能&#xff08;AI&#xff09;领域&#xff08;特别是AI大模型&#xff09;理论与实践之间鸿沟的实用手册。书中介绍了AI大模型的基础知识和关键技术&#xff0c;如Transformer、BERT、ALBERT、T5、GPT系列、InstructGPT、ChatGPT、GPT 4、…

无人机之云台的作用

无人机云台在无人机技术中扮演着至关重要的角色&#xff0c;其作用主要体现在以下几个方面&#xff1a; 一、 确保拍摄稳定性 防抖动&#xff1a;无人机在飞行过程中&#xff0c;尤其是在复杂环境下&#xff0c;如遇到风力干扰或进行高速飞行时&#xff0c;机身容易产生震动和…

Prometheus+Grafana的安装和入门

概念 什么是Prometheus? Prometheus受启发于Google的Brogmon监控系统&#xff08;相似kubernetes是从Brog系统演变而来&#xff09;&#xff0c; 从2012年开始由google工程师Soundclouds使用Go语言开发的开源监控报警系统和时序列数据库(TSDB)。&#xff0c;并且与2015年早起…

【Linux —— POSIX信号量 - 基于环形队列的生产消费模型】

Linux —— POSIX信号量 - 基于环形队列的生产消费模型 POSIX信号量信号量的概念POSIX信号量的类型信号量的操作 POSIX信号量函数基于环形队列的生产消费模型设计思路同步和安全性代码 POSIX信号量 信号量的概念 POSIX信号量是一种用于进程和线程之间同步的机制&#xff0c;主…

【netty系列-08】深入Netty组件底层原理和基本实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

数据结构(邓俊辉)学习笔记】串 16——Karp-Rabin算法:串即是数

文章目录 1. 化串为数2. 凡物皆数3. 亦是数 1. 化串为数 接下来的这节&#xff0c;我们再来讨论一种十分另类的串匹配算法&#xff0c;也就是所谓的 Karp-Rabin 算法。回顾此前所介绍的几种串匹配算法&#xff0c;我们所面临的难题是一样的。也就是说在这里&#xff0c;我们每次…

Windows 10/11降级漏洞的工具包现已发布 仅供安全测试

早前有研究人员在分析 Windows 10/11 更新机制时发现微软虽然已经考虑到潜在的安全问题增加了各种限制&#xff0c;但还是存在失误因此存在弱点&#xff0c;研究人员则通过该弱点成功降级了系统。通过该漏洞不仅可以成功降级系统&#xff0c;同时系统还会认为自己已经完成更新并…

Java面试题·区别题·JavaSE部分

系列文章目录 总章 Java区别题 文章目录 系列文章目录前言private/默认/protected/public权限修饰符的区别&和&&区别和联系&#xff0c;I和II区别和联系if和switch的不同之处和equals的区别和联系数组做形参和可变参数做形参联系和区别接口和抽象类的异同之处面向…

嵌入式day36

数据库 专业存储数据、大量数据 数组、链表、变量---->内存&#xff1a;程序运行结束、掉电数据丢失 文件---->硬盘&#xff1a;程序运行结束、掉电数据不丢失 数据库---->硬盘 数据库文件与普通文件区别&#xff1a; 1.普通文件对数据管理&#xff08;增删改查…

Linux入门攻坚——30、sudo、vsftpd

su&#xff1a;Switch User&#xff0c;即切换用户 su [-l user] -c ‘COMMAND’ 如&#xff1a;su -l root -c ‘COMMAND’ 如果没有指定-l user&#xff0c;则默认是root sudo&#xff1a;可以让某个用户不需要拥有管理员的密码&#xff0c;而可以执行管理员的权限。 需…