品优购项目笔记 day20

目标

  • 运用SpringTask实现任务调度
  • 运用MavenProfile实现并发和生产环境切换
  • 了解MongoDB数据库的应用场景
  • 说出其他业务功能的需求和实现思路

1. 任务调度SpringTask

1. 什么是任务调度

在企业级应用中,经常会制定一些“计划任务”,即在某个时间点做某件事情,核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作。常见的任务调度框架有Quartz和SpringTask等。

2. SpringTask入门小demo

创建模块pinyougou-task-service,引入spring相关依赖 dao 和common工程,tomcat7端口为9108

添加web.xml

添加配置文件applicationContext-task.xml,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd">

  <context:component-scan base-package="com.pinyougou.task"/>
  <task:annotation-driven/>
</beans>

创建包com.pinyougou.task

编写类

@Component
public class SeckillTask {

  @Scheduled(cron = "* * * * * ?")
  public void refreshSeckillGoods(){
    System.out.println("执行了任务调度:"+new Date());
  }
}

执行后控制台每秒都输出当前时间,cron设置的是表达式,执行时间规则

3. Cron表达式

1. 表达式格式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

(1)Seconds Minutes Hours DayofMonth Month DayofWeek Year

(2)Seconds Minutes Hours DayofMonth Month DayofWeek

每一个域可出现的字符如下:

Seconds:可出现", - * /"四个字符,有效范围为0-59的整数

Minutes:可出现", - * /"四个字符,有效范围为0-59的整数

Hours:可出现", - * /"四个字符,有效范围为0-23的整数

DayofMonth:可出现", - * / ? L W C"八个字符,有效范围为1-31的整数

Month:可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc

DayofWeek:可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一,依次类推

Year:可出现", - * /"四个字符,有效范围为1970-2099年

每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:

(1)*:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。

(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

(3)-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

(5),:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

(7)W: 表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一 到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

2. 例子

eg. 每分钟:第3秒到第10秒 再从第15秒 到第20秒 第45秒执行一次 `15-20, 3-10, 45

eg. 公司 每月第10天计算(发放)工资 10W

eg. 每月最后一个工作日结账 LW

eg. 母亲节、父亲节发送短信 4#2 表示某月的第二个星期三

4. 秒杀商品列表的增量更新

每分钟执行查询秒杀商品表,将符合条件的记录并且缓存中不存在的秒杀商品存入缓存

@Scheduled(cron = "0 * * * * ?")
public void refreshSeckillGoods(){
  System.out.println("执行了秒杀商品增量更新 任务调度:"+new Date());

  // 查询缓存中的秒杀商品id集合
  List goodsIdList = new ArrayList(redisTemplate.boundHashOps("seckillGoods").keys());

  TbSeckillGoodsExample example = new TbSeckillGoodsExample();
  TbSeckillGoodsExample.Criteria criteria = example.createCriteria();
  criteria.andStatusEqualTo("1");//审核通过的商品
  criteria.andStockCountGreaterThan(0);//库存数大于0
  criteria.andStartTimeLessThanOrEqualTo(new Date());//开始日期小于等于当前日期
  criteria.andEndTimeGreaterThanOrEqualTo(new Date());//截止日期大于等于当前日期

  if(goodsIdList.size()>0){
    criteria.andIdNotIn(goodsIdList);// 排除缓存中已经存在的商品id集合
  }

  List<TbSeckillGoods> seckillGoodsList = seckillGoodsMapper.selectByExample(example);
  // 将列表数据装入缓存
  for (TbSeckillGoods seckillGoods : seckillGoodsList) {
    redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getId(),seckillGoods);
    System.out.println("增量更新秒杀商品id:"+seckillGoods.getId());
  }
}

5. 过期秒杀商品的移除

每秒钟在缓存的秒杀商品列表查询过期的商品,发现过期同步到数据库,并在缓存中移除该秒杀商品

@Scheduled(cron = "* * * * * ?")
public void removeSeckillGoods(){
  // 查询出缓存中的数据,扫描每条记录,判断时间,如果当前时间超过截止时间,移除此记录
  List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
  System.out.println("执行了清除秒杀商品的任务");
  for (TbSeckillGoods seckillGoods : seckillGoodsList) {
    if(seckillGoods.getEndTime().getTime()<new Date().getTime()){
      // 同步到数据库
      seckillGoodsMapper.updateByPrimaryKey(seckillGoods);
      // 清除缓存
      redisTemplate.boundHashOps("seckillGoods").delete(seckillGoods.getId());
      System.out.println("秒杀商品"+seckillGoods.getId()+"已过期");
    }
  }
  System.out.println("执行了清除秒杀商品的任务...end...");
}

2. Maven Profile

1. 什么是MavenProfile

在我们平常的java开发中,会经常使用到很多配制文件(xxx.properties,xxx.xml),而当我们在本地开发(dev),测试环境测试(test),线上生产使用(product)时,需要不停的去修改这些配制文件,次数一多,相当麻烦。现在,利用maven的filter和profile功能,我们可实现在编译阶段简单的指定一个参数就能切换配制,提高效率,还不容易出错.。

profile可以让我们定义一系列的配置信息,然后指定其激活条件。这样我们就可以定义多个profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果。

2. 入门

修改page-web的pom.xml

<properties>
  <port>9105</port>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.2</version>
      <configuration>
        <!-- 指定端口 -->
        <port>${port}</port>
        <!-- 请求路径 -->
        <path>/</path>
      </configuration>
    </plugin>
  </plugins>
</build>

运行发现结果一样,其实就是之前学习的maven的变量

如何切换为生产环境的9205呢?

修改pom文件,添加profile定义

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <port>9105</port>
    </properties>
  </profile>
  <profile>
    <id>pro</id>
    <properties>
      <port>9205</port>
    </properties>
  </profile>
</profiles>

在这里插入图片描述

执行后发现运行的端口改变为9205了

3. 切换数据库连接配置

1. 编写不同环境的配置文件

  1. 在pinyougou-dao的src/main/resources下创建filter文件夹
  2. filter文件夹下创建db_dev.properties,用于配置开发环境用到的数据库
env.jdbc.driver=com.mysql.jdbc.Driver
env.jdbc.url=jdbc:mysql://localhost:3306/pinyougoudb?characterEncoding=utf-8
env.jdbc.username=root
env.jdbc.password=root
  1. filter文件夹下创建db_pro.properties
env.jdbc.driver=com.mysql.jdbc.Driver
env.jdbc.url=jdbc:mysql://localhost:3306/pinyougoudb_pro?characterEncoding=utf-8
env.jdbc.username=root
env.jdbc.password=root
  1. 修改properties下的db.properties
jdbc.driver=${env.jdbc.driver}
jdbc.url=${env.jdbc.url}
jdbc.username=${env.jdbc.username}
jdbc.password=${env.jdbc.password}

2. 定义profile

修改pom.xml

<properties>
  <env>dev</env>
</properties>

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <env>dev</env>
    </properties>
  </profile>
  <profile>
    <id>pro</id>
    <properties>
      <env>pro</env>
    </properties>
  </profile>
</profiles>

定义了2个profile,分别是开发环境和生产环境

3. 资源过滤和变量替换

修改pom.xml,在build节点中添加如下配置

<build>
  <filters>
    <filter>src\main\resources\filters\db_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

利用filter实现对资源文件(resources)过滤

maven filter可利用指定的xxx.properties中对应的key=value对资源文件中的${key}进行替换,最终把你的资源文件中username=value

4. 打包

在pinyougou-dao工程执行命令:package -P pro 解压生成的jar包,观察db.properties配置文件内容,替换为生产环境的值

在这里插入图片描述

解压后发现db已经变成了pinyougoudb_pro

5. 测试运行

4. 切换注册中心连接配置

1. 集中配置注册中心地址

  1. 在pinyougou-common工程properties下创建dubbox.properties
address=192.168.25.128:2181
  1. spring目录下创建spring配置文件applicationContext-dubbox.xml配置如下
<context:property-placeholder location="classpath*:properties/*.properties" />
<dubbo:registry protocol="zookeeper" address="${address}"/>
  1. 所有服务工程和web工程都要依赖pinyougou-common,并删除每个工程中关于注册中心地址的配置
  2. 测试运行 正常

2. MavenProfile配置

  1. 在pinyougou-common工程创建filters目录,目录下建立dubbox_dev.properties和dubbox_pro.properties
env.address=192.168.25.128:2181
env.address=192.168.25.122:2181
  1. 修改dubbox.properties
address=${env.address}
  1. 修改pinyougou-common的pom.xml
<properties>
  <env>dev</env>
</properties>

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <env>dev</env>
    </properties>
  </profile>

  <profile>
    <id>pro</id>
    <properties>
      <env>pro</env>
    </properties>
  </profile>
</profiles>

...
<build>
  <filters>
    <filter>src/main/resources/filters/dubbox_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

3. MongoDB简介

1. 什么是mongodb

MongoDB 是一个跨平台的,面向文档的数据库,是当前 NoSQL 数据库产品中最热门的一种。它介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的产品。它支持的数据结构非常松散,是类似JSON 的 BSON 格式,因此可以存储比较复杂的数据类型

2. 特点

MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库。

具体特点总结如下:

(1)面向集合存储,易于存储对象类型的数据

(2)模式自由

(3)支持动态查询

(4)支持完全索引,包含内部对象

(5)支持复制和故障恢复

(6)使用高效的二进制数据存储,包括大型对象(如视频等)

(7)自动处理碎片,以支持云计算层次的扩展性

(8)支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++语言的驱动程序,社区中也提供了对 Erlang 及.NET 等平台的驱动程序

(9)文件存储格式为 BSON(一种 JSON 的扩展)

3. 体系结构

MongoDB 的逻辑结构是一种层次结构。主要由:

文档(document)、集合(collection)、数据库(database)这三部分组成的。逻辑结构是面向用户

的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构。

(1)MongoDB 的文档(document),相当于关系数据库中的一行记录。

(2)多个文档组成一个集合(collection),相当于关系数据库的表。

(3)多个集合(collection),逻辑上组织在一起,就是数据库(database)。

(4)一个 MongoDB 实例支持多个数据库(database)。

4. 在品优购中的应用

品优购的评价系统、收藏系统采用等信息存储在mongodb

4. 品优购-其他业务功能分析

1. 用户中心(WEB)

用户在首页登录系统后会进入到用户中心首页

1. 订单中心

功能需求:

(1)实现对订单的查询功能

(2)未付款订单的付款功能

(3)未付款订单的取消功能

(4)已付款提醒订单发货功能

(5)确认收货

(6)退货

(7)用户评价

(8)物流信息跟踪

2. 秒杀订单中心

同上

3. 我的收藏

购物车中有将我的购物车商品移到我的收藏功能,在用户中心中可以查看我收藏的商品

对于这样的用户收藏数据,我们可以使用mongoDB来实现

(1)我的收藏列表

(2)删除收藏

4. 我的足迹

(1)查看足迹列表

(2)删除我的足迹

5. 个人信息设置

(1)个人信息

(2)地址信息

(3)密码重置

(4)绑定手机

2. 商家后台-订单管理(WEB)

1. 订单管理

(1)订单查询

(2)订单发货

(3)订单退货

2. 秒杀订单管理

(1)秒杀中订单查询(查询redis )

(2)已完成秒杀订单查询(查询数据库)

(3)秒杀订单发货

(4)秒杀订单退货查询

3. 运营商后台-订单管理(WEB)

1. 订单管理

根据商家、订单号、用户ID等信息查询订单列表

2. 秒杀订单管理

(1)查询秒杀中订单

(2)查询已付款订单

4. 评价系统

针对评论这样数据量大并且价值不高的数据,我们通常采用MongoDB来实现存储

1. 评价系统-数据访问层

评价数据访问层-操作mongoDB

2. 评价系统-服务层

3. web工程调用评价系统

(1)在商品详细页显示该商品的所有评论信息(CORS跨域)

(2)用户中心web工程引用评价服务 可以对已收货的订单追加评价。

(3)商家后台web工程引用评价服务 可以查看订单的评价

(4)运营商后台web工程引用评价服务 可以查看订单的评价

(5)任务服务pinyougou-task-service引用评价服务和搜索服务,统计每个商品的评价更新到solr索引库中。

5. 商家首页

构建商家首页工程,引用搜索服务,显示该商家的商品列表

6. 资金结算

用户购买商品是直接付款给平台的,而发货的是商家,那商家如何获得货款呢?这就需要运营商定期将货款转账给商家

1. 佣金和佣金比例

说到平台与商家之间的资金结算,我们必须要提一下佣金。佣金就是运营商以销售额为基础抽取的销售提成。 商品类型不同,设定相应的佣金比例也不同。例如食品类佣金比例为0.5% ,那么商家每产生100元的销售额就需要支付给运营商平台相应比例的佣金

2. 结算流程图

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wjl31802/article/details/90723819