diff --git a/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java b/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java index 9df0da0..01d8620 100644 --- a/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java +++ b/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java @@ -92,6 +92,15 @@ public class CacheConstants { * dg游戏 */ public static final String DG_GAMES = "dg_games:"; + /** + * fb体育 + */ + public static final String FB_Sports = "fp_sports:"; + + /** + * db体育 + */ + public static final String DB_Sports = "db_sports:"; /** * pg游戏投注货币 */ @@ -121,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:"; + + } diff --git a/ff-base/src/main/java/com/ff/base/enums/ErrorCode.java b/ff-base/src/main/java/com/ff/base/enums/ErrorCode.java index 89bfeb4..a2df09e 100644 --- a/ff-base/src/main/java/com/ff/base/enums/ErrorCode.java +++ b/ff-base/src/main/java/com/ff/base/enums/ErrorCode.java @@ -20,8 +20,8 @@ public enum ErrorCode { CURRENCY_NOT_EXIST(1004, "游戏平台不支持的货币"), GAME_NOT_EXIST(1005, "游戏不存在"), CURRENCY_EXCHANGE(1006, "不支持币种的汇率"), - FREQUENT_INTERFACE_REQUESTS (1007, "接口请求频繁"), - BALANCE_TRANSFER_FAILED (1008, "余额转移失败"), + FREQUENT_INTERFACE_REQUESTS(1007, "接口请求频繁"), + BALANCE_TRANSFER_FAILED(1008, "余额转移失败"), LANG_NOT_EXIST(1009, "游戏平台不支持的语言"), ORDER_NOT_EXIST(1010, "订单不存在"), PLAYERS_ARE_PLAYING(1011, "玩家游玩中"), @@ -30,7 +30,14 @@ public enum ErrorCode { ACCOUNT_NOT_ONLINE(1014, "账号不在线"), FREQUENT_BALANCE_TRANSFER(1015, "当前游戏账号余额转移频繁"), PLATFORM_NOT_METHODS(1016, "游戏平台不支持的方法"), - DUPLICATE_ORDER_ID (1017, "重复的订单id"), + Create_Member_Failure(1017, "创建会员失败"), + Transfer_In_Failure(1018, "转入失败"), + Transfer_Out_Failure(1019, "转出失败"), + Get_Member_Info_Failure(1020, "获取会员信息失败"), + Transfer_Not_Exist(1021, "转帐操作不存在"), + Get_Url_Failure(1022, "获取URL失败"), + Miss_Config(1023, "缺少配置"), + DUPLICATE_ORDER_ID (1024, "重复的订单id"), ; // 获取错误码 diff --git a/ff-base/src/main/java/com/ff/base/enums/FBSportsType.java b/ff-base/src/main/java/com/ff/base/enums/FBSportsType.java new file mode 100644 index 0000000..f3fff75 --- /dev/null +++ b/ff-base/src/main/java/com/ff/base/enums/FBSportsType.java @@ -0,0 +1,60 @@ +package com.ff.base.enums; + + +import lombok.Getter; + +import java.util.Optional; +import java.util.stream.Stream; + + +/** + * xkgame类型 + * + * @author shi + * @date 2024/11/13 + */ +@Getter +public enum FBSportsType { + + Sports("8", 8,"FB体育"), + ; + + private final String code; + private final Integer systemCode; + private final String info; + FBSportsType(String code, Integer systemCode, String info) + { + this.code = code; + this.systemCode = systemCode; + this.info = info; + } + + + /** + * 按代码查找系统 + * + * @param code 代码 + * @return {@link String } + */ + public static Integer findSystemByCode(String code) { + Optional system = Stream.of(FBSportsType.values()) + .filter(gameType -> gameType.getCode().equals(code)) + .map(FBSportsType::getSystemCode) + .findFirst(); + return system.orElse(null); + } + + /** + * 按代码查找信息 + * + * @param code 代码 + * @return {@link String } + */ + public static String findInfoByCode(String code) { + Optional system = Stream.of(FBSportsType.values()) + .filter(gameType -> gameType.getCode().equals(code)) + .map(FBSportsType::getInfo) + .findFirst(); + return system.orElse(null); + } +} diff --git a/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java b/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java index 5036923..a6152c1 100644 --- a/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java +++ b/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java @@ -17,7 +17,9 @@ public enum GamePlatforms { AE("AE", "AE"), KM("KM", "KM"), PGT("PGT", "PGT"), - ; + FBSports("FBSports", "FB体育"), + SV388("SV388", "SV388真人"), + DBSports("DBSports", "DB体育"); private final String code; private final String info; diff --git a/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java b/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java index 8740bba..f393575 100644 --- a/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java +++ b/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java @@ -83,9 +83,6 @@ public class ApiGameController extends BaseController { @Resource private ITenantGameQuotaService tenantGameQuotaService; - @Resource - private ITenantGameQuotaFlowService tenantGameQuotaFlowService; - @Resource private IGameBettingDetailsService gameBettingDetailsService; diff --git a/ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java b/ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java index cf85cf6..31eced5 100644 --- a/ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java +++ b/ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java @@ -289,14 +289,11 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService { ApiException.notNull(tenantPlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode()); - // 获取用户信息 Member member = memberService.selectMemberByAccount(gameBalanceExchange.getAccount(), gameBalanceExchange.getCurrencyCode(), gameBalanceExchange.getPlatformCode()); ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode()); - - // 检查用户是否存在,否则抛出异常 ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode()); @@ -326,6 +323,9 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService { .agentKey(gameBalanceExchange.getAgentKey()) .build(); balanceRequestAmount = iGamesService.getMemberInfo(gamesBaseRequestDTO).getBalance(); + if (balanceRequestAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode()); + } } @@ -390,12 +390,10 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService { // 如果是扣账操作,首先处理假额度 String falseTenantQuotaType = QuotaUtils.getFalseTenantQuota(gameBalanceExchange.getPlatformCode(), gameBalanceExchange.getCurrencyCode()); TenantGameQuota falseTenantQuota = selectTenantGameQuotaByTenantKey(tenantSecretKey.getTenantKey(), falseTenantQuotaType); - balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED))); + balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED))); if (falseTenantQuota.getBalance().compareTo(BigDecimal.ZERO) > 0) { - - BigDecimal falseQuotaBalance = falseTenantQuota.getBalance(); if (falseQuotaBalance.compareTo(balanceRequestAmount) >= 0) { // 假额度足够扣除本次所需金额 diff --git a/ff-game/src/main/java/com/ff/delay/DelayService.java b/ff-game/src/main/java/com/ff/delay/DelayService.java new file mode 100644 index 0000000..c880667 --- /dev/null +++ b/ff-game/src/main/java/com/ff/delay/DelayService.java @@ -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 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); + } + } + } +} diff --git a/ff-game/src/main/java/com/ff/delay/DelayTask.java b/ff-game/src/main/java/com/ff/delay/DelayTask.java new file mode 100644 index 0000000..e32cc7d --- /dev/null +++ b/ff-game/src/main/java/com/ff/delay/DelayTask.java @@ -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(); +} + diff --git a/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java index 67a09dd..8aeaee6 100644 --- a/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java @@ -16,10 +16,13 @@ import com.ff.base.utils.uuid.IdUtils; import com.ff.common.dto.GameBalanceExchange; import com.ff.common.service.ITenantGameQuotaService; import com.ff.config.KeyConfig; +import com.ff.delay.DelayService; +import com.ff.delay.DelayTask; import com.ff.game.api.IGamesService; import com.ff.game.api.ae.client.AEClient; import com.ff.game.api.ae.dto.*; import com.ff.game.api.request.*; +import com.ff.game.api.sv388.impl.SV388GamesServiceImpl; import com.ff.game.api.xk.dto.XKKickMemberDTO; import com.ff.game.domain.*; import com.ff.game.service.IGameBettingDetailsService; @@ -79,6 +82,9 @@ public class GamesAEServiceImpl implements IGamesService { @Resource private IGameBettingDetailsService gameBettingDetailsService; + @Resource + private DelayService delayService; + /** * 游戏id */ @@ -319,15 +325,20 @@ public class GamesAEServiceImpl implements IGamesService { .build(); } + class GetRealtimeRecordTask extends DelayTask { + BetRecordByTimeDTO betRecordByTimeDTO; - /** - * 按时间获取投注记录 - * - * @param betRecordByTimeDTO 按时间dto投注记录 - * @return {@link List }<{@link GameBettingDetails }> - */ - @Override - public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + public GetRealtimeRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + getRealtimeRecord(betRecordByTimeDTO); + } + } + + void getRealtimeRecord(BetRecordByTimeDTO betRecordByTimeDTO) { //请求参数 log.info("GamesAEServiceImpl [getBetRecordByTime] 请求参数 {}", betRecordByTimeDTO); Map params = this.getKey(betRecordByTimeDTO); @@ -347,23 +358,32 @@ public class GamesAEServiceImpl implements IGamesService { if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { //数据组装 this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); - return Boolean.TRUE; + if (aeBetRecordResponse.getTransactions().size() >= 20000) { + delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO)); + } + //return Boolean.TRUE; } else { redisCache.deleteObject(CacheConstants.AE_TIME_FROM); log.error("GamesAEServiceImpl [getBetRecordByTime] 获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); - throw new BaseException(aeBetRecordResponse.getDesc()); + //throw new BaseException(aeBetRecordResponse.getDesc()); } } - /** - * 按历史时间获取投注记录 - * - * @param betRecordByTimeDTO 按时间dto投注记录 - * @return {@link Boolean } - */ - @Override - public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + class GetHistoryRecordTask extends DelayTask { + BetRecordByTimeDTO betRecordByTimeDTO; + + public GetHistoryRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + getHistoryRecord(betRecordByTimeDTO); + } + } + + void getHistoryRecord(BetRecordByTimeDTO betRecordByTimeDTO) { log.info("GamesAEServiceImpl [getBetRecordByHistoryTime] 请求参数 {}", betRecordByTimeDTO); Map params = this.getKey(betRecordByTimeDTO); String startTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00"; @@ -379,13 +399,40 @@ public class GamesAEServiceImpl implements IGamesService { if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { //数据组装 this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); - return Boolean.TRUE; + if (aeBetRecordResponse.getTransactions().size() >= 20000) { + delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO)); + } +// return Boolean.TRUE; } else { log.error("GamesAEServiceImpl [getBetRecordByHistoryTime] 获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); - throw new BaseException(aeBetRecordResponse.getDesc()); +// throw new BaseException(aeBetRecordResponse.getDesc()); } } + /** + * 按时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link List }<{@link GameBettingDetails }> + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO)); + return Boolean.TRUE; + } + + /** + * 按历史时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO)); + return Boolean.TRUE; + } + /** * 赠送免费局数 * @@ -558,7 +605,7 @@ public class GamesAEServiceImpl implements IGamesService { .gameCode(resultBean.getGameCode()) .gameType(PlatformType.GAME_HALL.getCode()) .platformCode(GamePlatforms.AE.getCode()) - .gameId(GAME_ID) + .gameId(/*GAME_ID*/ StringUtils.addSuffix(GamePlatforms.AE.getCode(), 1)) .gameName(resultBean.getGameName()) .gameStatus(gameStatus) .gameStatusType(resultBean.getSettleStatus()) diff --git a/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java index ca49446..a8c1730 100644 --- a/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java @@ -553,7 +553,8 @@ public class GamesDGServiceImpl implements IGamesService { .gameCode(String.valueOf(resultBean.getGameId())) .gameType(PlatformType.CARD_GAME.getCode()) .platformCode(GamePlatforms.DG.getCode()) - .gameId(GAME_ID) + //.gameId(GAME_ID) + .gameId(game.getGameId()) .gameName(game.getGameName()) .gameStatus(gameStatus) .gameStatusType(resultBean.getGameType()) diff --git a/ff-game/src/main/java/com/ff/game/api/fc/dto/ApiFCGameListResponseDTO.java b/ff-game/src/main/java/com/ff/game/api/fc/dto/ApiFCGameListResponseDTO.java index 784fd18..879d51b 100644 --- a/ff-game/src/main/java/com/ff/game/api/fc/dto/ApiFCGameListResponseDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/fc/dto/ApiFCGameListResponseDTO.java @@ -37,7 +37,7 @@ public class ApiFCGameListResponseDTO { /** * 系统游戏id */ - private Long systemGameId; + private String systemGameId; /** * 游戏id */ diff --git a/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java index d5a4f85..209e0d2 100644 --- a/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java @@ -285,7 +285,7 @@ public class GamesFCServiceImpl implements IGamesService { game = games.get(0); } - gameDetails.setSystemGameId(game.getId()); + gameDetails.setSystemGameId(game.getGameId()); gameDetails.setGameId(gameIdKey); gameDetailsList.add(gameDetails); } diff --git a/ff-game/src/main/java/com/ff/game/api/jili/dto/JILIGamesDataDTO.java b/ff-game/src/main/java/com/ff/game/api/jili/dto/JILIGamesDataDTO.java index 55a35da..f653ae0 100644 --- a/ff-game/src/main/java/com/ff/game/api/jili/dto/JILIGamesDataDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/jili/dto/JILIGamesDataDTO.java @@ -22,7 +22,7 @@ public class JILIGamesDataDTO { /** *自己系统游戏id */ - private Long systemGameId; + private String systemGameId; /** * 名称 diff --git a/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java index 367f30e..954330a 100644 --- a/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java @@ -253,7 +253,7 @@ public class GamesJILIServiceImpl implements IGamesService { } else { game = games.get(0); } - gamesDataDTO.setSystemGameId(game.getId()); + gamesDataDTO.setSystemGameId(game.getGameId()); } @@ -788,7 +788,7 @@ public class GamesJILIServiceImpl implements IGamesService { .gameCode(jiliBetRecordDataResponseDTO.getGameId()) .gameType(JILIGameType.findSystemByCode(jiliBetRecordDataResponseDTO.getGameCategoryId())) .platformCode(GamePlatforms.JILI.getInfo()) - .gameId(gamesDataDTO.getSystemGameId()) + .gameId(/*gamesDataDTO.getSystemGameId()*/gamesDataDTO.getSystemGameId()) .gameName(gamesDataDTO.getName().getZhCN()) .gameStatus(jiliBetRecordDataResponseDTO.getStatus()) .gameStatusType(jiliBetRecordDataResponseDTO.getType()) diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java index cd160d1..57eb949 100644 --- a/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java @@ -91,7 +91,7 @@ public class KMGameResponse { /** * 系统游戏id */ - private Long systemGameId; + private String systemGameId; /** * 游戏是否提供试玩 diff --git a/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java index 07fb272..fcbd8c0 100644 --- a/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java @@ -302,7 +302,7 @@ public class GamesKMServiceImpl implements IGamesService { } else { game = games.get(0); } - gamesDataDTO.setSystemGameId(game.getId()); + gamesDataDTO.setSystemGameId(game.getGameId()); } return gameList.getGames(); } diff --git a/ff-game/src/main/java/com/ff/game/api/meitian/dto/MeiTianGameDataDTO.java b/ff-game/src/main/java/com/ff/game/api/meitian/dto/MeiTianGameDataDTO.java index 2a89a6b..45cccf1 100644 --- a/ff-game/src/main/java/com/ff/game/api/meitian/dto/MeiTianGameDataDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/meitian/dto/MeiTianGameDataDTO.java @@ -22,7 +22,7 @@ public class MeiTianGameDataDTO { /** *自己系统游戏id */ - private Long systemGameId; + private String systemGameId; private String gameId; private String cnName; private String enName; diff --git a/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java index 8ebe789..84ef3d9 100644 --- a/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java @@ -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; + /** * 获得就是成功 * @@ -245,8 +248,7 @@ public class MeiTianGameServiceImpl implements IGamesService { } else { game = games.get(0); } - gamesDataDTO.setSystemGameId(game.getId()); - + gamesDataDTO.setSystemGameId(game.getGameId()); } redisCache.deleteObject(CacheConstants.MeiTian_GAMES); @@ -276,7 +278,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()) @@ -316,7 +318,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)); @@ -349,7 +351,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)); @@ -389,7 +391,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(); @@ -403,6 +405,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); + } + } + /** * 按时间获取投注记录 * @@ -411,18 +441,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 rawMap = new LinkedHashMap<>(); rawMap.put("recordID", recordID); @@ -448,7 +481,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); @@ -457,7 +492,7 @@ public class MeiTianGameServiceImpl implements IGamesService { } else { config.setConfigValue(dataBean.getRecordID()); sysConfigServiceImpl.updateConfig(config); - } + }*/ // 它每次返回25000条,所以需要判断,如果大于25000条,则继续拉取 if (dataList.size() >= 25000) { doSyncRecordByRecordID(betRecordByTimeDTO); @@ -473,22 +508,7 @@ public class MeiTianGameServiceImpl implements IGamesService { String date = getDateStr(daysToSubtract); String configKey = GamePlatforms.MT.getCode() + ":lastSyncDate"; - String syncDateStr = sysConfigServiceImpl.selectConfigByKey(configKey); - Map 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 rawMap = new LinkedHashMap<>(); rawMap.put("rowID", recordID); @@ -516,8 +536,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); @@ -526,7 +547,7 @@ public class MeiTianGameServiceImpl implements IGamesService { } else { config.setConfigValue(JSON.toJSONString(syncDateMap)); sysConfigServiceImpl.updateConfig(config); - } + }*/ // 它每次返回25000条,所以需要判断,如果大于25000条,则继续拉取 if (dataList.size() >= 25000) { @@ -547,9 +568,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; } @@ -779,10 +801,6 @@ public class MeiTianGameServiceImpl implements IGamesService { } } - public LocalDate getDate(int daysToSubtract) { - return LocalDate.now().minusDays(daysToSubtract); // 获取当前日期减去两天 - } - public String getDateStr(int daysToSubtract) { // 获取当前日期减去指定天数 LocalDate date = LocalDate.now().minusDays(daysToSubtract); diff --git a/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java index 9c950c5..878b9c0 100644 --- a/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java @@ -685,7 +685,7 @@ public class GamesPGServiceImpl implements IGamesService { .gameCode(gamesDataDTO.getGameCode()) .gameType(NGGameType.findSystemByCode(resultBean.getGameType())) .platformCode(NGPlatforms.getByCode(resultBean.getPlatType()).getPlatform()) - .gameId(gamesDataDTO.getId()) + .gameId(gamesDataDTO.getGameId()) .gameName(gamesDataDTO.getGameName()) .gameStatus(gameStatus) .gameStatusType(1) diff --git a/ff-game/src/main/java/com/ff/game/api/pgt/dto/PGTGameListResponse.java b/ff-game/src/main/java/com/ff/game/api/pgt/dto/PGTGameListResponse.java index 28d0704..717680e 100644 --- a/ff-game/src/main/java/com/ff/game/api/pgt/dto/PGTGameListResponse.java +++ b/ff-game/src/main/java/com/ff/game/api/pgt/dto/PGTGameListResponse.java @@ -83,7 +83,7 @@ public class PGTGameListResponse { /** * 系统游戏id */ - private Long systemGameId; + private String systemGameId; /** * 游戏排名 diff --git a/ff-game/src/main/java/com/ff/game/api/pgt/impl/GamesPGTServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/pgt/impl/GamesPGTServiceImpl.java index f95ced5..5523c06 100644 --- a/ff-game/src/main/java/com/ff/game/api/pgt/impl/GamesPGTServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/pgt/impl/GamesPGTServiceImpl.java @@ -244,7 +244,7 @@ public class GamesPGTServiceImpl implements IGamesService { } else { game = games.get(0); } - gameIdKey.setSystemGameId(game.getId()); + gameIdKey.setSystemGameId(game.getGameId()); } @@ -619,6 +619,7 @@ public class GamesPGTServiceImpl implements IGamesService { .gameCode(resultBean.getGameCode()) .gameType(PGTGameType.findSystemByCode(gamesDataDTO.getCategory())) .platformCode(GamePlatforms.PGT.getCode()) + //.gameId(gamesDataDTO.getSystemGameId()) .gameId(gamesDataDTO.getSystemGameId()) .gameName(gamesDataDTO.getName()) .gameStatus(PGTPayoutStatus.getByCode(resultBean.getPayoutStatus()).getSystemCode()) diff --git a/ff-game/src/main/java/com/ff/game/api/pgx/dto/PGXGameListResponse.java b/ff-game/src/main/java/com/ff/game/api/pgx/dto/PGXGameListResponse.java index f53e146..500a792 100644 --- a/ff-game/src/main/java/com/ff/game/api/pgx/dto/PGXGameListResponse.java +++ b/ff-game/src/main/java/com/ff/game/api/pgx/dto/PGXGameListResponse.java @@ -48,7 +48,7 @@ public class PGXGameListResponse { /** * 系统游戏id */ - private Long systemGameId; + private String systemGameId; /** 游戏代码 (字符串类型) */ @JsonProperty("gameCode") diff --git a/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java index a125396..0492158 100644 --- a/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java @@ -252,7 +252,7 @@ public class GamesPGXServiceImpl implements IGamesService { } else { game = games.get(0); } - gamesDataDTO.setSystemGameId(game.getId()); + gamesDataDTO.setSystemGameId(game.getGameId()); } diff --git a/ff-game/src/main/java/com/ff/game/api/request/MemberInfoRequestDTO.java b/ff-game/src/main/java/com/ff/game/api/request/MemberInfoRequestDTO.java index 40a3182..78c20cd 100644 --- a/ff-game/src/main/java/com/ff/game/api/request/MemberInfoRequestDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/request/MemberInfoRequestDTO.java @@ -6,7 +6,7 @@ import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** - * 创建成员请求dto + * 查询用户信息 * * @author shi * @date 2024/10/22 @@ -19,7 +19,7 @@ public class MemberInfoRequestDTO extends GamesBaseRequestDTO { /** * 账户 */ - private String accounts; + private String accounts; } diff --git a/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java index 8b44bdc..9011d9d 100644 --- a/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java @@ -582,7 +582,7 @@ public class GamesSAServiceImpl implements IGamesService { .gameCode(resultBean.getGameID()) .gameType(PlatformType.CARD_GAME.getCode()) .platformCode(GamePlatforms.SA.getInfo()) - .gameId(GAME_ID) + .gameId(/*GAME_ID*/game.getGameId()) .gameName(game.getGameName()) .gameStatus(gameStatus) .gameStatusType(1) diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/address/SV388Adrress.java b/ff-game/src/main/java/com/ff/game/api/sv388/address/SV388Adrress.java new file mode 100644 index 0000000..4474614 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/address/SV388Adrress.java @@ -0,0 +1,31 @@ +package com.ff.game.api.sv388.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; + + +/** + * 我jili address来源 + * + * @author shi + * @date 2025/02/10 + */ +@Component +public class SV388Adrress implements AddressSource { + + @Resource + private IPlatformService platformService; + + @Override + public ForestAddress getAddress(ForestRequest request) { + String apiBaseUrl = platformService.get(GamePlatforms.SV388.getCode()) + .getUrlInfo().getUrl(); + return new ForestAddress("https", apiBaseUrl, 443, ""); + } +} \ No newline at end of file diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/client/SV388Client.java b/ff-game/src/main/java/com/ff/game/api/sv388/client/SV388Client.java new file mode 100644 index 0000000..7f57520 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/client/SV388Client.java @@ -0,0 +1,136 @@ +package com.ff.game.api.sv388.client; + +import com.dtflys.forest.annotation.*; +import com.ff.game.api.jili.dto.JILIExchangeMoneyResponseDTO; +import com.ff.game.api.jili.dto.JILIKickMemberAllDTO; +import com.ff.game.api.jili.dto.JILIKickMemberDTO; +import com.ff.game.api.sv388.address.SV388Adrress; +import com.ff.game.api.sv388.dto.*; +import com.ff.game.api.xk.dto.XKKickMemberAllDTO; +import com.ff.game.api.xk.dto.XKKickMemberDTO; + +import java.util.Map; + +/** + * xk 请求 + * + * @author shi + * @date 2025/02/10 + */ +@Address(source = SV388Adrress.class) +public interface SV388Client { + /** + * 创建成员 + * + * @param params 参数 + * @return {@link String } + */ + @Post(url ="/wallet/createMember", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388Response createMember(@Body Map params); + + /** + * 获取会员信息 + * + * @param params 参数 + * @return {@link SV388MemberInfo } + */ + @Post(url ="/wallet/getBalance", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388MemberInfo getMemberInfo(@Body Map params); + + /** + * 无重定向登录 + * + * @param params 参数 + * @return {@link SV388LoginResponse } + */ + @Post("/wallet/login") + SV388LoginResponse loginWithoutRedirect(@Body Map params); + + + + /** + * 按代理id进行交换转账 + * + * @param params 参数 + * @return {@link JILIExchangeMoneyResponseDTO } + */ + @Post(url ="/wallet/deposit", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388AETransactionResponse deposit(@Body Map params); + + + @Post(url ="/wallet/withdraw", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388AETransactionResponse withdraw(@Body Map params); + + + /** + * 汇兑转移状态 + * + * @param params 参数 + * @return {@link SV388ExchangeTransferStatusResponse } + */ + @Post(url ="/wallet/checkTransferOperation", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388ExchangeTransferStatusResponse exchangeTransferStatus(@Body Map params); + + /** + * 按时间获取投注记录 + * + * @param params 参数 + * @return {@link SV388BetRecordResponse } + */ + @Post(url ="{fetchUrl}/fetch/gzip/getTransactionByUpdateDate", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388BetRecordResponse getBetRecordByTime(@Body Map params, + @Var("fetchUrl") String fetchUrl); + + /** + * 按时间获取投注历史记录 + * + * @param params 参数 + * @return {@link SV388BetRecordResponse } + */ + @Post(url ="{fetchUrl}/fetch/gzip/getTransactionByTxTime", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map params, + @Var("fetchUrl") String fetchUrl); + /** + * 踢出队员 + * + * @param params 参数 + * @return {@link JILIKickMemberDTO } + */ + @Post(url ="/wallet/logout", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + XKKickMemberDTO kickMember(@Body Map params); + + /** + * 踢出所有队员 + * + * @param params 参数 + * @return {@link JILIKickMemberAllDTO } + */ + @Get("/kickMemberAll") + XKKickMemberAllDTO kickMemberAll(@JSONBody Map params); + + +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388AETransactionResponse.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388AETransactionResponse.java new file mode 100644 index 0000000..5e029e0 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388AETransactionResponse.java @@ -0,0 +1,60 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.ZonedDateTime; + +/** + * 电子交易响应 + * + * @author shi + * @date 2025/04/01 + */ +@Data +public class SV388AETransactionResponse { + + /** + * 响应状态 + */ + @JsonProperty("status") + private String status; + + + /** + * 金额 + */ + @JsonProperty("amount") + private BigDecimal amount; + + /** + * 交易方法(如:DEPOSIT, WITHDRAW) + */ + @JsonProperty("method") + private String method; + + /** + * 数据库ID + */ + @JsonProperty("databaseId") + private int databaseId; + + /** + * 当前余额 + */ + @JsonProperty("currentBalance") + private BigDecimal currentBalance; + + /** + * 最后修改时间 + */ + @JsonProperty("lastModified") + private ZonedDateTime lastModified; + + /** + * 交易代码 + */ + @JsonProperty("txCode") + private String txCode; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388BetRecordResponse.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388BetRecordResponse.java new file mode 100644 index 0000000..0303e08 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388BetRecordResponse.java @@ -0,0 +1,186 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * sv388记录响应 + * + * @author shi + * @date 2025/04/01 + */ +@Data +public class SV388BetRecordResponse { + + /** + * 状态代码 + */ + @JsonProperty("status") + private Integer status; + + /** + * 描述信息 (String类型) + * "Success" - 请求成功 + * 其他 - 错误信息 + */ + private String desc; + /** + * 交易记录 + */ + @JsonProperty("transactions") + private List transactions; + + @Data + public static class Transaction { + + /** + * 游戏类型 + */ + @JsonProperty("gameType") + private String gameType; + + /** + * 返还金额(包含下注金额) + */ + @JsonProperty("winAmount") + private BigDecimal winAmount; + + /** + * 交易时间 + */ + @JsonProperty("txTime") + private Date txTime; + + /** + * 结算状态 + */ + @JsonProperty("settleStatus") + private Integer settleStatus; + + /** + * 游戏信息 + */ + @JsonProperty("gameInfo") + private String gameInfo; + + /** + * 真实返还金额 + */ + @JsonProperty("realWinAmount") + private BigDecimal realWinAmount; + + /** + * 更新时间 + */ + @JsonProperty("updateTime") + private String updateTime; + + /** + * 真实下注金额 + */ + @JsonProperty("realBetAmount") + private BigDecimal realBetAmount; + + /** + * 用户ID + */ + @JsonProperty("userId") + private String userId; + + /** + * 下注类型 + */ + @JsonProperty("betType") + private String betType; + + /** + * 平台名称 + */ + @JsonProperty("platform") + private String platform; + + /** + * 交易状态 + */ + @JsonProperty("txStatus") + private Integer txStatus; + + /** + * 下注金额 + */ + @JsonProperty("betAmount") + private BigDecimal betAmount; + + /** + * 游戏名称 + */ + @JsonProperty("gameName") + private String gameName; + + /** + * 平台注单号 + */ + @JsonProperty("platformTxId") + private String platformTxId; + + /** + * 下注时间 + */ + @JsonProperty("betTime") + private Date betTime; + + /** + * 平台游戏代码 + */ + @JsonProperty("gameCode") + private String gameCode; + + /** + * 玩家货币代码 + */ + @JsonProperty("currency") + private String currency; + + /** + * 累积奖金的获胜金额 + */ + @JsonProperty("jackpotWinAmount") + private BigDecimal jackpotWinAmount; + + /** + * 累积奖金贡献金额 + */ + @JsonProperty("jackpotBetAmount") + private BigDecimal jackpotBetAmount; + + /** + * 平台有效投注 + */ + @JsonProperty("turnover") + private BigDecimal turnover; + + /** + * 游戏商的回合识别码 + */ + @JsonProperty("roundId") + private String roundId; + + + + /** + * 子注单号 + */ + @JsonProperty("refPlatformTxId") + private String refPlatformTxId; + + /** + * 是否为特殊收费游戏 + */ + @JsonProperty("isPremium") + private Boolean isPremium; + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388ExchangeTransferStatusResponse.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388ExchangeTransferStatusResponse.java new file mode 100644 index 0000000..a773ad1 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388ExchangeTransferStatusResponse.java @@ -0,0 +1,56 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; + + +/** + * aeexchange传输状态响应 + * + * @author shi + * @date 2025/04/01 + */ +@Data +public class SV388ExchangeTransferStatusResponse { + + /** + * 响应状态 + */ + @JsonProperty("status") + private String status; + + /** + * 情境 1:响应 status = 0000 & txStatus = 1 表示存款/提款成功 + * 情境 2:响应 status = 0000 & txStatus = 0 表示存款/提款失败 + * 情境 3:响应 status = 0000 & txStatus = 2 表示存款/提款处理中,可 3 秒后调用 checkTransferOperation API 确认回覆直到 txStatus = 0 或 1 的回覆 + * 情境 4:响应 status = 1017 表示交易不存在 + */ + @JsonProperty("txStatus") + private String txStatus; + + /** + * 当前余额 + */ + @JsonProperty("balance") + private BigDecimal balance; + + /** + * 转账金额 + */ + @JsonProperty("transferAmount") + private BigDecimal transferAmount; + + /** + * 转账类型(如:DEPOSIT 或 WITHDRAW) + */ + @JsonProperty("transferType") + private String transferType; + + /** + * 交易代码 + */ + @JsonProperty("txCode") + private String txCode; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LoginResponse.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LoginResponse.java new file mode 100644 index 0000000..033abbf --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LoginResponse.java @@ -0,0 +1,41 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * aelogin回应 + * + * @author shi + * @date 2025/04/01 + */ +@Data +public class SV388LoginResponse { + /** + * 响应状态 + */ + @JsonProperty("status") + private Integer status; + + + /** + * 描述信息 (String类型) + * "Success" - 请求成功 + * 其他 - 错误信息 + */ + private String desc; + + /** + * 登录 URL + */ + @JsonProperty("url") + private String url; + + /** + * 扩展字段,类型为列表,可以存储多项数据 + */ + @JsonProperty("extension") + private List extension; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LogoutUserResponse.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LogoutUserResponse.java new file mode 100644 index 0000000..b345034 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LogoutUserResponse.java @@ -0,0 +1,33 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * 注销用户响应 + * + * @author shi + * @date 2025/04/01 + */ +@Data +public class SV388LogoutUserResponse { + /** + * 状态代码 + */ + @JsonProperty("status") + private String status; + + /** + * 已注销的用户列表 + */ + @JsonProperty("logoutUsers") + private List logoutUsers; + + /** + * 用户数量 + */ + @JsonProperty("count") + private Integer count; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388MemberInfo.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388MemberInfo.java new file mode 100644 index 0000000..f50c61f --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388MemberInfo.java @@ -0,0 +1,64 @@ +package com.ff.game.api.sv388.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * AEMEMBER信息 + * + * @author shi + * @date 2025/03/31 + */ +@Data +public class SV388MemberInfo { + /** + * 响应结果列表,包含用户信息 + */ + @JsonProperty("results") + private List result; + + /** + * 记录数量 + */ + @JsonProperty("count") + private int count; + + /** + * 查询时间 + */ + @JsonProperty("querytime") + private Date queryTime; + + /** + * 响应状态 + */ + @JsonProperty("status") + private Integer status; + + @Data + public static class UserInfo { + + /** + * 用户ID + */ + @JsonProperty("userId") + private String userId; + + /** + * 用户余额 + */ + @JsonProperty("balance") + private BigDecimal balance; + + /** + * 最后修改时间 + */ + @JsonProperty("lastModified") + private Date lastModified; + } + +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388Response.java b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388Response.java new file mode 100644 index 0000000..85bc006 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388Response.java @@ -0,0 +1,26 @@ +package com.ff.game.api.sv388.dto; + +import lombok.Data; + +/** + * 航空响应 + * + * @author shi + * @date 2025/03/28 + */ +@Data +public class SV388Response { + /** + * 状态码 (String类型) + * 0000 - 成功 + * 其他 - 错误状态码 + */ + private String status; + + /** + * 描述信息 (String类型) + * "Success" - 请求成功 + * 其他 - 错误信息 + */ + private String desc; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java new file mode 100644 index 0000000..b2ca059 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java @@ -0,0 +1,642 @@ +package com.ff.game.api.sv388.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +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.JsonUtil; +import com.ff.base.utils.StringUtils; +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.request.*; +import com.ff.game.api.sv388.client.SV388Client; +import com.ff.game.api.sv388.dto.*; +import com.ff.game.api.xk.dto.XKKickMemberDTO; +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 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.*; +import java.util.stream.Collectors; + + +/** + * AE 游戏 impl + * + * @author shi + * @date 2024/11/12 + */ +@Service("SV388Service") +@Slf4j +public class SV388GamesServiceImpl implements IGamesService { + + + @Resource + private RedisCache redisCache; + + @Resource + private IGameExchangeMoneyService gameExchangeMoneyService; + + @Resource + private IGameService gameService; + + @Resource + private IMemberService memberService; + + @Resource + private SV388Client sv388Client; + + @Resource + private IGameBettingDetailsService gameBettingDetailsService; + + @Resource + private DelayService delayService; + + /** + * 游戏id + */ + private static final Long GAME_ID = 1122L; + + + /** + * 获得就是成功 + * + * @param errorCode 错误代码 + * @return {@link Boolean } + */ + private Boolean getIsSuccess(Integer errorCode) { + return 0 == errorCode; + } + + + /** + * 获取密钥 + * + * @param gamesBaseRequestDTO 游戏请求dto + * @return {@link Map }<{@link String }, {@link Object }> + */ + private Map getKey(GamesBaseRequestDTO gamesBaseRequestDTO) { + Map params = new LinkedHashMap<>(); + params.put("cert", gamesBaseRequestDTO.getAgentKey()); + params.put("agentId", gamesBaseRequestDTO.getAgentId()); + return params; + } + + /** + * 创建成员 + * + * @param createMemberRequestDTO 创建成员请求dto + * @return {@link Boolean } + */ + @Override + public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) { + + Platform platform = createMemberRequestDTO.getVendor(); + Object o = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency()); + if (!(o instanceof Map)) { + throw new ApiException(ErrorCode.Miss_Config.getCode()); + } + Map betLimit = new HashMap<>(); + betLimit.put(GamePlatforms.SV388.getCode(), new HashMap() {{ + put("LIVE", new HashMap((Map) o) {{ + remove("@type"); + }}); + }} + ); + Map params = this.getKey(createMemberRequestDTO); + params.put("userId", createMemberRequestDTO.getAccount()); + params.put("currency", createMemberRequestDTO.getCurrency()); + params.put("betLimit", JsonUtil.objToString(betLimit)); + SV388Response aeResponse = sv388Client.createMember(params); + String errorCode = aeResponse.getStatus(); + if (this.getIsSuccess(Integer.valueOf(errorCode))) { + return Boolean.TRUE; + } + //判断是否获取成功 + return Boolean.FALSE; + } + + + /** + * 获取会员信息 + * + * @param memberInfoRequestDTO 会员信息请求dto + * @return {@link MemberInfoResponseDTO } + */ + @Override + public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO) { + Map params = this.getKey(memberInfoRequestDTO); + params.put("alluser", 0); + params.put("userIds", memberInfoRequestDTO.getAccounts()); + SV388MemberInfo memberInfo = sv388Client.getMemberInfo(params); + //判断是否获取成功 + if (this.getIsSuccess(memberInfo.getStatus())) { + SV388MemberInfo.UserInfo userInfo = memberInfo.getResult().get(0); + return MemberInfoResponseDTO.builder() + .status(GameMemberStatus.UNKNOWN.getCode()) + .balance(userInfo.getBalance()) + .account(memberInfoRequestDTO.getAccounts()) + .build(); + } else { + throw new ApiException(ErrorCode.ERROR.getCode()); + } + } + + /** + * 无重定向登录 + * + * @param gamesLogin 游戏登录 + * @return {@link String } + */ + @Override + public String loginWithoutRedirect(GamesLogin gamesLogin) { + Map params = this.getKey(gamesLogin); + params.put("userId", gamesLogin.getAccount()); + params.put("externalURL", gamesLogin.getHomeUrl()); + params.put("language", gamesLogin.getLang()); + params.put("betLimit", gamesLogin.getBetLimit()); + SV388LoginResponse aeLoginResponse = sv388Client.loginWithoutRedirect(params); + //判断是否获取成功 + if (this.getIsSuccess(aeLoginResponse.getStatus())) { + return aeLoginResponse.getUrl(); + } else { + throw new BaseException(aeLoginResponse.getDesc()); + } + } + + + /** + * 获取游戏列表 + * + * @param gamesBaseRequestDTO 游戏请求dto + * @return {@link String } + */ + @Transactional + @Override + public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) { + int platformType = PlatformType.VIDEO.getCode(); + Game condition = new Game(); + condition.setPlatformCode(GamePlatforms.SV388.getCode()); + condition.setPlatformType(platformType); + List gameList = gameService.selectGameList(condition); + Platform platform = gamesBaseRequestDTO.getVendor(); + //不存在这个游戏 + if (ObjectUtils.isEmpty(gameList)) { + Game game = new Game(); + game.setId(IdUtil.getSnowflakeNextId()); + game.setSortNo(gameService.selectMaxSortNo(platformType, GamePlatforms.SV388.getCode()) + 1); + game.setPlatformCode(platform.getPlatformCode()); + game.setPlatformType(platformType); + game.setGameCode("1"); + game.setGameSourceType(String.valueOf(1)); + game.setGameName("SV388真人"); + game.setCreateBy(Constants.SYSTEM); + NameInfo nameInfo = new NameInfo(); + nameInfo.setLang("zh-CN"); + nameInfo.setName("SV388真人"); + game.setNameInfo(Collections.singletonList(nameInfo)); + game.setGameId(StringUtils.addSuffix(GamePlatforms.SV388.getCode(), 1)); + gameService.insertGame(game); + } + return CacheConstants.SV388_GAMES; + } + + /** + * 按代理id进行交换转账 + * + * @param exchangeTransferMoneyRequestDTO 外汇转账moeny dto + * @return {@link Long } + */ + @Override + @Transactional + public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) { + + Member member = memberService.selectMemberByGameAccount(exchangeTransferMoneyRequestDTO.getAccount()); + String transactionId = GamePlatforms.SV388.getCode() + IdUtils.simpleUUID(); + List gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList( + GameExchangeMoney.builder() + .tenantKey(exchangeTransferMoneyRequestDTO.getTenantKey()) + .orderId(exchangeTransferMoneyRequestDTO.getOrderId()) + .build() + ); + Assert.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), "订单号重复"); + + //获取下一个自增id + GameExchangeMoney exchangeMoney = GameExchangeMoney + .builder() + .tenantKey(exchangeTransferMoneyRequestDTO.getTenantKey()) + .orderId(exchangeTransferMoneyRequestDTO.getOrderId()) + .quota(exchangeTransferMoneyRequestDTO.getQuota()) + .balance(exchangeTransferMoneyRequestDTO.getAmount()) + .exchangeType(exchangeTransferMoneyRequestDTO.getTransferType()) + .currencyCode(exchangeTransferMoneyRequestDTO.getSystemCurrency()) + .memberId(member.getId()) + .transactionId(transactionId) + .platformCode(GamePlatforms.SV388.getCode()) + .build(); + exchangeMoney.setCreateBy(Constants.SYSTEM); + exchangeMoney.setStatus(StatusType.IN_PROGRESS.getValue()); + exchangeMoney.setStep(GameExchangeStep.CREATE_ORDER.getCode()); + exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode()); + gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney); + + + Map params = this.getKey(exchangeTransferMoneyRequestDTO); + SV388AETransactionResponse deposit = null; + try { + if (TransferType.GAMES.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) { + + params.put("userId", exchangeTransferMoneyRequestDTO.getAccount()); + params.put("txCode", transactionId); + params.put("transferAmount", exchangeTransferMoneyRequestDTO.getAmount()); + deposit = sv388Client.deposit(params); + + } else { + params.put("userId", exchangeTransferMoneyRequestDTO.getAccount()); + params.put("txCode", transactionId); + params.put("withdrawType", 1); + deposit = sv388Client.withdraw(params); + } + } finally { + BigDecimal coinBefore; + if (TransferType.GAMES.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) { + coinBefore = NumberUtil.sub(deposit.getCurrentBalance(), deposit.getAmount()); + } else { + coinBefore = NumberUtil.add(deposit.getCurrentBalance(), deposit.getAmount()); + } + //判断是否转移成功 + if ("0000".equals(deposit.getStatus())) { + exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode()); + exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode()); + } else { + exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode()); + exchangeMoney.setStepStatus(GameExchangeStepStatus.IN_PROGRESS.getCode()); + } + //更新数据 + exchangeMoney.setBalance(deposit.getAmount()); + exchangeMoney.setCoinBefore(coinBefore); + exchangeMoney.setCoinAfter(deposit.getCurrentBalance()); + exchangeMoney.setCurrencyBefore(exchangeMoney.getCoinBefore()); + exchangeMoney.setCurrencyAfter(exchangeMoney.getCoinAfter()); + gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney); + } + + return exchangeMoney.getId(); + } + + /** + * 汇兑转移状态 + * + * @param exchangeTransferMoneyRequestDTO 兑换转账请求dto + * @return {@link ExchangeTransferStatusResponseDTO } + */ + @Override + public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) { + + Map paramsMap = this.getKey(exchangeTransferMoneyRequestDTO); + paramsMap.put("txCode", exchangeTransferMoneyRequestDTO.getOrderId()); + + SV388ExchangeTransferStatusResponse exchangeTransferStatusResponse = sv388Client.exchangeTransferStatus(paramsMap); + Integer status = StatusType.IN_PROGRESS.getValue(); + if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "1".equals(exchangeTransferStatusResponse.getTxStatus())) { + status = StatusType.SUCCESS.getValue(); + } else if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "0".equals(exchangeTransferStatusResponse.getTxStatus())) { + status = StatusType.FAILURE.getValue(); + } else if ("1017".equals(exchangeTransferStatusResponse.getStatus())) { + status = StatusType.FAILURE.getValue(); + } + GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeMoneyId()); + //更新 + BigDecimal coinBefore; + if (TransferType.GAMES.getCode().equals(exchangeMoney.getExchangeType())) { + coinBefore = NumberUtil.sub(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount()); + } else { + coinBefore = NumberUtil.add(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount()); + } + + + return ExchangeTransferStatusResponseDTO.builder() + .statusType(status) + .balance(exchangeTransferStatusResponse.getTransferAmount()) + .coinBefore(coinBefore) + .coinAfter(exchangeTransferStatusResponse.getBalance()) + .build(); + } + + + class GetRealtimeRecordTask extends DelayTask { + BetRecordByTimeDTO betRecordByTimeDTO; + + public GetRealtimeRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + getRealtimeRecord(betRecordByTimeDTO); + } + } + + void getRealtimeRecord(BetRecordByTimeDTO betRecordByTimeDTO) { + //请求参数 + Map params = this.getKey(betRecordByTimeDTO); + + String timeFrom = redisCache.getCacheObject(CacheConstants.SV388_TIME_FROM); + if (StringUtils.isEmpty(timeFrom)) { + 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, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl()); + + //判断是否获取成功 + if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { + //数据组装 + this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); + if (aeBetRecordResponse.getTransactions().size() >= 20000) { + delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO)); + } + } else { + redisCache.deleteObject(CacheConstants.SV388_TIME_FROM); + log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); + } + } + + void getHistoryRecord(BetRecordByTimeDTO betRecordByTimeDTO) { + Map params = this.getKey(betRecordByTimeDTO); + String startTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00"; + String endTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00"; + + params.put("startTime", startTime); + params.put("endTime", endTime); + params.put("platform", /*"SEXYBCRT"*/ GamePlatforms.SV388.getCode()); + + SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl()); + + //判断是否获取成功 + if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { + //数据组装 + this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); + if (aeBetRecordResponse.getTransactions().size() >= 20000) { + delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO)); + } + } else { + log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); + } + } + + class GetHistoryRecordTask extends DelayTask { + BetRecordByTimeDTO betRecordByTimeDTO; + + public GetHistoryRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + getHistoryRecord(betRecordByTimeDTO); + } + } + + /** + * 按时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link List }<{@link GameBettingDetails }> + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO)); + return Boolean.TRUE; + } + + /** + * 按历史时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO)); + return Boolean.TRUE; + } + + /** + * 赠送免费局数 + * + * @param createFreeSpinRequest 创建自由旋转请求 + * @return {@link Boolean } + */ + @Override + public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 获取游戏详细信息 + * + * @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) { + Map params = this.getKey(kickMemberRequestDTO); + params.put("userIds", kickMemberRequestDTO.getAccount()); + XKKickMemberDTO xkKickMemberDTO = sv388Client.kickMember(params); + //判断是否获取成功 + if (this.getIsSuccess(xkKickMemberDTO.getCode())) { + return Boolean.TRUE; + } else { + throw new BaseException(xkKickMemberDTO.getMsg()); + } + } + + /** + * 踢成员全部 + * + * @param kickMemberAllDTO 踢成员全部dto + * @return {@link Boolean } + */ + @Override + public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 免费游戏玩家使用的纪录 + * + * @param getFreeSpinDashflowRequestDTO 获取自由旋转dashflow请求dto + * @return {@link List }<{@link GameFreeRecord }> + */ + @Override + public List 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 aeBetRecordResponse ae下注记录响应dto + */ + private void batchInsert(SV388BetRecordResponse aeBetRecordResponse, BetRecordByTimeDTO betRecordByTimeDTO) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List dataBean = aeBetRecordResponse.getTransactions(); + + if (CollectionUtils.isEmpty(dataBean)) { + return; + } + + String timeFrom = null; + //数据转化 + for (SV388BetRecordResponse.Transaction bean : dataBean) { + GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder() + .platform(betRecordByTimeDTO.getVendor()) + .data(bean).build()); + if (!ObjectUtils.isEmpty(bettingDetails)) { + bettingDetails.setId(IdUtil.getSnowflakeNextId()); + gameBettingDetails.add(bettingDetails); + } + wagersIds.add(bean.getPlatform() + bean.getPlatformTxId()); + timeFrom = bean.getUpdateTime(); + } + if (!CollectionUtils.isEmpty(gameBettingDetails)) { + //查询重复数据id + List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.SV388.getCode()); + //用steam流清除list中与wagersIds集合相同的数据 + gameBettingDetails = gameBettingDetails.stream() + .filter(detail -> !removeWagersIds.contains(detail.getWagersId())) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(gameBettingDetails)) { + gameBettingDetailsService.batchInsert(gameBettingDetails); + } + } + if (StringUtils.isEmpty(timeFrom)) { + timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00"; + } + redisCache.setCacheObject(CacheConstants.SV388_TIME_FROM, timeFrom); + + } + + /** + * 数据构建 + * + * @param gamesDataBuildDTO 数据 + * @return {@link GameBettingDetails } + */ + @Override + public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) { + //转化类 + SV388BetRecordResponse.Transaction resultBean = (SV388BetRecordResponse.Transaction) gamesDataBuildDTO.getData(); + + + Member member = memberService.selectMemberByGameAccount(resultBean.getUserId()); + if (ObjectUtils.isEmpty(member)) { + return null; + } + + + // 判断输赢 + Integer gameStatus = GameStatus.FLAT.getCode(); + BigDecimal payoffAmount = BigDecimal.ZERO; + if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) > 0) { + gameStatus = GameStatus.WIN.getCode(); + payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount()); + } else if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) < 0) { + gameStatus = GameStatus.FAIL.getCode(); + payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount()).abs(); + } + + //结算状态 + int settlementStatus = SettlementStatusEnum.REVOKED.getCode(); + if (resultBean.getTxStatus() == 1) { + settlementStatus = SettlementStatusEnum.COMPLETED.getCode(); + } + + //数据构造 + GameBettingDetails gameBettingDetails = GameBettingDetails.builder() + .tenantKey(member.getTenantKey()) + //保存我们的币种id + .currencyCode(gamesDataBuildDTO.getPlatform().getOurCurrency(resultBean.getCurrency())) + .memberId(member.getId()) + .gameCode(resultBean.getGameCode()) + .gameType(PlatformType.VIDEO.getCode()) + .platformCode(GamePlatforms.SV388.getCode()) + .gameId(/*GAME_ID*/GamePlatforms.SV388.getCode() + "_" + 1) + .gameName(resultBean.getGameName()) + .gameStatus(gameStatus) + .gameStatusType(resultBean.getSettleStatus()) + .gameCurrencyCode(resultBean.getCurrency()) + .account(resultBean.getUserId()) + .wagersId(resultBean.getPlatform() + resultBean.getPlatformTxId()) + .wagersTime(resultBean.getBetTime().getTime()) + .betAmount(resultBean.getRealBetAmount()) + .payoffTime(resultBean.getTxTime().getTime()) + .payoffAmount(payoffAmount) + .betContent(resultBean.getGameInfo()) + .settlementTime(resultBean.getTxTime().getTime()) + .turnover(resultBean.getTurnover()) + .orderNo(String.valueOf(resultBean.getRoundId())) + .settlementStatus(settlementStatus) + .build(); + gameBettingDetails.setCreateBy(Constants.SYSTEM); + gameBettingDetails.setCreateTime(DateUtils.getNowDate()); + return gameBettingDetails; + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/xk/dto/XKGamesDTO.java b/ff-game/src/main/java/com/ff/game/api/xk/dto/XKGamesDTO.java index da08376..d430ae8 100644 --- a/ff-game/src/main/java/com/ff/game/api/xk/dto/XKGamesDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/xk/dto/XKGamesDTO.java @@ -43,7 +43,7 @@ public class XKGamesDTO { /** *自己系统游戏id */ - private Long systemGameId; + private String systemGameId; /** * jp */ diff --git a/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java index ce73a29..ba9de37 100644 --- a/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java @@ -252,7 +252,7 @@ public class GamesXKServiceImpl implements IGamesService { } else { game = games.get(0); } - gamesDataDTO.setSystemGameId(game.getId()); + gamesDataDTO.setSystemGameId(game.getGameId()); } redisCache.deleteObject(CacheConstants.XK_GAMES); diff --git a/ff-game/src/main/java/com/ff/game/domain/ExtInfo.java b/ff-game/src/main/java/com/ff/game/domain/ExtInfo.java index 2736dba..0818b06 100644 --- a/ff-game/src/main/java/com/ff/game/domain/ExtInfo.java +++ b/ff-game/src/main/java/com/ff/game/domain/ExtInfo.java @@ -19,6 +19,8 @@ public class ExtInfo implements Serializable { */ private Map timeout; + private Map betLimit; + public String getOurCurrency(String currencyId) { return currency == null ? null : currency.get(currencyId); } diff --git a/ff-game/src/main/java/com/ff/game/domain/GameBettingDetails.java b/ff-game/src/main/java/com/ff/game/domain/GameBettingDetails.java index 76a5785..3853137 100644 --- a/ff-game/src/main/java/com/ff/game/domain/GameBettingDetails.java +++ b/ff-game/src/main/java/com/ff/game/domain/GameBettingDetails.java @@ -53,7 +53,7 @@ public class GameBettingDetails extends BaseEntity /** 游戏id */ @Excel(name = "游戏id") @JsonSerialize(using = ToStringSerializer.class) - private Long gameId; + private String gameId; /** 游戏类型 ff_game_type 字典 */ @Excel(name = "游戏类型 ff_game_type 字典") diff --git a/ff-game/src/main/java/com/ff/quartz/config/ScheduleConfig.java b/ff-game/src/main/java/com/ff/quartz/config/ScheduleConfig.java index 475653c..c5b6fe3 100644 --- a/ff-game/src/main/java/com/ff/quartz/config/ScheduleConfig.java +++ b/ff-game/src/main/java/com/ff/quartz/config/ScheduleConfig.java @@ -1,57 +1,57 @@ -package com.ff.quartz.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.quartz.SchedulerFactoryBean; -import javax.sql.DataSource; -import java.util.Properties; - -/** - * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) - * - * @author ff - */ -@Configuration -public class ScheduleConfig -{ - @Bean - public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) - { - SchedulerFactoryBean factory = new SchedulerFactoryBean(); - factory.setDataSource(dataSource); - - // quartz参数 - Properties prop = new Properties(); - prop.put("org.quartz.scheduler.instanceName", "ffScheduler"); - prop.put("org.quartz.scheduler.instanceId", "AUTO"); - // 线程池配置 - prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); - prop.put("org.quartz.threadPool.threadCount", "20"); - prop.put("org.quartz.threadPool.threadPriority", "5"); - // JobStore配置 - prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); - // 集群配置 - prop.put("org.quartz.jobStore.isClustered", "true"); - prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); - prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10"); - prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); - - // sqlserver 启用 - // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); - prop.put("org.quartz.jobStore.misfireThreshold", "12000"); - prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); - factory.setQuartzProperties(prop); - - factory.setSchedulerName("ffScheduler"); - // 延时启动 - factory.setStartupDelay(1); - factory.setApplicationContextSchedulerContextKey("applicationContextKey"); - // 可选,QuartzScheduler - // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 - factory.setOverwriteExistingJobs(true); - // 设置自动启动,默认为true - factory.setAutoStartup(true); - - return factory; - } -} +//package com.ff.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ff +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "ffScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("ffScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/ff-game/src/main/java/com/ff/sports/api/fb/address/FbAddress.java b/ff-game/src/main/java/com/ff/sports/api/fb/address/FbAddress.java deleted file mode 100644 index 62ba9ec..0000000 --- a/ff-game/src/main/java/com/ff/sports/api/fb/address/FbAddress.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.ff.sports.api.fb.address; - -import com.dtflys.forest.callback.AddressSource; -import com.dtflys.forest.http.ForestAddress; -import com.dtflys.forest.http.ForestRequest; -import com.ff.base.system.service.ISysConfigService; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - - -/** - * @author shi - * @date 2025/02/10 - */ -@Component -public class FbAddress implements AddressSource { - - public static final String API_BASE_URL = "fb.api.base.url"; - @Resource - private ISysConfigService configService; - - - @Override - public ForestAddress getAddress(ForestRequest request) { - String apiBaseUrl = configService.selectConfigByKey(API_BASE_URL); - return new ForestAddress("https", apiBaseUrl, 443, "services"); - } -} \ No newline at end of file diff --git a/ff-game/src/main/java/com/ff/sports/db/address/DBSportsAddress.java b/ff-game/src/main/java/com/ff/sports/db/address/DBSportsAddress.java new file mode 100644 index 0000000..c2e1843 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/address/DBSportsAddress.java @@ -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; + + +/** + * DB体育对接文档 + * 账号: 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, ""); + } +} \ No newline at end of file diff --git a/ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java b/ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java new file mode 100644 index 0000000..8d58440 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java @@ -0,0 +1,102 @@ +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/user/create", + headers = { + "Content-type: application/x-www-form-urlencoded" + } + ) + CreateUserResponse createMember(@Body CreateUserRequest request, + @Header("requestId") @Var("requestId") String requestId, + @Header("lang") @Var("lang") String lang); + + /** + * 用户金额转入到FB体育平台,支持两位小数,最小0.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); + + /** + * FB体育用拉取订单文件的方式同步订单数据,FB体育每5分钟生成一次订单文件, + * 通过此接口获取某一段时间内的文件ID列表,再通过文件ID获取具体订单数据 + */ + @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); + + + /** + * 获取用户app端鉴权token和相关服务url,获取到的token用于app调用接口鉴权 + * + * @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); +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserRequest.java new file mode 100644 index 0000000..e985d26 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserRequest.java @@ -0,0 +1,28 @@ +package com.ff.sports.db.dto; + +import com.ff.base.utils.sign.Md5Utils; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author cengy + */ +@Data +public class CreateUserRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + private String userName; // 用户名(可以包含但是不要等同于特殊字符或者空格,长度控制在30个字符以下) + private String merchantCode; // 商户code + private String timestamp = System.currentTimeMillis() + ""; // 13位时间戳 + private String currency; // 币种 + private String nickname; // N 昵称 + private String agentId; // N 信用网(代理id) + private String sign; // 签名 signature =MD5(MD5(userName +”&”+ merchantCode +”&”+ timestamp) + ”&”+ key) + + public void calcSign(String key) { + String signature = Md5Utils.md5New(Md5Utils.md5New(userName + "&" + merchantCode + "&" + timestamp) + "&" + key); + this.sign = signature; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserResponse.java new file mode 100644 index 0000000..6098450 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserResponse.java @@ -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; +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/Enums.java b/ff-game/src/main/java/com/ff/sports/db/dto/Enums.java new file mode 100644 index 0000000..5cbd46b --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/Enums.java @@ -0,0 +1,9 @@ +package com.ff.sports.db.dto; + +/** + * @author cengy + */ +public class Enums { + + +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoRequest.java new file mode 100644 index 0000000..015bc89 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoRequest.java @@ -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 map = new LinkedHashMap<>(); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoResponse.java new file mode 100644 index 0000000..60cb4c7 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoResponse.java @@ -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 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 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenRequest.java new file mode 100644 index 0000000..83bfe60 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenRequest.java @@ -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; + // 平台类型,pc,h5, mobile , see enum: plat_form_enum + private String platForm; + // 客户端用户ip地址,尽可能提供,我们用于风控 + private String ip; // 可选 + + public String toJSON() { + Map map = new LinkedHashMap<>(); + map.put("merchantUserId", merchantUserId); + map.put("platForm", platForm); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenResponse.java new file mode 100644 index 0000000..3dfb0df --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetTokenResponse.java @@ -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 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 domains; // 域名集合 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlRequest.java new file mode 100644 index 0000000..4ccc4d6 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlRequest.java @@ -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 "{}"; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlResponse.java new file mode 100644 index 0000000..d622169 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/GetUrlResponse.java @@ -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 data; + private Integer code; + + @Data + public static class UrlDTO implements Serializable { + private static final long serialVersionUID = 1L; + private int type; //域名类型,1:API,2:PUSH,3:H5,4:PC,5:IMAGE , see enum: domain_type_enum + private List domainList; // 域名集合 + } + + @Data + public static class DomainDTO implements Serializable { + private static final long serialVersionUID = 1L; + private String domain; // 域名 + private int weight; // 权限值 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesRequest.java new file mode 100644 index 0000000..315b698 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesRequest.java @@ -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 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; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesResponse.java new file mode 100644 index 0000000..2367773 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesResponse.java @@ -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 data; + + @Data + public static class FileId implements Serializable { + private static final long serialVersionUID = 1L; + private Long fileId; + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoRequest.java new file mode 100644 index 0000000..9d51d78 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoRequest.java @@ -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 map = new LinkedHashMap<>(); + map.put("fileId", fileId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoResponse.java new file mode 100644 index 0000000..b6910f6 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoResponse.java @@ -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 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 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; // 数据变更标记 + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailRequest.java new file mode 100644 index 0000000..8bb2c9e --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailRequest.java @@ -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 map = new LinkedHashMap<>(); + map.put("businessId", businessId); + map.put("merchantUserId", merchantUserId); + map.put("transferType", transferType); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailResponse.java new file mode 100644 index 0000000..8f56651 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailResponse.java @@ -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 + + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferInRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferInRequest.java new file mode 100644 index 0000000..31f13a4 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferInRequest.java @@ -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 map = new LinkedHashMap<>(); + map.put("amount", amount); + map.put("businessId", businessId); + map.put("currencyId", currencyId); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferInResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferInResponse.java new file mode 100644 index 0000000..3de12e9 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferInResponse.java @@ -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; +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutRequest.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutRequest.java new file mode 100644 index 0000000..bd26537 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutRequest.java @@ -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 map = new LinkedHashMap<>(); + map.put("amount", amount); + map.put("businessId", businessId); + map.put("currencyId", currencyId); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutResponse.java b/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutResponse.java new file mode 100644 index 0000000..da1590c --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/TransferOutResponse.java @@ -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; +} diff --git a/ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java b/ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java new file mode 100644 index 0000000..6aa4fef --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java @@ -0,0 +1,748 @@ +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 requestDTO 创建成员请求dto + * @return {@link Boolean } + */ + @Override + public Boolean createMember(CreateMemberRequestDTO requestDTO) { + + CreateUserRequest request = new CreateUserRequest(); + request.setUserName(requestDTO.getAccount()); + request.setMerchantCode(requestDTO.getAgentId()); + request.setCurrency(requestDTO.getCurrency()); + request.calcSign(requestDTO.getAgentKey()); + + String lang = "zh"; + String requestId = IdUtils.fastUUID(); + CreateUserResponse response = dbSportsClient.createMember(request, requestId, lang); + if (isSuccess(response.getCode())) { + 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 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"); // pc,h5, 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 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 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(GamePlatforms.DBSports.getInfo()); + 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 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 获取自由旋转dashflow请求dto + * @return {@link List }<{@link GameFreeRecord }> + */ + @Override + public List 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 settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List 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 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 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("1") + .gameType(PlatformType.SPORTS.getCode()) // 体育 + .platformCode(GamePlatforms.DBSports.getCode()) + .gameId(GamePlatforms.DBSports.getCode() + "_1") + .gameName(GamePlatforms.DBSports.getInfo()) + .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; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/A.java b/ff-game/src/main/java/com/ff/sports/fb/A.java deleted file mode 100644 index badc18a..0000000 --- a/ff-game/src/main/java/com/ff/sports/fb/A.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ff.sports.fb; - -/** - * @author cengy - */ -public class A { -} diff --git a/ff-game/src/main/java/com/ff/sports/fb/address/FBSportsAddress.java b/ff-game/src/main/java/com/ff/sports/fb/address/FBSportsAddress.java new file mode 100644 index 0000000..e0aaa84 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/address/FBSportsAddress.java @@ -0,0 +1,30 @@ +package com.ff.sports.fb.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; + + +/** + * 对接文档地址 + * + * @author cengy + */ +@Component +public class FBSportsAddress implements AddressSource { + + @Resource + private IPlatformService platformService; + + @Override + public ForestAddress getAddress(ForestRequest request) { + String apiBaseUrl = platformService.get(GamePlatforms.FBSports.getCode()) + .getUrlInfo().getUrl(); + return new ForestAddress("https", apiBaseUrl, 443, "fb/data"); + } +} \ No newline at end of file diff --git a/ff-game/src/main/java/com/ff/sports/fb/client/FBSportsClient.java b/ff-game/src/main/java/com/ff/sports/fb/client/FBSportsClient.java new file mode 100644 index 0000000..dc28d2d --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/client/FBSportsClient.java @@ -0,0 +1,102 @@ +package com.ff.sports.fb.client; + +import com.dtflys.forest.annotation.*; +import com.ff.sports.fb.address.FBSportsAddress; +import com.ff.sports.fb.dto.*; + +/** + * 数据接口文档
+ * 网页接入文档 + * + * @author cengy + */ +@Address(source = FBSportsAddress.class) +public interface FBSportsClient { + /** + * 创建投注用户 + * + * @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); + + /** + * 用户金额转入到FB体育平台,支持两位小数,最小0.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); + + /** + * FB体育用拉取订单文件的方式同步订单数据,FB体育每5分钟生成一次订单文件, + * 通过此接口获取某一段时间内的文件ID列表,再通过文件ID获取具体订单数据 + */ + @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); + + + /** + * 获取用户app端鉴权token和相关服务url,获取到的token用于app调用接口鉴权 + * + * @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); +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserRequest.java new file mode 100644 index 0000000..95c942d --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserRequest.java @@ -0,0 +1,30 @@ +package com.ff.sports.fb.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 currencyIds = null; // 币种id集合 , see enum: currency + private Integer oddsLevel = null; // 赔率级别,不传则为默认, see enum: user_odds_level_enum + + public String toJSON() { + Map map = new LinkedHashMap<>(); + map.put("currencyIds", currencyIds); + map.put("merchantUserId", merchantUserId); + map.put("oddsLevel", oddsLevel); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserResponse.java new file mode 100644 index 0000000..015abf1 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserResponse.java @@ -0,0 +1,19 @@ +package com.ff.sports.fb.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; +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/Enums.java b/ff-game/src/main/java/com/ff/sports/fb/dto/Enums.java new file mode 100644 index 0000000..cf2bd48 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/Enums.java @@ -0,0 +1,9 @@ +package com.ff.sports.fb.dto; + +/** + * @author cengy + */ +public class Enums { + + +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoRequest.java new file mode 100644 index 0000000..51e7129 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoRequest.java @@ -0,0 +1,27 @@ +package com.ff.sports.fb.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 map = new LinkedHashMap<>(); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoResponse.java new file mode 100644 index 0000000..0911248 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoResponse.java @@ -0,0 +1,38 @@ +package com.ff.sports.fb.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 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 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenRequest.java new file mode 100644 index 0000000..1f46602 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenRequest.java @@ -0,0 +1,30 @@ +package com.ff.sports.fb.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; + // 平台类型,pc,h5, mobile , see enum: plat_form_enum + private String platForm; + // 客户端用户ip地址,尽可能提供,我们用于风控 + private String ip; // 可选 + + public String toJSON() { + Map map = new LinkedHashMap<>(); + map.put("merchantUserId", merchantUserId); + map.put("platForm", platForm); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenResponse.java new file mode 100644 index 0000000..32a5029 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenResponse.java @@ -0,0 +1,49 @@ +package com.ff.sports.fb.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 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 domains; // 域名集合 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlRequest.java new file mode 100644 index 0000000..15d3213 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlRequest.java @@ -0,0 +1,17 @@ +package com.ff.sports.fb.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 "{}"; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlResponse.java new file mode 100644 index 0000000..e0ea5f8 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlResponse.java @@ -0,0 +1,33 @@ +package com.ff.sports.fb.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 data; + private Integer code; + + @Data + public static class UrlDTO implements Serializable { + private static final long serialVersionUID = 1L; + private int type; //域名类型,1:API,2:PUSH,3:H5,4:PC,5:IMAGE , see enum: domain_type_enum + private List domainList; // 域名集合 + } + + @Data + public static class DomainDTO implements Serializable { + private static final long serialVersionUID = 1L; + private String domain; // 域名 + private int weight; // 权限值 + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java new file mode 100644 index 0000000..5cbbeed --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java @@ -0,0 +1,41 @@ +package com.ff.sports.fb.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 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; + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesResponse.java new file mode 100644 index 0000000..8d9f1b5 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesResponse.java @@ -0,0 +1,27 @@ +package com.ff.sports.fb.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 data; + + @Data + public static class FileId implements Serializable { + private static final long serialVersionUID = 1L; + private Long fileId; + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoRequest.java new file mode 100644 index 0000000..251dc02 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoRequest.java @@ -0,0 +1,26 @@ +package com.ff.sports.fb.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 map = new LinkedHashMap<>(); + map.put("fileId", fileId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoResponse.java new file mode 100644 index 0000000..fb48ce7 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoResponse.java @@ -0,0 +1,115 @@ +package com.ff.sports.fb.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 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 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; // 数据变更标记 + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailRequest.java new file mode 100644 index 0000000..85f678d --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailRequest.java @@ -0,0 +1,39 @@ +package com.ff.sports.fb.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 map = new LinkedHashMap<>(); + map.put("businessId", businessId); + map.put("merchantUserId", merchantUserId); + map.put("transferType", transferType); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailResponse.java new file mode 100644 index 0000000..614edd0 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailResponse.java @@ -0,0 +1,39 @@ +package com.ff.sports.fb.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 + + } + +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInRequest.java new file mode 100644 index 0000000..52b4c62 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInRequest.java @@ -0,0 +1,46 @@ +package com.ff.sports.fb.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 map = new LinkedHashMap<>(); + map.put("amount", amount); + map.put("businessId", businessId); + map.put("currencyId", currencyId); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInResponse.java new file mode 100644 index 0000000..e5ab6ca --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferInResponse.java @@ -0,0 +1,20 @@ +package com.ff.sports.fb.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; +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutRequest.java new file mode 100644 index 0000000..37fc465 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutRequest.java @@ -0,0 +1,46 @@ +package com.ff.sports.fb.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 map = new LinkedHashMap<>(); + map.put("amount", amount); + map.put("businessId", businessId); + map.put("currencyId", currencyId); + map.put("merchantUserId", merchantUserId); + return JSON.toJSONString(map); + } +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutResponse.java b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutResponse.java new file mode 100644 index 0000000..9fb8897 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutResponse.java @@ -0,0 +1,20 @@ +package com.ff.sports.fb.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; +} diff --git a/ff-game/src/main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java b/ff-game/src/main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java new file mode 100644 index 0000000..3f5f6d9 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java @@ -0,0 +1,755 @@ +package com.ff.sports.fb.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.fb.client.FBSportsClient; +import com.ff.sports.fb.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("FBSportsService") +@Slf4j +public class FBSportsServiceImpl implements IGamesService { + + @Resource + private RedisCache redisCache; + + @Resource + private IGameExchangeMoneyService gameExchangeMoneyService; + + @Resource + private IGameService gameService; + + + @Resource + private IMemberService memberService; + + @Resource + private FBSportsClient fbSportsClient; + + @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 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 = fbSportsClient.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.FBSports.getCode() + IdUtils.simpleUUID(); + List 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.FBSports.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 = fbSportsClient.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 = fbSportsClient + .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 = fbSportsClient.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"); // pc,h5, 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 = fbSportsClient.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 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 game = gameService.selectGameById(GAME_ID); + //不存在这个游戏 + if (ObjectUtils.isEmpty(game)) { + game = new Game(); + game.setId(/*IdUtil.getSnowflakeNextId()*/GAME_ID); + game.setSortNo(1); + //game.setPlatformId(gamePlatform.getId()); + game.setPlatformCode(platform.getPlatformCode()); + game.setPlatformType(PlatformType.SPORTS.getCode()); + game.setGameCode("1"); + game.setGameSourceType(String.valueOf(1)); + game.setGameName("FB体育"); + game.setCreateBy(Constants.SYSTEM); + NameInfo nameInfo = new NameInfo(); + nameInfo.setLang("zh-CN"); + nameInfo.setName("FB体育"); + 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.FB_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 = fbSportsClient.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 = fbSportsClient.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 = fbSportsClient.getOrderJsonData( + request, + sign, + timestamp, + requestDTO.getAgentId() + ); + if (!this.isSuccess(orderInfoResponse.getCode())) { + return; + } + List 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 = fbSportsClient.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 获取自由旋转dashflow请求dto + * @return {@link List }<{@link GameFreeRecord }> + */ + @Override + public List 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 settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List 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 removeWagersIds = gameBettingDetailsService + .selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.FBSports.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 gameList = redisCache.getCacheList(CacheConstants.FB_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(8) // 体育 + .platformCode(GamePlatforms.FBSports.getCode()) + .gameId(game.getGameId()) + .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; + } +} diff --git a/ff-game/src/main/java/com/ff/utils/CalculateDateDaysAgo.java b/ff-game/src/main/java/com/ff/utils/CalculateDateDaysAgo.java new file mode 100644 index 0000000..dc5363b --- /dev/null +++ b/ff-game/src/main/java/com/ff/utils/CalculateDateDaysAgo.java @@ -0,0 +1,25 @@ +package com.ff.utils; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * @author cengy + */ +public class CalculateDateDaysAgo { + + public static LocalDate get(int daysToSubtract) { + return LocalDate.now().minusDays(daysToSubtract); + } + + public static String getStr(int daysToSubtract) { + // 获取当前日期减去指定天数 + LocalDate date = get(daysToSubtract); + + // 定义日期格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + // 返回格式化日期字符串 + return date.format(formatter); + } +} diff --git a/ff-game/src/main/java/com/ff/utils/SortByAttributeNameASC.java b/ff-game/src/main/java/com/ff/utils/SortByAttributeNameASC.java new file mode 100644 index 0000000..6e351b1 --- /dev/null +++ b/ff-game/src/main/java/com/ff/utils/SortByAttributeNameASC.java @@ -0,0 +1,40 @@ +package com.ff.utils; + +import com.alibaba.fastjson2.JSON; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author cengy + */ +public class SortByAttributeNameASC { + public static String get(Object o) { + // Create a map to store field names and values + Map fieldMap = new LinkedHashMap<>(); + + // Get all fields of the class + Field[] fields = o.getClass().getDeclaredFields(); + + // Sort field names + Arrays.sort(fields, (f1, f2) -> f1.getName().compareTo(f2.getName())); + + // Fill the map with sorted fields and their values + for (Field field : fields) { + field.setAccessible(true); // Make private fields accessible + Object value = null; + try { + value = field.get(o); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + fieldMap.put(field.getName(), value); + } + + + return JSON.toJSONString(fieldMap); + } + +} diff --git a/ff-game/src/main/java/com/ff/utils/TimestampFromString.java b/ff-game/src/main/java/com/ff/utils/TimestampFromString.java new file mode 100644 index 0000000..8a3ddd6 --- /dev/null +++ b/ff-game/src/main/java/com/ff/utils/TimestampFromString.java @@ -0,0 +1,27 @@ +package com.ff.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @author cengy + */ +public class TimestampFromString { + + public static final String PATTERN_DATE = "yyyy-MM-dd HH:mm:ss"; + + public static Long from(String date) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(PATTERN_DATE); + try { + Date parse = simpleDateFormat.parse(date); + return parse.getTime(); + } catch (ParseException e) { + return 0L; + } + } + + public static Long to(String timestamp) { + return Long.parseLong(timestamp); + } +}