關於多資料來源(除自己資料庫外,另一部分資料需通過介面調取第三方獲取)的查詢問題
同樣,還是一次工作上遇到的問題的記錄,當時也是搞了挺久才搞出來的,雖然不太完美,但是誤差方面在我的專案裡還算可以接受,哪位老哥有更好的辦法還請指導下,哪怕評論裡留個網頁連結我去看也好,多謝了。
具體問題描述大致如下:
客戶需要app能查詢出所有訂單,分頁並且按時間倒序排列,現在所有的消費訂單資料在我們自己的資料庫裡,而所有的充值訂單資料其實在第三方那裡(其實我們自己也記錄了一筆,但是資料結果不可靠,因此還是要取用對方的),關於從第三方獲取資料的引數概括是(使用者唯一標識,每頁顯示行數,當前頁數),即(userId,pageSize,pageNo),其中pageSize要求小於50。
經過思考,需要解決的問題主要在於:
1.如何從第三方獲取我需要的資料
2.整合我們庫裡的資料,第三方獲取的資料,進行時間倒序排列,並返回給app
實際上第二點很好解決,只要寫一個排序類,對整合後的資料進行排序即可,最主要難點在於第一點,如何確定調取第三方介面的入參pageSize和pageNo,然後還要把之前已經展示過的資料排除掉。
具體解決方法如下:
1.首先,我用了cache,用於記錄app已展示的(消費記錄數)(充值記錄數),即(我方庫資料展示量)(第三方資料展示量),暫定引數為own和other,當app端傳的頁數為第一頁時,將這兩個資料都置為0.
2.查詢接下來需要展示的資料中,我方的資料,這點沒什麼好說的,正常的查自己資料庫即可,需要查出來的量為own<rownum<own+appPageSize,
3.然後查詢需要展示的資料中,第三方的資料,關於確定調取第三方介面入參的方法如下:
1 /** 2 * 獲取第三方訂單查詢介面所需要的引數 3 * @param start 起始資料量 4 * @param end 結束資料量 5 * @param size 展示資料量 6 * @return 7 * Map,包含兩個引數,pagenum頁數,pagesize頁顯示行數 8 */ 9 publicstatic Map<String,Integer> getWdPara(int start,int end,int size){ 10 Map<String,Integer> map=new HashMap<>(); 11 if (start<size||start/size<1) { 12 map.put("pagenum", 1); 13 map.put("pagesize", 10*((end/10)+1)); 14 }else{ 15 if (start/size==end/size) { 16 map.put("pagenum", (end/size)+1); 17 map.put("pagesize", size); 18 }else { 19 map=getWdPara(start, end, size+1); 20 } 21 } 22 return map; 23 }
其中三個引數的關係是end=start+size,實際上start入參的值就是other,而size入參的值就是appPageSize。而出參的兩個引數就分別是調取第三方時候的入參pageSize和pageNo了。關於如何確定這個方法可以獲取想要的引數,說起來有點繞,我數學也不太好,當時打了挺久的草稿,參照如下,如果看不懂直接跳過就好。。。因為我自己也經常忘記什麼意思:
預設app端展示資料量為10條,所以從第三方中獲取的資料需要包含other+10條,因為根據時間倒序,這中間存在沒有資料是消費資料,全是充值資料的情況,即所有展示資料均來自於第三方。
第三方資料的起始-截止資料量 pageSize引數,即s pageNo引數,即n 實際取到的資料量
8-18 20 1 1-20
18-28 15 2 15-30
28-38 20 2 20-40
。。。
一開始是這樣草稿打下去的,後來找規律後發現,其實就和查自己資料庫一樣,保證查到的資料包含起始資料和終止資料即可,即
( 頁數-1)*頁顯示行數>起始資料,頁數*頁顯示行數>終止資料
然後進行幾次舉例驗證後,就得出上述獲取引數的方法了。
至此,最難的一步已經完成了,即已經獲取到所有需要的資料了。
4.然後進行下一步,需要去除掉之前已經展示過了的資料,就如上面例子,我們獲取到的資料量,其實是大於需要的資料量,有一部分資料可能之前已經展示了,因此在這裡需要將之前展示了的資料去除掉,程式碼如下
1 /** 2 * 處理訂單資料,排除之前顯示過的資料 3 * @param dataList 待處理的list 4 * @param start 資料起始 5 * @param size 頁顯示行數 6 */ 7 public static void dealList(List<Map<String, String>> dataList,int start,int size){ 8 int removeNum=start%size; 9 for (int i = 0; i < removeNum; i++) { 10 dataList.remove(0); 11 } 12 }
即起始資料除以頁顯示行數,取餘數,然後獲取到的資料量從前往後刪除這個餘數量的資料即可,這個餘數量即之前已經展示用過的資料量。和上面獲取引數一樣,這個計算方法,也是我自己寫了好幾個例子,然後找規律寫出來的。。。數學不好,見諒,我也不知道邏輯上該怎麼解釋,反正根據例子找規律,發現這樣寫沒問題。
5.再然後就沒啥問題了,要展示的充值資料,消費資料都已經全部獲取到了,只要將一些引數名稱處理下,確保保持一致,放在一個list裡面,然後寫個排序方法,根據訂單時間進行倒序,排完序以後,獲取前appPageSize個數據返回給前端即可,排序程式碼如下:
1 /** 2 * 資料進行排序,按照時間倒序排序 3 * @param list 4 */ 5 @SuppressWarnings("unchecked") 6 public static void sortTimeMethod(List<Map<String,String>> list){ 7 Comparator comp= new Comparator<Map<String, String>>(){ 8 //返回值大於0時,兩者交換順序 9 @Override 10 public int compare(Map<String, String> p1,Map<String, String> p2){ 11 String p1Date=Tools.processNull(p1.get("order_date")); 12 String p2Date=Tools.processNull(p2.get("order_date")); 13 if ("".equals(p1Date) 14 ||"".equals(p2Date)) { 15 return 0; 16 }else { 17 //如果o1時間早於等於o2時間,則o1排在後面 18 if (!DateUtil.formatDate(p1Date).after(DateUtil.formatDate(p2Date))) { 19 return 1; 20 }else { 21 return -1; 22 } 23 } 24 } 25 }; 26 Collections.sort(list, comp); 27 }
6.當然,這時候也別忘記更新cache裡面的資料,根據最終返回給前端的資料,判斷有多少消費資料和充值資料,對own和other進行相應的累加。
以上就全部完成。不過就和我之前說的,有誤差,很有侷限性,跟我們使用場景有關,因此不是很具備參考性,相關說明如下:
1.我們是APP展示用的,場景中無法跳頁,也就是使用者只能選擇先看第一頁,再看第二頁資料,這樣依次下排,如果存在跳頁,我這個方法應該不行。
2.當用戶在看訂單的同時,無法進行充值或消費操作,進行充值或消費等產生訂單的操作後,重新來查詢必定是從第一頁開始,因此那種查了一會兒,突然增加一條訂單了,這種情況是不會出現的,否則我這方法會存在問題,即cache裡的值不準了,導致的結果就是查詢的資料也不準了,有個訂單會展示兩次。
可能看上去條理不是很清晰,有點混亂。。。但是盡力了,其實一開始沒打算寫,後來隔了半個多月,突然發現自己這個程式碼看不懂了,然後重新理解了半天,想想這個當初也是自己搞了挺久才弄出來的,怕以後又忘記了,於是此時開始寫這篇隨筆,本身就是隔了半個月後再寫,思路就不是很清晰,結果寫了一小半,客戶又來事情了,於是忙去了。。。然後一段時間沒閒下來,再然後忙著忙著就給忘了。。。。直到今天,已經是半年後了,再次來續寫的時候,腦子就更亂了,強行看著半篇內容加自己原先的程式碼,將其續寫完成。主要用處估計也就是以後自己如果遇到相同問題,給自己一個提點,一個思路,防止到時候自己腦子又轉不過來。