首页 > 编程知识 正文

简单的抽奖系统,自定义抽奖系统

时间:2023-05-03 14:37:37 阅读:221401 作者:4970

在线抽奖系统: 1、项目介绍:(1)功能介绍:(2)开发环境与技术栈:(3)项目演示: 2、项目准备:(1)代码框架:(源码)(2)数据库设计: 3、后端对前端接口的实现:(1)用户的登录、注册、注销(2)查询奖项设置、修改抽奖人数:(3)新增、修改、删除奖项:(4)新增、修改、删除抽奖人员:(5)抽奖、删除获奖人员: 4、代码设计:

1、项目介绍: (1)功能介绍:

主要业务:为公司活动(如年会等)提供在线抽奖功能,满足奖品、抽奖人员的管理,及抽奖活动的需要。

用户注册用户登录、会话管理抽奖设置:奖品管理,抽奖人员管理人员抽奖 (2)开发环境与技术栈: windowsMavenLombokSpring、SpringMVC、SpringBootMySQL、Mybatis、Druid (3)项目演示: 用户登录:

用户注册:

奖项设置:

抽奖人员设置:

抽奖:

2、项目准备: (1)代码框架:(源码)

全部代码源码 Github链接:

https://github.com/JACK-QBS/Project

(2)数据库设计:

数据库表关系图:

(业务上一对一)

为什么要有设置表?

1对1关联的表,其实可以只使用1张表保存所有字段;但是在一些可能出现的业务扩展,方便系统扩展使用,所以设计表时,考虑1对1设计。拓展业务 ,一个用户进来设置,当多个用户进来设置的时候(多个用户属于同一公司),如果是只有用户表的话,及无法支撑业务,用户表关联公司,设置表在关联公司 3、后端对前端接口的实现:

要实现功能,需要先明确前后端约定好的接口。需要说明的是,接口的定义一般是前后端约定好的,所以也和前端代码息息相关,前端需要什么数据,需要什么格式的数据,也会在接口中体现。

接口主要体现在:

请求需要的信息:请求方法,请求路径,请求数据响应数据 (1)用户的登录、注册、注销 用户登录:

前端请求:

POST api/user/login (请求路径)
Content-Type: application/json
{username: “qbs”, password: “123”}

响应:

{ “success” : true }

后端实现接口:

用户注册:

前端请求:

POST api/user/register (请求路径)
Content-Type: multipart/form-data; boundary=----
WebKitFormBoundarypOUwkGIMUyL0aOZT

username: qbs
password: 123
nickname: 帅哥
email: 666@163.com
age: 18
headFile: (binary)

响应:

{ “success” : true }

后端实现:

用户注销:

前端请求:

POST api/user/login (请求路径)

后端实现:

(2)查询奖项设置、修改抽奖人数: 查询奖项设置:


前端请求:

GET api/setting/query(请求路径)

后端实现:

修改抽奖人数:

前端请求:

GET api/setting/update?batchNumber=5(请求路径)
(接口对应抽奖设置页面中,点每次抽奖人数下拉菜单切换时修改)

后端实现:

(3)新增、修改、删除奖项:

新增奖项:

前端请求:

POST api/award/add (请求路径)
Content-Type: application/json

{name: “特等奖”, count: 1, award: “全球旅行7日游”}

后端实现:

修改奖项:

前端请求:

POST api/award/update (请求路径)
Content-Type: application/json

后端响应:

删除奖项:

前端请求:

GET api/award/delete/4(请求路径)

最后的数字4,对应奖项的id

后端实现:

(4)新增、修改、删除抽奖人员:

新增抽奖人员:

前端请求:

POST api/member/add (请求路径)
Content-Type: application/json

后端实现:

修改抽奖人员

前端请求:

POST api/member/update
Content-Type: application/json

后端实现:

删除抽奖人员

前端请求:

GET api/member/delete/97
(最后的数字为抽奖人员的id)

后端实现:

(5)抽奖、删除获奖人员: 抽奖:


前端请求:

POST api/record/add/3
Content-Type: application/json
(以上路径中最后的数字代表奖项id,请求数据为抽奖人员id组成的数组)

后端实现:

抽奖后端只是插入记录(人员id、奖项id),具体的抽奖是前端实现的,而且也是简单的实现方式,没有任何算法。(只是在当前奖项剩余名额中,每次抽奖人数,在所有未中奖的人员列表中,随机抽取)

删除获奖人员:


前端请求:

GET api/record/delete/member?id=22
(根据 人员id 删除对应的获奖记录)

GET api/record/delete/award?id=3
(根据 奖项id 删除对应所有获奖人员记录)

后端实现:

4、代码设计:

(1)设计数据库的实体类:

通过 mybatis 生成工具(tool包):生成 mapper、数据库表实体类(model包)、xml 文件

(2)设计统一响应类:

主要是为了返回数据的统一字段设计

/** * 统一响应的数据格式 */public class JSONResponse { private boolean success; private String code; private String message; private Object data;} /** * 统一数据封装 */public class RequestResponseBodyMethodProcessorWrapper implements HandlerMethodReturnValueHandler { private final HandlerMethodReturnValueHandler delegate; public RequestResponseBodyMethodProcessorWrapper(HandlerMethodReturnValueHandler delegate) { this.delegate = delegate; } @Override public boolean supportsReturnType(MethodParameter returnType) { return delegate.supportsReturnType(returnType); } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { //returnValue是Controller请求方法执行完,返回值 if(!(returnValue instanceof JSONResponse)){//返回值本身就是需要的类型,不进行处理 JSONResponse json = new JSONResponse(); json.setSuccess(true); json.setData(returnValue); returnValue = json; } delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }}

(3)设计自定义异常:

主要针对不同的场景,需要抛异常来处理时,能定位业务含义。

主要分为:

1、 客户端请求错误时的异常:需要给定错误码,方便前端提示用户,如用户名存在不允许注册2、 业务发生错误时的异常:需要给定错误码,方便后端定位问题,一般如程序上的业务错误都可以抛(BUG)3、 系统发生错误时的异常:需要给定错误码,方便后端定位问题,程序出错,如数据库连接获取失败都可以抛(一般是系统发生错误,如网络断了,数据库挂了等等)自定义异常前端需要显示错误码和错误消息,用户可以根据提示信息判断原因。4、非自定义异常,异常信息一般是框架或JDK抛出的英文,是给开发人员描述错误的,无法给用户提示,所以错误信息提示为未知异常。 /** * 自定义异常:保存错误码和错误消息 */@Getter@Setterpublic 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; }} //统一异常处理@ControllerAdvice@Slf4j//使用lombok日志日志注解,之后使用log属性来完成日志打印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; }}

(4)设计统一会话管理的拦截器

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; }}

(5)设计Mybatis中Mapper的基类:

使用Mybatis的接口方法,所有接口方法都是类似,只是传入参数和返回值不同,可以考虑设计统一的基类,以泛型的方式定义出不同的参数类型、返回类型

/** * 所有 mapper 父接口 */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);}
margin

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。