使用springboot建立商城系統

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

技術棧

由於初學者,因此項目使用的都是最基礎的技術框架。以下:
html

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

需求分析

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

用戶前臺需求分析

用戶中心:
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積分兌換記錄查詢

思惟導圖

能夠看見,思惟導圖就是在需求分析後整理成可視化的圖形結構,使得整個項目功能一目瞭然,同時還可使用標記來記錄本身的完成狀況,就很舒服。一樣地固然,我這一步作的也不太好,開始時列舉功能不夠詳細,思路不清晰,也能夠看出,個人功能至關單薄(因此說,菜就是原罪啊!)。因此,思惟導圖的列舉,必定要具體詳細,每完成一項就作一個標記,成就感滿滿!mysql

項目實戰(開始擼代碼)

這一大模塊,我將採用本身完成項目的時間步驟書寫。基原本說,就是管理平臺後端——管理平臺前端——商城後端——商城前端的順序。其中,前端部分我花了五分之三還多的時間,後端花費時間較少(前端實在是不會啊!!!)。因爲不少內容是簡單的複用(好比後臺對用戶,商品的管理操做),因此不會所有列舉。c++

在這裏,首先列舉一下數據表和項目結構。其餘具體內容之後慢慢展現。

數據庫表
git

項目結構
web

配置文件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/*spring

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包。

後臺用戶模塊

1、登陸註冊

用戶實體類

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操做,網上的資料實在太多了,很是簡單也很容易掌握。而後這裏的登陸註冊涉及到了異常捕獲和入參校驗,這兩部分你要以爲麻煩徹底能夠不要,直接寫登陸註冊邏輯,可是這樣的設計是很不完善的。這兩部份內容網上一樣有不少資料,我將再也不贅述。

相關文章
相關標籤/搜索