論壇系統(中-1)

軟件開發

編寫公共代碼


定義狀態碼

對執?業務處理邏輯過程中可能出現的成功與失敗狀態做針對性描述(根據需求分析階段可以遇見的問題提前做出定義),?枚舉定義狀態碼,先定義?部分,業務中遇到新的問題再添加
定義狀態碼如下

狀態碼類型描述
0SUCCESS操作成功
1000FAILED操作失敗
1001FAILED_UNAUTHORIZED未授權
1002FAILED_PARAMS_VALIDATE參數校驗失敗
1003FAILED_FORBIDDEN禁?訪問
1004FAILED_CREATE新增失敗
1005FAILED_NOT_EXISTS資源不存在
1101FAILED_USER_EXISTS??已存在
1102FAILED_USER_NOT_EXISTS??不存在
1103FAILED_LOGIN??名或密碼錯誤
1104FAILED_USER_BANNED您已被禁?, 請聯系管理員, 并重新登錄.
1105FAILED_TWO_PWD_NOT_S AME兩次輸?的密碼不?致
2000ERROR_SERVICES兩次輸?的密碼不?致
2001ERROR_IS_NULLIS NULL.

? 在 org.xiaobai.forum.common 包下創建枚舉類型命名為ResultCode

這里有個很好用的東西,alt+鼠標左鍵,然后往下移動,就可以列編輯

具體的代碼

package org.xiaobai.forum.common;public enum ResultCode {SUCCESS(0, "操作成功"),FAILED(1000, "操作失敗"),FAILED_UNAUTHORIZED(1001, "未授權"),FAILED_PARAMS_VALIDATE(1002, "參數校驗失敗"),FAILED_FORBIDDEN(1003, "禁止訪問"),FAILED_CREATE(1004, "新增失敗"),FAILED_NOT_EXISTS(1005, "資源不存在"),FAILED_USER_EXISTS(1101, "用戶已存在"),FAILED_USER_NOT_EXISTS(1102, "用戶不存在"),FAILED_LOGIN(1103, "??名或密碼錯誤"),FAILED_USER_BANNED(1104, "您已被禁?, 請聯系管理員, 并重新登錄."),FAILED_TWO_PWD_NOT_SAME(1105, "兩次輸?的密碼不?致"),ERROR_SERVICES(2000, "兩次輸?的密碼不?致");int code;String message;// 構造方法ResultCode(int code, String message) {this.code = code;this.message = message;}@Overridepublic String toString() {return "code = " + code + ", message = " + message + ". ";}// get 和 set 方法public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}

定義返回結果

系統實現前后端分離,統?返回JSON格式的字符串,需要定義?個類,其中包含狀態碼,描述信息,返回的結果數據


? 在org.xiaobai.forum.forum.common包下創建AppResult類

具體代碼, 此時還沒規定傳過來的數據是json

package org.xiaobai.forum.common;public class AppResult<T> {int code;String message;T data;// 提供構造方法public AppResult(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}public AppResult(int code, String message) {this(code,message,null);}//對外提供靜態方法方便調用//    成功public static AppResult success(){// 返回一個AppResult 對象return new AppResult(ResultCode.SUCCESS.getCode(),ResultCode.FAILED.getMessage());}// 成功自定義信息, 注冊成功, 登錄成功public static AppResult success(String message){return new AppResult(ResultCode.SUCCESS.getCode(),message);}// 成功自定義數據public static <T> AppResult<T> success(T data){return new AppResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(),data);}// 成功自定義信息和數據public static <T> AppResult<T> success(String message,T data){return new AppResult<>(ResultCode.SUCCESS.getCode(), message,data);}// 失敗// 失敗直接返回寫死的的錯誤碼和信息public static AppResult failed(){return new AppResult(ResultCode.FAILED.getCode() ,ResultCode.FAILED.getMessage());}// 失敗返回寫死的錯誤碼和自定義信息public static AppResult failed(String message){return new AppResult(ResultCode.FAILED.getCode(),message);}// 失敗返回ResultCodepublic static AppResult failed(ResultCode resultCode){return new AppResult(resultCode.getCode(),resultCode.getMessage());}// 生成get和set方法public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

注意:

java 枚舉本質上是一個類的實例,因此可以像普通對象一樣調用其方法和訪問屬性。

自定義異常?

創建?個異常類,加?狀態碼與狀態描述屬性

在org.xiaobai..forum.exception包下創建ApplicationException

主要的功能

自定義異常的代碼?

package org.xiaobai.forum.exception;import org.xiaobai.forum.common.AppResult;
// 自定義異常
public class ApplicationException extends RuntimeException{// 自定義錯誤// 在異常中持有一個錯誤信息對象protected AppResult errorResult;public AppResult getErrorResult() {return errorResult;}
//     構造方法// 自己寫的public ApplicationException (AppResult errorResult){// 傳給父類// 打印ApplicationException, 而不是Runtime...super(errorResult.getMessage());this.errorResult = errorResult;}// 父類是RuntimeException, 我們重寫它的方法public ApplicationException(String message) {super(message);}public ApplicationException(String message, Throwable cause) {super(message, cause);}public ApplicationException(Throwable cause) {super(cause);}
}

全局處理異常

使?@ControllerAdvice(類)?+ @ExceptionHandler(方法) 注解實現統?異常處理
補充:?@ControllerAdvice 表?控制器通知類, @ExceptionHandler 是異常處理器,兩個結合表?當出現異常的時候執?某個通知,也就是執?某個?法事件

在org.xiaobai.forum.exception包下創建GlobalExceptionHandler

接?返回為數據時, 需要加 @ResponseBody 注解

?具體代碼

package org.xiaobai.forum.exception;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;// 全局異常處理
@Slf4j
@ControllerAdvice // 控制器通知類
public class GlobalExceptionHandler {//處理異常@ResponseBody@ExceptionHandler(ApplicationException.class)// 指定要處理的是哪個異常,異常處理器public AppResult applicationExceptionHandler(ApplicationException e) {// TODO 自定義的異常// 打印異常信息e.printStackTrace();// 生產的時候要刪除// 打印日志log.error(e.getMessage());// 如果不為空就返回AppResultif (e.getErrorResult() != null) {return e.getErrorResult();}// e.getMessage()非空校驗, 最保底的一個返回if (e.getMessage() == null || e.getMessage().equals("")) {// ERROR_SERVICES              (2000, "服務器內部錯誤"),return AppResult.failed(ResultCode.ERROR_SERVICES);}// 為空就返回具體的異常信息return AppResult.failed(e.getMessage());}// TODO 兜底的異常@ResponseBody@ExceptionHandler(Exception.class)//指定處理的是哪個異常public AppResult exceptionHandler(Exception e) {// 打印異常信息e.printStackTrace();// 打印日志log.error((e.getMessage()));// e.getMessage()非空校驗, 最保底的一個返回if (e.getMessage() == null || e.getMessage().equals("")) {// ERROR_SERVICES              (2000, "服務器內部錯誤"),return AppResult.failed(ResultCode.ERROR_SERVICES);}// 返回異常信息return AppResult.failed(e.getMessage());}
}

測試異常處理

代碼

package org.xiaobai.forum.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.exception.ApplicationException;//返回的是數據不是url
@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping("/exception")public AppResult testException () throws Exception {throw  new Exception("這是一個Exception");}@RequestMapping("appException")public String testApplicationException(){throw new ApplicationException("這是一個自定義的ApplicationException");}
}

結果

?登錄攔截器(現在先不實現)

實現API 自動生成

單個進行輸入網址測試接口不利于管理, 十分的零散

使?Springfox Swagger?成API,并導?Postman,完成API單元測試

Swagger 簡介:Swagger是?套API定義的規范,按照這套規范的要求去定義接?及接?相關信息,再通過可以解析這套規范?具,就可以?成各種格式的接??檔,以及在線接?調試??,通過?動?檔的?式,解決了接??檔更新不及時的問題

Springfox 簡介:是對Swagger規范解析并?成?檔的?個實現

Springfox ?檔:Springfox Reference Documentation

?但是因為Springfox 很久每更新了,不支持spring boot 3.x 因此我們就使用另一個: Springdoc OpenAPI

Springfox Reference Documentation

swagger: 可以自動生成接口文檔的東西,其他倆個是基于它來實現的

  • Swagger?是一個廣泛的 API 文檔規范和工具集,核心是?OpenAPI 規范
  • Springfox Swagger?是 Swagger 的一個實現,專為 Spring 項目生成 Swagger 2 規范的 API 文檔。
  • Springdoc OpenAPI?是 Spring 的現代化庫,專注于生成符合 OpenAPI 3.0 規范的 API 文檔,逐漸取代了 Springfox。

使用:?Springdoc OpenAPI 具體看這個博客:?https://blog.csdn.net/2201_75880772/article/details/147875485?sharetype=blogdetail&sharerId=147875485&sharerefer=PC&sharesource=2201_75880772&sharefrom=mp_from_link

1> pom.xml來引入相關依賴

注意Spring Boot 版本不能太高我此時的版本是3.2.2

springdoc依賴

        <!-- 加入 springdoc 依賴 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.5.0</version></dependency><repositories><!--阿里云鏡像--><repository><id>alimaven</id><name>aliyun maven</name><url>https://maven.aliyun.com/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository>
</repositories>

2> 配置yml

server:
? port: 8080 (你項目的端口號)

springdoc:
? api-docs:
? ? enabled: true # 開啟OpenApi接口
? ? path: /v3/api-docs ?# 自定義路徑,默認為 "/v3/api-docs"
? swagger-ui:
? ? enabled: true # 開啟swagger界面,依賴OpenApi,需要OpenApi同時開啟
? ? path: /swagger-ui/index.html # 自定義路徑,默認為"/swagger-ui/index.html"

3> 配置swagger
在config 包下編寫SwaggerConfig

package org.xiaobai.forum.config;import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author HHHY* @ClassName* @Date: 2024/4/2 14:21* @Description: Swagger 配置*/@Configuration
public class SwaggerConfig {@Beanpublic OpenAPI springShopOpenAPI() {return new OpenAPI().info(new Info().title("Spring Boot 中使用 Swagger UI 構建 RESTful API").contact(new Contact()).description("百草中醫藥信息管理平臺提供的 RESTful API").version("v1.0.0").license(new License().name("Apache 2.0").url("http://springdoc.org"))).externalDocs(new ExternalDocumentation().description("外部文檔").url("https://springshop.wiki.github.org/docs"));}
}

4> 訪問:?http://localhost:8080/swagger-ui/swagger-ui/index.html#/?

就有以下成功頁面顯示

如果覺得不行可以看看這個佬的博客:Spring Boot 3.x 引入springdoc-openapi (內置Swagger UI、webmvc-api)_springdoc-openapi-starter-webmvc-ui-CSDN博客

創建工具類(MD5加密工具類)

工具類類型

1> 注冊和登錄都會對用戶密碼進行加密, 因此可以對外提供一個加密的工具類

2> 生成隨機字符串(鹽值), 我們也要提供一個工具類

3> String類型做非空校驗, 也要提供一個工具類

創建MD5加密工具類

1> pom.xml中導?依賴,SpringBoot已經對這個包做了版本管理,所以這?不?指定版本號

<!-- 編碼解碼加密?具包-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>

2> 在 org.xiaobai.forum.utils包下創建MD5Util類

密碼加密過程

1) 原密碼

2) 生成擾動字符

3) 原密碼(明文)進行MD5加密?=> 密文1

4) 密文1 + 擾動字符 = 密文2

5) 對密文2 進行MD5加密

代碼如下:?

package org.xiaobai.forum.utils;import org.apache.commons.codec.digest.DigestUtils;public class MD5Util {/*** @param str  明文* @return 密文*/public static String md5(String str){return DigestUtils.md5Hex(str);}/*** * @param str 密碼明文* @param salt 擾動字符* @return 密文 */public static String md5Salt(String str, String salt){// ((明文)md5加密+擾動字符)md5加密return md5(md5(str)+salt);}}
生成UUID工具類

生成隨機字符串(鹽值) 36位字符

org.xiaobai.forum.utils包下創建UUIDUtil類?

package org.xiaobai.forum.utils;import java.util.UUID;public class StringUtil {/*** 生成一個36位的UUID* @return*/public static String UUID_36(){return UUID.randomUUID().toString();}/*** 生成一個32位的UUID* @return*/public static String UUID_32(){return UUID.randomUUID().toString().replace("-","");}}
創建字符串工具類

String類型做非空校驗

org.xiaobai.forum.utils包下創建StringUtil類?

package org.xiaobai.forum.utils;public class StringUtil {/*** 判斷字符串是為空* @param value* @return true 空 <br/> false 非空*/public static boolean isEmpty(String value){return value == null || value.length() == 0;}
}

實現業務功能

業務實現過程中主要的包和?錄及主要功能:

? model 包:實體對象 根據數據庫生成, 還有一些和業務相關的屬性

? dao 包:數據庫訪問 調用mapper, 和數據庫進行交互

? services 包:業務處理相關的接?與實現,所有業務都在Services中實現?1. 定義接口 2. 主要處理業務邏輯,?可能會涉及到事務操作, 遇到異常的時候, 統一拋出ApplicationException

? controller 包:提供URL映射,?來接收參數并做校驗,調?Service中的業務代碼,返回執?結果 用來接收請求, 包括參數, 并且對參數做校驗, 然后傳給service層進行業務處理, 然后進行返回

? src/main/resources/mapper ?錄:Mybaits映射?件,配置數據庫實體與類之間的映射關系

定義SQL

? src/main/resources/static ?錄:前端資源

注冊

順序圖

有??跳轉的流程我們提供了UML順序圖,后?邏輯相對簡單的接?,我們在做之前只列出實現邏輯幫?家理清思路即可

參數要求

? 注冊時需要??提交的參數列表

前三個參數是和數據庫交互要關注的參數

第四個參數是在Controller層就對密碼和確認密碼進行校驗, 不通過就不傳給service直接返回錯誤

參數名描述類型默認值條件
username??名String必須
nickname昵稱String與??名相同必須
password密碼String必須
passwordRepeat確認密碼String必須,與密碼相同
接口規范

// 請求
POST /user/register HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=user222&nickname=user222&password=123456&passwordRepeat=123456
// 響應
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":null}

注冊功能實現后端代碼
具體的實現步驟

實現過程:??Mapper -> Dao -> Service(進行單元測試) -> Controller(也需要進行單元測試)

1. 定義SQL, 按照用戶名查詢用戶信息

2.? DAO 中對應類, 添加SQL中定義的方法

3. 進行Service中業務方法的編寫

4. 進行單元測試

5. 編寫Controller中的代碼

6. 測試Controller中對外提供的API

1. 定義SQL, 按用戶名查詢用戶信息

? src/main/resources/mapper?錄下創建extension

? 由于src/main/resources/mapper ?錄下是?動?成的映射?件,為防?后?修改數據再次?動成時把我們寫的SQL給覆蓋掉,新建?個擴展?錄?來存放我們業務的SQL

? 在extension?錄下新建 UserExtMapper.xml

a. 注意namespace表?命名空間,指定要與 UserMapper, xml中的namespace相同不同的映射?件指定了相同的namespace后,定義的所有?id或name標識的結果集映射都可以

b. 統??org.xiaobai.forum.dao.UserMapper, 也就是UserMapper的完全限定名(包名+類名)

c. 不同的映射?件指定了相同的namespace后,定義的所有?id或name標識的結果集映射都可以不同?件中共享

? 代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.xiaobai.forum.dao.UserMapper"><!--
1. 注意namespace表?命名空間,要與 UserMapper.xml中的namespace相同
2. 統??org.xiaobai.forum.dao.UserMapper, 也就是UserMapper的完全限定名(包名+類名)
3. 不同的映射?件指定了相同的namespace后,定義的所有?id或name標識的結果集映射都可以
在不同的?件中共享
-->
<!--    根據用戶名查詢用戶信息--><select id="selectByUserName" resultMap="BaseResultMap" parameterType="java.lang.String">select<include refid="Base_Column_List"></include>form t_userwheredeleteState = 0andusername =  #{username,jdbcType=VARCHAR}</select></mapper>
2.? DAO 中對應類, 添加SQL中定義的方法

?org.xiaobai.forum.dao包下的UserMapper中添加?法聲明

? 注意?法名要與UserExtMapper.xml中SQL標簽的id相同

@Param里面的參數是SQL的參數名, 后面的參數是java代碼里面使用的變量名

3. 進行Service中業務方法的編寫

?org.xiaobai.forum.service包下IUserService接?

實現接口

org.xiaobai.forum.service.impl包下創建UserServiceImpl類并實現IUserService接口,實現邏輯參考代碼中的注釋(impl下面是接口實現類)

具體的步驟:

1) 進行非空校驗

2) 按用戶名查詢信息.(要判斷用戶是否存在)

3) 新增用戶, 設置默認值

4) 新增用戶, 寫入數據庫

具體代碼:

package org.xiaobai.forum.service.impl;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;
import org.xiaobai.forum.dao.UserMapper;
import org.xiaobai.forum.exception.ApplicationException;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.IUserService;
import org.xiaobai.forum.utils.StringUtil;import java.util.Date;@Slf4j
@Service
public class UserServiceImpl implements IUserService {// 注入 mapper@Resourceprivate UserMapper userMapper;@Overridepublic void createNormalUser(User user) {// 1. 進行非空校驗// 對非空的必填選項進行非空校驗if(user == null || StringUtil.isEmpty(user.getUsername())|| StringUtil.isEmpty(user.getNickname())|| StringUtil.isEmpty(user.getPassword())|| StringUtil.isEmpty(user.getSalt())){// 打印日志: 參數校驗失敗log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 統一拋出 ApplicationException 自定義異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 2. 按用戶名查詢用戶信息User existsUser = userMapper.selectByUserName(user.getUsername());//1> 判斷用戶是否存在// 不存在說明是第一次注冊if(existsUser!=null){// 打印日志log.info(ResultCode.FAILED_USER_EXISTS.toString());// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));}// 3. 新增用戶, 設置默認值user.setGender((byte)2);user.setArticleCount(0);user.setIsAdmin((byte)0);user.setState((byte)0);user.setDeleteState((byte)0);// 日期Date date = new Date();// 設置創建時間和修改時間user.setCreateTime(date);user.setUpdateTime(date);// 4. 新增用戶,寫入數據庫int row = userMapper.insertSelective(user);// 插入失敗if(row != 1){// 打印日志// 新增失敗log.info(ResultCode.FAILED_CREATE.toString());// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}// 插入成功log.info("新增用戶成功. username = "+user.getUsername()+". ");}
}
4. 進行單元測試

步驟

運行結果

5. 編寫Controller中的代碼

接收請求, 包括參數, 并且對參數做校驗, 然后傳給service層進行業務處理, 然后進行返回

org.xiaobai.forum.controller包下創建UserController類,實現邏輯參考代碼中的注釋

1> 對用戶名, 昵稱, 密碼, 確認密碼進行非空校驗

2> 校驗倆次輸入的密碼進行校驗是否一致

3> 創建User, 對必傳值進行設置

4> 對密碼進行MD5加密處理.1) 生成鹽. 2) 生成密碼的密文(md5加密) 3) 設置密碼和鹽

5> 調用service 層, 把創建好的對象傳過去

6> 返回成功

package org.xiaobai.forum.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.impl.UserServiceImpl;
import org.xiaobai.forum.utils.MD5Util;
import org.xiaobai.forum.utils.UUIDUtil;// 對Controller進行API接口的描述
@Tag(name = "用戶接口",description = "和用戶相關的接口, 登錄, 注冊...")
// 日志注解
@Slf4j
// 這是一個返回數據的Controller
@RestController
// 路由映射, 一級路徑
@RequestMapping("/user")
public class UserController {@ResourceUserServiceImpl userService;@Operation(summary = "用戶注冊")@PostMapping("/register") // 直接使用api注解來實現非空校驗public AppResult register(@Parameter(description = "用戶名")@RequestParam("username")@NonNull String username,@Parameter(description = "昵稱")@RequestParam("nickname")@NonNull String nickname,@Parameter(description = "密碼")@RequestParam("password")@NonNull String password,@Parameter(description = "確認密碼")@RequestParam("passwordRepeat")@NonNull String passwordRepeat){// 校驗密碼和重復密碼是否相同if(!password.equals(passwordRepeat)){// 倆次輸入的密碼不一致log.warn(ResultCode.FAILED_TWO_PWD_NOT_SAME.toString());// 返回錯誤信息return AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME);}// 準備要插入的數據(必傳值進行設置)User user = new User();user.setUsername(username);user.setNickname(nickname);// 處理密碼// 1> 生成鹽String salt = UUIDUtil.UUID_32();// 2> 生成密碼的密文String encryptPassword = MD5Util.md5Salt(password,salt);// 3> 設置密碼和鹽user.setPassword(encryptPassword);user.setSalt(salt);// 調用Service層userService.createNormalUser(user);// 返回成功return AppResult.success();}
}

注意:??

關于非空校驗

傳統方式, 但是此時我用的是SpringDoc API里面的注解

關于SpringDoc API 的注解進行非空校驗

6. 測試Controller中對外提供的API

我們此時測試成功, 其他情況有機會再測

注冊功能實現前端代碼
1> 前端技術介紹

HTML, CSS, JavaScript 最基礎的前端技術

參考?站:https://developer.mozilla.org/zh-CN

jQuery? 對原生JS進行了封裝, 主要使用AJAX,DOM元素的操作相關方法

選擇器

1) 標簽選擇器 $("div")

2) ID 選擇器 $("#myDiv")

3) 類選擇器 $(".myClass")

官?:https://jquery.com

HTML, CSS, JavaScript, jQuery相關中?資料?上有很多,可??搜索

Bootstrap? ? ? ? ? ? ? ? ?定義了HTML 元素的樣式, 和頁面布局

官?: https://getbootstrap.com Github: https://github.com/twbs 中?站:https://www.bootcss.com

Tabler? ? ? ? ? ? ? ? ? ? ? 對Bootstrap美化后的元素進行排版和組合, 形成一個個可以直接使用的組件, 比如: 表單, 列表, 圖片墻...

Github: https://github.com/tabler/tabler

官?: https://tabler.io

圖標? ? ? ? ? ? ? ? ? ? ? ? 豐富多彩的圖標, 可以根據需要選擇

https://tabler-icons.io/

toast? ? ? ? ? ? ? ? ? ? ??用于提示信息的JS插件(類似于JAVA中的依賴, 提供一些好用的功能)

官?: https://kamranahmed.info/toast

2> 集成前端代碼

把前端所有的資源放在src/main/resource/static 下(資源綁定自己下載)

測試: 進入http://127.0.0.1:8080/sign-up.html#

后續我們編寫前端代碼, 我們使用vscode作為開發工具

1)? 導入相關的依賴 CSS

2) 引入JS

3) 處理業務邏輯

注意: jQuery提供的所有功能都是方法, 因此調用后必須加()

對用戶名, 昵稱,密碼, 重復密碼進參數校驗

構造發送的數據

?構造ajax: 根據后端的響應結果來構造ajax

具體前端代碼

<!doctype html><html lang="zh-CN"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><link rel="shortcut icon" href="/favicon.ico"><title>比特論壇 - 用戶注冊</title><!-- 導入CSS --><link href="./dist/css/tabler.min.css?1674944402" rel="stylesheet" /><link rel="stylesheet" href="./dist/css/jquery.toast.css"><!-- 設置字體 --><!-- <style>@import url('https://rsms.me/inter/inter.css');:root {--tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;}body {font-feature-settings: "cv03", "cv04", "cv11";}</style> -->
</head><body class="d-flex flex-column"><!-- 正文 --><div class="page page-center"><div class="container container-tight py-4"><div class="text-center mb-4"><img src="./image/bit-forum-logo01.png" height="50" alt=""></div><form id="signUpForm" class="card card-md" autocomplete="off" novalidate><div class="card-body"><h2 class="text-center mb-4">用戶注冊</h2><!-- 用戶名 --><div class="mb-3"><label class="form-label required">用戶名</label><input type="text" class="form-control " placeholder="請輸入用戶名" name="username" id="username"><div class="invalid-feedback">用戶名不能為空</div></div><!-- 昵稱 --><div class="mb-3"><label class="form-label required">昵稱</label><input type="text" class="form-control" placeholder="請輸入昵稱" name="nickname" id="nickname"><div class="invalid-feedback">昵稱不能為空</div></div><!-- 密碼 --><div class="mb-3"><label class="form-label required">密碼</label><div class="input-group input-group-flat"><input type="password" class="form-control" placeholder="請輸入密碼" autocomplete="off" name="password"id="password"><span class="input-group-text"><a href="javascript:void(0);" class="link-secondary" id="password_a" title="顯示密碼"data-bs-toggle="tooltip"><!-- Download SVG icon from http://tabler-icons.io/i/eye --><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24"stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><pathd="M22 12c-2.667 4.667 -6 7 -10 7s-7.333 -2.333 -10 -7c2.667 -4.667 6 -7 10 -7s7.333 2.333 10 7" /></svg></a></span><div class="invalid-feedback">密碼不能為空</div></div></div><!-- 確認密碼 --><div class="mb-3"><label class="form-label required">確認密碼</label><div class="input-group input-group-flat"><input type="password" class="form-control" placeholder="再次輸入密碼" autocomplete="off" name="passwordRepeat"id="passwordRepeat"><span class="input-group-text"><a href="javascript:void(0);" class="link-secondary" id="passwordRepeat_a" title="顯示密碼"data-bs-toggle="tooltip"><!-- Download SVG icon from http://tabler-icons.io/i/eye --><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24"stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><pathd="M22 12c-2.667 4.667 -6 7 -10 7s-7.333 -2.333 -10 -7c2.667 -4.667 6 -7 10 -7s7.333 2.333 10 7" /></svg></a></span><div class="invalid-feedback">請檢查確認密碼</div></div></div><div class="mb-3"><label class="form-check"><input type="checkbox" class="form-check-input" id="policy" /><span class="form-check-label">同意 <a href="#" tabindex="-1">比特論壇使用條款和隱私政策</a>.</span></label></div><div class="form-footer"><button type="button" class="btn btn-primary w-100" id="submit">注冊</button></div></div></form><div class="text-center text-muted mt-3">我已有一個賬戶? <a href="./sign-in.html" tabindex="-1">登錄</a></div></div></div>
</body>
<!-- 導入JS -->
<script src="./dist/js/tabler.min.js"></script>
<script src="./dist/js/jquery-3.6.3.min.js"></script>
<script src="./dist/js/jquery.toast.js"></script>
<script>// 當前頁加載成功后執行的方法$(function () {// 獲取表單并校驗$('#submit').click(function () {let checkForm = true;// 校驗用戶名if (!$('#username').val()) {$('#username').addClass('is-invalid');checkForm = false;}// 校驗昵稱if (!$('#nickname').val()) {$('#nickname').addClass('is-invalid');checkForm = false;}// 校驗密碼非空if (!$('#password').val()) {$('#password').addClass('is-invalid');checkForm = false;}// 校驗確認密碼非空, 校驗密碼與重復密碼是否相同if (!$('#passwordRepeat').val() || $('#password').val() != $('#passwordRepeat').val()) {$('#passwordRepeat').addClass('is-invalid');checkForm = false;}// 檢驗政策是否勾選if (!$('#policy').prop('checked')) {$('#policy').addClass('is-invalid');checkForm = false;}// 根據判斷結果提交表單if (!checkForm) {return;}// 構造數據let postData = {username: $('#username').val(),nickname: $('#nickname').val(),password: $('#password').val(),passwordRepeat: $('#passwordRepeat').val()};// 發送AJAX請求 // contentType = application/x-www-form-urlencoded// 成功后跳轉到 sign-in.html$.ajax({type: 'post',url: 'user/register',contentType: 'application/x-www-form-urlencoded',data: postData,// 回調方法success: function (respData) {// 判斷返回的狀態碼if (respData.code == 0) {// 跳轉到登錄頁面location.assign('/sign-in.html');} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error: function () {// 提示信息$.toast({heading: '錯誤',text: '訪問出現問題,請與管理員聯系.',icon: 'error'});}});});// 表單元單獨檢驗$('#username, #nickname, #password').on('blur', function () {if ($(this).val()) {$(this).removeClass('is-invalid');$(this).addClass('is-valid');} else {$(this).removeClass('is-valid');$(this).addClass('is-invalid');}})// 檢驗確認密碼$('#passwordRepeat').on('blur', function () {if ($(this).val() && $(this).val() == $('#password').val()) {$(this).removeClass('is-invalid');$(this).addClass('is-valid');} else {$(this).removeClass('is-valid');$(this).addClass('is-invalid');}})// 校驗政策是否勾選$('#policy').on('change', function () {if ($(this).prop('checked')) {$(this).removeClass('is-invalid');$(this).addClass('is-valid');} else {$(this).removeClass('is-valid');$(this).addClass('is-invalid');}})// 密碼框右側明文密文切換按鈕$('#passwordRepeat_a').click(function () {if ($('#passwordRepeat').attr('type') == 'password') {$('#passwordRepeat').attr('type', 'text');} else {$('#passwordRepeat').attr('type', 'password');}});$('#password_a').click(function () {if ($('#password').attr('type') == 'password') {$('#password').attr('type', 'text');} else {$('#password').attr('type', 'password');}});});</script></html>

登錄

順序圖

參數要求
參數名描述類型條件
username??名String必須
password密碼String必須
接口規范

// 請求
POST /user/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=bitboy&password=123456
// 響應
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":null}

登錄功能實現后端代碼
1. 在Mapper.xml中編寫SQL語句
2. 在Mapper.java中定義方法

1.2步在注冊的時候已經寫過了

3. 定義Service接口

定義一個login(username,password)

4. 實現Service接口

實現步驟

1) 對用戶名和密碼進行非空校驗

2) 調用mapper層的根據用戶名查詢, 返回用戶信息

3) 對查詢到的用戶進行非空校驗(null 說明沒查到)

4) 對密碼進行MD5校驗, 看經過MD5算法得到的密碼和數據庫的密碼是否一致

5) 登錄成功,打印日志

具體代碼

  /*** @param username 用戶名* @return 根據用戶名來進行查詢, 查詢是否登錄的用戶在數據庫存在, 存在久返回查詢結果*/@Overridepublic User selectByUserName(String username) {// 非空校驗if (StringUtil.isEmpty(username)) {// 打印日志// 參數校驗失敗log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 調用mapper, 返回查詢結果return userMapper.selectByUserName(username);}/*** 進行登錄** @param username 用戶名* @param password 密碼* @return 登錄成功返回登錄用戶*/@Overridepublic User login(String username, String password) {// 1. 非空校驗if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {// 打印日志// 參數校驗失敗log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 2. 按用戶名進行查詢User user = selectByUserName(username);// 3. 對查詢的結果進行非空校驗if (user == null) {// 說明沒有查到// 參數校驗失敗log.warn(ResultCode.FAILED_LOGIN.toString() + ", username" + username);// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_LOGIN));}// 4. 對密碼進行非空校驗: MD5(MD5(當前輸入的密碼)+隨機字符(鹽))String encryptPassword = MD5Util.md5Salt(password,user.getSalt());// 5. 用密文和數據庫中存的用戶密碼進行比較if(!encryptPassword.equals(user.getPassword())){// 參數校驗失敗log.warn(ResultCode.FAILED_LOGIN.toString() + ", 密碼錯誤, username" + username);// 拋出異常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_LOGIN));}// 打印成功日志log.info("登錄成功 username = " + username);// 登錄成功 返回用戶信息return user;}
5. 單元測試

編寫測試代碼,進行測試

注意: 使用@Transaction 會在測試后進行事務的回滾, 不會污染數據庫中的數據

6. Controller實現方法并對外提供API接口

在UserController中實現登錄?法

步驟

1) 使用注解進行非空校驗

2) 調用Service中的登錄方法, 返回User對象

3) 登錄成功就把User對象設置到Session作用域中

4) 返回結果
具體代碼

 /*** 用戶登錄* @param request 獲取http請求, 設置session* @param username 用戶名* @param password 密碼* @return 返回登錄成功/失敗*/@Operation(summary = "用戶登錄")@PostMapping("/login") //1. 使用注解進行非空校驗public AppResult login (HttpServletRequest request,@Parameter(description = "用戶名") @RequestParam("username") @NonNull String username,@Parameter(description = "密碼") @RequestParam("password") @NonNull String password){// 2. 調用Service中的登錄方法, 返回User對象User user = userService.login(username,password);if(user == null){// 打印日志log.warn(ResultCode.FAILED_LOGIN.toString());// 返回結果return AppResult.failed(ResultCode.FAILED_LOGIN);}// 3. 如果登錄成功就把User對象設置到Session作業域中HttpSession session = request.getSession(true);// 沒有session就創建新的sessionsession.setAttribute(AppConfig.USER_SESSION,user);// 4. 返回結果return AppResult.success("登錄成功");}

注意: 要設置為true?, 這樣沒有session對象的時候就會新建一個

7. 測試API接口

啟動項目, 進入這個url:?http://localhost:8080/swagger-ui/swagger-ui/index.html#/

登錄功能實現前端代碼?

編寫前端代碼

1) 為相關的控件寫一個id

2) 編寫ajax請求

具體代碼, 太多了只放ajax的

 // 構造數據let postData = {username : usernameEl.val(),password : passwordEl.val()}// 發送AJAX請求,成功后跳轉到index.html$.ajax({type: 'post',url : 'user/login',contentType : 'application/x-www-form-urlencoded',data : postData,success : function (respData) {if (respData.code == 0) {location.assign('/index.html');} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error : function () {// 提示信息$.toast({heading: '錯誤',text: '訪問出現問題,請與管理員聯系.',icon: 'error'});}});

進行測試:?

此時我只放一個錯誤的

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/82878.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82878.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82878.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

E+H流量計通過Profibus DP主站轉Modbus TCP網關與上位機輕松通訊

EH流量計通過Profibus DP主站轉Modbus TCP網關與上位機輕松通訊 在現代工業自動化的廣闊舞臺上&#xff0c;Profibus DP與Modbus TCP這兩種通信協議各領風騷&#xff0c;它們在不同的應用場景中發揮著舉足輕重的作用。但工業生產的復雜性往往要求不同設備、系統之間能夠順暢溝…

服務器中存儲空間不足該怎么辦?

服務器作為存儲數據信息的重要網絡設備&#xff0c;隨著企業業務的不斷拓展&#xff0c;所需要存儲的數據信息也在不斷增加&#xff0c;最終會導致服務器中存儲空間不足&#xff0c;這不僅會影響到服務器系統性能&#xff0c;還會造成業務無法正常執行&#xff0c;那么&#xf…

C++23 views::chunk_by (P2443R1) 詳解

文章目錄 引言C23 范圍庫概述范圍視圖&#xff08;Range Views&#xff09;范圍算法&#xff08;Range Algorithms&#xff09;范圍適配器&#xff08;Range Adapters&#xff09; std::views::chunk_by 介紹基本概念特性使用場景 示例代碼簡單示例自定義謂詞示例 總結 引言 在…

零碳園區能源系統-多能互補體系

構建以可再生能源為核心的零碳園區能源系統&#xff0c;需整合光儲直柔、光伏發電、微電網、氫能與儲能技術&#xff0c;通過多能協同與智能調控實現能源生產、存儲、消費全鏈條優化。以下是系統性實施方案&#xff1a; 一、系統架構設計 1. 多能互補體系 &#xff08;圖示&a…

elastic search學習

首先在自己電腦上安裝elastic search。安裝成功后&#xff0c;查看ES是否啟動成功。 安裝過程參考&#xff1a;ElasticSearch入門1: mac 安裝 - 霜井 - 博客園 安裝完成后&#xff0c;直接執行bin目錄中的elastic search命令后&#xff0c;就可以啟動成功&#xff01; 在網頁…

mysql8常用sql語句

查詢結果帶行號 -- 表名為 mi_user&#xff0c; 假設包含列 id &#xff0c;address SELECT ROW_NUMBER() OVER (ORDER BY id) AS row_num, t.id, t.address FROM mi_user t ; SELECT ROW_NUMBER() OVER ( ) AS row_num, t.id, t.address FROM mi_user t ; 更新某列數…

Memcached 服務搭建和集成使用的詳細步驟示例

以下是 Memcached 服務搭建和集成使用的詳細步驟示例&#xff1a; 一、搭建 Memcached 服務 安裝 Memcached Linux 系統 yum 安裝&#xff1a;執行命令 yum install -y memcached memcached-devel。源碼安裝 下載源碼&#xff1a;wget http://www.memcached.org/files/memcach…

2. 盒模型/布局模塊 - 響應式產品展示頁_案例:電商產品網格布局

2. 盒模型/布局模塊 - 響應式產品展示頁 案例&#xff1a;電商產品網格布局 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><style type"text/css">:root {--primary-color…

Go基于plugin的熱更新初體驗

背景 對于一個部署在生產環境的項目來說&#xff0c;我們希望當代碼出現bug的時候&#xff0c;可以不用重啟進程而達到動態修改代碼的目的—— 這就是代碼熱部署&#xff01; 使用java做游戲服務器&#xff0c;最大的好處是&#xff0c;當代碼出現bug&#xff0c;可以直接熱…

【RabbitMQ】工作隊列和發布/訂閱模式的具體實現

文章目錄 建立連接工作隊列模式實現創建隊列和交換機生產者代碼消費者代碼運行程序啟動消費者啟動生產者 發布/訂閱模式實現創建隊列和交換機生產者代碼創建交換機聲明兩個隊列綁定隊列和交換機發送消息完整代碼 消費者代碼完整代碼 運行程序啟動生產者啟動消費者 建立連接 我…

Codeforces Round 998 (Div. 3)

A. Fibonacciness 題目大意 給你四個數字abde&#xff0c;讓你找到一個中間值c&#xff0c;問 a b c a b c abc &#xff0c; b c d b c d bcd &#xff0c; c d e c d e cde 最多能有幾個式子成立 解題思路 顯然最多就六種情況&#xff0c;暴力枚舉即可 代…

火山引擎發展初始

火山引擎是字節跳動旗下的云計算服務品牌&#xff0c;其云服務業務的啟動和正式商業化時間線如下&#xff1a; 1. **初期探索&#xff08;2020年之前&#xff09;** 字節跳動在早期為支持自身業務&#xff08;如抖音、今日頭條等&#xff09;構建了強大的基礎設施和技術中…

【認知思維】光環效應:第一印象的持久力量

什么是光環效應 光環效應&#xff08;Halo Effect&#xff09;是指人們傾向于讓對某人或某物的一個顯著特質的印象影響對其他特質的評價的認知偏差。簡單來說&#xff0c;當我們對某人的一個特質&#xff08;如外表、智力或某項技能&#xff09;形成積極印象時&#xff0c;我們…

Java Solon v3.3.0 發布(國產優秀應用開發基座)

Solon 框架&#xff01; Solon 是新一代&#xff0c;Java 企業級應用開發框架。從零開始構建&#xff08;No Java-EE&#xff09;&#xff0c;有靈活的接口規范與開放生態。采用商用友好的 Apache 2.0 開源協議&#xff0c;是“杭州無耳科技有限公司”開源的根級項目&#xff…

力扣-104.二叉樹的最大深度

題目描述 給定一個二叉樹 root &#xff0c;返回其最大深度。 二叉樹的 最大深度 是指從根節點到最遠葉子節點的最長路徑上的節點數。 class Solution { public:int maxDepth(TreeNode* root) {if(!root){return 0;}return max(maxDepth(root->left), maxDepth(root->…

單反和無反(私人筆記)

① 單反相機&#xff1a; 定義&#xff1a; 單反相機&#xff08;Single-Lens Reflex&#xff0c;SLR&#xff09;是一種帶有反光鏡結構的數碼相機。光線通過鏡頭進入后&#xff0c;先被反光鏡反射到五棱鏡/五面鏡&#xff0c;再通過取景器進入人眼。按下快門時&#xff0c;反…

超詳細講解C語言轉義字符\a \b \r \t \? \n等等

轉義字符 C語言有一組字符很特殊&#xff0c;叫做轉義字符&#xff0c;顧名思義&#xff0c;改變原來的意思的字符。 1 \? ??)是一個三字母詞&#xff0c;在以前的編譯器它會被編譯為] (??會被編譯為[ 因此在以前輸入(are you ok ??)就會被編譯為are you ok ] 解決這個…

Java Spring MVC -01

SpringMVC 是一種基于 的實現 MVC 設計模式的請求驅動類型的輕量級 Web 框架&#xff0c;屬于 Spring FrameWork 的后續產品&#xff0c;已經融合在 Spring Web Flow 中。 First:SpringMVC-01-SpringMVC 概述 SpringMVC 是 Spring 框架的一個模塊&#xff0c;用于構建 Web 應…

Spring MessageSource 詳解:如何在國際化消息中傳遞參數

在開發多語言應用程序時,Spring 的 MessageSource 是處理國際化(i18n)文本的核心組件。它允許我們根據用戶的 Locale (區域設置) 顯示不同的消息。然而,很多時候我們的消息并不是靜態的,而是需要包含動態數據,比如用戶名、數量、文件名等。這時,我們就需要在獲取國際化消…

Datawhale 5月llm-universe 第1次筆記

課程地址&#xff1a;GitHub - datawhalechina/llm-universe: 本項目是一個面向小白開發者的大模型應用開發教程&#xff0c;在線閱讀地址&#xff1a;https://datawhalechina.github.io/llm-universe/ 難點&#xff1a;配置conda環境變量 我用的vscode github方法 目錄 重要…