【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结

项目简介

**项目地址:**https://github.com/newbee-ltd/newbee-mall

**项目介绍:**newbee-mall 项目是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 2.X 及相关技术栈开发。 前台商城系统包含首页门户、商品分类、新品上线、首页轮播、商品推荐、商品搜索、商品展示、购物车、订单结算、订单流程、个人订单管理、会员中心、帮助中心等模块。后台管理系统包含数据面板、轮播图管理、商品管理、订单管理、会员管理、分类管理、设置等模块。

经笔者验证,是一个值得一看的Spring项目!

项目运行

想要运行该项目需要进行相应的配置:

  • 安装Mysql

如果是Mac电脑可以参考:Mac电脑安装mysql

安装好Mysql后新建一个数据库,并运行项目文件夹下的newbee-mall-schema.sql文件,将数据插入数据库,可参考:如何执行sql文件插入数据库

  • 配置Mysql

修改application.properties文件中的mysql数据库名为自己的数据库名:

spring.datasource.name=newbee(自己的数据库名)
spring.datasource.username=root
spring.datasource.password=12345678
  • 解压img,修改路径

将项目路径下的upload.zip解压,并记录下路径;

在common/constansts中修改路径:

//public final static String FILE_UPLOAD_DIC = "/opt/image/upload/";//上传文件的默认url前缀,根据部署设置自行修改
    public final static String FILE_UPLOAD_DIC = "/Users/xiuyi/Desktop/javaer/newbee-mall/src/main/resources/upload/";
  • 运行、访问

运行成功之后,打开网页,访问:localhost:28089即可。

4e4ed2edc36ef3a928a2e5cdddf381a8.png

项目中还出现问题可以参考:如何运行new-bee

项目框架

项目整体框架如下:

其中,

  • common:定义一些公用字段名;
  • util:提供一些静态帮助方法。如邮箱格式验证、MD5码生成等;
  • config:WebMvcConfigure的配置,主要设置拦截器以及资源文件;
  • dao跟数据层的交互接口,获取货品信息、用户信息、订单信息等;项目使用的数据库是Mybatis,通过XML配置dao接口跟数据库的映射关系;
  • Interceptor:拦截器的具体实现,完成身份验证、购物车数量实时更新;
  • controller:MVC框架中的控制器,根据web端的请求调用相应的service层进行逻辑处理;
  • entity:项目中的常用字段类;
  • service:服务层,由控制器进行调用;

功能细分

这个部分对项目的各个功能进行进一步的分析,捋清楚调用关系并补充一些细节。

其实这是一个典型的MVC项目,即Model-View-Controller,根据对应的客户端请求,由Spring MVC调度相应的Controller进行处理,Controller调用service进行业务逻辑处理,而后交给Thymeleaf模板引擎对结果Entity进行渲染,将处理结果返回给用户,从而显示在浏览器上,整个流程如下:

商城首页

首页,即访问:网页地址/index(或/,或/),获取新蜂商城首页。

4e4ed2edc36ef3a928a2e5cdddf381a8.png


在IndexController中,当接收到来自客户端“/”的请求时,访问数据库去获取分类数据、轮播图、新品、推荐数据,经由模板引擎(用的是thymeleaf)生成html返回给客户端:

@GetMapping({"/index", "/", "/index.html"})
public String indexPage(HttpServletRequest request) {
    List<NewBeeMallIndexCategoryVO> categories = newBeeMallCategoryService.getCategoriesForIndex();
    if (CollectionUtils.isEmpty(categories)) {
        return "error/error_5xx";
    }
    List<NewBeeMallIndexCarouselVO> carousels = newBeeMallCarouselService.getCarouselsForIndex(Constants.INDEX_CAROUSEL_NUMBER);
    List<NewBeeMallIndexConfigGoodsVO> hotGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_HOT.getType(), Constants.INDEX_GOODS_HOT_NUMBER);
    List<NewBeeMallIndexConfigGoodsVO> newGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_NEW.getType(), Constants.INDEX_GOODS_NEW_NUMBER);
    List<NewBeeMallIndexConfigGoodsVO> recommendGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_RECOMMOND.getType(), Constants.INDEX_GOODS_RECOMMOND_NUMBER);
    request.setAttribute("categories", categories);//分类数据
    request.setAttribute("carousels", carousels);//轮播图
    request.setAttribute("hotGoodses", hotGoodses);//热销商品
    request.setAttribute("newGoodses", newGoodses);//新品
    request.setAttribute("recommendGoodses", recommendGoodses);//推荐商品
    return "mall/index";
}

注意到上述Controller的返回值是“mail/index”注意到上述Controller的返回值是“mail/index”——这是因为Spring Boot集成的Thymeleaf默认配置会自动渲染**classpath:templates/网页名。**Thymeleaf的详细教程可参考Thymeleaf详解

因此在使用Thymeleaf模板引擎时,编写Controller只需要返回指定的网页名即可,而不用想WebMVC一样,通过ModelView来进行渲染:

@RequestMapping(value = "/greeting") 
public ModelAndView test(ModelAndView mv) { 
    mv.setViewName("/greeting"); 
    mv.addObject("title","欢迎使用Thymeleaf!"); 
    return mv; 
} 

用户注册、登录

点击注册按钮,即跳转至用户登录界面:

43a47aa68635f81eb28408e3dbd37df9.png

这两个按钮对应的路径为/rigister和/login,处理请求的是mall/PersonalController,如果是获取登录、注册页面,则调用相应的模板引擎进行渲染:

    @GetMapping({"/login", "login.html"})
    public String loginPage() {
        return "mall/login";
    }

    @GetMapping({"/register", "register.html"})
    public String registerPage() {
        return "mall/register";
    }

如果是获取登录、注册的表单,则应该调用相应的post方法,以登录为例:

@PostMapping("/login")
@ResponseBody
public Result login(@RequestParam("loginName") String loginName,
                    @RequestParam("verifyCode") String verifyCode,
                    @RequestParam("password") String password,
                    HttpSession httpSession) {
    if (StringUtils.isEmpty(loginName)) {
        return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_NULL.getResult());
    }
    if (StringUtils.isEmpty(password)) {
        return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_PASSWORD_NULL.getResult());
    }
    if (StringUtils.isEmpty(verifyCode)) {
        return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_NULL.getResult());
    }
    String kaptchaCode = httpSession.getAttribute(Constants.MALL_VERIFY_CODE_KEY) + "";
    if (StringUtils.isEmpty(kaptchaCode) || !verifyCode.toLowerCase().equals(kaptchaCode)) {
        return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_ERROR.getResult());
    }
    String loginResult = newBeeMallUserService.login(loginName, MD5Util.MD5Encode(password, "UTF-8"), httpSession);
    //登录成功
    if (ServiceResultEnum.SUCCESS.getResult().equals(loginResult)) {
        //删除session中的verifyCode
        httpSession.removeAttribute(Constants.MALL_VERIFY_CODE_KEY);
        return ResultGenerator.genSuccessResult();
    }
    //登录失败
    return ResultGenerator.genFailResult(loginResult);
}

值得注意的是,在处理登录时,会先对用户名和密码进行基本的格式验证,而后交由数据库进行验证。

商品详情页

以HUAWEI mate30 Pro为例:

image.png

其控制器为mail/GoodsController,通过访问数据库获取商品数据,放进模板引擎进行渲染:

@GetMapping("/goods/detail/{goodsId}")
public String detailPage(@PathVariable("goodsId") Long goodsId, HttpServletRequest request) {
    if (goodsId < 1) {
        return "error/error_5xx";
    }
    NewBeeMallGoods goods = newBeeMallGoodsService.getNewBeeMallGoodsById(goodsId);
    if (goods == null) {
        NewBeeMallException.fail(ServiceResultEnum.GOODS_NOT_EXIST.getResult());
    }
    if (Constants.SELL_STATUS_UP != goods.getGoodsSellStatus()) {
        NewBeeMallException.fail(ServiceResultEnum.GOODS_PUT_DOWN.getResult());
    }
    NewBeeMallGoodsDetailVO goodsDetailVO = new NewBeeMallGoodsDetailVO();
    BeanUtil.copyProperties(goods, goodsDetailVO);
    goodsDetailVO.setGoodsCarouselList(goods.getGoodsCarousel().split(","));
    request.setAttribute("goodsDetail", goodsDetailVO);
    return "mall/detail";
}

商品搜索

商品搜索时,其控制器也是mail/GoodsController,通过关键词去数据库中搜索属性对应的商品

并显示。这里还有分页设计,限制每一页的显示货品个数,同时还有排序属性:

image.png

另外,可以看出这里的搜索栏目与首页的分类栏目所用的接口是一样,只不过搜索时是借助keyword,而分类使用的是category。


@GetMapping({"/search", "/search.html"})
public String searchPage(@RequestParam Map<String, Object> params, HttpServletRequest request) {
    if (StringUtils.isEmpty(params.get("page"))) {
        params.put("page", 1);
    }
    params.put("limit", Constants.GOODS_SEARCH_PAGE_LIMIT);
    //封装分类数据
    if (params.containsKey("goodsCategoryId") && !StringUtils.isEmpty(params.get("goodsCategoryId") + "")) {
        Long categoryId = Long.valueOf(params.get("goodsCategoryId") + "");
        SearchPageCategoryVO searchPageCategoryVO = newBeeMallCategoryService.getCategoriesForSearch(categoryId);
        if (searchPageCategoryVO != null) {
            request.setAttribute("goodsCategoryId", categoryId);
            request.setAttribute("searchPageCategoryVO", searchPageCategoryVO);
        }
    }
    //封装参数供前端回显
    if (params.containsKey("orderBy") && !StringUtils.isEmpty(params.get("orderBy") + "")) {
        request.setAttribute("orderBy", params.get("orderBy") + "");
    }
    String keyword = "";
    //对keyword做过滤 去掉空格
    if (params.containsKey("keyword") && !StringUtils.isEmpty((params.get("keyword") + "").trim())) {
        keyword = params.get("keyword") + "";
    }
    request.setAttribute("keyword", keyword);
    params.put("keyword", keyword);
    //搜索上架状态下的商品
    params.put("goodsSellStatus", Constants.SELL_STATUS_UP);
    //封装商品数据
    PageQueryUtil pageUtil = new PageQueryUtil(params);
    request.setAttribute("pageResult", newBeeMallGoodsService.searchNewBeeMallGoods(pageUtil));
    return "mall/search";
}

其实这几个环节中商品搜索部分是我最感兴趣的:它是怎么实现搜索的呢?

笔者测试了一下搜索系统,结果比较准确,难不成还集成了搜索引擎?好奇心驱使之下遂继续往下面挖,直到:

<select id="findNewBeeMallGoodsListBySearch" parameterType="Map" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from tb_newbee_mall_goods_info
    <where>
        <if test="keyword!=null and keyword!=''">
            and (goods_name like CONCAT('%',#{keyword},'%') or goods_intro like CONCAT('%',#{keyword},'%'))
        </if>
        <if test="goodsCategoryId!=null and goodsCategoryId!=''">
            and goods_category_id = #{goodsCategoryId}
        </if>
        <if test="goodsSellStatus!=null">
            and goods_sell_status = #{goodsSellStatus}
        </if>
    </where>
    <if test="orderBy!=null and orderBy!=''">
        <choose>
            <when test="orderBy == 'new'">
                <!-- 按照发布时间倒序排列 -->
                order by goods_id desc
            </when>
            <when test="orderBy == 'price'">
                <!-- 按照售价从小到大排列 -->
                order by selling_price asc
            </when>
            <otherwise>
                <!-- 默认按照库存数量从大到小排列 -->
                order by stock_num desc
            </otherwise>
        </choose>
    </if>
    <if test="start!=null and limit!=null">
        limit #{start},#{limit}
    </if>
</select>

从上述的Mybatis的动态Sql语句可以看出,整个商品搜索系统其实都是由底层的Mysql数据库提供的,而所谓的商品搜索也只是通过Like加%通配符的方式与商品名和商品简介进行匹配而已。

而这种搜索方式遇到模糊名词或者错误名词就难过了,什么都搜不出来,而且以通配符的方式进行搜索当数据量大的场景下,效率极其低。在真实的电商场景中,商品搜索是一个极其重要的组件,一般是基于倒排索引数据库(ES、OpenSearch等)的搜索引擎来提供商品搜索服务。

订单系统

购物车相关的逻辑控制都在mall/shoppingcartController中,暂不赘述:

image.png


顺带一提,支付的时候还会进行地址的验证,很棒!

PS:在一个健壮的系统中,在合适的地方进行数据验证能够有效地防止错误。

后台管理系统

后台管理系统可以看让商家对自己商品进行管理(用户名:admin,密码:123456),诸如增加商品、设置轮播图、修改排序等等:


image.png

后台管理系统可以进行商品管理、订单管理、会员管理等等,如果说之前的系统是对数据库的查询动手的话,那么这个环节主要是实现对数据库的修改。(现在的开发只要会CRUD就不怕没饭吃啊)

这个后台管理系统简直神了!

可以说前端、数据库、动态网页等技术全都用上了,很有必要仔细的学习!

总结

这个开源项目的内容包含了太多可以学习的知识:

  • Spring Boot的配置
  • Spring MVC的使用
  • Thymeleaf模板引擎
  • Mybatis的使用
  • Mysql的配置
  • Interceptot拦截器
  • 身份验证
  • kaptcha验证码生成
  • AdminLTE仪表盘****等知识

可以说要完全吃透这一个项目需要的准备知识很多,但是收获也多!

这个开源项目确实很不错,谢谢十三老师的分享!

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
258 3
|
3月前
|
安全 Java 编译器
new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析
逃逸分析是一种静态程序分析技术,用于判断对象的可见性与生命周期。它帮助即时编译器优化内存使用、降低同步开销。根据对象是否逃逸出方法或线程,分析结果分为未逃逸、方法逃逸和线程逃逸三种。基于分析结果,编译器可进行同步锁消除、标量替换和栈上分配等优化,从而提升程序性能。尽管逃逸分析计算复杂度较高,但其在热点代码中的应用为Java虚拟机带来了显著的优化效果。
120 4
|
1月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
1月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
117 8
|
1月前
|
存储 Java Go
【Java】(3)8种基本数据类型的分析、数据类型转换规则、转义字符的列举
牢记类型转换规则在脑海中将编译和运行两个阶段分开,这是两个不同的阶段,不要弄混!
183 2
|
1月前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
198 1
|
2月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
4月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
391 3
|
3月前
|
机器学习/深度学习 安全 Java
Java 大视界 -- Java 大数据在智能金融反洗钱监测与交易异常分析中的应用(224)
本文探讨 Java 大数据在智能金融反洗钱监测与交易异常分析中的应用,介绍其在数据处理、机器学习建模、实战案例及安全隐私等方面的技术方案与挑战,展现 Java 在金融风控中的强大能力。
|
4月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
227 0