在線抽獎系統:
- 1、專案介紹:
- (1)功能介紹:
- (2)開發環境與技術堆疊:
- (3)專案演示:
- 2、專案準備:
- (1)代碼框架:(原始碼)
- (2)資料庫設計:
- 3、后端對前端介面的實作:
- (1)用戶的登錄、注冊、注銷
- (2)查詢獎項設定、修改抽獎人數:
- (3)新增、修改、洗掉獎項:
- (4)新增、修改、洗掉抽獎人員:
- (5)抽獎、洗掉獲獎人員:
- 4、代碼設計:
1、專案介紹:
(1)功能介紹:
主要業務:為公司活動(如年會等)提供在線抽獎功能,滿足獎品、抽獎人員的管理,及抽獎活動的需要,
- 用戶注冊
- 用戶登錄、會話管理
- 抽獎設定:獎品管理,抽獎人員管理
- 人員抽獎
(2)開發環境與技術堆疊:
- windows
- Maven
- Lombok
- Spring、SpringMVC、SpringBoot
- MySQL、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
@Setter
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;
}
}
//統一例外處理
@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);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286221.html
標籤:其他
上一篇:6步教你封殺惡意登錄服務器的ip
