使用springboot創建商城系統


首先,恭喜自己(偽)獨立完成了一個包括后台管理和前端商城的(簡陋至極的)商城系統。
在這一過程中,我最主要的問題是——呃,什么都不會!
沒錯,真的是什么都不會。一個月前,我所擁有的對這個項目唯一有幫助的基礎是:使用eclipse自學過兩周java編程算法。除此之外,沒有任何web項目經驗,沒有任何spring經驗,沒有任何J2EE經驗。在學校里只學過c++,而且僅限於算法。
老實說,當我面對這個項目命題時,我感到了深深的絕望和對以往荒廢時光的惋惜。於是,我開始做的第一件事就是,登錄b站,打開點擊最高的spring,springmvc,mybatis,springboot視頻。。。。。
咳咳,閑話扯遠了,回到正題!
這篇博客我將盡可能完整地展示一個springboot商城系統的構建思路和業務邏輯。當然,項目還存在一些不夠友好的bug,以及功能單薄的缺點(可我實在懶得改了)。博客的發布,最主要目的是我對過去一個月的思考和知識的鞏固匯總,所以不會一一列舉所有的項目內容(比如對對象的增刪改查操作等),尤其是我自己覺得很簡單的部分,所以可能會顯得不夠全面,如果你需要全面的代碼,那么可以去git上搜索靠前的原碼。
那么問題來了,依靠這篇博客你可以成功完成一個項目嗎?
答案顯然是no。因為我只是總結一些個人在項目中遇到的重點,我自己本身也是通過視頻,博客,git等途徑逐步學習完成的整個項目。也就是說,這不是一篇教學類的博客。
如果你要完成一個自己的項目,你需要看更多的視頻,學更多的基礎知識,看更多的博客,這只是一個參考,並且不一定是你能用到的參考。僅此。
基本來說,我將采取每天(???)更新的方式,逐步完成。

技術棧

因為初學者,所以項目使用的都是最基礎的技術框架。如下:

(懶得手敲,直接ppt截圖)
其中springboot括號里的組件是我在完成基本功能后,通過度娘添加補充的。

需求分析

這是項目開始之初,首先要做的工作。當然,我這一步做的就不好,直到項目差不多了,我才整理好需求分析,可以說是很不專業了。

用戶前台需求分析

用戶中心:
1注冊與登錄
2用戶個人信息(包括地址和積分信息)的查看與修改
3用戶購物車的查看與修改
4個人訂單的查看、取消、下單
5積分兌換商品記錄查詢
商城系統:
1商品展示
2商品分類及展示
3商品詳情
4商品加入購物車以及下單	
訂單系統:
1商品下單
2支付訂單以及退單
積分商城:
1積分商城
2積分獲取規則
3積分兌換商品

管理員后台需求分析

管理員登錄

用戶管理:

1用戶信息搜索查詢
2用戶信息增刪改
3地址信息搜索查詢
4用戶地址信息增刪改

商品管理:

1商城商品增刪改查
2積分商品增刪改查
3商品類別增刪改查

訂單管理:

1訂單的分類查詢
2訂單的刪改查

積分管理:

1用戶積分查詢與修改
2積分兌換記錄查詢

思維導圖

可以看見,思維導圖就是在需求分析后整理成可視化的圖形結構,使得整個項目功能一目了然,同時還可以使用標記來記錄自己的完成情況,就很舒服。同樣地當然,我這一步做的也不太好,開始時列舉功能不夠詳細,思路不清晰,也可以看出,我的功能相當單薄(所以說,菜就是原罪啊!)。所以,思維導圖的列舉,一定要具體詳細,每完成一項就做一個標記,成就感滿滿!

項目實戰(開始擼代碼)

這一大模塊,我將采用自己完成項目的時間步驟書寫。基本來說,就是管理平台后端——管理平台前端——商城后端——商城前端的順序。其中,前端部分我花了五分之三還多的時間,后端花費時間較少(前端實在是不會啊!!!)。由於很多內容是簡單的復用(比如后台對用戶,商品的管理操作),所以不會全部列舉。

在這里,首先列舉一下數據表和項目結構。其他具體內容以后慢慢展示。

數據庫表

項目結構

配置文件application.yml

spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
username: ######
password: ######
url: jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver

Jackson配置

jackson:
default-property-inclusion: NON_NULL
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
mvc:
view:
suffix: .html
prefix: /

resources:

#classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
#static-locations: classpath:/css/, classpath:/image/, classpath:/js/
#static-path-pattern: /static/*

thymeleaf:
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
cache: false

配置駝峰映射

configuration:
map-underscore-to-camel-case: true

mybatis配置

mybatis:
typeAliasesPackage: com.pf.businessdemo.dataobject
mapperLocations: classpath:mapping/*.xml
configuration:
map-underscore-to-camel-case: true
(這里是一個最簡單的配置,學過一點基礎的想必都懂。)
除此之外的依賴注入我也不再列舉,都是一個web項目所必需的jar包。

后台用戶模塊

一、登錄注冊

用戶實體類

public class UserDo {
private Integer id;

private String name;

private String gender;

private Integer age;

private String telphone;

private String registerMode;

private String thirdPartyId;

private String receiverAddress;

private Integer integral;

getset省略

service層接口

public interface UserService{
/**
*@Description: 通過id獲取用戶信息
*@Param: id
*@return: usermodel用戶領域模型
*/
UserModel getUserById(Integer id);

/**
*@Description:通過id刪除用戶信息
*@Param: id
*@return: void
*/
void deleteUser(Integer id);

/**
*@Description:通過name刪除用戶
*@Param:userdo
*@return:
*/
int  deleteByName(UserDo userDo );

/**
*@Description:通過id更改用戶信息
*@Param: 要更改的用戶信息
*@return: void
*/
void updateUserInfo(UserDo userDo);

/**
*@Description:完善用戶信息
*@Param:用戶填寫的具體信息
*@return:
*/
UserDo insertUserInfo(UserDo userDo);

/**
*@Description:查詢獲取所有用戶信息
*@Param:
*@return:list<userdo>
*/
List<UserDo> findUserAll();

/**
*@Description:聯合查詢用戶及密碼信息
*@Param:
*@return:
*/
List<UserPassWordDo> findDouble();

/**
*@Description:通過名字搜索用戶
*@Param:用戶信息
*@return:
*/
List<UserDo> findUserByName(UserDo userDo);

/**
*@Description: 用戶注冊接口
*@Param: 用戶領域模型
*@return:
*/
void register(UserModel userModel)throws  BusinessException;

/**
*@Description:用戶帶校驗的登陸接口
*@Param: 用戶手機,用戶加密密碼
*@return:
*/
UserModel validateLogin(String telphone,String encrptPassword) throws BusinessException;

這里列舉了所有的用戶service接口,目前只需要關注登錄注冊相關的即可。

UserServiceImpl

@Override
public List findUserByName(UserDo userDo) {
return userDoMapper.findUserByName(userDo);
}

@Override
@Transactional
public void register(UserModel userModel) throws BusinessException {
    if (userModel==null){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
    }
    ValidationResult result=validator.validate(userModel);
    if (result.isHasError()){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
    }
    //實現model->dataobject方法
    UserDo userDo=convertFromDataObject(userModel);
    try {
        userDoMapper.insertSelective(userDo);
    }catch (DuplicateKeyException ex){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"手機號已重復注冊");
    }
    userModel.setId(userDo.getId());
    UserPassWordDo userPassWordDo=convertPasswordFromDataObject(userModel);
    userpasswordDoMapper.insertSelective(userPassWordDo);
    return;
}

@Override
public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException {
    //通過用戶手機獲取用戶信息
    UserDo userDo=userDoMapper.selectByTelphone(telphone);
    if (userDo==null){
        throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL  );
    }
    UserPassWordDo userPassWordDo=userpasswordDoMapper.selectByUserId(userDo.getId());
    UserModel userModel=convertFromDataObject(userDo,userPassWordDo);
    //比對用戶信息內加密的密碼是否和傳輸進來的密碼相匹配
    if (!StringUtils.equals(encrptPassword,userModel.getEncrptPassword())){
        throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL  );
    }
    return userModel;
}

private UserPassWordDo convertPasswordFromDataObject(UserModel userModel){
    if (userModel==null){
        return null;
    }
    UserPassWordDo userPassWordDo=new UserPassWordDo();
    userPassWordDo.setEncrptPassword(userModel.getEncrptPassword());
    userPassWordDo.setUserId(userModel.getId());
    return userPassWordDo;
}


private UserDo convertFromDataObject(UserModel userModel){
    if (userModel==null){
        return null;
    }
    UserDo userDo=new UserDo();
    BeanUtils.copyProperties(userModel,userDo);
    return userDo;
}

private UserModel convertFromDataObject(UserDo userDo, UserPassWordDo userpasswordDo){
    if(userDo==null){
        return null;
    }
    UserModel userModel=new UserModel();
    BeanUtils.copyProperties(userDo,userModel);
    if(userpasswordDo!=null){
        userModel.setEncrptPassword(userpasswordDo.getEncrptPassword());
    }
    return userModel;
}

1首先是register(注冊)方法:
在這里需要說明一下,我的用戶表是不包含用戶密碼的,而是單獨寫了一張user_password表,如下

接着用一個用戶領域模型UserModel來包含用戶所有字段
public class UserModel {
private Integer id;
@NotBlank(message = "用戶名不能為空")
private String name;
@NotNull(message = "性別為必填選項")
private String gender;
@NotNull(message = "年齡為必填選項")
@Min(value = 0,message = "年齡必須大於0歲")
@Max(value = 150,message = "年齡必須小於150歲")
private Integer age;
@NotNull(message = "手機號不能為空")
private String telphone;
private String registerMode;
private String thirdPartyId;
@NotNull(message = "密碼不能為空")
private String encrptPassword;
@NotNull(message = "手機號不能為空")
private String receiverAddress;
private Integer integral;
getset省略
因為是對兩張表操作,所以自定義了類似convertPasswordFromDataObject的方法,邏輯非常簡單,用到了BeanUtils.copyProperties(a,b)(將a賦給b)。
對代碼逐行分析。首先是對UserModel的判空處理,定義異常,使用ExceptionHandler捕獲,接着是對注冊信息的輸入校驗,這里使用了HibernateValidator。
接下來,調用用戶的mapper接口UserDoMapper和密碼的mapper接口UserPasswordDoMapper的insert方法,這里邏輯比較簡單,不再贅述。
2登錄validateLogin方法
登錄的邏輯是,首先通過用戶手機號得到用戶信息,接着對比用戶加密密碼是否和傳輸進來的密碼相匹配。具體實現看代碼即可。

UserController

/**
*@Description:用戶登錄接口
*@Param:用戶登錄信息
*@return:
*/
@RequestMapping("/index")
public String index() {
return "admin/adminHomepage";
}

@RequestMapping(value = "/login")
@ResponseBody
public CommonReturnType login(@RequestParam(name = "telphone") String telphone,
                    @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
    //入參校驗
    if (org.apache.commons.lang3.StringUtils.isEmpty(telphone) ||
            StringUtils.isEmpty(password)) {
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
    }
    //用戶登錄服務,用來校驗用戶登錄是否合法
    UserModel userModel = userService.validateLogin(telphone, this.EncodeByMd5(password));
    //將登錄憑證加入到用戶登陸成功的session內
    this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
    this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);
    String url = "/shop/home";
    return CommonReturnType.create(url);
}

/**
*@Description:用戶注冊接口
*@Param:注冊信息
*@return:
*/
@RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType register(@RequestParam(name = "telphone") String telphone,
                                 @RequestParam(name = "otpCode") String otpCode,
                                 @RequestParam(name = "name") String name,
                                 @RequestParam(name = "gender") String gender,
                                 @RequestParam(name = "password") String password,
                                 @RequestParam(name = "age") Integer age,
                                 @RequestParam(name = "receiverAddress") String receiverAddress) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
    //驗證手機號和對應的otpCode相符合
    String inSessionOtpCode = (String) this.httpServletRequest.getSession().getAttribute(telphone);
    if (!com.alibaba.druid.util.StringUtils.equals(otpCode, inSessionOtpCode)) {
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信驗證碼不符合");
    }
    //用戶的注冊流程
    UserModel userModel = new UserModel();
    userModel.setName(name);
    userModel.setGender(gender);
    userModel.setAge(age);
    userModel.setTelphone(telphone);
    userModel.setRegisterMode("byphone");
    userModel.setEncrptPassword(this.EncodeByMd5(password));
    userModel.setReceiverAddress(receiverAddress);
    userService.register(userModel);
    return CommonReturnType.create(null);
}

1登錄controller
Controller里的代碼寫的非常清楚(最開始的index()方法可以忽略,這是我寫的跳轉后台首頁),同樣是入參校驗(這里是僅是判空),接着調用service層的登錄方法,最后將登錄信息存入session,方便之后的狀態控制。要特別說明的是最后的CommonReturnType類型,直接上代碼:
private String status;
private Object data;
private String url;

/**
*@Description:定義一個通用的創建方法
*@Param:
*@return:
*/
public static CommonReturnType create(Object result){
    return CommonReturnType.create(result ,"success");
}

public static CommonReturnType create(String url){
    return CommonReturnType.create(null,"success",url);
}

public static CommonReturnType create(Object result,String status){
    CommonReturnType type=new CommonReturnType();
    type.setStatus(status);
    type.setData(result);
    return type;
}

public static CommonReturnType create(Object result,String status,String url){
    CommonReturnType type=new CommonReturnType();
    type.setStatus(status);
    type.setData(result);
    type.setUrl(url);
    return type;
}

getset省略
這個類放在前面的response包,表示一個通用的返回類型,主要由狀態status,數據data構成(最后的屬性url是因為我的登錄頁面是靜態頁面,而我后來其他頁面用的是模板頁面寫的,所以強行前后端不分離,如果你要寫前后端分離,可以不加)。類里面定義了一個creat方法,如果入參為null則默認status為success,接着重寫方法將入參填入即可。
2注冊controller
使用@RequestParam注解將參數注入,接着驗證手機號和對應的otpCode是否符合(otp短信驗證碼后面講),最后set屬性。這里涉及到Md5加密,我在同一個類里定義了一個EncodeByMd5方法,如下:
@ResponseBody
public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
//確定計算方法
MessageDigest md5 = MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
//加密字符串
String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
return newstr;
}
接下來是用戶獲取otp短信接口:
@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType getOtp(@RequestParam(name = "telphone") String telphone) {
//需要按照一定的規則生成otp驗證碼
Random random = new Random();
int randomInt = random.nextInt(99999);
randomInt += 10000;
String otpCode = String.valueOf(randomInt);
//將otp驗證碼同對應用戶的手機號關聯,使用httpsession的方式綁定用戶手機號與otpcCode
httpServletRequest.getSession().setAttribute(telphone, otpCode);
//將otp驗證碼通過短信通道發送給用戶,省略
System.out.println("telphone = " + telphone + " &otpCode = " + otpCode);
return CommonReturnType.create(null);
}
我這里是將otp驗證碼打印到控制台,只為模擬短信驗證流程。(這部分內容網上有很多,我也是照貓畫虎copy的)

關於用戶后台模塊的其他內容我覺得很沒有必要寫,因為用戶后台模塊都是一些簡單的crud操作,網上的資料實在太多了,非常簡單也很容易掌握。然后這里的登錄注冊涉及到了異常捕獲和入參校驗,這兩部分你要覺得麻煩完全可以不要,直接寫登錄注冊邏輯,但是這樣的設計是很不完善的。這兩部分內容網上同樣有很多資料,我將不再贅述。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM