商城仿写开发总结

项目介绍

mall网上商城是一个综合性的 B2B2C 平台,类似京东商城、天猫商城。网站采用商家入驻的模式,商家入驻平台提交申请,有平台进行资质审核,审核通过后,商家拥有独立的管理后台录入商品信息。商品经过平台审核后即可发布。mall网上商城主要分为网站前台、运营商后台、商家管理后台三个子系统。

  • 网站前台主要包括网站首页(广告)、商家首页、商品详细页、搜索页、会员中心、订单与支付相关页面、秒杀频道等业务功能。
  • 运营商后台主要包括商家审核、品牌管理、规格管理、模板管理、商品分类管理、商品审核、广告类型管理、广告管理、订单查询、商家结算等业务功能。
  • 商家管理包括商品上架功能。

系统架构

SOA架构(后端框架采用Spring +SpringMVC+mybatis +Dubbox 。前端采用angularJS + Bootstrap。)

构架图:
在这里插入图片描述

项目模块

项目共包括以下模块:

  • mall-parent (聚合工程)
  • mall-pojo (通用实体类层)
  • mall-dao (通用数据访问层)
  • mall-common (通用工具类模块)
  • mall-sellergoods-interface (商家商品服务接口层)
  • mall-sellergoods-service (商家商品服务)
  • mall-sellergoods-web (运营商管理系统)
  • mall-shop-web (商家管理系统)
  • mall-content-interface (广告服务接口)
  • mall-content-service (广告服务)
  • mall-portal-web (门户)
  • mall-solr-util (搜索导入工具)
  • mall-search-service (商品搜索服务接口)
  • mall-search-service (商品搜索服务)
  • mall-search-web (商品搜索)
  • mall-page-interface (页面静态化服务接口)
  • mall-page-service (页面静态化服务)
  • mall-page-web (静态化页面)
  • itcast_sms (短信发送工具)
  • pinnyougou-user-interface (用户服务接口)
  • pinnyougou-user-web (用户中心服务)
  • pinnyougou-user-web (用户中心)
  • mall-cart-interface (购物车服务接口)
  • mall-cart-service (购物车服务)
  • mall-cart-web (购物车)
  • mall-order-interface (订单服务接口)
  • mall-order-service (订单服务)
  • mall-order-web (订单系统)
  • mall-seckill-interface (秒杀服务接口)
  • mall-seckill-service (秒杀服务)
  • mall-seckill-web (秒杀频道)
  • mall-task-service (业务调度服务)

服务之间依赖关系:
在这里插入图片描述

应用技术点

Dubbox 分布式服务框架

Dubbox是一个基于RPC的远程服务调用的分布式服务框架。

官方推荐使用 zookeeper 注册中心。注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。

通过Maven引入依赖后,在Spring配置文件引入dubbo配置即可:

<!-- 引用dubbo 服务 -->
<dubbo:application name="mall-shop-web" />
<dubbo:registry address="zookeeper://192.168.25.132:2181"/>
<dubbo:annotation package="com.mall.shop.controller" /> 

前端框架AngularJS

AngularJS是一款优秀的前端JS框架。AngularJS有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、依赖注入等等。

可以方便地进行异步请求:

    <script src="angular.min.js"></script>	
	<script>
		var app=angular.module('myApp',[]); //定义了一个叫myApp的模块
		//定义控制器
		app.controller('myController',function($scope,$http){		
			$scope.findAll=function(){
				$http.get('data.json').success(
					function(response){
						$scope.list=response;
					}					
				);				
			}			
		});	
	</script>	

Spring Security

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。

创建spring 配置文件spring-security.xml

	<!-- 以下页面不被拦截 -->
	<http pattern="/login.html" security="none"></http>
	<http pattern="/login_error.html" security="none"></http>
	<!-- 页面拦截规则 -->
	<http use-expressions="false">
		<intercept-url pattern="/*" access="ROLE_USER" />
		<form-login login-page="/login.html" default-target-url="/index.html" authentication-failure-url="/login_error.html"/>	
		<csrf disabled="true"/>
	</http>


FastDFS

FastDFS 是用C语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

客户端上传的文件最终存储在 Storage 服务器上,文件上传后,FastDFS会返回其在存储服务器中的文件ID:

group1/M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg

Redis

应用在首页广告

首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪,可以将前台页面数据存入Redis中,加快访问速度,减小数据库压力。

先尝试从Redis中取出,如果Redis中没有该数据,再从MySQL数据库中取出,再放入Redis中

    @Override
    public List<TbContent> findByCategoryId(Long categoryId) {

        List<TbContent> list = (List<TbContent>) redisTemplate.boundHashOps("content").get(categoryId);

        if (list == null) {
            System.out.println("从数据库中查询数据并放入缓存 ");
            TbContentExample example = new TbContentExample();
            Criteria criteria = example.createCriteria();
            criteria.andCategoryIdEqualTo(categoryId);//指定条件:分类ID
            criteria.andStatusEqualTo("1");//指定条件:有效
            example.setOrderByClause("sort_order");//排序
            list = contentMapper.selectByExample(example);
            redisTemplate.boundHashOps("content").put(categoryId, list);//放入缓存
        } else {
            System.out.println("从缓存中查询数据 ");
        }
        return list;
    }

应用在搜索

为了能够提高查询速度,把查询面板的品牌、规格数据提前放入Redis。

应用在注册

点击页面上的”获取短信验证码”连接,向后端传递手机号。后端随机生成6位数字作为短信验证码,将其保存在Redis中(手机号作为key),并发送到短信网关。用户注册时,后端根据手机号查询Redis中的验证码与用户填写的验证码是否相同,如果不同则提示用户不能注册。

应用在购物车

在用户登录的情况下,将购物车的商品信息放入Redis,以username为key,以购物车列表为值。

Solr服务器

Elasticsearch和Solr的区别

背景:它们都是基于Lucene搜索服务器基础之上开发,一款优秀的,高性能的企业级搜索服务器。高性能的原因是基于分词技术构建的倒排索引的方式进行查询。

开发语言:都是基于java语言开发

诞生时间:Solr(2004年)、Elasticsearch(2010年,更新,功能更强大)

区别:

  1. 当实时建立索引时,Solr会产生io阻塞,而es不会,es查询性能要高于solr;
  2. 在不断动态添加数据的时候,solr的检索速率会变慢,但es则没有变化;
  3. Solr利用zookeeper进行分布式管理,es自身带有分布式系统管理功能;
  4. Solr一般要部署到web服务器上,如tomcat上,Solr的本质是一个动态的web项目;
  5. Solr支持更多的格式数据(xml,json,csv等),而es仅支持json文件格式;
  6. Solr是传统搜索的有力解决放案,但es更适用于新兴的实时搜索应用;
  7. Solr官网提供的功能更多,而es本身更注重于核心功能。

Solr中的相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的Field(域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。修改solrhome的schema.xml 文件,设置业务系统 Field,配置的关键为:
indexed:是否索引
stored:是否存储

复制域的作用在于将某一个Field中的数据复制到另一个域中,用于实现联合搜索:

<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
    <copyField source="item_title" dest="item_keywords"/>
    <copyField source="item_category" dest="item_keywords"/>
    <copyField source="item_seller" dest="item_keywords"/>
    <copyField source="item_brand" dest="item_keywords"/>

Freemarker

网页静态化技术Freemarker

网页静态化技术和缓存技术的共同点都是为了减轻数据库的访问压力,但是具体的应用场景不同,缓存比较适合小规模的数据,而网页静态化比较适合大规模且相对变化不太频繁的数据。另外网页静态化还有利于SEO。

FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。

step1:创建模板文件

模板文件中四种元素:

  1. 文本,直接输出的部分
  2. 注释,即<#–…–>格式不会输出
  3. 插值(Interpolation):即${…}部分,将使用数据模型中的部分替代输出
  4. FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出。

step2:生成静态页面

//1.创建配置类
Configuration configuration=new Configuration(Configuration.getVersion());
//2.设置模板所在的目录 
configuration.setDirectoryForTemplateLoading(new File("D:/pinyougou_work/freemarkerDemo/src/main/resources/"));
//3.设置字符集
configuration.setDefaultEncoding("utf-8");
//4.加载模板
Template template = configuration.getTemplate("test.ftl");
//5.创建数据模型
Map map=new HashMap();
map.put("name", "张三 ");
map.put("message", "欢迎来到神奇的品优购世界!");
//6.创建Writer对象
Writer out =new FileWriter(new File("d:\\test.html"));
//7.输出
template.process(map, out);
//8.关闭Writer对象
out.close();

消息中间件ActiveMQ

通过引入消息中间件activeMQ,使得运营商系统与搜索服务、页面生成服务解除了耦合

ActiveMQ对于消息的传递有两种类型:

  1. 一种是点对点的,即一个生产者和一个消费者一一对应;
  2. 另一种是发布/ 订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。

ActiveMQ与Spring整合后,可以用JmsTemplate操作消息队列。

  • 运用消息中间件ActiveMQ实现运营商后台与搜索服务的零耦合。运营商执行商品审核后,向ActiveMQ发送消息(SKU列表),搜索服务从ActiveMQ接收到消息并导入到solr索引库。可以解除搜索服务与运营商控制层的耦合。

  • 运用消息中间件ActiveMQ实现运营商后台与网页生成服务的零耦合。运营商执行商品审核后,向ActiveMQ发送消息(商品ID),网页生成服务从ActiveMQ接收到消息后执行网页生成操作。

  • 运用ActiveMQ进行注册中的短信发送,由专门的短信发送(阿里大于)模块进行。

CAS单点登录

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

在用户页面中,将CAS和SpringSecurity集成,二者责任不同:

  • CAS:登录认证(单点登录)也就是在当前项目下登录, 互相信任的其他项目可以自动认证是否登录过
  • SpringSecurity:权限管理(判断当前登录过的用户的权限是管理员还是普通用户都能访问哪些资源)

用户请求进入系统先访问CAS, 经过了CAS的登录流程后, CAS会将登录后的用户的用户名交给SpringSecurity框架SpringSecurity框架负责判断当前用户具有哪些访问权限。

CAS的原理见博客

业务

运营商业务

品牌、规格、模板的增删改查

商家审核、商品审核等:

广告类型和广告录入。

面包屑导航

通过点击面包屑导航栏可以跳转到不同的级别。

    //设置级别
    $scope.setGrade = function (value) {
        $scope.grade = value;
    }

    $scope.selectList = function (p_entity) {
        if ($scope.grade == 1) {//如果为1级
            $scope.entity_1 = null;
            $scope.entity_2 = null;
        }
        if ($scope.grade == 2) {//如果为2级
            $scope.entity_1 = p_entity;
            $scope.entity_2 = null;
        }
        if ($scope.grade == 3) {//如果为3级
            $scope.entity_2 = p_entity;
        }
        $scope.findByParentId(p_entity.id);	//查询此级下级列表

    }

绑定面包屑:

<ol class="breadcrumb">	                        	
  <li><a href="#" ng-click="grade=1;selectList({id:0})">顶级分类列表</a></li>
  <li><a href="#" ng-click="grade=2;selectList(entity_1)">{{entity_1.name}}</a></li>
  <li><a href="#" ng-click="grade=3;selectList(entity_2)">{{entity_2.name}}</a></li>
</ol>

商品录入

商品录入的难点是图片的上传,采用FatDFS进行文件存储。

前端服务层:

app.service('uploadService',function ($http) {
    this.uploadFile = function () {
        var  formdata = new FormData();
        formdata.append('file',file.files[0]);

        return $http({
            url:'../upload.do',
            method:'post',
            data:formdata,
            headers:{'Content-Type':undefined},
            transformRequest: angular.identity

        });
    }
})

后端:

    @RequestMapping("/upload")
    public Result upload(MultipartFile file) {

        //获取文件
        String originalFilename = file.getOriginalFilename();
        //得到扩展名
        String extNmae = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);

        try {
            FastDFSClient client = new FastDFSClient("classpath:config/fdfs_client.conf");
            String fileId = client.uploadFile(file.getBytes(), extNmae);
            String url = FILE_SERVER_URL + fileId;//图片完整地址

            return new Result(true,url);

        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, "上传失败");
        }

    }

商品三级分类选择框

类似js常见的省市联动联系,通过AngularJS可以更方便地实现($watch方法用于监控某个变量的值,当被监控的值发生变化,就自动执行相应函数。

生成SKU列表

点击上边的规格,下面的表格自动生成!

三重循环生成该列表:

//创建SKU列表
$scope.createItemList=function(){	
	$scope.entity.itemList=[{spec:{},price:0,num:99999,status:'0',isDefault:'0' } ];//初始
	var items=  $scope.entity.goodsDesc.specificationItems;	
	for(var i=0;i< items.length;i++){
		$scope.entity.itemList = addColumn( $scope.entity.itemList,items[i].attributeName,items[i].attributeValue );    
	}	
}

//添加列值 
addColumn=function(list,columnName,conlumnValues){
	var newList=[];//新的集合
	for(var i=0;i<list.length;i++){
		var oldRow= list[i];
		for(var j=0;j<conlumnValues.length;j++){
			var newRow= JSON.parse( JSON.stringify( oldRow )  );//深克隆
			newRow.spec[columnName]=conlumnValues[j];
			newList.push(newRow);
		}    		 
	} 		
	return newList;
}

搜索服务

利用Spring Data Solr实现搜索操作:

@Service(timeout=3000)
public class ItemSearchServiceImpl implements ItemSearchService{
	@Autowired
	private SolrTemplate solrTemplate;

	@Override
	public Map<String, Object> search(Map searchMap) {
		Map<String,Object> map=new HashMap<>();
		Query query=new SimpleQuery();
		//添加查询条件
		Criteria criteria=new Criteria("item_keywords").is(searchMap.get("keywords"));
		query.addCriteria(criteria);
		ScoredPage<TbItem> page = solrTemplate.queryForPage(query, TbItem.class);
		map.put("rows", page.getContent());
		return map;
	}
}

搜索结果高亮显示:

直接在Criteria条件中添加高亮条件,需要在要高亮的内容上设置高亮前缀和后缀:

<em style='color:red'></em>
	private Map searchList(Map searchMap){
		Map map=new HashMap();
		
		//高亮效果设置
		HighlightQuery query=new SimpleHighlightQuery();
		HighlightOptions highlightOptions=new HighlightOptions().addField("item_title");//设置高亮的域
		highlightOptions.setSimplePrefix("<em style='color:red'>");//高亮前缀 
		highlightOptions.setSimplePostfix("</em>");//高亮后缀
		query.setHighlightOptions(highlightOptions);//设置高亮选项
		
		//按照关键字查询
		Criteria criteria=new Criteria("item_keywords").is(searchMap.get("keywords"));
		query.addCriteria(criteria);
		HighlightPage<TbItem> page = solrTemplate.queryForHighlightPage(query, TbItem.class);
		
		//获取结果
		for(HighlightEntry<TbItem> h: page.getHighlighted()){//循环高亮入口集合
			TbItem item = h.getEntity();//获取原实体类			
			if(h.getHighlights().size()>0 && h.getHighlights().get(0).getSnipplets().size()>0){
				item.setTitle(h.getHighlights().get(0).getSnipplets().get(0));//设置高亮的结果
			}			
		}		
		map.put("rows",page.getContent());
		return map;
	}


搜索面板上有商品分类、品牌、各种规格和价格区间等条件:

业务流程为:

(1)当用户输入关键字搜索后,除了显示列表结果外,还应该显示通过这个关键字搜索到的记录都有哪些商品分类

(2)根据第一个商品分类查询对应的模板,根据模板查询出品牌列表

(3)根据第一个商品分类查询对应的模板,根据模板查询出规格列表

(4)当用户点击搜索面板的商品分类时,显示按照这个关键字查询结果的基础上,筛选此分类的结果。

(5)当用户点击搜索面板的品牌时,显示在以上结果的基础上,筛选此品牌的结果

(6)当用户点击搜索面板的规格时,显示在以上结果的基础上,筛选此规格的结果

(7)当用户点击价格区间时,显示在以上结果的基础上,按价格进行筛选的结果

(8)当用户点击搜索面板的相应条件时,隐藏已点击的条件。

执行流程为:

private Map searchList(Map searchMap){
    .....
    //1.1关键字查询
    .....	
    //1.2按分类筛选
    .....
    //1.3按品牌筛选
    .....
    //1.4过滤规格
    .....		
    //1.5按价格筛选
    ......		
    //1.6 分页查询
    ......	
    //1.7排序
    ......	
    //1.8高亮显示处理
    ......	
}

具体实现点此连接

购物车

当顾客没有登录时:将添加的商品加入Cookies;
在用户登陆的情况下:将购物车数据存入Redis;
如果用户登陆时,Cookies中存在购物车,需要将Cookies的购物车合并到Redis中存储。

添加商品到购物车的流程:

  1. 根据商品SKU ID查询SKU商品信息;
  2. 获取商家ID;
  3. 根据商家ID判断购物车列表中是否存在该商家的购物车;
  4. 如果购物车列表中不存在该商家的购物车,新建购物车对象;
  5. 将新建的购物车对象添加到购物车列表;
  6. 如果购物车列表中存在该商家的购物车;
  7. 查询购物车明细列表中是否存在该商品;
  8. 如果没有,新增购物车明细;
  9. 如果有,在原购物车明细上添加数量,更改金额。

点击订单结算页的提交订单 ,将购物车保存到订单表和订单明细表中,并将购物车数据清除

扫码支付

我们通过HttpClient工具类实现对远程支付接口的调用。
接口链接:https://api.mch.weixin.qq.com/pay/orderquery

具体参数参见查询订单API,在controller方法中轮询调用查询订单(间隔3秒),当返回状态为success时,在Controller方法返回结果。前端代码收到结果后跳转到成功页面。

如果用户到了二维码页面一直未支付,或是关掉了支付页面,代码会一直循环调用微信接口,这样会对程序造成很大的压力。所以加入一个时间限制或是循环次数限制,当超过时间或次数时,跳出循环。

@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
    Result result=null;		
    int x=0;		
    while(true){
        //调用查询接口
        .......		
        try {
            Thread.sleep(3000);//间隔三秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }	
        
        //为了不让循环无休止地运行,我们定义一个循环变量,如果这个变量超过了这个值则退出循环,设置时间为5分钟
        x++;
        if(x>=100){
            result=new  Result(false, "二维码超时");
            break;
        }
    }
    return result;
}

其他技术点

  • MyBatis分页插件PageHelper
  • 前端分层开发
  • 下拉框select2
  • BCrypt加密算法
  • kindeditor富文本编辑器
  • 分布式文件服务器FastDFS
  • Redis缓存数据库——Spring Data Redis
  • 搜索解决方案Solr——Spring Data Solr
  • 中文分析器IK Analyzer
  • Solr(高亮显示、分类列表、过滤查询、价格区间搜索、排序)
  • 网页静态化Freemarker
  • FTL指令
  • 消息中间件ActiveMQ减小耦合
  • SpringBoot微服务框架
  • 短信发送平台-阿里大于
  • 单点登录CAS
  • CAS客户端与SpringSecurity集成
  • Cookie与Redis存储购物车
  • JS跨域请求
  • 分布式ID生成器(snowflake算法)
  • 二维码生成
  • 微信支付SDK
  • Redis实现秒杀业务
  • 任务调度SpringTask
  • Maven Profile
  • MongoDB数据库
  • Zookeeper集群
  • SolrCloud集群
  • Redis Cluster集群
  • 数据库分片中间件-MyCat
  • 反向代理服务器Nginx
  • Docker容器
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值