一款搶紅包掃雷app實現
最近公司沒啥專案,幾個小夥伴私下玩紅包掃雷玩的不亦樂乎,首先明白掃雷是一個什麼機制,就是別人發紅包,紅包裡面有雷,你發紅包你也可以在紅包裡面設定雷的數字,如果別人領你的紅包尾數是你設定的數字,會回款1.5倍的紅包給你。比如你發30紅包有一個人踩了1雷了就要賠給你45,兩個人踩就給你90,你運氣好可以中三雷,30元變135元,暴利就是這樣。當然這是不合法的事情,所以我們只是幾個朋友同事拉了一個微信群自己玩;玩了一段時間發現了問題,微信群老被封,可能是某個同事輸錢了不高心舉報了...而且效率不高,每次要檢視誰中了雷,等等,雖然只有幾個人玩,但還是很不爽!!於是乎,自己寫一個!
就誕生下面這個東西,當然不止這倆個介面,包括一些資金明細啊神們的等等...

微信截圖_20180921094447.png

微信截圖_20180921094557.png
當然我們是以一個內部自娛自樂的平臺來自己玩耍的,不會用它來賺錢.
開發中主要遇到以下幾個問題:
一,長連結用神們做,怎們維護長連結
長連結主要用Socket/">WebSocket來做,我們的okhttp其實就可以簡單的做,關於伺服器使用springboot自帶的WebSocket來管理
客戶端:
final OkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(3, TimeUnit.SECONDS)//設定讀取超時時間 .writeTimeout(3, TimeUnit.SECONDS)//設定寫的超時時間 .connectTimeout(3, TimeUnit.SECONDS)//設定連線超時時間 .build(); Request request = new Request.Builder() .url(API.R_SOCKET + tonken + "/" + groupId) .build(); mWebSocket = okHttpClient.newWebSocket(request, new WebSocketListener() { @Override public void onOpen(final WebSocket webSocket, final Response response) { ViseLog.e("建立socekt連結"); } @Override public void onMessage(final WebSocket webSocket, String text) { ViseLog.e(text); //回撥給控制器 final String message = ""; timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { mWebSocket.send(message); } }, 10000); if (msgCallBack != null) { if (!TextUtils.isEmpty(text)) { final GroupMsgInfo groupMsgInfo = GsonUtil.gson().fromJson(text, GroupMsgInfo.class); HandlerUtil.runOnUiThread(new Runnable() { @Override public void run() { msgCallBack.onMsgCallBack(groupMsgInfo); } }); } } } //webSocket關閉時,關閉執行緒池 @Override public void onClosed(WebSocket webSocket, int code, String reason) { super.onClosed(webSocket, code, reason); if (timer != null) { timer.cancel(); } okHttpClient.dispatcher().executorService().shutdown(); ViseLog.e("關閉socekt連結" + code + "---------" + reason); } });
服務端:
@ServerEndpoint("/websocket/{token}/{groupId}") @Component public class GroupWebSocket { private String token; private String groupId; private Timer timer = new Timer(); /** * 連線建立成功呼叫的方法 */ @OnOpen public void onOpen(Session session, @PathParam("token") String token, @PathParam("groupId") String groupId) { this.token = token; this.groupId = groupId; GroupMannager.getGroupLooper().add(token, groupId, session); //開啟心跳包 timer.schedule(new TimerTask() { @Override public void run() { try { session.getBasicRemote().sendText(""); } catch (IOException e) { e.printStackTrace(); } } },10000,10000); } /** * 連線關閉呼叫的方法 */ @OnClose public void onClose() { if (timer!=null){ timer.cancel(); } GroupMannager.getGroupLooper().remove(token, groupId); } /** * 錯誤處理 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { System.out.println(error.getMessage()); error.printStackTrace(); } }
伺服器將每個長連結根據使用者的token儲存起來,做適當的管理
簡單的開啟了一個心跳包機制
然後需要處理的一點是,在聊天室中,當網路發生改變的時候,長連結會斷開,所以再對網路狀態的變化做處理,監聽廣播即可!
二,紅包演算法怎們實現
紅包這裡的處理一般有倆種,一種是動態分配紅包,一種是實現生成紅包儲存起來在進行分配;我這裡用的是第二種;
首先了解紅包的機制,我們請紅包最少會搶到0.01元,所以我們要根據紅包的數量去生成N份0.01,再講剩下的隨機生成分配到每一份,保證沒份紅包最少有0.01元
/** * 拆分紅包 * * @param total * @param num * @param min */ public static ArrayList<String> distributionHb(double total, int num, double min) { ArrayList<String> redpack = new ArrayList<>(); for (int i = 1; i < num; i++) { double safe_total = (total - (num - i) * min) / (num - i); double money = Math.random() * (safe_total - min) + min; BigDecimal money_bd = new BigDecimal(money); money = money_bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); total = total - money; BigDecimal total_bd = new BigDecimal(total); total = total_bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); System.out.println("第" + i + "個紅包:" + df.format(money) + ",餘額為:" + df.format(total) + "元"); redpack.add(df.format(money)); } redpack.add(df.format(total)); System.out.println("第" + num + "個紅包:" + total + ",餘額為:0.00元"); return redpack; }
3,發紅包,搶紅包怎麼設計,怎們避免併發下的資料不一致性
這裡我嘗試倆種做法,一種是利用鎖機制來做,一種是利用redis來做,用訊息佇列來搶紅包,我用的是第一種,因為第二種可能發生資料丟失,優勢是速度快,但是我們這個沒有多大的資料,不會有很明顯的速度變化,所以我選擇鎖機制
這裡用到的是互斥鎖來對每個紅包進行加鎖,同一個紅包形成一個佇列,同一時間只能有一個使用者進行搶紅包!
ConcurrentHashMap<String, Object> reds = new ConcurrentHashMap<>(); /** * 搶紅包 * * @param token * @param redId * @return */ @Override public BaseResponseBody getRed(Token token, String redId) { Object object = reds.get(redId); if (object == null) { Object o = new Object(); reds.put(redId, o); } Object object2 = reds.get(redId); synchronized (object2) { ....... 邏輯程式碼.... } }
4,支付功能怎們做,我們沒有企業支付渠道
這個有點黑科技的樣子,其實發現很多類似的專案都是用這種方式做的
先看一張圖,
what!,這不就是一張個人收款碼麼?是的,就是用這個,不過需要配合手機端進行處理,監聽使用者的微信收款通知,然後就可以更具使用者的支付時間來回調支付通知
具體如下:
1,使用者點選充值,生成一個充值二維碼,並鎖定該二維碼,其他使用者無法使用該二維碼,返回給使用者一個訂單號,使用者的客戶端輪詢查詢該訂單號狀態
2,使用者進行充值
3,微信收款到賬,
4,監聽微信收款,將金額返回給伺服器,伺服器根據充值的金額進行處理,因為同一段時間(3分鐘內)同一個二維碼的金額只有一個使用者支付
5,使用者收到充值成功訂單

微信截圖_20180921114000.png
大體專案沒有太大難點,就是支付這裡想了很多辦法,提現的做法是手動轉賬
另外還做了一個十級分成的功能,目前幾個小夥伴每天晚上玩的不亦樂乎,再也不怕微信封群了,全自動,噠噠噠,也沒出過什麼bug;有興趣的小夥伴可以下載一起玩耍體驗;
需要全套原始碼的同學可以加我wx記得備註原始碼:wxxewxlxf,畢竟這種原始碼不方便放到github

微信截圖_20180921114834.png