首页 > 技术文章 > MMall项目完整分析总结

zcl1116 2021-02-03 17:09 原文

Linux服务器

线上环境:

  1.jdk    2.vsftpd   3.nginx

  4.mysql       5.tomcat

  6.git   7.maven    8.Redis

 

 

 

 

项目采用Tomcat集群方式:

 

在此架构图中,nginx使用的是轮询的负载均衡策略。session不交给tomcat自己管理,已经交由左侧的redis分布式集群来管理。

服务器的文件独立存放于文件服务器上。用户在请求的时候会经过nginx的分配访问到各个Tomcat服务器上。

 


 

 

 

项目的模块划分目录:

  1.用户管理模块    2.分类管理模块    3.商品模块

  4.购物车模块       5.收货地址模块    6.支付模块    7.订单模块

 


 

 

 

项目各模块详细解析

 

 

 

-----------------1.用户管理模块-----------------

  功能模块:

    登录        login(String username, String password);

    用户名/邮箱验证    checkValid(String str,String type);

    注册        register(User user);

    忘记密码      forgetResetPassword(String username,String passwordNew,String forgetToken); 

    提交问题答案    checkAnswer(String username,String question,String answer);

    重置密码      resetPassword(String passwordOld,String passwordNew,User user);

    获取用户信息(无密码) getInformation(Integer userId);

    更新用户信息    updateInformation(User user);

    退出登录      logout(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse)

  技术学习:

    横向越权、纵向越权安全漏洞

    MD5明文加密及增加salt值

    Guava缓存的使用

    高复用服务响应对象的设计思想及抽象封装

    Mybatis-plugin

    Session的使用

   

   用户模块注意:      

      1.在密码存入数据库前使用MD5进行编码,取出时再使用MD5解码(普通MD5加密形式网上已有解码方式,为提高安全性,需另外在md5上添加逻辑)
      2.注册用户时需要在数据库中查询是否有相同的用户名和邮箱(可单独写一个方法查询用户名与邮件是否已存在)
      3.忘记修改密码时当验证问题正确时会附带有一个记号返回。(记号用于传递给修改密码的时候,可以提高用户的安全性,防止他人传值修改密码。当修改密码的时候会验证记号是否在存在与有效期内,若是存在且在有效期内方可修改)
      4.客户修改信息时,在将User传回数据库修改时username是不可修改的。且email在修改时需要验证新的email是否已存在。如果存在相同email的话,不能是我们当前的这个用户的
      5.在登录后台页面时需验证是否为管理员

 

 

 

-----------------2.分类管理模块-----------------

  功能模块:

    获取节点      getChildrenParallelCategory(Integer categoryId);

    增加节点      addCategory(String categoryName, Integer parentId);

    修改节点      updateCategoryName(Integer categoryId,String categoryName);

    获取分类ID     getChildrenParallelCategory(Integer categoryId);

    递归子节点ID    selectCategoryAndChildrenById(Integer categoryId);

  技术学习:

    如何设计及封装无限层级的树状数据结构

    递归算法的设计思想

    如何处理复杂对象排重

    重写hashcode和equal的注意事项

 

   产品模块注意:

      1.在上传文件时需验证是否为管理员,在上传文件的大小上限制最大为4m
      2.上传文件流程为,先将文件保存到tomcat服务器上的update文件夹中,随后连接FTP服务器,配置好上传FTP工具类后将tomcat服务器上存放的文件通过工具类转移到FTP服务器指定文件夹下,然后删除掉tomcat服务器中的原先文件。
      3.富文本上传与SpringMVC上传最明显的区别在于返回值上,其次富文本上传成功后在返回时需设置响应头response.addHeader("Access-Control-Allow-Headers","X-File-Name");

 

 

 

 

-----------------3.商品模块-----------------------

  功能模块:

    前台

      商品搜索    searchProduct(String productName,Integer productId,int pageNum,int pageSize);   

      商品列表    list(String keyword,Integer categoryId,int pageNum,int pageSize,String orderBy)

      商品详情    getProductDetail(Integer productId);

    后台

         商品列表    getList(HttpServletRequest httpServletRequest, int pageNum,int pageSize)

      商品搜索    productSearch(String productName,Integer productId,int pageNum,int pageSize);

      图片上传    upload(HttpServletRequest httpServletRequest,MultipartFile file,HttpServletRequest request)

      富文本图片上传 richtextImgUpload(HttpServletRequest httpServletRequest, MultipartFile file, HttpServletRequest request, HttpServletResponse response)

      商品详情    getDetail(HttpServletRequest httpServletRequest, Integer productId)

      商品上下架   setSaleStatus(HttpServletRequest httpServletRequest, Integer productId, Integer status)

      增加商品    productSave(HttpServletRequest httpServletRequest, Product product)

 

  技术学习:

    FTP服务的对接

    SpringMVC文件上传

    流读取Properties配置文件

    抽象POJO、BO、VO对象之间的转换关系及解决思路

    joda-time快速入门

    静态块

    Mybatis-PageHelper高效准确地分页及动态排序

    Mybatis对List遍历的实现方法

    Mybatis对where语句动态拼装的几个版本演变

 

   产品模块注意:

      1.在上传文件时需验证是否为管理员,在上传文件的大小上限制最大为4m
      2.上传文件流程为,先将文件保存到tomcat服务器上的update文件夹中,随后连接FTP服务器,配置好上传FTP工具类后将tomcat服务器上存放的文件通过工具类转移到FTP服务器指定文件夹下,然后删除掉tomcat服务器中的原先文件。
      3.富文本上传与SpringMVC上传最明显的区别在于返回值上,其次富文本上传成功后在返回时需设置响应头response.addHeader("Access-Control-Allow-Headers","X-File-Name");

-----------------4.购物车模块--------------------

  功能模块:

      加入商品      add(HttpServletRequest httpServletRequest, Integer count, Integer productId)

      更新商品数     update(HttpServletRequest httpServletRequest, Integer count, Integer productId)

      查询商品数     getCartProductCount(HttpServletRequest httpServletRequest)

      移除商品      deleteProduct(HttpServletRequest httpServletRequest,String productIds)

      单选/取消       select(HttpServletRequest httpServletRequest,Integer productId),unSelect(HttpServletRequest httpServletRequest,Integer productId)

      全选/取消       selectAll(HttpServletRequest httpServletRequest),unSelectAll(HttpServletRequest httpServletRequest)

      购物车列表       list(HttpServletRequest httpServletRequest, Integer count, Integer productId)

 

  技术学习:

       购物车模块的设计思想

      如何封装一个高复用购物车核心方法

      Bigdecimal解决浮点型商业运算中丢失精度的问题

   购物车模块注意:

      1.在购物车内产品数量修改时验证是否超过数据库中产品库存,若超出则改为产品库存最大量(如修改为200台手机,此时发现数据库只有100台,则将购物车中购买数改为100)
      2.在购物车开发时肯定会接触到价格数字上的运算,这时候若是用普通的数据类型进行运算则容易产生丢失精度。此时可以用到java中的BigDecimal类进行运算,该类中的字符串构造方法可保证运算时精度的准确度。
      3.在购物车的价格数字上的运算上偶尔会有除不尽的情况,此时应该尽可能保持精度然后选择保留几位小数且四舍五入

    

 

-----------------5.收货地址模块-----------------

  功能模块:

      添加地址    add(Shipping shipping)

      删除地址    del(HttpServletRequest httpServletRequest, Integer shippingId)

      修改地址    update(HttpServletRequest httpServletRequest, Shipping shipping)

      地址列表分页  select(HttpServletRequest httpServletRequest, @RequestParam(value = "pageNum",defaultValue = "1") int pageNum,@RequestParam(value = "pageSize",defaultValue = "10") int pageSize)

      地址详情    select(HttpServletRequest httpServletRequest, Integer shippingId)

 

  技术学习:

         SpringMVC数据绑定中对象绑定

      mybatis自动生成主键、配置和使用

      如何避免横向越权漏洞的巩固

 

   地址模块注意:

      1.避免横向越权(用户登录后,在接口处传递shippingID时使用他人的shippingid)(在sql语句上处理shipping时加上userId判断即可)

 

-----------------6.支付模块-----------------------

  功能模块:

      支付宝对接    pay(HttpServletRequest httpServletRequest, Long orderNo, HttpServletRequest request)

      支付回调     alipayCallback(HttpServletRequest request)

      查询支付状态     queryOrderPayStatus(HttpServletRequest httpServletRequest, Long orderNo)

 

  技术学习:

       支付宝调试技巧

      熟悉支付宝对接核心文档,调通支付宝支付功能官方Demo

      解析支付宝SDK对接源码

      RSA1和RSA2验证签名及加解密

      避免支付宝重复通知和数据校验

      生成二维码,并持久化到图片服务器

 

   支付模块注意:

      1.在订单的回调时会访问地址,由于我们的外网穿透使用的是免费的所以域名不稳定,每次都需要重新配置,可购买域名以及稳定的通道进行绑定。
      2.在支付宝的回调验签时,会判断是否为支付宝发出的签并且还要避免重复的通知
      3.支付宝的回调验签默认为utf-8编码,若想指定编码则需在构造ClientBuilder类时指定好编码

-----------------7.订单模块-----------------------

  功能模块:

      前台

        创建订单    create(HttpServletRequest httpServletRequest, Integer shippingId)

        选中商品信息  getOrderCartProduct(HttpServletRequest httpServletRequest)

        订单列表    list(int pageNum, int pageSize)

        订单详情    detail(HttpServletRequest httpServletRequest,Long orderNo)

        取消订单    cancel(HttpServletRequest httpServletRequest, Long orderNo)

      后台 

        订单列表    orderList(HttpServletRequest httpServletRequest, int pageNum, int pageSize)

        订单搜索    orderSearch(HttpServletRequest httpServletRequest, Long orderNo,int pageNum,int pageSize)

        订单详情    orderDetail(HttpServletRequest httpServletRequest, Long orderNo)

        订单发货    orderSendGoods(HttpServletRequest httpServletRequest, Long orderNo)

 

 

  技术学习:

         避免业务逻辑中横向越权和纵向越权等安全漏洞

        设计实用、安全、扩展性强大的常量、枚举类

        订单号生成规则、订单严谨性判断

        POJO和VO之间的实际操练

        mybatis批量插入

 

    订单模块注意:

     1.订单模块涉及到很多商品,订单条目,地址,订单详细等内容,在该模块我们会创建很多相对应的装配对象(VO),将对象装配好再返回。  

 

 

 

 

 


 

项目后端开发分析

 

1.在Git托管服务上选择使用国内的Gitee,之所以选择Gitee而不是选择GitHub是因为GitHub为国外的平台,国内的用户在使用上经常遇到的问题就是
访问的速度太慢,有时候还会出现无法连接的情况。和GitHub相比Gitee也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对
于团队协作开发,Gitee的还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。

2.项目统一字符集UTF-8

3.为了方便前期的项目接口测试 没有限制访问方式(Get/Post/Put....)

4.为避免横向越权(当前用户去操作其他用户的所有),在大部分的sql查询上添加上了userId这一条件,这样就不会去查询到不属于该用户的所有了

5.在访问数据库之前尽可能做好条件判断,如果不成立,则直接手动返回错误,尽量减少对数据库的访问。
  例如(查询当前用户订单时,提前判断好接口传递过来的参数是否有值,若无值则直接返回参数有误)
  (参数有误在接口开发上需大量使用到,则直接声明在常量类上)

6.在做订单支付时需处理好状态码且等待第三方的回调,等回调成功后方可改为支付成功(避免单方帐)
 (单方帐:买方提交订单后未支付成功订单,然后后台显示支付成功。或是相反)

7.为方便项目后期的更新改进,在接口处要使前后端分离

8.项目中大量使用到分页功能,于是我们可以将分页独立出一个对象

9.在项目的搭建时对代码做好校验,在可能出现错误的地方做好try-catch,避免项目放在运行的时候报错,这样会很麻烦。

10.项目中的pojo类大量使用到Getter和Setter方法,这里我们引入Lombok工具,它可以通过简单注解来精简代码达到消除冗长代码的目的。

11.在项目的开发会面临到各个阶段的开发时期,有本地开发环境(Local)、开发环境(Dev)、测试环境(Beta)、线上环境(Prod)
   在各个时期使用到的环境,数据库FTPServer等等都是不一样的,所以这里我们引入Maven环境隔离

12.在项目开发时,为提高服务端效率,项目采用了tomcat集群。既然采用了集群,那就要考虑到session的存放,这里我们选用了Redis进行集体存放
   在存放的时候呢我们采用JSessionID当键存入Redis,但是nginx负载均衡会将服务分发给各个tomcat,每个tomcat的JSessionID都不相同,在Redis取值的时候
   也面临取不到值的状况。此时我们改用Cookie来存放对应的键,当用户在进行单点登录的时候,被负载均衡到另一个服务器的时候,Cookie也会跟随着浏览器去             到另一个服务器。

13.在我们用户登录后将session存放(User)到Redis的时候(存储时间30分钟)。此时我们在网站中浏览30分钟后,发现Redis中存储的对应数据到指定时间被清除了。
   这个时候我们设置了一个过滤器(Filter),当我们在项目中进行操作(*.do)的时候触发过滤器,将我们存放在Redis的数据的消除时间重计

14.在用户模块中有个忘记密码功能,为了避免横向越权,我们加入了TokenCache做了记号,在忘记密码修改密码的时候需带记号进行修改

   初期没做tomcat项目集群的时候,TokenCache是存放在tomcat上的。在做了集群后呢,我们应当将其迁移到Redis上。

15.后期若是还要提升,可以将用户模块单独拿出做成服务中心,这样也可以对项目有个解耦的效果

16.在项目的开发中会面临很多的传参,在传参的时候我们要考虑到是使用基本类型还是包装类型,拿int和Integer举例,当我
   们在面临age的传参时,如果是基本类型int,那这个key是必须传参数的,不能为Null。如果是包装类型Integer,那这个
   key是可以为null的。在项目的开发中应当尽量避免报错,所以在参数类型的选择上应该选择好

17.考虑到项目的管理员模块涉及到许多的登录/权限的校验,我们采用SpringMVC拦截器进行权限拦截。

 

推荐阅读