feat(sports): 新增 DB体育平台支持

- 添加 DB体育相关的缓存常量、数据传输对象、接口客户端等
- 实现 DB 体育平台的服务逻辑,包括用户创建、资金转账、获取会员信息等功能
- 新增延迟任务处理机制,用于处理异步任务
- 修改错误码枚举,增加新的错误类型
- 更新游戏平台枚举,添加 DB 体育选项
main-meitian
liaoyong 2025-04-09 20:05:22 +08:00
parent 6410c6bb56
commit ff36a40799
31 changed files with 1773 additions and 53 deletions

View File

@ -96,6 +96,11 @@ public class CacheConstants {
* fb
*/
public static final String FB_Sports = "fp_sports:";
/**
* db
*/
public static final String DB_Sports = "db_sports:";
/**
* pg
*/
@ -125,6 +130,15 @@ public class CacheConstants {
*/
public static final String KM_USER_TOKEN = "km:user:token:";
/**
* ae
*/
public static final String SV388_TIME_FROM= "sv388:time:from";
public static final String SV388_GAMES = "sv388_games:";
}

View File

@ -35,7 +35,8 @@ public enum ErrorCode {
Transfer_Out_Failure(1019, "转出失败"),
Get_Member_Info_Failure(1020, "获取会员信息失败"),
Transfer_Not_Exist(1021, "转帐操作不存在"),
Get_Url_Failure(1022, "获取URL失败")
Get_Url_Failure(1022, "获取URL失败"),
Miss_Config(1023, "缺少配置"),
;
// 获取错误码

View File

@ -16,8 +16,8 @@ public enum GamePlatforms {
KM("KM", "KM"),
PGT("PGT", "PGT"),
FBSports("FBSports", "FB体育"),
SV388("SV388", "SV388"),
;
SV388("SV388", "SV388真人"),
DBSports("DBSports", "DB体育");
private final String code;
private final String info;

View File

@ -0,0 +1,65 @@
package com.ff.delay;
import com.ff.base.manager.AsyncManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author cengy
*/
@Service
@Slf4j
public class DelayService {
private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void addTask(DelayTask delayTask) {
delayQueue.put(delayTask);
}
// 启动延迟队列任务处理
@PostConstruct
public void startProcessing() {
// 每 1 秒钟执行一次任务检查
scheduler.scheduleAtFixedRate(this::processTasks, 10, 1, TimeUnit.SECONDS);
}
// 处理过期任务
private void processTasks() {
DelayTask task = null; // 阻塞,直到队列中有任务到期
try {
task = delayQueue.take();
} catch (InterruptedException e) {
log.error("获取过期任务失败", e);
}
if (null == task) {
return;
}
AsyncManager.me().execute(new DelayRunnable(task));
}
public static class DelayRunnable implements Runnable {
DelayTask delayTask;
public DelayRunnable(DelayTask delayTask) {
this.delayTask = delayTask;
}
@Override
public void run() {
try {
delayTask.execute();
} catch (Exception e) {
log.error("处理过期任务出错", e);
}
}
}
}

View File

@ -0,0 +1,44 @@
package com.ff.delay;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author cengy
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class DelayTask implements Delayed {
private long delayTime;
private long expireTime;
public DelayTask(long delayTime) {
this.delayTime = delayTime;
this.expireTime = System.currentTimeMillis() + delayTime; // 设置过期时间
}
@Override
public long getDelay(TimeUnit unit) {
long diff = expireTime - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this.expireTime < ((DelayTask) o).expireTime) {
return -1;
}
if (this.expireTime > ((DelayTask) o).expireTime) {
return 1;
}
return 0;
}
abstract public void execute();
}

View File

@ -10,14 +10,14 @@ import com.ff.base.core.redis.RedisCache;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.system.domain.SysConfig;
import com.ff.base.system.service.impl.SysConfigServiceImpl;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.delay.DelayService;
import com.ff.delay.DelayTask;
import com.ff.game.api.IGamesService;
import com.ff.game.api.km.dto.KMBalanceTransferStatusResponseDTO;
import com.ff.game.api.meitian.client.MeiTianClient;
import com.ff.game.api.meitian.dto.*;
import com.ff.game.api.request.*;
@ -80,6 +80,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
@Autowired
private SysConfigServiceImpl sysConfigServiceImpl;
@Autowired
private DelayService delayService;
/**
*
*
@ -301,7 +304,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
String merchantId = exchangeTransferMoneyRequestDTO.getAgentId();
String playerName = exchangeTransferMoneyRequestDTO.getAccount();
String coins = exchangeTransferMoneyRequestDTO.getAmount().setScale(4, RoundingMode.DOWN).toString();
if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())){
if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())) {
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(exchangeTransferMoneyRequestDTO.getAgentId())
@ -341,7 +344,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
//判断是否转移成功
if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) {
//更新数据
BigDecimal transAmount =new BigDecimal(coins);
BigDecimal transAmount = new BigDecimal(coins);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().subtract(transAmount));
@ -374,7 +377,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
//判断是否转移成功
if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) {
//更新数据
BigDecimal transAmount =new BigDecimal(coins);
BigDecimal transAmount = new BigDecimal(coins);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().add(transAmount));
@ -414,7 +417,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
exchangeTransferMoneyRequestDTO.getOrderId()
);
Integer status = StatusType.IN_PROGRESS.getValue();
if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode())&& "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) {
if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode()) && "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) {
status = StatusType.SUCCESS.getValue();
} else {
status = StatusType.FAILURE.getValue();
@ -428,6 +431,34 @@ public class MeiTianGameServiceImpl implements IGamesService {
}
class GetRecordByTimeTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRecordByTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
doSyncRecordByRecordID(betRecordByTimeDTO);
}
}
class GetRecordByHistoryTimeTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRecordByHistoryTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
doSyncRecordByDate(betRecordByTimeDTO, 1);
}
}
/**
*
*
@ -436,18 +467,21 @@ public class MeiTianGameServiceImpl implements IGamesService {
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
return doSyncRecordByRecordID(betRecordByTimeDTO);
delayService.addTask(new GetRecordByTimeTask(betRecordByTimeDTO));
return Boolean.TRUE;
//return doSyncRecordByRecordID(betRecordByTimeDTO);
}
boolean doSyncRecordByRecordID(BetRecordByTimeDTO betRecordByTimeDTO) {
String configKey = GamePlatforms.MT.getCode() + ":lastRecordID";
String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey);
long recordID = 0;
if (lastRecordID == null || lastRecordID.isEmpty()) {
String configKey = GamePlatforms.MT.getCode() + ":lastSyncRecordID";
long recordID = redisCache.getCacheObject(configKey);
//String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey);
/*if (lastRecordID == null || lastRecordID.isEmpty()) {
} else {
recordID = Long.parseLong(lastRecordID);
}
}*/
String merchantId = betRecordByTimeDTO.getAgentId();
Map<String, Object> rawMap = new LinkedHashMap<>();
rawMap.put("recordID", recordID);
@ -473,7 +507,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
//数据插入
this.batchInsert(recordResponse, betRecordByTimeDTO);
MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1);
SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID()));
/*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
if (config == null) {
config = new SysConfig();
config.setConfigKey(configKey);
@ -482,7 +518,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
} else {
config.setConfigValue(dataBean.getRecordID());
sysConfigServiceImpl.updateConfig(config);
}
}*/
// 它每次返回25000条所以需要判断如果大于25000条则继续拉取
if (dataList.size() >= 25000) {
doSyncRecordByRecordID(betRecordByTimeDTO);
@ -498,22 +534,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
String date = getDateStr(daysToSubtract);
String configKey = GamePlatforms.MT.getCode() + ":lastSyncDate";
String syncDateStr = sysConfigServiceImpl.selectConfigByKey(configKey);
Map<String, Long> syncDateMap = new HashMap<>();
long recordID = 0;
if (syncDateStr == null || syncDateStr.isEmpty()) {
} else {
syncDateMap = JSON.parseObject(syncDateStr, Map.class);
}
if (syncDateMap.containsKey(date)) {
recordID = syncDateMap.get(date);
if (syncDateMap.size() > 10) {
syncDateMap.clear();
syncDateMap.put(date, recordID);
}
} else {
syncDateMap.put(date, recordID);
}
long recordID = redisCache.getCacheObject(configKey);
String merchantId = betRecordByTimeDTO.getAgentId();
Map<String, Object> rawMap = new LinkedHashMap<>();
rawMap.put("rowID", recordID);
@ -541,8 +562,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
//数据插入
this.batchInsert(recordResponse, betRecordByTimeDTO);
MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1);
syncDateMap.put(date, Long.parseLong(dataBean.getRowID()));
SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
//syncDateMap.put(date, Long.parseLong(dataBean.getRowID()));
redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID()));
/*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
if (null == config) {
config = new SysConfig();
config.setConfigKey(configKey);
@ -551,7 +573,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
} else {
config.setConfigValue(JSON.toJSONString(syncDateMap));
sysConfigServiceImpl.updateConfig(config);
}
}*/
// 它每次返回25000条所以需要判断如果大于25000条则继续拉取
if (dataList.size() >= 25000) {
@ -572,9 +594,10 @@ public class MeiTianGameServiceImpl implements IGamesService {
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
doSyncRecordByDate(betRecordByTimeDTO, 0);
doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday
//doSyncRecordByDate(betRecordByTimeDTO, 0);
//doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday
delayService.addTask(new GetRecordByHistoryTimeTask(betRecordByTimeDTO));
return true;
}

View File

@ -92,11 +92,12 @@ public interface SV388Client {
* @param params
* @return {@link SV388BetRecordResponse }
*/
@Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByUpdateDate",
@Post(url ="{fetchUrl}/fetch/gzip/getTransactionByUpdateDate",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388BetRecordResponse getBetRecordByTime(@Body Map<String, Object> params);
SV388BetRecordResponse getBetRecordByTime(@Body Map<String, Object> params,
@Var("fetchUrl") String fetchUrl);
/**
*
@ -104,11 +105,12 @@ public interface SV388Client {
* @param params
* @return {@link SV388BetRecordResponse }
*/
@Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByTxTime",
@Post(url ="{fetchUrl}/fetch/gzip/getTransactionByTxTime",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map<String, Object> params);
SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map<String, Object> params,
@Var("fetchUrl") String fetchUrl);
/**
*
*

View File

@ -105,7 +105,17 @@ public class SV388GamesServiceImpl implements IGamesService {
public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
Platform platform = createMemberRequestDTO.getVendor();
Object betLimit = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency());
Object o = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency());
if (!(o instanceof Map)) {
throw new ApiException(ErrorCode.Miss_Config.getCode());
}
Map<String, Object> betLimit = new HashMap<>();
betLimit.put(GamePlatforms.SV388.getCode(), new HashMap<String, Object>() {{
put("LIVE", new HashMap<String, Object>((Map<String, ?>) o) {{
remove("@type");
}});
}}
);
Map<String, Object> params = this.getKey(createMemberRequestDTO);
params.put("userId", createMemberRequestDTO.getAccount());
params.put("currency", createMemberRequestDTO.getCurrency());
@ -201,7 +211,7 @@ public class SV388GamesServiceImpl implements IGamesService {
game.setGameId(StringUtils.addSuffix(GamePlatforms.SV388.getCode(), 1));
gameService.insertGame(game);
}
return CacheConstants.AE_GAMES;
return CacheConstants.SV388_GAMES;
}
/**
@ -235,7 +245,7 @@ public class SV388GamesServiceImpl implements IGamesService {
.currencyCode(exchangeTransferMoneyRequestDTO.getSystemCurrency())
.memberId(member.getId())
.transactionId(transactionId)
.platformCode(GamePlatforms.AE.getInfo())
.platformCode(GamePlatforms.SV388.getCode())
.build();
exchangeMoney.setCreateBy(Constants.SYSTEM);
exchangeMoney.setStatus(StatusType.IN_PROGRESS.getValue());
@ -338,16 +348,16 @@ public class SV388GamesServiceImpl implements IGamesService {
//请求参数
Map<String, Object> params = this.getKey(betRecordByTimeDTO);
String timeFrom = redisCache.getCacheObject(CacheConstants.AE_TIME_FROM);
String timeFrom = redisCache.getCacheObject(CacheConstants.SV388_TIME_FROM);
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
}
params.put("timeFrom", timeFrom);
params.put("platform", GamePlatforms.SV388.getCode());
params.put("delayTime", 10000);
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetRecordByTime(params);
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl());
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
@ -355,7 +365,7 @@ public class SV388GamesServiceImpl implements IGamesService {
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
return Boolean.TRUE;
} else {
redisCache.deleteObject(CacheConstants.AE_TIME_FROM);
redisCache.deleteObject(CacheConstants.SV388_TIME_FROM);
log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
throw new BaseException(aeBetRecordResponse.getDesc());
}
@ -378,7 +388,8 @@ public class SV388GamesServiceImpl implements IGamesService {
params.put("startTime", startTime);
params.put("endTime", endTime);
params.put("platform", /*"SEXYBCRT"*/ GamePlatforms.SV388.getCode());
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params);
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl());
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
@ -504,7 +515,7 @@ public class SV388GamesServiceImpl implements IGamesService {
}
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.AE.getInfo());
List<String> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.SV388.getCode());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
@ -516,7 +527,7 @@ public class SV388GamesServiceImpl implements IGamesService {
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00";
}
redisCache.setCacheObject(CacheConstants.AE_TIME_FROM, timeFrom);
redisCache.setCacheObject(CacheConstants.SV388_TIME_FROM, timeFrom);
}
/**

View File

@ -0,0 +1,32 @@
package com.ff.sports.db.address;
import com.dtflys.forest.callback.AddressSource;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.http.ForestRequest;
import com.ff.base.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* <a href="http://api-doc-2.dbsporxxxw1box.com/#/login">DB</a>
* api
* a12345678
*
* @author cengy
*/
@Component
public class DBSportsAddress implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.DBSports.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("https", apiBaseUrl, 443, "");
}
}

View File

@ -0,0 +1,100 @@
package com.ff.sports.db.client;
import com.dtflys.forest.annotation.*;
import com.ff.sports.db.address.DBSportsAddress;
import com.ff.sports.db.dto.*;
/**
*
* @author cengy
*/
@Address(source = DBSportsAddress.class)
public interface DBSportsClient {
/**
*
*
* @return {@link CreateUserResponse}
*/
@Post(url = "/api/v2/new/user/create")
CreateUserResponse createMember(@JSONBody CreateUserRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* FB0.01
*
* @param request
* @param sign
* @param timestamp
* @param merchantId
* @return {@link TransferInResponse}
*/
@Post(url = "/api/v2/new/transfer/in")
TransferInResponse transferIn(@JSONBody TransferInRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/new/transfer/out")
TransferOutResponse transferOut(@JSONBody TransferOutRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/new/user/detail")
GetMemberInfoResponse getMemberInfo(@JSONBody GetMemberInfoRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* /
*/
@Post(url = "/api/v2/transfer/detail")
TransferDetailResponse transferDetail(@JSONBody TransferDetailRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/service/domain/list")
GetUrlResponse getUrl(@JSONBody GetUrlRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* FBFB5
* IDID
*/
@Post(url = "/api/v2/order/file/ids")
OrderFilesResponse orderFiles(@JSONBody OrderFilesRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* Json
*/
@Post(url = "/api/v2/order/list")
OrderInfoResponse getOrderJsonData(@JSONBody OrderInfoRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* apptokenurltokenapp
*
* @param request
* @param sign
* @param timestamp
* @param merchantId
* @return
*/
@Post(url = "/api/v2/token/get")
GetTokenResponse getToken(@JSONBody GetTokenRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
}

View File

@ -0,0 +1,30 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class CreateUserRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String merchantUserId;// 渠道用户id支持40位字符串必须唯一
private List<Integer> currencyIds = null; // 币种id集合 , see enum: currency
private Integer oddsLevel = null; // 赔率级别,不传则为默认, see enum: user_odds_level_enum
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("currencyIds", currencyIds);
map.put("merchantUserId", merchantUserId);
map.put("oddsLevel", oddsLevel);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,19 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class CreateUserResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private Integer data;
private Integer code;
private String message;
}

View File

@ -0,0 +1,9 @@
package com.ff.sports.db.dto;
/**
* @author cengy
*/
public class Enums {
}

View File

@ -0,0 +1,27 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class GetMemberInfoRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id,
*/
private String merchantUserId;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,38 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetMemberInfoResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private MemberInfo data;
private Integer code;
@Data
public static class MemberInfo implements Serializable{
private static final long serialVersionUID = 1L;
private String merchantUserId;
private Integer userId; // FB体育用户id
private Integer walletType; // 用户钱包类型 , see enum: wallet_type
private Integer currencyType; // 用户币种类型 , see enum: currency_type
private List<Wallet> wallets; // 钱包集合
private Integer oddsLevel;// 赔率级别 , see enum: user_odds_level_enum
}
@Data
public static class Wallet implements Serializable{
private static final long serialVersionUID = 1L;
private Integer currencyType; // 币种类型 , see enum: currency_type
private BigDecimal balance; // 余额
private Integer currencyId; // 币种id , see enum: currency
}
}

View File

@ -0,0 +1,30 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class GetTokenRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String merchantUserId;
// 平台类型pch5, mobile , see enum: plat_form_enum
private String platForm;
// 客户端用户ip地址尽可能提供我们用于风控
private String ip; // 可选
public String toJSON() {
Map<String, String> map = new LinkedHashMap<>();
map.put("merchantUserId", merchantUserId);
map.put("platForm", platForm);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,49 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetTokenResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private Integer code;
private TokenDTO data;
@Data
public static class TokenDTO implements Serializable {
private String token; // 用户鉴权token用于客户端鉴权
private ServerInfo serverInfo; // 服务器地址信息
private List<Domain> domains; // 全部服务器地址信息
private String themeBgColor; // 主题背景色
private String themeFgColor; // 主题前景色
private Integer userId; // FB用户ID
}
@Data
public static class ServerInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String apiServerAddress; // app接口服务地址
private String apiEmbeddedServerAddress; // app内嵌网页地址
private String pushServerAddress; // 推送服务地址
private String pcAddress; // PC投注网站地址
private String h5Address; // h5投注网站地址
private String virtualAddress; // 虚拟体育投注网站地址
private String virtualMatchVideoAddress; // 虚拟赛事视频地址
private String ouH5Address; // 欧版h5地址
private String ouPcAddress; // 欧版pc地址
}
@Data
public static class Domain {
private Integer type; // 域名类型
private List<String> domains; // 域名集合
}
}

View File

@ -0,0 +1,17 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class GetUrlRequest implements Serializable {
private static final long serialVersionUID = 1L;
public String toJSON() {
return "{}";
}
}

View File

@ -0,0 +1,33 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetUrlResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private List<UrlDTO> data;
private Integer code;
@Data
public static class UrlDTO implements Serializable {
private static final long serialVersionUID = 1L;
private int type; //域名类型1API2PUSH3H54PC5IMAGE , see enum: domain_type_enum
private List<DomainDTO> domainList; // 域名集合
}
@Data
public static class DomainDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String domain; // 域名
private int weight; // 权限值
}
}

View File

@ -0,0 +1,41 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class OrderFilesRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 13,null
*/
private Long startTime;
/**
* 13,null
*/
private Long endTime;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("endTime", endTime);
map.put("startTime", startTime);
return JSON.toJSONString(map);
// String endTimeStr = endTime ; // 转换为字符串
// String startTimeStr = startTime; // 转换为字符串
//
// String json = "{" +
// "\"endTime\": \"" + endTimeStr + "\", " +
// "\"startTime\": \"" + startTimeStr + "\"" +
// "}";
// return json;
}
}

View File

@ -0,0 +1,27 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class OrderFilesResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private Integer code;
private List<FileId> data;
@Data
public static class FileId implements Serializable {
private static final long serialVersionUID = 1L;
private Long fileId;
}
}

View File

@ -0,0 +1,26 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class OrderInfoRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Id/order/file/ids
*/
private Long fileId;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("fileId", fileId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,115 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class OrderInfoResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private Integer code;
private List<OrderDTO> data;
@Data
public static class OrderDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String id; // 订单号
private Integer rejectReason; // 拒单原因码 see enum: order_reject_type
private String rejectReasonStr; // 拒单原因
private String userId; // FB平台用户ID
private String merchantId; // 渠道ID
private String merchantUserId; // 渠道用户ID
private Integer currency; // 币种 see enum: currency
private String exchangeRate; // 汇率快照
private Integer seriesType; // 关次类型 0 单关、1 串关, see enum: series_type
private String betType; // 投注类型
private Integer allUp; // 总关数
private Integer allUpAlive; // 存活关数
private String stakeAmount; // 投注额(本金)
private String liabilityStake; // 名义投注额(名义本金)
private String settleAmount; // 结算派奖金额
private Integer orderStatus; // 订单状态 see enum: order_status
private Integer payStatus; // 付款状态
private Integer oddsChange; // 是否接受赔率变更 0不接受1 接受更好赔率2接受任意赔率 , see enum: odds_change_enum
private String device; // 设备类型 pc、h5、mobile) , see enum: plat_form_enum
private String ip; // 投注IP地址
private String settleTime; // 订单结算时间
private String createTime; // 订单创建时间
private String modifyTime; // 订单确认时间
private String cancelTime; // 订单取消时间
private String thirdRemark; // 第三方备注
private String relatedId; // 三方关联ID
private String maxWinAmount; // 最大可赢金额
private String loseAmount; // 最大赔付金额
private Integer rollBackCount; // 回滚次数
private Integer itemCount; // 选项数
private Integer seriesValue; // 串几关
private Integer betNum; // 子单数
private String cashOutTotalStake; // 提前结算总本金
private String liabilityCashoutStake; // 提前结算名义总本金
private String cashOutPayoutStake; // 提前结算总派奖额
private String reserveId; // 预约订单单号
private Integer cashOutCount; // 提前结算次数
private String unitStake; // 每单金额
private Integer reserveVersion; // 预约订单版本号
private List<BetDTO> betList; // 注单集合
}
@Data
public static class BetDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String id; // ID
private String orderId; // 订单ID
private Integer sportId; // 运动ID
private String matchId; // 比赛ID
private String matchName; // 比赛名称
private Integer period; // 阶段ID
private String marketId; // 玩法ID
private Integer marketType; // 玩法类型
private Integer optionType; // 投注项类型
private String optionName; // 选项名称
private String marketName; // 玩法名称
private String tournamentId; // 联赛ID
private String tournamentName; // 联赛名称
private String odds; // 欧式赔率
private Integer oddsFormat; // 投注时赔率类型
private String betOdds; // 投注时赔率
private Integer settleStatus; // 结算状态
private Integer settleResult; // 结算结果
private Boolean isInplay; // 是否滚球
private String remark; // 备注
private Double p1; // 变量1 (例如:让几个球)
private Double p2; // 变量2
private Double p3; // 变量3
private String extendedParameter; // 亚洲让球线
private String extraInfo; // 当前比分
private String pendingTime; // 延迟等待时间
private String betScore; // 下注当时比分
private Integer cancelReason; // 取消原因
private String cancelReasonName; // 取消原因文本
private Integer matchType; // 赛事类型
private String matchTime; // 开赛时间
private Integer virtualMatchDay; // 轮次
private Integer virtualChampId; // 赛季
private Integer virtualLegOrder; // 淘汰赛回合
private Integer virtualWeekDay; // 小组赛比赛日
private Integer virtualBlockId; // 期
private Integer leaguePhase; // 联赛阶段
private String maxStake; // 最大投注额
private String validSettleStakeAmount; // 有效已结算投注额
private String validSettleAmount; // 有效返还额
private String cashOutCancelStake; // 提前结算取消总额
private Integer walletType; // 钱包类型
private Integer version; // 数据变更标记
}
}

View File

@ -0,0 +1,39 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class TransferDetailRequest implements Serializable {
/**
* id,null
*/
private String businessId;
/**
* ID,null
*/
private String merchantUserId;
/**
* , see enum: transfer_type_enum
*/
private String transferType;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("businessId", businessId);
map.put("merchantUserId", merchantUserId);
map.put("transferType", transferType);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,39 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class TransferDetailResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String message;
private TransferDetail data;
private Integer code;
@Data
public static class TransferDetail implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Integer userId;
private String merchantUserId;
private String businessId;
// IN 转入
// OUT 转出
private String transferType;
private BigDecimal beforeTransferAmount;
private BigDecimal afterTransferAmount;
private Integer status; // 状态 , see enum: transfer_status_enum, 1 Successful 0 Failure
private Long createTime; // 记录创建时间
private Integer currencyId;// 币种id , see enum: currency
}
}

View File

@ -0,0 +1,46 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class TransferInRequest implements Serializable {
/**
* ,null
* 0
*/
private BigDecimal amount;
/**
* id,null
*/
private String businessId;
/**
* ID,null
*/
private String merchantUserId;
/**
* id,
*/
private Integer currencyId;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("amount", amount);
map.put("businessId", businessId);
map.put("currencyId", currencyId);
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,20 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class TransferInResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private BigDecimal data;
private Integer code;
private String message;
}

View File

@ -0,0 +1,46 @@
package com.ff.sports.db.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author cengy
*/
@Data
public class TransferOutRequest implements Serializable {
/**
* ,null
* 0
*/
private BigDecimal amount;
/**
* id,null
*/
private String businessId;
/**
* ID,null
*/
private String merchantUserId;
/**
* id,
*/
private Integer currencyId;
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("amount", amount);
map.put("businessId", businessId);
map.put("currencyId", currencyId);
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,20 @@
package com.ff.sports.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class TransferOutResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private BigDecimal data;
private Integer code;
private String message;
}

View File

@ -0,0 +1,757 @@
package com.ff.sports.db.impl;
import cn.hutool.core.util.IdUtil;
import com.ff.base.constant.CacheConstants;
import com.ff.base.constant.Constants;
import com.ff.base.core.redis.RedisCache;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.*;
import com.ff.game.domain.*;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import com.ff.sports.db.client.DBSportsClient;
import com.ff.sports.db.dto.*;
import com.ff.utils.TimestampFromString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* FB
*
* @author shi
* @date 2024/10/21
*/
@Service("DBSportsService")
@Slf4j
public class DBSportsServiceImpl implements IGamesService {
@Resource
private RedisCache redisCache;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameService gameService;
@Resource
private IMemberService memberService;
@Resource
private DBSportsClient dbSportsClient;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
/**
*
*
* @param errorCode
* @return {@link Boolean }
*/
private Boolean isSuccess(Integer errorCode) {
return 0 == errorCode;
}
String getSign(String bodyJsonString, String merchantId, String merchantApiSecret, long requestTimestamp) {
String stringThatNeedsToBeSigned = bodyJsonString + "." + merchantId + "." + requestTimestamp + "." + merchantApiSecret;
String sign = Md5Utils.md5New(stringThatNeedsToBeSigned);
return sign;
}
/**
*
*
* @param createMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
long timestamp = System.currentTimeMillis();
CreateUserRequest request = new CreateUserRequest();
request.setMerchantUserId(createMemberRequestDTO.getAccount());
ArrayList<Integer> currencyIds = new ArrayList<>();
currencyIds.add(Integer.parseInt(createMemberRequestDTO.getCurrency()));
request.setCurrencyIds(currencyIds);
String jsonBody = /*SortByAttributeNameASC.get(request)*/ request.toJSON();
String sign = getSign(jsonBody,
createMemberRequestDTO.getAgentId(),
createMemberRequestDTO.getAgentKey(),
timestamp
);
CreateUserResponse response = dbSportsClient.createMember(
request,
sign,
timestamp, createMemberRequestDTO.getAgentId());
if (isSuccess(response.getCode())) {
log.info("创建会员成功, account:{}->{}", createMemberRequestDTO.getAccount(), response.getData());
return Boolean.TRUE;
}
log.error("创建会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMessage());
throw new ApiException(ErrorCode.Create_Member_Failure.getCode());
}
/**
* id
*
* @param requestDTO moeny dto
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO requestDTO) {
Member member = memberService.selectMemberByGameAccount(requestDTO.getAccount());
String transactionId = GamePlatforms.DBSports.getCode() + IdUtils.simpleUUID();
List<GameExchangeMoney> gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList(
GameExchangeMoney.builder()
.tenantKey(requestDTO.getTenantKey())
.orderId(requestDTO.getOrderId())
.build()
);
Assert.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), "订单号重复");
//获取下一个自增id
GameExchangeMoney exchangeMoney = GameExchangeMoney
.builder()
.orderId(requestDTO.getOrderId())
.tenantKey(requestDTO.getTenantKey())
.quota(requestDTO.getQuota())
.balance(requestDTO.getAmount())
.exchangeType(requestDTO.getTransferType())
.currencyCode(requestDTO.getSystemCurrency())
.memberId(member.getId())
.transactionId(transactionId)
.platformCode(GamePlatforms.DBSports.getCode())
.build();
exchangeMoney.setCreateBy(Constants.SYSTEM);
//接口限制限制50字符
exchangeMoney.setTransactionId(transactionId);
// 转入
if (requestDTO.getTransferType().equals(TransferType.GAMES.getCode())) {
TransferInRequest request = new TransferInRequest();
request.setMerchantUserId(requestDTO.getAccount());
request.setAmount(requestDTO.getAmount());
request.setBusinessId(requestDTO.getOrderId());
request.setCurrencyId(Integer.parseInt(requestDTO.getCurrency()));
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
TransferInResponse response = dbSportsClient.transferIn(
request,
sign,
timestamp,
requestDTO.getAgentId());
if (isSuccess(response.getCode())) {
exchangeMoney.setBalance(response.getData());
BigDecimal transAmount = requestDTO.getAmount();
exchangeMoney.setCoinBefore(response.getData().subtract(transAmount));
exchangeMoney.setCoinAfter(response.getData());
exchangeMoney.setCurrencyBefore(response.getData().subtract(transAmount));
exchangeMoney.setCurrencyAfter(response.getData());
exchangeMoney.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS
gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney);
} else {
throw new ApiException(ErrorCode.Transfer_In_Failure.getCode());
}
} else {
// 获取第三方钱包余额
MemberInfoRequestDTO memberInfoRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(requestDTO.getAgentId())
.agentKey(requestDTO.getAgentKey())
.build();
BigDecimal balance = this.getMemberInfo(memberInfoRequestDTO).getBalance();
TransferOutRequest request = new TransferOutRequest();
request.setMerchantUserId(requestDTO.getAccount());
request.setAmount(/*requestDTO.getAmount()*/ balance);
request.setBusinessId(requestDTO.getOrderId());
request.setCurrencyId(Integer.parseInt(requestDTO.getCurrency()));
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
TransferOutResponse response = dbSportsClient
.transferOut(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
//判断是否转移成功
if (this.isSuccess(response.getCode())) {
//更新数据
exchangeMoney.setBalance(response.getData());
BigDecimal transAmount = balance;
exchangeMoney.setCoinBefore(response.getData().add(transAmount));
exchangeMoney.setCoinAfter(response.getData());
exchangeMoney.setCurrencyBefore(response.getData().add(transAmount));
exchangeMoney.setCurrencyAfter(response.getData());
exchangeMoney.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS
gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney);
} else {
throw new ApiException(ErrorCode.Transfer_Out_Failure.getCode());
}
}
return exchangeMoney.getId();
}
/**
*
*
* @param requestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO requestDTO) {
GetMemberInfoRequest request = new GetMemberInfoRequest();
request.setMerchantUserId(requestDTO.getAccounts());
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
GetMemberInfoResponse response = dbSportsClient.getMemberInfo(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
//判断是否获取成功
if (this.isSuccess(response.getCode())) {
BigDecimal balance = new BigDecimal("0.00");
for (GetMemberInfoResponse.Wallet wallet : response.getData().getWallets()) {
balance = balance.add(wallet.getBalance());
}
return MemberInfoResponseDTO.builder()
.status(GameMemberStatus.UNKNOWN.getCode())
.balance(balance)
.account(requestDTO.getAccounts())
.build();
} else {
throw new ApiException(ErrorCode.Get_Member_Info_Failure.getCode());
}
}
/**
*
*
* @param requestDTO
* @return {@link String }
*/
@Override
public String loginWithoutRedirect(GamesLogin requestDTO) {
GetTokenRequest request = new GetTokenRequest();
request.setMerchantUserId(requestDTO.getAccount());
request.setPlatForm(/*requestDTO.getPlatform()*/ "mobile"); // pch5, mobile , see enum: plat_form_enum
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
GetTokenResponse response = dbSportsClient.getToken(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
if (this.isSuccess(response.getCode())) {
String token = response.getData().getToken();
Integer userId = response.getData().getUserId();
GetTokenResponse.ServerInfo serverInfo = response.getData().getServerInfo();
String h5Address = serverInfo.getH5Address();
if (StringUtils.isEmpty(h5Address)) {
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
return h5Address +
"/index.html#/" +
"?token=" +
token +
"&nickname=" +
userId +
"&apiSrc=" +
serverInfo.getApiServerAddress() +
"&pushSrc=" +
serverInfo.getPushServerAddress() +
"&virtualSrc=" +
serverInfo.getVirtualAddress() +
"&platformName=XK体育";
}
/*GetUrlRequest request = new GetUrlRequest();
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
GetUrlResponse response = fbSportsClient.getUrl(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
//判断是否获取成功
if (this.isSuccess(response.getCode())) {
List<GetUrlResponse.UrlDTO> urlDTOS = new ArrayList<>();
for (GetUrlResponse.UrlDTO urlDTO : response.getData()) {
if (urlDTO.getType() == 3) { // 3:h5
urlDTOS.add(urlDTO);
}
}
if (urlDTOS.isEmpty()){
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
int randomIndex = new Random().nextInt(urlDTOS.size());
GetUrlResponse.UrlDTO urlDTO = urlDTOS.get(randomIndex);
Collections.shuffle(urlDTO.getDomainList());
return urlDTO.getDomainList().get(0).getDomain();
}*/
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
private static final Long GAME_ID = 11111L;
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
@Transactional
@Override
public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {
Platform platform = gamesBaseRequestDTO.getVendor();
Game condition = new Game();
condition.setPlatformCode(platform.getPlatformCode());
condition.setPlatformType(PlatformType.SPORTS.getCode());
List<Game> gameList = gameService.selectGameList(condition);
//不存在这个游戏
if (ObjectUtils.isEmpty(gameList)) {
Game game = new Game();
game.setId(IdUtil.getSnowflakeNextId());
game.setSortNo(1);
game.setPlatformCode(platform.getPlatformCode());
game.setPlatformType(PlatformType.SPORTS.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName("DB体育");
game.setCreateBy(Constants.SYSTEM);
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("DB体育");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.insertGame(game);
}
/*GameName gameName = gameNameService.selectGameNameById(GAME_NAME_ID);
if (ObjectUtils.isEmpty(gameName)) {
gameNameService.insertGameName(GameName.builder()
.id(GAME_NAME_ID)
.gameId(game.getId())
.gameName(game.getGameName())
.langCode("zh-CN")
.createBy(Constants.SYSTEM)
.build());
}*/
return CacheConstants.DB_Sports;
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) {
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId());
if (null == gameExchangeMoney) {
throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode());
}
Integer status = StatusType.IN_PROGRESS.getValue();
if (!Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) {
TransferDetailRequest request = new TransferDetailRequest();
request.setMerchantUserId(requestDTO.getAccount());
request.setTransferType(Objects.equals(gameExchangeMoney.getExchangeType(), TransferType.GAMES.getCode())
? "IN" : "OUT");
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
TransferDetailResponse response = dbSportsClient.transferDetail(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
if (this.isSuccess(response.getCode())) {
status = StatusType.SUCCESS.getValue();
} else {
status = StatusType.FAILURE.getValue();
}
}
return ExchangeTransferStatusResponseDTO.builder()
.statusType(status)
.balance(gameExchangeMoney.getBalance())
.coinBefore(gameExchangeMoney.getCoinBefore())
.coinAfter(gameExchangeMoney.getCoinAfter())
.build();
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO requestDTO) {
Long lend = requestDTO.getEndTime();
Long lstart = requestDTO.getStartTime();
//betRecordByTimeDTO.setStartTime(lstart);
//betRecordByTimeDTO.setEndTime(lend);
OrderFilesRequest request = new OrderFilesRequest();
request.setStartTime(lstart);
request.setEndTime(lend);
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
OrderFilesResponse orderFilesResponse = dbSportsClient.orderFiles(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
if (this.isSuccess(orderFilesResponse.getCode())) {
for (OrderFilesResponse.FileId fileId : orderFilesResponse.getData()) {
try {
getOrderData(fileId.getFileId(), requestDTO);
} catch (Exception e) {
log.error("获取订单数据失败,fileId:{},agentId:{}", fileId, requestDTO.getAgentId());
}
}
return true;
}
log.error("获取订单文件失败,agentId:{}", requestDTO.getAgentId());
return false;
}
void getOrderData(Long fileId, BetRecordByTimeDTO requestDTO) {
OrderInfoRequest request = new OrderInfoRequest();
request.setFileId(fileId);
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
OrderInfoResponse orderInfoResponse = dbSportsClient.getOrderJsonData(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
if (!this.isSuccess(orderInfoResponse.getCode())) {
return;
}
List<OrderInfoResponse.OrderDTO> settledOrderList = new ArrayList<>();
for (OrderInfoResponse.OrderDTO orderDTO : orderInfoResponse.getData()) {
// 0 Created 未确认
// 1 Confirming 确认中
// 2 Rejected 已拒单
// 3 Canceled 已取消
// 4 Confirmed 已接单
// 5 Settled 已结算
if (!orderDTO.getOrderStatus().equals(5)) { // 已结算
continue;
}
settledOrderList.add(orderDTO);
}
this.batchInsert(settledOrderList, requestDTO);
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO requestDTO) {
Long lend = requestDTO.getEndTime();
Long lstart = requestDTO.getStartTime();
//betRecordByTimeDTO.setStartTime(lstart);
//betRecordByTimeDTO.setEndTime(lend);
OrderFilesRequest request = new OrderFilesRequest();
request.setStartTime(lstart);
request.setEndTime(lend);
long timestamp = System.currentTimeMillis();
String jsonBody = request.toJSON();
String sign = getSign(jsonBody,
requestDTO.getAgentId(),
requestDTO.getAgentKey(),
timestamp
);
OrderFilesResponse orderFilesResponse = dbSportsClient.orderFiles(
request,
sign,
timestamp,
requestDTO.getAgentId()
);
if (this.isSuccess(orderFilesResponse.getCode())) {
for (OrderFilesResponse.FileId fileId : orderFilesResponse.getData()) {
try {
getOrderData(fileId.getFileId(), requestDTO);
} catch (Exception e) {
log.error("获取订单数据失败,fileId:{},agentId:{}", fileId, requestDTO.getAgentId());
}
}
return true;
}
log.error("获取订单文件失败,agentId:{}", requestDTO.getAgentId());
return false;
}
/**
*
*
* @param createFreeSpinRequest
* @return {@link Boolean }
*/
@Override
public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) {
throw new BaseException("暂不支持免费局数");
}
/**
*
*
* @param getGameDetailRequestDTO dto
* @return {@link GetGameDetailResponseDTO }
*/
@Override
public GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param kickMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) {
return Boolean.FALSE;
}
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param cancelFreeSpinRequestDTO
* @return {@link Boolean }
*/
@Override
public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param gameDemoLoginRequestDTO dto
* @return {@link GameDemoLoginResponseDTO }
*/
@Override
public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param settledOrderList
*/
private void batchInsert(List<OrderInfoResponse.OrderDTO> settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<OrderInfoResponse.OrderDTO> dataList = settledOrderList;
if (CollectionUtils.isEmpty(dataList)) {
return;
}
//数据转化
for (OrderInfoResponse.OrderDTO dataBean : dataList) {
GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder()
.platform(betRecordByTimeDTO.getVendor())
.data(dataBean).build());
if (!ObjectUtils.isEmpty(bettingDetails)) {
bettingDetails.setId(IdUtil.getSnowflakeNextId());
gameBettingDetails.add(bettingDetails);
}
wagersIds.add(dataBean.getId());
}
if (CollectionUtils.isEmpty(gameBettingDetails)) {
return;
}
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService
.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.DBSports.getCode());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
gameBettingDetailsService.batchInsert(gameBettingDetails);
}
}
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
@Override
public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
//转化类
OrderInfoResponse.OrderDTO dataBean = (OrderInfoResponse.OrderDTO) gamesDataBuildDTO.getData();
Member member = memberService.selectMemberByGameAccount(dataBean.getMerchantUserId());
if (ObjectUtils.isEmpty(member)) {
return null;
}
List<Game> gameList = redisCache.getCacheList(CacheConstants.DB_Sports);
Game game = gameList.get(0);
BigDecimal originPayoffAmount = new BigDecimal(dataBean.getSettleAmount());
BigDecimal betAmount = new BigDecimal(dataBean.getStakeAmount());
int compareResult = originPayoffAmount.compareTo(betAmount);
long payoffTime = TimestampFromString.to(dataBean.getSettleTime());
long createTime = TimestampFromString.to(dataBean.getCreateTime());
Platform platform = gamesDataBuildDTO.getPlatform();
String systemCurrency = platform.getOurCurrency(dataBean.getCurrency().toString());
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(systemCurrency)
.memberId(member.getId())
.gameCode(game.getGameCode())
.gameType(PlatformType.SPORTS.getCode()) // 体育
.platformCode(GamePlatforms.DBSports.getCode())
.gameId(game.getId())
.gameName(game.getGameName())
.gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode())
.gameStatusType(1) // 一般下注
.gameCurrencyCode(dataBean.getCurrency().toString())
.account(dataBean.getMerchantUserId())
.wagersId(dataBean.getId())
.wagersTime(createTime)
.betAmount(betAmount)
.payoffTime(payoffTime)
.payoffAmount(originPayoffAmount.abs())
.settlementTime(payoffTime)
.turnover(betAmount)
.orderNo(dataBean.getId())
.settlementStatus(SettlementStatusEnum.COMPLETED.getCode())
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -383,7 +383,7 @@ public class FBSportsServiceImpl implements IGamesService {
game.setSortNo(1);
//game.setPlatformId(gamePlatform.getId());
game.setPlatformCode(platform.getPlatformCode());
game.setPlatformType(PlatformType.GAME_HALL.getCode());
game.setPlatformType(PlatformType.SPORTS.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName("FB体育");