天天看點

【SSM項目】電商平台項目第20天——品優購系統業務分析

課程目标

目标1:運用SpringTask實作任務排程

目标2:運用MavenProfile實作開發和生産環境切換

目标3:了解MongoDB資料庫的應用場景

目标4:說出其它業務功能的需求和實作思路

1.任務排程SpringTask

1.1什麼是任務排程

在企業級應用中,經常會制定一些“計劃任務”,即在某個時間點做某件事情,核心是以時間為關注點,即在一個特定的時間點,系統執行指定的一個操作。常見的任務排程架構有Quartz和SpringTask等。

1.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設定的為表達式,是執行的時間規則。

1.3 Cron表達式

1.3.1 Cron表達式格式

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,表示某月的第二個星期三。      

1.3.2 Cron表達式例子

0 0 10,14,16 * * ? 每天上午10點,下午2點,4點 
0 0/30 9-17 * * ? 朝九晚五工作時間内每半小時 
0 0 12 ? * WED 表示每個星期三中午12點 
"0 0 12 * * ?" 每天中午12點觸發 
"0 15 10 ? * *" 每天上午10:15觸發 
"0 15 10 * * ?" 每天上午10:15觸發 
"0 15 10 * * ? *" 每天上午10:15觸發 
"0 15 10 * * ? 2005" 2005年的每天上午10:15觸發 
"0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發 
"0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發 
"0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發 
"0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發 
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發 
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15觸發 
"0 15 10 15 * ?" 每月15日上午10:15觸發 
"0 15 10 L * ?" 每月最後一日的上午10:15觸發 
"0 15 10 ? * 6L" 每月的最後一個星期五上午10:15觸發 
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最後一個星期五上午10:15觸發 
"0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發      

1.4秒殺商品清單的增量更新

每分鐘執行查詢秒殺商品表,将符合條件的記錄并且緩存中不存在的秒殺商品存入緩存

/**
 * 重新整理秒殺商品
 */
@Scheduled(cron="0 * * * * ?")
public void refreshSeckillGoods(){
  System.out.println("執行了任務排程"+new Date());     
  //查詢所有的秒殺商品鍵集合
  List ids = new ArrayList( redisTemplate.boundHashOps("seckillGoods").keys());
  //查詢正在秒殺的商品清單   
  TbSeckillGoodsExample example=new TbSeckillGoodsExample();
  Criteria criteria = example.createCriteria();
  criteria.andStatusEqualTo("1");//稽核通過
  criteria.andStockCountGreaterThan(0);//剩餘庫存大于0
  criteria.andStartTimeLessThanOrEqualTo(new Date());//開始時間小于等于目前時間
  criteria.andEndTimeGreaterThan(new Date());//結束時間大于目前時間   
  criteria.andIdNotIn(ids);//排除緩存中已經有的商品    
  List<TbSeckillGoods> seckillGoodsList= seckillGoodsMapper.selectByExample(example);   
  //裝入緩存 
  for( TbSeckillGoods seckill:seckillGoodsList ){
    redisTemplate.boundHashOps("seckillGoods").put(seckill.getId(), seckill);
  }
  System.out.println("将"+seckillGoodsList.size()+"條商品裝入緩存");
}      

1.5過期秒殺商品的移除

每秒中在緩存的秒殺上皮清單中查詢過期的商品,發現過期同步到資料庫,并在緩存中移除該秒殺商品

/**
 * 移除秒殺商品
 */
@Scheduled(cron="* * * * * ?")
public void removeSeckillGoods(){
  System.out.println("移除秒殺商品任務在執行");
  //掃描緩存中秒殺商品清單,發現過期的移除
  List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
  for( TbSeckillGoods seckill:seckillGoodsList ){
    if(seckill.getEndTime().getTime()<new Date().getTime()  ){//如果結束日期小于目前日期,則表示過期
      seckillGoodsMapper.updateByPrimaryKey(seckill);//向資料庫儲存記錄
redisTemplate.boundHashOps("seckillGoods").delete(seckill.getId());//移除緩存資料
      System.out.println("移除秒殺商品"+seckill.getId());
    }     
  }
  System.out.println("移除秒殺商品任務結束");   
}      

2.Maven Profile

2.1什麼是MavenProfile

在我們平常的java開發中,會經常使用到很多配制檔案(xxx.properties,xxx.xml),而當我們在本地開發(dev),測試環境測試(test),線上生産使用(product)時,需要不停的去修改這些配制檔案,次數一多,相當麻煩。現在,利用maven的filter和profile功能,我們可實作在編譯階段簡單的指定一個參數就能切換配制,提高效率,還不容易出錯.

profile可以讓我們定義一系列的配置資訊,然後指定其激活條件。這樣我們就可以定義多個profile,然後每個profile對應不同的激活條件和配置資訊,進而達到不同環境使用不同配置資訊的效果。

2.2 Maven Profile入門

修改pinyougou-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>      

運作tomcat7:run ,發現運作結果是一樣的,因為port是變量,而變量值是定義為9105。這其實就是我們之前學習的maven的變量。

那我們現在思考一下,如果這個端口在開發時使用9105,如果在生産環境(或其他環境)為9205呢?如何解決值的動态切換呢?

這時我們修改pom.xml,增加profile定義

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

執行指令 tomcat7:run -P pro 發現以9205端口啟動

執行指令 tomcat7:run -P dev 發現以9105端口啟動

-P 後邊跟的是profile的id

如果我們隻執行指令tomcat7:run ,也是以9105啟動,因為我們一開始定義的變量值就是9105,就是在不指定profileID時的預設值.

2.3切換資料庫連接配接配置

2.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=123456      

(3)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=123456      

(4)修改properties下的db.properties

jdbc.driver=${env.jdbc.driver}
jdbc.url=${env.jdbc.url}
jdbc.username=${env.jdbc.username}
jdbc.password=${env.jdbc.password}      

2.3.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,分别是開發環境和生産環境

2.3.3資源過濾與變量替換

修改pom.xml ,在build節點中添加如下配置

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

這裡我們利用filter實作對資源檔案(resouces) 過濾

maven filter可利用指定的xxx.properties中對應的key=value對資源檔案中的${key}進行替換,最終把你的資源檔案中的username=${key}替換成username=value       

2.3.4打包

在pinyougou-dao 工程 執行指令:package -P pro , 解壓生成的jar包,觀察db.properties配置檔案内容,已經替換為生産環境的值。

在pinyougou-sellergoods-service工程 執行指令 pageage ,解壓生成的war包裡的pinyougou-dao的jar包,發現也是生成環境的值。

2.3.5測試運作

【1】連接配接生産資料庫

(1)在pinyougou-dao 工程執行指令:install -P pro
(2)在pinyougou-sellergoods-service:執行指令:tomcat7:run 
(3)在pinyougou-shop-web :  執行指令:tomcat7:run      

【2】連接配接開發資料庫

(1)在pinyougou-dao 工程執行指令:install -P dev  (或 install   )
(2)在pinyougou-sellergoods-service:執行指令:tomcat7:run 
(3)在pinyougou-shop-web :  執行指令:tomcat7:run      

2.4切換注冊中心連接配接配置

2.4.1集中配置注冊中心位址

(1)在pinyougou-common工程中properties下建立dubbox.properties

address=192.168.25.135:2181      

(2)Spring目錄下建立spring配置檔案 applicationContext-dubbox.xml 配置如下:

<dubbo:registry protocol="zookeeper" address="${address}"/>      

(3)所有的服務工程與web工程都要依賴pinyougou-common . 并删除每個工程中關于注冊中心位址的配置

(4)安裝pinyougou-common到本地倉庫,然後測試運作。

2.4.2 MavenProfile配置

(1)在pinyougou-common工程中建立filters目錄 ,目錄下建立dubbox_dev.properties

env.address=192.168.25.135:2181      

(2)建立dubbox_pro.properties

env.address=192.168.25.136:2181      

(3)修改dubbox.properties

address=${env.address}      

(4)修改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簡介

3.1什麼是MongoDB

MongoDB 是一個跨平台的,面向文檔的資料庫,是目前 NoSQL 資料庫産品中最熱門的一種。它介于關系資料庫和非關系資料庫之間,是非關系資料庫當中功能最豐富,最像關系資料庫的産品。它支援的資料結構非常松散,是類似JSON 的 BSON 格式,是以可以存儲比較複雜的資料類型。

MongoDB 的官方網站位址是:​​http://www.mongodb.org/​​

【SSM項目】電商平台項目第20天——品優購系統業務分析

3.2 MongoDB特點

MongoDB 最大的特點是他支援的查詢語言非常強大,其文法有點類似于面向對象的查詢語言,幾乎可以實作類似關系資料庫單表查詢的絕大部分功能,而且還支援對資料建立索引。它是一個面向集合的,模式自由的文檔型資料庫。

具體特點總結如下:

(1)面向集合存儲,易于存儲對象類型的資料
(2)模式自由
(3)支援動态查詢
(4)支援完全索引,包含内部對象
(5)支援複制和故障恢複
(6)使用高效的二進制資料存儲,包括大型對象(如視訊等)
(7)自動處理碎片,以支援雲計算層次的擴充性
(8)支援 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++語言的驅動程式,社群中也提供了對 Erlang 及.NET 等平台的驅動程式
(9) 檔案存儲格式為 BSON(一種 JSON 的擴充)      

3.3 MongoDB體系結構

MongoDB 的邏輯結構是一種層次結構。主要由:

文檔(document)、集合(collection)、資料庫(database)這三部分組成的。邏輯結構是面向使用者

的,使用者使用 MongoDB 開發應用程式使用的就是邏輯結構。

(1)MongoDB 的文檔(document),相當于關系資料庫中的一行記錄。
(2)多個文檔組成一個集合(collection),相當于關系資料庫的表。
(3)多個集合(collection),邏輯上組織在一起,就是資料庫(database)。
(4)一個 MongoDB 執行個體支援多個資料庫(database)。      

文檔(document)、集合(collection)、資料庫(database)的層次結構如下圖:

【SSM項目】電商平台項目第20天——品優購系統業務分析

下表是MongoDB與MySQL資料庫邏輯結構概念的對比

MongoDb         關系型資料庫Mysql
資料庫(databases)    資料庫(databases)
集合(collections)     表(table)
文檔(document)      行(row)      

3.4 MongoDB在品優購系統中的應用

我們品優購的評價系統、收藏系統采用等資訊存儲在MongoDB . MongoDB安裝及資料庫操作部分屬于自學内容,大家可以根據本課程提供的配套的自學資料學習此部分内容。

4.品優購-其它業務功能分析

4.1使用者中心(WEB)

使用者在首頁登陸系統後會進入到使用者中心首頁。      

4.1.1訂單中心

功能需求:

(1)實作對訂單的查詢功能
(2)未付款訂單的付款功能
(3)未付款訂單的取消功能
(4)已付款提醒訂單發貨功能
(5)确認收貨
(6)退貨
(7)使用者評價
(8)物流資訊跟蹤      

4.1.2秒殺訂單中心

同上。

4.1.3我的收藏

購物車中有将我的購物車商品移到我的收藏功能,在使用者中心中可以檢視我收藏的商品

對于這樣的使用者收藏資料,我們可以使用mongoDB來實作。

(1)我的收藏清單
(2)删除收藏      

4.1.4我的足迹

(1)檢視足迹清單
(2)删除我的足迹      

4.1.5個人資訊設定

(1)個人資訊
(2)位址資訊
(3)密碼重置
(4)綁定手機      

4.2商家背景-訂單管理(WEB)

4.2.1訂單管理

(1)訂單查詢
(2)訂單發貨
(3)訂單退貨      

4.2.2秒殺訂單管理

(1)秒殺中訂單查詢(查詢redis )
(2)已完成秒殺訂單查詢(查詢資料庫)
(3)秒殺訂單發貨
(4)秒殺訂單退貨查詢      

4.3營運商背景-訂單管理(WEB)

4.3.1訂單管理

根據商家、訂單号、使用者ID等資訊查詢訂單清單

4.3.2秒殺訂單管理

(1)查詢秒殺中訂單
(2)查詢已付款訂單      

4.4評價系統

針對評論這樣資料量大并且價值不高的資料,我們通常采用MongoDB來實作存儲。

4.4.1評價系統-資料通路層

評價資料通路層-操作mongoDB

4.4.2評價系統-服務層

評價服務層

4.4.3 web工程調用評價系統

(1)在商品詳細頁顯示該商品的所有評論資訊(CORS跨域)
(2)使用者中心web工程引用評價服務 可以對已收貨的訂單追加評價。
(3)商家背景web工程引用評價服務 可以檢視訂單的評價
(4)營運商背景web工程引用評價服務 可以檢視訂單的評價
(5)任務服務pinyougou-task-service 引用評價服務和搜尋服務,統計每個商品的評價更新到solr索引庫中。      

4.5商家首頁

建構商家首頁工程,引用搜尋服務,顯示該商家的商品清單

4.6資金結算

使用者購買商品是直接付款給平台的,而發貨的是商家,那商家如何獲得貨款呢?這就需要營運商定期将貨款轉賬給商家。

4.6.1傭金與傭金比例

說到平台與商家之間的資金結算,我們必須要提一下傭金。傭金就是營運商以銷售額為基礎抽取的銷售提成。 商品類型不同,設定相應的傭金比例也不同。例如食品類傭金比例為0.5% ,那麼商家每産生100元的銷售額就需要支付給營運商平台相應比例的傭金。