栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > 架构设计

shop第一天

架构设计 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

shop第一天

电商行业发展

近年来,中国的电子商务快速发展,交易额连创新高,电子商务在各领域的应用不断拓展和深化、相关服务业蓬勃发展、支撑体系不断健全完善、创新的动力和能力 不断增强。电子商务正在与实体经济深度融合,进入规模性发展阶段,对经济社会生活的影响不断增大,正成为我国经济发展的新引擎。

中国电子商务研究中心数据显示,截止到2012年底,中国电子商务市场交易规模达7.85万亿人民币,同比增长30.83%。其中,B2B电子商务交易额 达6.25万亿,同比增长27%。而2011年全年,中国电子商务市场交易额达6万亿人民币,同比增长33%,占GDP比重上升到13%;2012年,电子商务占GDP的比重已经高达15%。2013年我国电子商务规模已突破十万亿大关。

电商行业技术特点
  • 技术新

  • 技术范围广

  • 分布式

  • 高并发、集群、负载均衡、高可用

  • 海量数据

  • 业务复杂

  • 系统安全

shop商城介绍 电商行业的模式:
  • B2B:企业到企业,商家到商家。代表:阿里巴巴、慧聪网

  • B2C:商家到客户。代表:京东、淘宝商城(B2B2C)

  • C2C:客户到客户。淘宝集市

  • O2O:线上到线下

shop商城的模式

shop网上商城是一个综合性的B2C平台,类似京东商城、天猫商城。会员可以在商城浏览商品、下订单,以及参加各种活动。

管理员、运营可以在平台后台管理系统中管理商品、订单、会员等。客服可以在后台管理系统中处理用户的询问以及投诉。

功能描述

后台管理系统:管理商品、订单、类目、商品规格属性、用户管理以及内容发布等功能。

前台系统:用户可以在前台系统中进行注册、登录、浏览商品、首页、下单等操作。

会员系统:用户可以在该系统中查询已下的订单、收藏的商品、我的优惠券、团购等信息。

订单系统:提供下单、查询订单、修改订单状态、定时处理订单。

搜索系统:提供商品的搜索功能。

单点登录系统:为多个系统之间提供用户登录凭证以及查询登录用户的信息。

技术架构

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

  • 单一应用架构

    • 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
    • 此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。
    • 缺点:随着应用功能的增多,代码量越来越大,越来越难维护,那怎么解决代码一体化的问题?
  • 垂直应用架构

    • 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。
    • 此时,用于加速前端页面开发的 Web框架(MVC) 是关键。
    • 缺点:垂直架构中相同逻辑代码需要不断的复制,不能复用。每个垂直模块都相当于一个独立的系统。
  • 分布式服务架构

    • 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
    • 此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。
    • 缺点:服务越来越多,需要管理每个服务的地址,调用关系错综复杂,难以理清依赖关系,服务状态难以管理,无法根据服务情况动态管理。
  • 流动计算架构

    • 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
    • 此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。
    • 缺点:服务间会有依赖关系,一旦某个环节出错会影响较大,服务关系复杂,运维、测试部署困难,不符合DevOps思想。
  • 微服务架构:

    • 单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责

    • 微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。

    • 面向服务:面向服务是说每个服务都要对外暴露服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。

    • 自治:自治是说服务间互相独立,互不干扰

      • 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
      • 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
      • 前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动端开发不同接口
      • 数据库分离:每个服务都使用自己的数据源
      • 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护 Docker部署服务

思考:有什么问题?

  1. 模块之间耦合度太高,其中一个升级其他都得升级

  2. 开发困难,各个团队开发最后都要整合一起

  3. 系统的扩展性差

  4. 不能灵活的进行分布式部署。

解决方法:系统拆分

优点:把模块拆分成独立的工程,单点运行。如果某一个点压力大可以对这一个点单独增加配置。其他的点不受影响。

分布式架构:把系统按照模块拆分成多个子系统。

优点:

  1. 把模块拆分,使用接口通信,降低模块之间的耦合度。

  2. 把项目拆分成若干个子项目,不同的团队负责不同的子项目。

  3. 增加功能时只需要再增加一个子项目,调用其他系统的接口就可以。

  4. 可以灵活的进行分布式部署。

缺点:

  1. 系统之间交互需要使用远程通信,接口开发增加工作量。
技术选型
  • Java(核心编程语言)

  • SpringBoot、SpringMVC、Mybatis(三大框架)

  • Dubbo(分布式服务框架)

  • Zookeeper(服务注册中心)

  • Redis(缓存数据库)

  • Elasticseach(搜索引擎)

  • SSO(单点登录)

  • MySql(数据库)

  • Nginx(web服务器)

  • 七牛云(文件上传服务器)

  • RabbitMQ(消息队列)

  • Alipay(支付宝支付)

  • 腾讯验证码(验证)

  • jQuery、Bootstrap(前端框架)

  • doT.js(模板引擎)

  • UEditor(富文本编辑器)

  • Google Kaptcha(图形验证码)

开发环境和工具
  • IntelliJ IDEA

  • Maven 3.6.2

  • JDK 1.8.0_231

  • Tomcat 9.0.29

  • MySql 5.7.20

  • Nginx 1.16.1

  • Dubbo 2.6.0

  • Zookeeper 3.4.13

  • Redis 5.0.3

  • Elasticsearch 7.4.2

  • Win10(开发环境)

  • Centos7.3-1908(部署环境)

说明:单独使用可以随意选择版本,如果在集群下需要考虑多种技术之间的兼容性。比如:Redis、Elasticsearch可以使用单台服务器也可以使用多台服务器,使用多台服务器时需要考虑与其他技术的兼容性,或者统一都用高版本jar包,或者统一降低版本jar包,一高一低可能会出问题。

人员配置
  • 产品经理:3人,确定需求以及给出产品原型图。

  • 项目经理:1人,项目管理。

  • 架构师:2人,负责解决技术难点与定制标准(开发准则、接口标准、技术选型等)。

  • 前端团队:5人,根据产品经理给出的原型制作静态页面。

  • 安卓团队:5人,开发android客户端。

  • IOS团队:5人,开发IOS客户端。

  • 后端团队:20人,实现整个系统业务功能。

  • 测试团队:5人,测试所有的功能、性能、安装。

  • 运维团队:3人,项目的发布以及维护。

  • 如果采用oracle,需要配备DBA:2人。

shop商城研发优势
  1. 节约成本

包括硬件成本和软件成本,硬件包括店面、房租、装修、印刷、纸张等最必须用品,软件包括网上商城购物系统、网络信息、图片、视屏等,都可长期使用、良性循环、经济和环保。

  1. 营销推广经济、便捷

传统媒体广告费用高昂,更适合于进行品牌塑造;而网络营销主要是策略与定位把控的问题,实惠很多,费用与传统媒体相比微乎其微,并且流量与用户也更加精准。

  1. 信息更加立体、全面

通过互联网,企业的信息展示、品牌塑造和形象宣传可以通过文字、图片、音频、视频等多维度进行现实与虚拟相结合的展示,使用户对企业的了解更加立体和全面,有助于形成良好的形象与口碑。

  1. 管理高效、便捷

运用信息化的数据库管理,各类信息精准、清晰、无误的保存,避免出现人工操作出现低级错误的情况,可随时查阅、核算、统计。

功能点
  1. 商家入驻

商家入驻功能包括:商家入驻申请流程、商家店铺自定义装修功能、多套店铺模板选择、商家店铺街展示、商家独立店铺功能、商铺报表统计功能、搜索店铺列表页、商家自定义广告位、区分平台与商家分类、类型、订单分单功能(按商家)、订单退换功能、商家订单佣金结算等。

  1. 会员中心

会员中心功能包括:会员中个人主页美化、会员中心订单列表美化、会员中心收货地址列表美化 、会员中心缺货登记、会员中心退换货、会员中心退换货详情页 、用户信息、新增会员头像上传功能 、平台红包、物流跟踪功能、资金管理

  1. 购物车

购物车功能包括:购物车选择购买功能、购物车加强功能、商品促销满减、满赠、折扣功能、凑单功能、简化购物流程、购物车为空时去购物功能、提示购物车商品是否有库存。

  1. 订单

订单功能包括:商家自定义配送方式和运费、门店自提功能、发票功能、商品无库存时提交订单弹窗提示继续购物、结算页面无货提交弹出框、快递配送方式选择如韵达。

  1. 积分商城

积分商城功能包括:积分商品列表、兑换商品排序、精品推荐、热门兑换、兑换商品详情页。

  1. 拍卖活动

拍卖活动功能包括:拍卖活动列表、竞拍商品排序和搜索、拍卖商品详情页、出价记录、商家店铺。

  1. 优惠活动

优惠活动功能包括:优惠活动列表页、优惠范围、优惠方式。

后台管理系统——项目框架搭建 shop父模块 创建pom项目

Maven提供了聚合类型的项目,其本质就是一个分布式架构。

Maven分布式架构-聚合类型,需要一个parent项目,shop-parent就是parent项目。

编辑pom.xml配置文件


	4.0.0

	
	com.xxxx
	
	shop
	
	pom
	
	1.0-SNAPSHOT

	
	
		shop-common
		shop-generator
		shop-manager
		shop-order
		shop-portal
		shop-rpc
		shop-sso
	

	
	
	
		org.springframework.boot
		spring-boot-starter-parent
		2.2.2.RELEASE
	

	
	
		
		1.8
		
		2.1.1
		
		1.2.13
		
		8.0.18
		
		1.1.20
	

	
	
		
			
			
				org.mybatis.spring.boot
				mybatis-spring-boot-starter
				${mybatis.version}
			
			
			
				com.github.pagehelper
				pagehelper-spring-boot-starter
				${pagehelper.version}
			
			
			
				mysql
				mysql-connector-java
				${mysql.version}
			
			
			
				com.alibaba
				druid
				${druid.version}
			
        
	

shop-common子模块

用于添加公共的工具类、枚举类、拦截器等。

编辑pom.xml配置文件



  4.0.0

  com.xxxx
  shop-common
  1.0-SNAPSHOT

  
  
    com.xxxx
    shop
    1.0-SNAPSHOT
  

  
    UTF-8
    1.8
    1.8
  

  
    
    
      org.springframework.boot
      spring-boot-starter-web
    
    
    
      com.github.pagehelper
      pagehelper-spring-boot-starter
    
  

shop-manager子模块

后台管理系统

编辑pom.xml配置文件



  4.0.0

  com.xxxx
  shop-manager
  1.0-SNAPSHOT

  
  
    com.xxxx
    shop
    1.0-SNAPSHOT
  

  
    UTF-8
    1.8
    1.8
  

  
    
    
      com.xxxx
      shop-common
      1.0-SNAPSHOT
    
    
    
      org.springframework.boot
      spring-boot-starter-freemarker
    
    
    
      org.mybatis.spring.boot
      mybatis-spring-boot-starter
    
    
    
      mysql
      mysql-connector-java
    
    
    
      com.alibaba
      druid
    
    
    
      org.springframework.boot
      spring-boot-starter-test
      test
      
        
          org.junit.vintage
          junit-vintage-engine
        
      
    
  

application-dev.yml

server:
  port: 9090                                # 项目访问端口,默认 8080
  servlet:                                 # 项目访问路径,默认 /
    context-path: /shop-manager

# Spring
spring:
  # 数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    # 指定 druid 连接池以及 druid 连接池配置
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 1                       # 初始连接数
      max-active: 20                        # 最大连接数
      max-idle: 20                          # 最大空闲
      min-idle: 1                           # 最小空闲
      max-wait: 60000                       # 最长等待时间
  # freemarker 模板引擎
  freemarker:
    cache: false
    charset: UTF-8
    content-type: text/html;charset=UTF-8
    enabled: true
    suffix: .ftl
    template-loader-path: classpath:/views/
    # 配置模板里是否可以直接取request的属性 request是别名
    request-context-attribute: request
    # 配置将request和session中的键值添加到
    # AbstractTemplateView类的renderMergedOutputModel方法中的model这个Map参数中
    expose-request-attributes: true
    expose-spring-macro-helpers: true
    # 配置模板里是否可以直接取session的属性 true 是允许
    expose-session-attributes: true
    settings:
      tag_syntax: auto_detect               # 配置标签语法为自动,页面可以将 <> 改为 [],为了区别 html 标签
      template_update_delay: 0              # 模板更新时间,单位秒
      default_encoding: UTF-8               # 默认编码字符集
      output_encoding: UTF-8                # 模板输出编码字符集
      locale: zh_CN                         # 本地化配置
      date_format: yyyy-MM-dd               # 日期格式化
      time_format: HH:mm:ss                 # 时间格式化
      datetime_format: yyyy-MM-dd HH:mm:ss  # 日期时间格式化
      number_format: #.##                   # 数字格式化
      boolean_format: true,false            # boolean格式化
      # ignore,debug,html_debug,rethrow
      # 1.TemplateExceptionHandler.IGNORE_HANDLER简单地压制所有异常
      # 它对处理异常没有任何作用,也不会重新抛出异常,页面可以正常渲染,后台抛异常
      # 2.TemplateExceptionHandler.DEBUG_HANDLER打印堆栈信息和重新抛出异常。这是默认的异常控制器
      # 3.TemplateExceptionHandler.HTML_DEBUG_HANDLER和DEBUG_HANDLER相同
      # 但是可以格式化堆栈跟踪信息,HTML页面,建议使用它而不是DEBUG_HANDLER
      # 4.TemplateExceptionHandler.RETHROW_HANDLER简单重新抛出所有异常而不会做其他的事情
      # 5.使用自定义异常类实现TemplateExceptionHandler重写handleTemplateException方法
      template_exception_handler: html_debug
# MyBatis
mybatis:
  # 配置 MyBatis数据返回类型别名(默认别名是类名)
  type-aliases-package: com.xxxx.manager.pojo
  # 配置 MyBatis Mapper 映射文件
  mapper-locations: classpath:/mapper
@Controller
public class PageController {
	
	@RequestMapping("/{page}")
	public String page(@PathVariable String page) {
		System.out.println(page);
		return page;
	}
}

ManagerApplication.java

package com.xxxx.manager;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class ManagerApplication {
	public static void main(String[] args) {
		SpringApplication.run(ManagerApplication.class,args);
	}
}

Welcome.ftl


<#assign ctx=request.contextPath/>



    shop商城后台管理系统


${ctx}欢迎使用shop商城后台管理系统

程序运行前先把parent项目install一下

将html页面修改为freemarker页面 将.html复制到项目中,并将后缀改为.ftl

首页.html -> index.ftl

欢迎.html -> welcome.ftl

在.ftl文件顶部设置变量ctx,其值为项目的根路径

<#assign ctx=request.contextPath/>
批量修改freemarker中资源文件引用路径

css、js、images等资源文件的引用路径

再修改index.ftl的iframe的欢迎页面

Freemarker提取公用模板 公用模板head.ftl

<#assign ctx=request.contextPath/>

shop管理后台










<#---->















商品管理-商品分类页面-新增分类页面 提取freemarker页面

将商品分类.html和商品分类-新增.html文件导入项目中,修改为category/category-list.ftl和category/category-add.ftl,处理文件内代码,建议先处理静态文件商品分类.html然后再导入项目,因为文件过大,直接导入项目可能会导致IDEA很卡。

在.ftl文件顶部设置变量ctx,其值为项目的根路径

<#assign ctx=request.contextPath/>
Controller控制层

GoodsController.java

package com.xxxx.manager.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
@RequestMapping("goods")
public class GoodsController {

   
   @RequestMapping("category/add")
   public String categoryAdd(){
      return "goods/category/category-add";
   }

   
   @RequestMapping("category/list")
   public String categoryList(){
      return "goods/category/category-list";
   }
}
访问测试

修改ftl页面对应的访问地址,然后启动项目进行测试。

index.ftl

  • 商品分类
  • category-list.ftl

    
    
    配置访问根路径对应资源操作

    因为每次访问根页面都需要加一个index/,为了方便省事,我们可以通过页面跳转方式来实现:

    PageController.java

    @RequestMapping("/")
    public String index() {
        System.out.println("index");
        return "index";
    }
    
    MyBatis逆向工程generator的使用 generator是什么?

    ​ 是一个逆向工程,用于自动生成mapper.xml,mapper.java,pojo

    generator能干什么?

    ​ 对于单表而言,几乎是一个全能的工具,能很大提高编程效率。更多的关注业务逻辑的实现。

    怎么使用? 创建一个generator项目

    generator本身和我们商城项目没有关联,所以可以单独新建为一个Project,这边也做成Maven聚合项目里的一个子项目

    添加依赖
    
    
    
      4.0.0
    
      com.xxxx
      shop-generator
      1.0-SNAPSHOT
    
      
      
        com.xxxx
        shop
        1.0-SNAPSHOT
      
    
      
        UTF-8
        1.8
        1.8
      
    
      
        
        
          org.mybatis.spring.boot
          mybatis-spring-boot-starter
        
        
        
          org.mybatis.generator
          mybatis-generator-core
          1.3.7
        
      
    
      
      
        
          
          
            org.mybatis.generator
            mybatis-generator-maven-plugin
            1.3.7
            
              true
              
              true
              
              src/main/resources/mybatis-generator.xml
            
            
              
              
                mysql
                mysql-connector-java
                ${mysql.version}
              
              
              
                org.mybatis.generator
                mybatis-generator-core
                1.3.7
              
              
              
                com.xxxx
                shop-generator
                1.0-SNAPSHOT
              
            
          
        
      
    
    
    编写mybatis-generator.xml配置文件
    
    
    
    
    
    	
    		
    		
    		
    		
    		
    		
    
    		
    		
    		
    		
    		
    
    		
    		
    		
    			
    		
    
    		
    		
    		
    
    		
    		
    			
    		
    
    		
    		
    		
    			
    			
    			
    			
    		
    
    		
    		
    			
    			
    		
    
    		
    		
    			
    			
    		
    
    		
    		
    		
    ShopCommentGenerator工具类
    package com.xxxx.generator;
    
    import org.mybatis.generator.api.CommentGenerator;
    import org.mybatis.generator.api.IntrospectedColumn;
    import org.mybatis.generator.api.IntrospectedTable;
    import org.mybatis.generator.api.dom.java.CompilationUnit;
    import org.mybatis.generator.api.dom.java.Field;
    import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
    import org.mybatis.generator.api.dom.java.InnerClass;
    import org.mybatis.generator.api.dom.java.InnerEnum;
    import org.mybatis.generator.api.dom.java.JavaElement;
    import org.mybatis.generator.api.dom.java.Method;
    import org.mybatis.generator.api.dom.java.TopLevelClass;
    import org.mybatis.generator.api.dom.xml.XmlElement;
    import org.mybatis.generator.config.PropertyRegistry;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Properties;
    import java.util.Set;
    
    import static org.mybatis.generator.internal.util.StringUtility.isTrue;
    
    
    public class ShopCommentGenerator implements CommentGenerator {
    
    	
    	private Properties properties;
    	
    	private Properties systemPro;
    	
    	private boolean suppressDate;
    	
    	private boolean suppressAllComments;
    	
    	private String currentDateStr;
    
    	public ShopCommentGenerator() {
    		super();
    		properties = new Properties();
    		systemPro = System.getProperties();
    		suppressDate = false;
    		suppressAllComments = false;
    		currentDateStr = DateTimeFormatter.ofPattern("yyyy/MM/dd").format(LocalDateTime.now());
    	}
    
    	
    	protected String getDateString() {
    		String result = null;
    		if (!suppressDate) {
    			result = currentDateStr;
    		}
    		return result;
    	}
    
    	
    	@Override
    	public void addConfigurationProperties(Properties properties) {
    		this.properties.putAll(properties);
    		suppressDate = isTrue(properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE));
    		suppressAllComments = isTrue(properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));
    	}
    
    	
    	@Override
    	public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    		if (suppressAllComments)
    			return;
    		StringBuilder sb = new StringBuilder();
    		field.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
    		if (suppressAllComments)
    			return;
    		StringBuilder sb = new StringBuilder();
    		field.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
    		if (suppressAllComments) {
    			return;
    		}
    		topLevelClass.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable) {
    		if (suppressAllComments) {
    			return;
    		}
    		// 获取表注释
    		String remarks = introspectedTable.getRemarks();
    		innerClass.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable, boolean b) {
    		// 获取表注释
    		String remarks = introspectedTable.getRemarks();
    		innerClass.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) {
    		if (suppressAllComments)
    			return;
    		StringBuilder sb = new StringBuilder();
    		innerEnum.addJavaDocLine("");
    	}
    
    	
    	@Override
    	public void addGetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    	}
    
    	
    	@Override
    	public void addSetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    	}
    
    	
    	@Override
    	public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {
    	}
    
    	
    	@Override
    	public void addJavaFileComment(CompilationUnit compilationUnit) {
    	}
    
    	
    	@Override
    	public void addComment(XmlElement xmlElement) {
    	}
    
    	
    	protected void addJavadocTag(JavaElement javaElement, boolean markAsDoNotDelete) {
    	}
    
    	
    	@Override
    	public void addRootComment(XmlElement xmlElement) {
    	}
    
    	@Override
    	public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, Set set) {
    	}
    
    	@Override
    	public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set set) {
    	}
    
    	@Override
    	public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, Set set) {
    	}
    
    	@Override
    	public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set set) {
    	}
    
    	@Override
    	public void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable, Set set) {
    	}
    
    }
    
    执行

    执行前一定要先install,否则有可能报错

    结果 商品分类-新增分类-级联查询 Service服务层

    GoodsCategoryService.java

    package com.xxxx.manager.service;
    
    import com.xxxx.manager.pojo.GoodsCategory;
    
    import java.util.List;
    
    
    public interface GoodsCategoryService {
       
       List selectCategoryTopList();
    
       
       List selectCategoryListByParentId(short parentId);
    
       
       List selectCategoryList(short parentId);
    }
    

    GoodsCategoryServiceImpl.java

    package com.xxxx.manager.service.impl;
    
    import com.xxxx.manager.mapper.GoodsCategoryMapper;
    import com.xxxx.manager.pojo.GoodsCategory;
    import com.xxxx.manager.pojo.GoodsCategoryExample;
    import com.xxxx.manager.service.GoodsCategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    
    @Service
    public class GoodsCategoryServiceImpl implements GoodsCategoryService {
    
       @Autowired
       private GoodsCategoryMapper goodsCategoryMapper;
    
       
       @Override
       public List selectCategoryTopList() {
          // 创建查询对象
          GoodsCategoryExample example = new GoodsCategoryExample();
          // where条件
          example.createCriteria().andParentIdEqualTo((short) 0);
          return goodsCategoryMapper.selectByExample(example);
       }
    
       
       @Override
       public List selectCategoryListByParentId(short parentId) {
          // 创建查询对象
          GoodsCategoryExample example = new GoodsCategoryExample();
          // where条件
          example.createCriteria().andParentIdEqualTo(parentId);
          return goodsCategoryMapper.selectByExample(example);
       }
    
       
       @Override
       public List selectCategoryList(short parentId) {
          // 创建查询对象
          GoodsCategoryExample example = new GoodsCategoryExample();
          // where条件
          example.createCriteria().andParentIdEqualTo(parentId);
          return goodsCategoryMapper.selectByExample(example);
       }
    }
    
    Controller控制层

    GoodsController.java

    package com.ego.manager.controller;
    
    import com.xxxx.manager.pojo.GoodsCategory;
    import com.xxxx.manager.service.GoodsCategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.List;
    
    
    @Controller
    @RequestMapping("goods")
    public class GoodsController {
    
       @Autowired
       private GoodsCategoryService goodsCategoryService;
    
       
       @RequestMapping("category/add")
       public String categoryAdd(Model model){
          List gcList = goodsCategoryService.selectCategoryTopList();
          model.addAttribute("gcList",gcList);
          return "goods/category/category-add";
       }
    
       
       @RequestMapping("category/list")
       public String categoryList(){
          return "goods/category/category-list";
       }
    
       
       @RequestMapping("category/{parentId}")
       @ResponseBody
       public List selectCategoryList(@PathVariable Short parentId){
          return goodsCategoryService.selectCategoryList(parentId);
       }
    }
    
    ftl页面

    category-add.ftl

    function getCategory(id, next, select_id) {
        var url = '${ctx}/goods/category/' + id;
    // 用户重新选择顶级分类时,重置下级分类为:请选择商品分类,且清空下级分类信息
        var htmlStr = "";
        if(0==id){
            $("#"+next).html(htmlStr);
            return;
        }
        $.ajax({
            type: "GET",
            url: url,
            error: function (request) {
                layer.alert("获取子分类失败!");
            },
            success: function (result) {
                if (result.length > 0) {
                    for (i = 0; i < result.length; i++) {
                        htmlStr += ""
                    }
                    $("#"+next).html(htmlStr);
                }else {
                    layer.alert("获取子分类失败!");
                }
            }
        });
    }
    
    商品分类-新增分类-保存 编写公共保存状态返回对象

    shop-common的BaseResultEnum.java

    package com.xxxx.common.enums;
    
    
    public enum BaseResultEnum {
        // 自定义枚举类
        SUCCESS(200, "成功"),
        ERROR(400, "失败"),
        PASS_ERROR_01(501, "两次输入的密码不一致"),
        PASS_ERROR_02(502, "输入的原始密码不正确"),
        PASS_ERROR_03(503, "输入的验证码不正确"),
        PASS_ERROR_04(504, "用户名或者密码错误");
    
    
        private Integer code;
        private String message;
    
        private BaseResultEnum(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    }
    

    shop-common的BaseResult.java

    package com.xxxx.common.result;
    
    import com.github.pagehelper.PageInfo;
    import com.xxxx.common.enums.BaseResultEnum;
    
    import java.io.Serializable;
    
    
    public class BaseResult implements Serializable {
        // 状态编码
        private Integer code;
        // 状态描述
        private String message;
        // 分页对象(商品列表需要-pom.xml添加依赖)
        PageInfo pageInfo;
    
        //成功返回的对象
        public static BaseResult success() {
            BaseResult result = new BaseResult();
            result.setCode(BaseResultEnum.SUCCESS.getCode());
            result.setMessage(BaseResultEnum.SUCCESS.getMessage());
            return result;
        }
    
        //成功返回的对象-带分页对象
        public static BaseResult success(PageInfo pageInfo) {
            BaseResult result = new BaseResult();
            result.setCode(BaseResultEnum.SUCCESS.getCode());
            result.setMessage(BaseResultEnum.SUCCESS.getMessage());
            result.setPageInfo(pageInfo);
            return result;
        }
    
        //失败返回的对象
        public static BaseResult error() {
            BaseResult result = new BaseResult();
            result.setCode(BaseResultEnum.ERROR.getCode());
            result.setMessage(BaseResultEnum.ERROR.getMessage());
            return result;
        }
    
        public BaseResult(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public BaseResult() {
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public PageInfo getPageInfo() {
            return pageInfo;
        }
    
        public void setPageInfo(PageInfo pageInfo) {
            this.pageInfo = pageInfo;
        }
    
        @Override
        public String toString() {
            return "BaseResult [code=" + code + ", message=" + message + "]";
        }
    }
    
    Service服务层

    GoodsCategoryService.java

    int categorySave(GoodsCategory goodsCategory);
    

    GoodsCategoryServiceImpl.java

    @Override
    public int categorySave(GoodsCategory goodsCategory) {
       return goodsCategoryMapper.insertSelective(goodsCategory);
    }
    
    Controller控制层

    GoodsController.java

    @RequestMapping("category/save")
    @ResponseBody
    public BaseResult CategorySave(GoodsCategory goodsCategory) {
       int i = goodsCategoryService.categorySave(goodsCategory);
       //三目运算符
       return i > 0 ? BaseResult.success() : BaseResult.error();
    }
    
    ftl页面

    category-add.ftl

    添加l两个隐藏域,根据用户操作onchange动态赋值传往后台

    
    
    
    
    

    修改页面form表单中name的域名称与GoodsCategory对象中的属性一一对应

    js动态赋值,实现保存新增分类

    function getCategory(id, next, select_id) {
        var url = '${ctx}/goods/category/' + id;
        // 用户重新选择顶级分类时,重置下级分类为:请选择商品分类,且清空下级分类信息
        var htmlStr = "";
        //修改parentId
        $("#parentId").val(id);
        if (0 == id) {
            $("#" + next).html(htmlStr);
            //修改level
            $("#level").val(1);
            return;
        }
        $.ajax({
            type: "GET",
            url: url,
            error: function (request) {
                layer.alert("获取子分类失败!");
            },
            success: function (result) {
                if (result.length > 0) {
                    for (i = 0; i < result.length; i++) {
                        htmlStr += ""
                    }
                    $("#" + next).html(htmlStr);
                    $("#level").val(2);
                } else {
                    layer.alert("获取子分类失败!");
                }
            }
        });
    }
    
    
    function ajaxSubmitForm() {
        $.ajax({
            url: "${ctx}/goods/category/save",
            type: "post",
            data: $("#category_form").serialize(),
            dataType:"JSON",
            success:function (result) {
                if (result.code==200){
                    layer.confirm("保存成功",{btn:['继续新增','返回列表']},
                        function () {
                            window.location.href="${ctx}/goods/category/add";
                        },function () {
                            window.location.href="https://blog.csdn.net/m0_51071144/article/details/${ctx}/goods/category/list";
                    });
                }else {
                    layer.alert("保存失败");
                }
            },
            error:function () {
                layer.alert("保存失败");
            }
        });
    }
    
    
    function setParentId(parentId,level) {
        // 修改parentId和level
        if (0==parentId){
            $("#parentId").val($("#parent_id_1").val);
            $("#level").val(2);
            return;
        }
        // 修改parentId和level
        $("#parentId").val(parentId);
        $("#level").val(level);
    }
    
    商品分类-列表页 分析页面结构

    页面三级之间并无直接关系,而是由html、css页面样式实现了分级样式。

    页面结构三个普通tr,不同等级的tr缩进值(左边距)不同。

    第一级
    第二级
    第三级
    

    后台只需要获取每一级的商品列表内容返回页面循环处理展示即可

    后台代码实现 shop-manager-pojo添加VO对象

    GoodsCategoryVo.java

    package com.xxxx.manager.vo;
    
    import java.io.Serializable;
    import java.util.List;
    
    
    public class GoodsCategoryVo implements Serializable {
        
        
       private List children;
        
        private Short id;
    
        
        private String name;
    
        
        private String mobileName;
    
        
        private Short parentId;
    
        
        private String parentIdPath;
    
        
        private Byte level;
    
        
        private Byte sortOrder;
    
        
        private Byte isShow;
    
        
        private String image;
    
        
        private Byte isHot;
    
        
        private Byte catGroup;
    
        
        private Byte commissionRate;
    
        
        private static final long serialVersionUID = 1L;
    
        public List getChildren() {
            return children;
        }
    
        public void setChildren(List children) {
            this.children = children;
        }
    
        public Short getId() {
            return id;
        }
    
        public void setId(Short id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name == null ? null : name.trim();
        }
    
        public String getMobileName() {
            return mobileName;
        }
    
        public void setMobileName(String mobileName) {
            this.mobileName = mobileName == null ? null : mobileName.trim();
        }
    
        public Short getParentId() {
            return parentId;
        }
    
        public void setParentId(Short parentId) {
            this.parentId = parentId;
        }
    
        public String getParentIdPath() {
            return parentIdPath;
        }
    
        public void setParentIdPath(String parentIdPath) {
            this.parentIdPath = parentIdPath == null ? null : parentIdPath.trim();
        }
    
        public Byte getLevel() {
            return level;
        }
    
        public void setLevel(Byte level) {
            this.level = level;
        }
    
        public Byte getSortOrder() {
            return sortOrder;
        }
    
        public void setSortOrder(Byte sortOrder) {
            this.sortOrder = sortOrder;
        }
    
        public Byte getIsShow() {
            return isShow;
        }
    
        public void setIsShow(Byte isShow) {
            this.isShow = isShow;
        }
    
        public String getImage() {
            return image;
        }
    
        public void setImage(String image) {
            this.image = image == null ? null : image.trim();
        }
    
        public Byte getIsHot() {
            return isHot;
        }
    
        public void setIsHot(Byte isHot) {
            this.isHot = isHot;
        }
    
        public Byte getCatGroup() {
            return catGroup;
        }
    
        public void setCatGroup(Byte catGroup) {
            this.catGroup = catGroup;
        }
    
        public Byte getCommissionRate() {
            return commissionRate;
        }
    
        public void setCommissionRate(Byte commissionRate) {
            this.commissionRate = commissionRate;
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(getClass().getSimpleName());
            sb.append(" [");
            sb.append("Hash = ").append(hashCode());
            sb.append(", id=").append(id);
            sb.append(", name=").append(name);
            sb.append(", mobileName=").append(mobileName);
            sb.append(", parentId=").append(parentId);
            sb.append(", parentIdPath=").append(parentIdPath);
            sb.append(", level=").append(level);
            sb.append(", sortOrder=").append(sortOrder);
            sb.append(", isShow=").append(isShow);
            sb.append(", image=").append(image);
            sb.append(", isHot=").append(isHot);
            sb.append(", catGroup=").append(catGroup);
            sb.append(", commissionRate=").append(commissionRate);
            sb.append("]");
            return sb.toString();
        }
    }
    
    Service服务层

    GoodsCategoryService.java

    List selectCategoryListForView();
    

    GoodsCategoryServiceImpl.java

    @Override
    public List selectCategoryListForView() {
       //创建查询对象
       GoodsCategoryExample example = new GoodsCategoryExample();
       //where条件 parentId=0 and level = 1
       example.createCriteria().andParentIdEqualTo((short) 0).andLevelEqualTo((byte) 1);
       //查询一级分类
       List gcList01 = goodsCategoryMapper.selectByExample(example);
       //处理一级分类查询下级分类
       List gcvList01 = new ArrayList<>();
       for (GoodsCategory gc01 : gcList01) {
          //将GoodsCategory对象转成GoodsCategoryVo对象
          GoodsCategoryVo gcv01 = new GoodsCategoryVo();
          BeanUtils.copyProperties(gc01,gcv01);
       // 清空查询对象
          example.clear();
       //设置查询参数
          example.createCriteria().andParentIdEqualTo(gcv01.getId()).andLevelEqualTo((byte) 2);
          //查询二级分类
          List gcList02 = goodsCategoryMapper.selectByExample(example);
          //处理二级分类查询下级分类
          List gcvList02 = new ArrayList<>();
          for (GoodsCategoryVo gc02 : gcvList02) {
             //将GoodsCategory对象转成GoodsCategoryVo对象
             GoodsCategoryVo gcv02 = new GoodsCategoryVo();
             BeanUtils.copyProperties(gc02,gcv02);
             // 清空查询对象
             example.clear();
             //设置查询参数
             example.createCriteria().andParentIdEqualTo(gcv02.getId()).andLevelEqualTo((byte) 3);
             //查询三级分类
             List gcList03 = goodsCategoryMapper.selectByExample(example);
          // 处理三级分类添加至二级二级分类vo对象
             List gcvList03 = new ArrayList<>();
             for (GoodsCategory gc03 : gcList03) {
                //将GoodsCategory对象转成GoodsCategoryVo对象
                GoodsCategoryVo gcv03 = new GoodsCategoryVo();
                BeanUtils.copyProperties(gc03,gcv03);
                //将对象添加至三级分类vo对象List
                gcvList03.add(gcv03);
             }
          // 将三级分类List添加至二级分类
             gcv02.setChildren(gcvList03);
          // 讲对象添加至二级分类Vo对象List
             gcvList02.add(gcv02);
          }
       // 将二级分类List添加至一级分类
          gcv01.setChildren(gcvList02);
       // 将对象添加至一级分类Vo对象List
          gcvList01.add(gcv01);
       }
        
        //=====================JDK8新特性=========================
        //创建查询对象
        // GoodsCategoryExample example = new GoodsCategoryExample();
        // //查询所有的商品分类list
        // List list = goodsCategoryMapper.selectByExample(example);
        // //将List转成List
        // List gcvList = list.stream().map(e -> {
        // 	GoodsCategoryVo gcv = new GoodsCategoryVo();
        // 	BeanUtils.copyProperties(e, gcv);
        // 	return gcv;
        // }).collect(Collectors.toList());
        // 
        // Map> map =
        // 		gcvList.stream().collect(Collectors.groupingBy(GoodsCategoryVo::getParentId));
        // 
        // gcvList.forEach(e -> e.setChildrenList(map.get(e.getId())));
        // 
        // List gcvList01 = gcvList.stream().filter(e -> 1 == e.getLevel()).collect(Collectors.toList
        // ());
        //=====================JDK8新特性=========================
        
       return gcvList01;
    }
    
    Service服务层的测试类

    GoodsCategoryServiceITest.java

    package com.xxxx.manager;
    
    import com.xxxx.manager.service.GoodsCategoryService;
    import com.xxxx.manager.vo.GoodsCategoryVo;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.List;
    
    
    @SpringBootTest(classes = ManagerApplication.class)
    public class GoodsCategoryServiceTest {
    
       @Autowired
       private GoodsCategoryService goodsCategoryService;
    
       @Test
       public void testSelectCategoryListForView(){
          List gcvList = goodsCategoryService.selectCategoryListForView();
          System.out.println(gcvList);
       }
    }
    
    Controller控制层

    GoodsController.java

    @RequestMapping("category/list")
    public String categoryList(Model model) {
       List gcvList = goodsCategoryService.selectCategoryListForView();
       model.addAttribute("gcvList",gcvList);
       return "goods/category/category-list";
    }
    
    ftl页面

    category-list.ftl 的中的所有内容

    <#list gcvList as gcv01> <#list gcv01.children as gcv02> <#list gcv02.children as gcv03>
    分类ID 分类名称 手机显示名称 是否推荐 是否显示 分组 排序 操作
    ${gcv01.id} class="glyphicon glyphicon-plus btn-warning" id="icon_1_1" aria-hidden="false" onclick="rowClicked(this)">  >${gcv01.name} >${gcv01.mobileName}
    ${gcv02.id} class="glyphicon glyphicon-plus btn-warning" id="icon_2_12" aria-hidden="false" onclick="rowClicked(this)">  >${gcv02.name} >${gcv02.mobileName}
    ${gcv03.id} >${gcv03.name} >${gcv03.mobileName}
    分页
    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/910971.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号