1. 程式人生 > >Java商城面試題(三)

Java商城面試題(三)

SSO單點登入:

SSO系統:這裡涉及到攔截器。

       這裡是利用了sso的介面文件,即校驗介面、註冊、登入介面、根據token查詢使用者介面、安全退出介面。

   這個的呼叫服務層是利用jsonp的形式訪問的服務介面,實現跨域訪問。客戶端全部在jsp頁面實現的。

具體流程:

     當用戶點選註冊的時候,跳轉到註冊頁面,即使用者資訊的儲存功能。檢驗使用者名稱是否存在、手機號和郵箱不能為空。

       當用戶點選登入按鈕的時候,使用者輸入使用者名稱和密碼,檢驗使用者名稱是否在資料庫中存在,然後使用者名稱密碼是否正確。
這裡的密碼是用了spring的MD5加密技術。當全部成功後,給使用者頒發一個token令牌(利用uuid實現),
然後將token存入到redis中(token的key是它生成的號,值是使用者的名字),然後設定在redis的過期時間。
這相當於使用者的session。

   然後將token寫入cookie中,前臺頁面利用jsonp呼叫,根據cookie中的token的值,呼叫sso的根據token查詢使用者的服務,
檢視使用者是否有效,如果有效則將使用者返回前臺頁面,前臺頁面獲取使用者的使用者名稱顯示在首頁,表示***已登陸。

   這裡的cookie是設定了共享域,即全部子系統都可以訪問到cookie。

當用戶登入其他子系統時,先從從cookie中獲取token資訊,根據token資訊獲取使用者資訊,判斷使用者資訊是否有效,
如果有效則放行,如果無效,則利用攔截器攔截跳轉到登入頁面。使用者再次登入的時候重新整理redis的時間,重新設定有效期。

攔截器的攔截,在springMVC.xml中設定攔截的名稱。

訂單模組需要用到的表:

1、資料庫的設計:訂單相關表設計、訂單關聯的諸如商品列表、會員資訊、折扣、積分、打包銷售等;賬單相關表,
包括內部賬單和渠道支付賬單(如微信支付、支付寶支付等),還有就是操作日誌類。建議網上去找一些資料或者
開源電商產品參考一下,這塊第一次做考慮完整比較難的,當然是根據實際需求裁剪,但如果大面上設計有問題
後面功能擴充套件的時候會非常難受;

2、第三方支付:主要是支付過程中一些正常和異常的流程,微信支付你可以參考它幫助文件中推薦的測試用例,挺完整的;
另外就是後臺需要軋賬和平賬,就是你要每天和第三方平臺去對一次賬,看看兩邊資料庫裡的支付情況是否正確。

3、你在上面提到了及時到賬,那就證明可能有個人賬戶體系,這裡的充值、提現要想好怎麼搞?
一般第三方支付針對個人是沒有提現介面的,只有退款

購物車與商城以及訂單的關係:

從一般的商城來看,可以分為B2C與C2C,也就是單商城系統和多商城系統。單商城的系統,基本上就是全部商品生成一個訂單
,而多商城系統裡面的購物車則是可以根據店鋪來分別支付生成訂單(如微店)或者全部統一支付然後根據店鋪拆分訂單
(如有贊,淘寶等)。

     (1)根據每個店鋪生成訂單去支付,很好理解,例如我在店鋪A,買了1,2,3這幾個商品,我只需要生成一個訂單號,
然後去支付就可以了,後續的退款等各種處理,只需要根據該訂單號進行處理即可。

     (2) 那麼,最後一個,購物車裡面有多個店鋪的商品,又需要一起支付的時候怎麼辦呢?假定我們使用微信支付,
微信支付每次下單隻能使用唯一一個單號,那麼我們只能把不同的店鋪,例如店鋪A和店鋪B的所有商品,
都統一放到一個訂單號去微信下單支付。但是,這樣子又違反了訂單規則:不同的店鋪存在著不同的訂單業務,
店鋪和訂單是一對多的關係,而且每個訂單號必須是唯一的。怎麼辦?這個時候,我們可以把內部訂單號和微信下單號
做一個對映(也就是圖所說的拆單),後續做各種處理例如退款等,就可以通過對映關係去進行處理:

      總結一下他們之間的關係:

      (1)購物車可以存在多個店鋪多個商品,可以一次性給錢購買購物車所有商品

      (2)一個訂單隻能對應一個店鋪,一個店鋪可以擁有多個訂單

      (3)微信下單號只有一個,一個微信下單號可以對應多個內部訂單號,一個內部訂單號只能對應一個微信下單號

C2C商城購物車資料庫設計與技術實現

 由於B2C商城和C2C大同小異,這裡暫且不討論B2C的設計和實現,相信會C2C實現而不會B2C的同學是不存在的。
且縱觀目前的商城,大部分慢慢傾向於增加商家入住功能,所以建議預留多商鋪功能,即先把商鋪表加進去,
與商品相關的帶上商鋪id,只不過目前商鋪只有一個就是自己,就這樣可以減少業務需求改動帶來的大量資料庫結構和程式碼的改動。

如果使用者購物車內的商品都是一個店鋪的,那麼就不存在拆單、對映表這種說法,直接生成唯一訂單號作為微信訂單號支付就可以,
但是誰都不知道需求是如何變化的,既然淘寶都是可以統一支付不同店鋪的商品,那麼設計的時候最好是支援購物車所有商品統一支付的,
這樣子就通殺了,不管你是B2C的購物車,還是微店的購物車,還是淘寶的購物車,都能滿足需求。如果只能支援不同店鋪做分別支付,
類似微店這樣,那麼萬一產品要改成支付寶這樣子,就又得重新設計對映表,進行拆單了。本人所在公司的產品經理剛開始比較傾向於
微店這種產品設計,而我設計系統時,也僅僅往產品的需求思考,而沒考慮到淘寶的設計,現在換一個產品又要改為淘寶這種購物車,
就感覺深深地掉進了坑裡面。這裡學到了一個道理,那就是永遠不要相信產品經理(哭),當然也不要過度設計,這裡其實不是過度設計,
只是用多一點時間,就能減少以後的巨大時間,而且產品人員也很喜歡參考大公司的產品功能,畢竟一些基礎功能都是經過大量的使用者反饋的。

那麼我們就以有贊這種做法來設計我們的購物車和訂單吧。

      1、訂單表

     2、購物車表

      最主要的是:商鋪訂單號in_trade_no和第三方支付下單號out_trade_no


- 下單核心實現(非完整程式碼)

《一》購物車提交過來的下單最終是以不同店鋪組成的陣列
  如:             

     $data = arrray(
               '店鋪A' => array('商品1','商品2'),
               '店鋪B' => array('商品1','商品2'),
    );

《二》只有一個數組時,讓商鋪訂單號in_trade_no和第三方支付下單號out_trade_no一致,這樣子的好處是,
我們可以認為訂單號和下單號一致時就是在一個店鋪支付的,而不是多個,這樣就可以複用微信下單介面返回的資料,
從而對該待付款訂單進行付款,而不是再調下單介面生成新的訂單號去支付(既可以減少介面呼叫,也可以減少費單)
        如:            

    1234 =》 1234 
     
    $unified_order = WeixinPay::unifiedOrder(
             $this->conf['weixin']['pay_key'],
             $this->openid,1234,$order['pay']*100,$this->appid,
             $this->conf['weixin']['mch_id'],
             $this->conf['weixin']['pay_notify_url'],
             WeixinPay::getNonceStr(),$order['title']
    );
     
    $data = array(                              
        'appId' => $this->appid,
        'nonceStr' => $unified_order['nonce_str'],
        'package' => "prepay_id={$unified_order['prepay_id']}",
        'signType' => 'MD5',
         'timeStamp' => strval($_SERVER['time']),
    );
    $data['paySign'] = WeixinPay::MakeSign($data, $this->conf['weixin']['pay_key']);
     
    $this->vshop_orderWeixinPrepay->setPrePay(1234,$data);
    $this->err_msg['ok']['data'] = $data;
    $this->_showJsonMsg('ok');

使用者支付待付款訂單時直接把之前存著的下單資料拿出來:

    if(商鋪訂單號==第三方支付下單號){           //在一個店鋪內購買東西
             去微信檢查1234這個下單號是否已經支付過了,支付過了則退出,而且如果訂單不是未付款狀態,
例如網路超時等直接報錯退出
              通過執行下面程式碼
              $data = $this->vshop_orderWeixinPrepay->getPrePay(1234);
                 $this->err_msg['ok']['data'] = $data;
               $this->_showJsonMsg('ok');
     
    }else{          //不一致為什麼這麼做?請看《三》
        
        重新生成商鋪訂單號和第三方支付下單號,讓其一致
        呼叫微信下單介面,根據該訂單金額等,重新下單
     
    }

《三》多個數組時,每個店鋪訂單對應相同的第三方支付下單號,但是商鋪訂單號不能與下單號出現一致的情況,
否則就會導致超額付款問題。

    如
      

           1234 => 1111
           4567 => 1111
           8910 => 1111
     
           而不能是
           1234 => 4567
           4567 => 4567
           8910 => 4567

為什麼商鋪訂單號不能與下單號出現一致的情況,否則就會導致超額付款?
場景是使用者把幾個店鋪的商品提交過來,假如3個店鋪,我們內部會生成3個內部訂單號和一個微信下單號,
如果成功支付問題很好解決,假如沒有成功支付,前端就會像有贊那樣出現3個待付款的訂單。
假如這麼巧,使用者就對        4567 => 4567  這個單進行付款操作,按照上面《二》的邏輯,
就會進到getPrePay獲取到微信下單號4567的下單資料,記住,微信下單號4567的金額是
內部訂單號1234,4567, 8910三者金額的和,使用者本來想支付第二個內部訂單4567的錢,
最後卻變成了支付三個訂單的錢,這是萬萬不可的,因此要確保商鋪訂單號不能與下單號出現一致的情況,
這很簡單,例如加個字母字首等即可      

           1234 => o_4567
           4567 =>o_4567
           8910 =>o_4567

這樣子,使用者單獨支付訂單時,永遠走的是:《 重新生成商鋪訂單號和第三方支付下單號,讓其一致; 
呼叫微信下單介面,根據該訂單金額等,重新下單》這個邏輯,請看《二》。


《四》訂單的退款,成功付款,只需要結合內部訂單號和第三方下單號處理就可以了

SSO單點登入之跨域問題:(怎麼理解的)

第一次寫部落格,與大家共勉.

這裡用到的原理其實非常簡單,將cookie存在一個公共的站點的頁面上就可以了,這裡我們管那個站點叫主站S.

先說說所謂的跨域

環境1:a.xxx.com需要跟b.xxx.com實現跨域,這種比較簡單,只需要設定cookie的域名關聯域就可以了
 cookie.Domain = "xxx.com",這樣兩個域名間的cookie就可以互相訪問,實現跨域.

環境2:a.aaa.com需要跟b.bbb.com實現跨域,這種不同域名的情況下,想要實現就必須換種方式了.

 在這裡我將引入第三者,s.sss.com這個站點,就是某個瀏覽器同時打開了這3個站點,我們訪問A站點,
先判斷自身是否登入,如果session為空,就重定向到S站點,判斷S站點上面是否有cookie,如果S站點上面也沒有cookie,
則由S站點重定向到A站點的登入頁.

 這樣我們就實現了第一步,S站做的的就是隱藏在幕後,子站先判斷自己是否存在session,如果不存在,
就重定向到主站S上面去驗證.

第二步,驗證登入資訊合法性.這裡我引入token(令牌),網上有很多資料,描述token的傳遞,工作方式是這樣,
A登入成功,儲存自身的session,重定向到S,S在自己站點儲存一個session跟cookie,session儲存token物件
{tokenID,userName,startTime,endTime},cookie儲存tokenID,tokenID是一個Guid,把token物件快取在集合裡面,
另起一個執行緒,根據endTime(過期時間)來定期清理集合列表,重定向到A的時候再將tokenID傳遞過去,
拿到tokenID後,進入驗證環節,S站有提供一個介面,根據tokenID獲取token物件,如果獲取到物件,且沒有失效,
則tokenID合法,跳入index頁面.情況2,A登入,直接開啟B,這時候B自身沒有session,會主動請求主站,
主站會返回cookieID(S站存在客戶端的cookie),這個時候再走驗證環節,如果通過,則B根據token物件建立自身的session,
再跳入index

什麼是頂級域名,什麼是二級域名(理解)

 一個完整的域名由二個或二個以上部分組成,各部分之間用英文的句號"."來分隔,最後一個"."的右邊部分稱為頂級域名
(TLD,也稱為一級域名),最後一個"."的左邊部分稱為二級域名(SLD),二級域名的左邊部分稱為三級域名,
以此類推,每一級的域名控制它下一級域名的分配。

域名的構成 
頂級域名:一個域名由兩個以上的詞段構成,最右邊的就是頂級域名。 

目前,國際上出現的頂級域名有.com,.net,.org,.gov,.edu,.mil,.cc,.to,.tv以及國家或地區的程式碼,
其中最通用的是.com,.net,.org 
.COM - -適用於商業實體,它是最流行的頂級域名,任何人都可註冊一個.com域名。 
.NET - -最初用於網路機構如ISP,今天,任何一個人都可註冊一個.net域名。 
.ORG ---設計是用於各類組織機構,包括非盈利團體,今天,任何一個人都可註冊一個.org域名。
 國家程式碼:像cn(中國),fr(法國)和au(澳大利亞)這樣兩個字母的域名謂之國家程式碼頂級域名(ccTLDs),
通過ccTLDs,基本上可以辨明域名持有者的國家或地區。詳細的國家程式碼可在 查詢。 

二級域名:靠左邊的部分就是所謂的二級域名,在 中,cctv就是頂級域名.com下的二級域名, 
還可以有 的形式,這裡的mail可以謂之"主機"或"子域名"。 

域名的結構 
頂級域名 
域名由兩個或兩個以上的詞構成, 中間由點號分隔開。最右邊的那個詞稱為頂級域名。
下面是幾個常見的頂級域名及其用法: 
.COM--用於商業機構。它是最常見的頂級域名。任何人都可以註冊.COM 形式的域名。 
.NET--最初是用於網路組織,例如因特網服務商和維修商。現在任何人都可以註冊以.NET結尾的域名。 
.ORG--是為各種組織包括非盈利組織而定的。現在,任何人都可以註冊以.ORG 結尾的域名。 
國家程式碼由兩個字母組成的頂級域名如.cn, .uk, .de和.jp稱為國家程式碼頂級域名(ccTLDs), 其中.cn是中國專用的頂級域名 

activeMA是怎麼保證收到訊息的:

消費者註冊到activemq伺服器時,在activemq伺服器上會記錄消費者的資訊。並且消費者與伺服器之間會保持一種類似於心跳線的機制,
只要任意一方出現的異常導致程式退出,另外一方都會知道(可能是內部啟動一個執行緒每隔幾秒請求一次確定連線是否異常)。