プロモーションバックグラウンド-カスタマーサービスロット発券実現方案

3971 ワード

電子商取引の各業務はすべて人工的に発券する需要があって、客服あるいは運営してCRMの楽屋で直接発券する必要があって、PMは私に大量に発券する需要を投げます.
キーフィールド:ロット番号(BatchId)、カード番号(rpCodesは複数入力、カンマ区切りをサポート)、ユーザーID(userIdsは複数入力、カンマ区切りをサポート、一括アップロードExcelフォーマットをサポート)
フロントエンドはバックグラウンドのロット券インタフェースを呼び出した後、直接クエリーインタフェースに戻り、バックグラウンドのロット券インタフェースはマルチスレッド非同期券を発行する.
発券結果フィールド:ロット番号(BatchId)、カード番号、ロット数(rpCodes*userIds)、成功数、失敗数、リリース時間、ステータス
----------------------------------------------------------------------------------------------------------------------------------------------------------
以上が需要であり、以下に実現案を述べる.
補助券を作るだけで、他の業務ラインの人員が大量に券を出すのを避けるために、このインターフェースを歩いているので、券の総量を検査して、2000枚以上の券の流量をすべて止めます.
1.発行券数量の検証
2.一括発行基本情報を記録し、userIdsフィールドが大きすぎるため、DBAは別のテーブルを保存し、基本情報テーブルのプライマリ・キーで関連付けます.
3.発券
4.操作記録を記録する
第3部発券の実現の細部にはボスの意見と少し違いがある.
userIdsが500未満の場合、5つのスレッドから発券し、マルチスレッドが返す結果をどのように処理するかが重要です.
私の意見は、JavaのFork/Joinフレームワークを使用して、すべてのスレッドの結果をまとめ、一括発券基本情報テーブルを更新しますが、そうすると、スレッドがすべて完了し、データベースが更新されるか、データベースが更新されないか、スレッドが異常であれば、CRMは実際に成功した回数券、失敗した回数券を見ることができません.
Bossの意見では、スレッドごとに戻る場合、直接一括発券基本情報テーブルを更新するには、5回更新する必要があります.MySQLでupdateを行う際に条件が付けられており、条件にインデックスフィールドが含まれているため、MySQLの行ロックを利用して、スレッドが一括発券基本情報テーブルのレコードを同時に更新しても、汚れたデータが書き込まれることはありません.あるスレッドが間違っていても、他のスレッドの戻りには影響しません.
発券部分のコードを添付します.
/**
 *  , 500 , 5 
 * @param rpCodes
 * @param memberIds
 * @param batchId
 */
private void receiveRps(String[] rpCodes, String[] memberIds, String batchId) {
    List memberId = Arrays.asList(memberIds);
    if(memberIds.length <= 500){
        sendCoupons(rpCodes, memberId, batchId);
    } else {
        List> splitList = splitMemberIds(memberId);
        for(List subList : splitList){
            sendCoupons(rpCodes, subList, batchId);
        }
    }
}
/**
 *  
 * @param rpCodes
 * @param memberIds
 * @param batchId
 */
private void sendCoupons(String[] rpCodes, List memberIds, String batchId){
    threadPool.execute(()-> {
        try {
            LOG.info("****.sendCoupons  ,memberIds:{}, rpCodes:{}, batchId:{}", JsonUtil.toString(memberIds), JsonUtil.toString(rpCodes), batchId);
            // 
            int successCount = 0;
            int faultCount = 0;
            for(String memberId : memberIds){
                SendPersonalRewardDto sendPersonalRewardDto = sendPersonalReward(memberId, rpCodes, batchId);
                successCount += sendPersonalRewardDto.getSuccessCount();
                faultCount += sendPersonalRewardDto.getFaultsCount();
            }
            // , , MySQL update 
            synchronized (this){
                HashMap param = new HashMap<>();
                param.put("batchId",batchId);
                param.put("successCount",successCount);
                param.put("faultCount",faultCount);
                int updateRes = iBatchCouponDao.updateBatchBasicInfoRecord(param);
                if(updateRes != 1){
                    LOG.error("****.sendCoupons  , ,memberIds:{}, rpCodes:{}, batchId:{}" , JsonUtil.toString(memberIds), JsonUtil.toString(rpCodes), batchId);
                }
            }
        }catch (Exception e){
            LOG.error("****.sendCoupons  ,memberIds:{}, rpCodes:{}, batchId:{}", JsonUtil.toString(memberIds), JsonUtil.toString(rpCodes), batchId);
        }
    });
}

 
private List> splitMemberIds(List memberId){
    List> list = new ArrayList<>();
    int avgNumber= memberId.size() / 5;
    for(int i = 0; i < 4; i++){
        List sublist = memberId.subList(i * avgNumber, i * avgNumber + avgNumber);
        list.add(sublist);
    }
    list.add(memberId.subList(4 * avgNumber, memberId.size()));
    return list;
}

説明:sendPersonalReward関数は詳細すぎて、敏感なビジネス情報が含まれていて、展示しにくいです.