From 72810d4d0e644fe1d6199b36fc575582e95336aa Mon Sep 17 00:00:00 2001 From: cengy Date: Tue, 8 Apr 2025 13:33:18 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat(fb):=20=E6=96=B0=E5=A2=9E=20FB=20?= =?UTF-8?q?=E4=BD=93=E8=82=B2=E5=B9=B3=E5=8F=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 FB 体育相关的数据结构和接口定义 - 实现 FB 体育平台的会员创建、资金转账、获取会员信息等功能 - 集成 FB 体育平台的 URL 获取和登录逻辑 - 为 FB 体育平台添加错误码定义 -优化游戏列表获取逻辑,支持 FB 体育游戏数据同步 --- .../com/ff/base/constant/CacheConstants.java | 4 + .../java/com/ff/base/enums/ErrorCode.java | 10 +- .../java/com/ff/base/enums/GamePlatforms.java | 5 +- .../ff/api/controller/ApiGameController.java | 3 - .../impl/TenantGameQuotaServiceImpl.java | 32 +- .../meitian/impl/MeiTianGameServiceImpl.java | 40 +- .../api/request/MemberInfoRequestDTO.java | 4 +- .../ff/sports/api/fb/address/FbAddress.java | 29 - ff-game/src/main/java/com/ff/sports/fb/A.java | 7 - .../ff/sports/fb/address/FBSportsAddress.java | 30 + .../ff/sports/fb/client/FBSportsClient.java | 77 +++ .../ff/sports/fb/dto/CreateUserRequest.java | 30 + .../ff/sports/fb/dto/CreateUserResponse.java | 19 + .../main/java/com/ff/sports/fb/dto/Enums.java | 9 + .../sports/fb/dto/GetMemberInfoRequest.java | 27 + .../sports/fb/dto/GetMemberInfoResponse.java | 38 ++ .../com/ff/sports/fb/dto/GetUrlRequest.java | 17 + .../com/ff/sports/fb/dto/GetUrlResponse.java | 33 + .../ff/sports/fb/dto/OrderFilesRequest.java | 33 + .../ff/sports/fb/dto/OrderFilesResponse.java | 27 + .../ff/sports/fb/dto/OrderInfoRequest.java | 26 + .../ff/sports/fb/dto/OrderInfoResponse.java | 115 ++++ .../sports/fb/dto/TransferDetailRequest.java | 39 ++ .../sports/fb/dto/TransferDetailResponse.java | 39 ++ .../ff/sports/fb/dto/TransferInRequest.java | 46 ++ .../ff/sports/fb/dto/TransferInResponse.java | 20 + .../ff/sports/fb/dto/TransferOutRequest.java | 46 ++ .../ff/sports/fb/dto/TransferOutResponse.java | 20 + .../sports/fb/impl/FBSportsServiceImpl.java | 618 ++++++++++++++++++ .../com/ff/utils/CalculateDateDaysAgo.java | 25 + .../com/ff/utils/SortByAttributeNameASC.java | 40 ++ .../com/ff/utils/TimestampFromString.java | 23 + .../main/resources/mapper/game/GameMapper.xml | 54 +- 33 files changed, 1450 insertions(+), 135 deletions(-) delete mode 100644 ff-game/src/main/java/com/ff/sports/api/fb/address/FbAddress.java delete mode 100644 ff-game/src/main/java/com/ff/sports/fb/A.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/address/FBSportsAddress.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/client/FBSportsClient.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/CreateUserResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/Enums.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetMemberInfoResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetUrlResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/OrderInfoResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferDetailResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferInRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferInResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/TransferOutResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java create mode 100644 ff-game/src/main/java/com/ff/utils/CalculateDateDaysAgo.java create mode 100644 ff-game/src/main/java/com/ff/utils/SortByAttributeNameASC.java create mode 100644 ff-game/src/main/java/com/ff/utils/TimestampFromString.java 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 1e5c620..8df3d35 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 @@ -84,6 +84,10 @@ public class CacheConstants { * dg游戏 */ public static final String DG_GAMES = "dg_games:"; + /** + * fb体育 + */ + public static final String FB_Sports = "fp_sports:"; /** * pg游戏投注货币 */ 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 ddb5bb7..29e6031 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,6 +30,12 @@ public enum ErrorCode { ACCOUNT_NOT_ONLINE(1014, "账号不在线"), FREQUENT_BALANCE_TRANSFER(1015, "当前游戏账号余额转移频繁"), PLATFORM_NOT_METHODS(1016, "游戏平台不支持的方法"), + 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失败") ; // 获取错误码 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 5689a02..a952382 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 @@ -13,8 +13,9 @@ public enum GamePlatforms { DG("DG", "DG"), MT("MT", "美天棋牌"), AE("AE", "AE"), - KM("KM", "KM") - ; + KM("KM", "KM"), + + FBSports("FBSports", "FB体育"); 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 76ecf27..18f64c1 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 @@ -75,9 +75,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 4307341..7bafc53 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 @@ -1,42 +1,40 @@ package com.ff.common.service.impl; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.List; -import java.util.Map; - import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.NumberUtil; import com.ff.base.constant.Constants; import com.ff.base.enums.*; import com.ff.base.exception.base.ApiException; import com.ff.base.system.domain.TenantPlatform; +import com.ff.base.system.domain.TenantSecretKey; import com.ff.base.system.service.ITenantPlatformService; +import com.ff.base.system.service.ITenantSecretKeyService; import com.ff.base.utils.DateUtils; import com.ff.base.utils.QuotaUtils; -import com.ff.base.utils.StringUtils; +import com.ff.common.domain.TenantGameQuota; import com.ff.common.domain.TenantGameQuotaFlow; import com.ff.common.domain.TenantQuotaExchange; -import com.ff.base.system.domain.TenantSecretKey; import com.ff.common.dto.BalanceChangesDTO; import com.ff.common.dto.BalanceRealChangesDTO; import com.ff.common.dto.GameBalanceExchange; +import com.ff.common.mapper.TenantGameQuotaMapper; import com.ff.common.service.ITenantGameQuotaFlowService; +import com.ff.common.service.ITenantGameQuotaService; import com.ff.common.service.ITenantQuotaExchangeService; -import com.ff.base.system.service.ITenantSecretKeyService; import com.ff.game.api.IGamesService; import com.ff.game.api.request.MemberInfoRequestDTO; import com.ff.member.domain.Member; import com.ff.member.service.IMemberService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.ff.common.mapper.TenantGameQuotaMapper; -import com.ff.common.domain.TenantGameQuota; -import com.ff.common.service.ITenantGameQuotaService; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; /** * 租户游戏配额Service业务层处理 @@ -296,14 +294,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()); @@ -332,6 +327,11 @@ 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()); + } + balanceRequestAmount = NumberUtil.add(balanceRequestAmount, NumberUtil.mul(balanceRequestAmount, NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED))); // 计算累计转入和转出金额 @@ -393,12 +393,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/game/api/meitian/impl/MeiTianGameServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java index 4741aa6..47af3fa 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 @@ -24,6 +24,8 @@ 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.utils.CalculateDateDaysAgo; +import com.ff.utils.TimestampFromString; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,10 +38,6 @@ import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -368,7 +366,7 @@ public class MeiTianGameServiceImpl implements IGamesService { exchangeMoney.setCoinAfter(exchangeMoneyResponse.getBalance()); exchangeMoney.setCurrencyBefore(exchangeMoneyResponse.getBalance().subtract(transAmount)); exchangeMoney.setCurrencyAfter(exchangeMoneyResponse.getBalance()); - exchangeMoney.setStatus(1); // SUCCESS + exchangeMoney.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney); } else { @@ -396,7 +394,7 @@ public class MeiTianGameServiceImpl implements IGamesService { exchangeMoney.setCoinAfter(exchangeMoneyResponse.getBalance()); exchangeMoney.setCurrencyBefore(exchangeMoneyResponse.getBalance().add(transAmount)); exchangeMoney.setCurrencyAfter(exchangeMoneyResponse.getBalance()); - exchangeMoney.setStatus(1); // SUCCESS + exchangeMoney.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney); } else { throw new BaseException(MeiTianExchangeMoneyResponseDTO.TransferOut.get(exchangeMoneyResponse.getErrorCode()).getMessage()); @@ -485,7 +483,7 @@ public class MeiTianGameServiceImpl implements IGamesService { boolean doSyncRecordByDate(BetRecordByTimeDTO betRecordByTimeDTO, int daysToSubtract) { - String date = getDateStr(daysToSubtract); + String date = CalculateDateDaysAgo.getStr(daysToSubtract); String configKey = GamePlatforms.MT.getCode() + ":lastSyncDate"; String syncDateStr = sysConfigServiceImpl.selectConfigByKey(configKey); Map syncDateMap = new HashMap<>(); @@ -750,7 +748,7 @@ public class MeiTianGameServiceImpl implements IGamesService { BigDecimal originPayoffAmount = new BigDecimal(dataBean.getIncome()); // 这个值是到手的 int compareResult = originPayoffAmount.compareTo(BigDecimal.ZERO); - long gameTime = getTime(dataBean.getGameDate()); + long gameTime = TimestampFromString.from(dataBean.getGameDate()); Platform platform = gamesDataBuildDTO.getPlatform(); String systemCurrency = platform.getOurCurrency(dataBean.getCurrency()); //数据构造 @@ -782,30 +780,4 @@ public class MeiTianGameServiceImpl implements IGamesService { gameBettingDetails.setCreateTime(DateUtils.getNowDate()); return gameBettingDetails; } - - public long getTime(String date) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - try { - Date parse = simpleDateFormat.parse(date); - return parse.getTime(); - } catch (ParseException e) { - return System.currentTimeMillis(); - } - } - - public LocalDate getDate(int daysToSubtract) { - return LocalDate.now().minusDays(daysToSubtract); // 获取当前日期减去两天 - } - - public String getDateStr(int daysToSubtract) { - // 获取当前日期减去指定天数 - LocalDate date = LocalDate.now().minusDays(daysToSubtract); - - // 定义日期格式 - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - - // 返回格式化日期字符串 - return date.format(formatter); - } - } 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/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/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..a1f47b8 --- /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; + + +/** + * 文档地址:https://doc.newsportspro.com/apidoc_data.html#%E5%85%A5%E5%8F%82 + * @author shi + * @date 2025/02/10 + */ +@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..674a4e4 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/client/FBSportsClient.java @@ -0,0 +1,77 @@ +package com.ff.sports.fb.client; + +import com.dtflys.forest.annotation.*; +import com.ff.sports.fb.address.FBSportsAddress; +import com.ff.sports.fb.dto.*; + +/** + * https://doc.newsportspro.com/apidoc_data.html + * + * @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/transfer/detail") + OrderFilesResponse orderFiles(@JSONBody OrderFilesRequest 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/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..3d3e48b --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java @@ -0,0 +1,33 @@ +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); + } +} 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..3042774 --- /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 Integer 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..5b75ecb --- /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 Integer 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..cb82873 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java @@ -0,0 +1,618 @@ +package com.ff.sports.fb.impl; + +import cn.hutool.core.lang.generator.SnowflakeGenerator; +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.sign.Md5Utils; +import com.ff.base.utils.uuid.IdUtils; +import com.ff.game.api.IGamesService; +import com.ff.game.api.meitian.dto.MeiTianBetRecordResponseDTO; +import com.ff.game.api.meitian.dto.MeiTianGameDataDTO; +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.*; +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) { + 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 = 1904452832756013817L; + /** + * 获取游戏列表 + * + * @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.setSortNo(1); + //game.setPlatformId(gamePlatform.getId()); + game.setPlatformCode(platform.getPlatformCode()); + game.setPlatformType(PlatformType.GAME_HALL.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 Boolean exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) { + + GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId()); + if (null == gameExchangeMoney) { + throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode()); + } + if (Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) { + return Boolean.TRUE; + } + 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())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + + + /** + * 按时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + return doSyncRecordByRecordID(betRecordByTimeDTO); + } + + boolean doSyncRecordByRecordID(BetRecordByTimeDTO betRecordByTimeDTO) { + + return Boolean.FALSE; + } + + boolean doSyncRecordByDate(BetRecordByTimeDTO betRecordByTimeDTO, int daysToSubtract) { + return Boolean.FALSE; + } + + /** + * 按历史时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + doSyncRecordByDate(betRecordByTimeDTO, 0); + doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday + + return true; + } + + + /** + * 赠送免费局数 + * + * @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) { + + return null; + } + + /** + * 强制会员从游戏注销 + * + * @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 recordResponse 投注记录 + */ + private void batchInsert(MeiTianBetRecordResponseDTO recordResponse, BetRecordByTimeDTO betRecordByTimeDTO) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List dataList = recordResponse.getDataList(); + if (CollectionUtils.isEmpty(dataList)) { + return; + } + + //数据转化 + for (MeiTianBetRecordResponseDTO.DataBean 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.getRowID()); + } + if (!CollectionUtils.isEmpty(gameBettingDetails)) { + //查询重复数据id + List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.MT.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) { + + //转化类 + MeiTianBetRecordResponseDTO.DataBean dataBean = (MeiTianBetRecordResponseDTO.DataBean) gamesDataBuildDTO.getData(); +// GameSecretKeyCurrencyDTO gameSecretKey = +// gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() +// .currency(dataBean.getCurrency()) +// .platformCode(GamePlatforms.MT.getCode()).build()); + + + Member member = memberService.selectMemberByGameAccount(dataBean.getPlayerName()); + if (ObjectUtils.isEmpty(member)) { + return null; + } + List gameDatas = redisCache.getCacheList(CacheConstants.MeiTian_GAMES); + Map dataDTOMap = gameDatas.stream().collect(Collectors.toMap(MeiTianGameDataDTO::getGameId, e -> e)); + MeiTianGameDataDTO gamesDataDTO = dataDTOMap.get(dataBean.getGameCode()); + BigDecimal originPayoffAmount = new BigDecimal(dataBean.getIncome()); // 这个值是到手的 + + int compareResult = originPayoffAmount.compareTo(BigDecimal.ZERO); + long gameTime = TimestampFromString.from(dataBean.getGameDate()); + Platform platform = gamesDataBuildDTO.getPlatform(); + String systemCurrency = platform.getOurCurrency(dataBean.getCurrency()); + //数据构造 + GameBettingDetails gameBettingDetails = GameBettingDetails.builder() + .tenantKey(member.getTenantKey()) + //保存我们的币种id + .currencyCode(systemCurrency) + .memberId(member.getId()) + .gameCode(dataBean.getGameCode()) + .gameType(MeiTianGameType.findSystemByCode(Integer.parseInt(dataBean.getGameType()))) + .platformCode(GamePlatforms.MT.getCode()) + .gameId(gamesDataDTO.getSystemGameId()) + .gameName(gamesDataDTO.getCnName()) + .gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode()) + .gameStatusType(1) // 一般下注 + .gameCurrencyCode(dataBean.getCurrency()) + .account(dataBean.getPlayerName()) + .wagersId(dataBean.getRowID()) + .wagersTime(gameTime) + .betAmount(new BigDecimal(dataBean.getBetAmount())) + .payoffTime(gameTime) + .payoffAmount(originPayoffAmount.abs()) + .settlementTime(gameTime) + .turnover(new BigDecimal(dataBean.getCommissionable())) + .orderNo(dataBean.getRowID()) + .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..0280b2c --- /dev/null +++ b/ff-game/src/main/java/com/ff/utils/TimestampFromString.java @@ -0,0 +1,23 @@ +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; + } + } +} diff --git a/ff-game/src/main/resources/mapper/game/GameMapper.xml b/ff-game/src/main/resources/mapper/game/GameMapper.xml index 3baffbe..910da1b 100644 --- a/ff-game/src/main/resources/mapper/game/GameMapper.xml +++ b/ff-game/src/main/resources/mapper/game/GameMapper.xml @@ -7,7 +7,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - @@ -16,6 +15,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -23,7 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, sort_no, platform_id, game_code,ingress, game_source_type, game_name, freespin, demo_status, stop_status, create_by, create_time, update_by, update_time from ff_game + select * from ff_game @@ -33,7 +34,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and sort_no = #{sortNo} - and platform_id = #{platformId} and game_code = #{gameCode} and ingress = #{ingress} and game_source_type = #{gameSourceType} @@ -48,40 +48,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -112,7 +91,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{id}, #{sortNo}, - #{platformId}, #{gameCode}, #{ingress}, #{gameSourceType}, @@ -131,7 +109,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update ff_game sort_no = #{sortNo}, - platform_id = #{platformId}, game_code = #{gameCode}, ingress = #{ingress}, game_source_type = #{gameSourceType}, @@ -192,8 +169,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" g.ingress, gp.platform_type from ff_game g - inner join ff_game_platform gp on g.platform_id=gp.id - where gp.stop_status=1 and g.stop_status=1 + where g.stop_status=1 From b087305ba1e5f15ae12880c12673bfdfef3bdde0 Mon Sep 17 00:00:00 2001 From: cengy Date: Tue, 8 Apr 2025 13:36:57 +0800 Subject: [PATCH 2/9] =?UTF-8?q?test(fb):=20=E4=BF=AE=E6=94=B9=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=20ID=20=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将静态常量 GAME_ID 修改为 11111 - 在创建新游戏时,将游戏 ID 设置为 GAME_ID,而不是使用 Snowflake算法生成的随机 ID --- .../main/java/com/ff/sports/fb/impl/FBSportsServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index cb82873..f56fce4 100644 --- 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 @@ -317,7 +317,7 @@ public class FBSportsServiceImpl implements IGamesService { } throw new ApiException(ErrorCode.Get_Url_Failure.getCode()); } - private static final Long GAME_ID = 1904452832756013817L; + private static final Long GAME_ID = 11111L; /** * 获取游戏列表 * @@ -333,7 +333,7 @@ public class FBSportsServiceImpl implements IGamesService { //不存在这个游戏 if (ObjectUtils.isEmpty(game)) { game = new Game(); - game.setId(IdUtil.getSnowflakeNextId()); + game.setId(/*IdUtil.getSnowflakeNextId()*/GAME_ID); game.setSortNo(1); //game.setPlatformId(gamePlatform.getId()); game.setPlatformCode(platform.getPlatformCode()); From 8ec1afd87594fe7f93b0bab302642a7cb6ef7829 Mon Sep 17 00:00:00 2001 From: cengy Date: Tue, 8 Apr 2025 17:11:31 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat(fb-sports):=20=E5=AE=9E=E7=8E=B0=20FB?= =?UTF-8?q?=20=E4=BD=93=E8=82=B2=E6=95=B0=E6=8D=AE=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FB 体育类型枚举类 FBSportsType - 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录 - 优化数据处理逻辑,实现批量插入功能 -移除不必要的定时任务配置 --- .../java/com/ff/base/enums/FBSportsType.java | 60 ++++ .../meitian/impl/MeiTianGameServiceImpl.java | 4 +- .../com/ff/quartz/config/ScheduleConfig.java | 114 ++++---- .../ff/sports/fb/address/FBSportsAddress.java | 6 +- .../ff/sports/fb/client/FBSportsClient.java | 29 +- .../com/ff/sports/fb/dto/GetTokenRequest.java | 30 ++ .../ff/sports/fb/dto/GetTokenResponse.java | 49 ++++ .../ff/sports/fb/dto/OrderFilesRequest.java | 8 + .../ff/sports/fb/dto/OrderFilesResponse.java | 2 +- .../ff/sports/fb/dto/OrderInfoRequest.java | 2 +- .../sports/fb/impl/FBSportsServiceImpl.java | 262 +++++++++++++----- .../com/ff/utils/TimestampFromString.java | 4 + 12 files changed, 437 insertions(+), 133 deletions(-) create mode 100644 ff-base/src/main/java/com/ff/base/enums/FBSportsType.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/fb/dto/GetTokenResponse.java 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-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 47af3fa..305a9eb 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 @@ -746,8 +746,8 @@ public class MeiTianGameServiceImpl implements IGamesService { Map dataDTOMap = gameDatas.stream().collect(Collectors.toMap(MeiTianGameDataDTO::getGameId, e -> e)); MeiTianGameDataDTO gamesDataDTO = dataDTOMap.get(dataBean.getGameCode()); BigDecimal originPayoffAmount = new BigDecimal(dataBean.getIncome()); // 这个值是到手的 - - int compareResult = originPayoffAmount.compareTo(BigDecimal.ZERO); + BigDecimal betAmount = new BigDecimal(dataBean.getBetAmount()); + int compareResult = originPayoffAmount.compareTo(betAmount); long gameTime = TimestampFromString.from(dataBean.getGameDate()); Platform platform = gamesDataBuildDTO.getPlatform(); String systemCurrency = platform.getOurCurrency(dataBean.getCurrency()); 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/fb/address/FBSportsAddress.java b/ff-game/src/main/java/com/ff/sports/fb/address/FBSportsAddress.java index a1f47b8..e0aaa84 100644 --- 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 @@ -11,9 +11,9 @@ import javax.annotation.Resource; /** - * 文档地址:https://doc.newsportspro.com/apidoc_data.html#%E5%85%A5%E5%8F%82 - * @author shi - * @date 2025/02/10 + * 对接文档地址 + * + * @author cengy */ @Component public class FBSportsAddress implements AddressSource { 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 index 674a4e4..dc28d2d 100644 --- 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 @@ -5,7 +5,8 @@ import com.ff.sports.fb.address.FBSportsAddress; import com.ff.sports.fb.dto.*; /** - * https://doc.newsportspro.com/apidoc_data.html + * 数据接口文档
+ * 网页接入文档 * * @author cengy */ @@ -68,10 +69,34 @@ public interface FBSportsClient { * FB体育用拉取订单文件的方式同步订单数据,FB体育每5分钟生成一次订单文件, * 通过此接口获取某一段时间内的文件ID列表,再通过文件ID获取具体订单数据 */ - @Post(url = "/api/v2/transfer/detail") + @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/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/OrderFilesRequest.java b/ff-game/src/main/java/com/ff/sports/fb/dto/OrderFilesRequest.java index 3d3e48b..5cbbeed 100644 --- 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 @@ -29,5 +29,13 @@ public class OrderFilesRequest implements Serializable { 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 index 3042774..8d9f1b5 100644 --- 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 @@ -21,7 +21,7 @@ public class OrderFilesResponse implements Serializable { @Data public static class FileId implements Serializable { private static final long serialVersionUID = 1L; - private Integer fileId; + 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 index 5b75ecb..251dc02 100644 --- 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 @@ -16,7 +16,7 @@ public class OrderInfoRequest implements Serializable { /** * 文件Id,需要从/order/file/ids接口获取到 */ - private Integer fileId; + private Long fileId; public String toJSON() { Map map = new LinkedHashMap<>(); 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 index f56fce4..8cc5533 100644 --- 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 @@ -1,6 +1,5 @@ package com.ff.sports.fb.impl; -import cn.hutool.core.lang.generator.SnowflakeGenerator; import cn.hutool.core.util.IdUtil; import com.ff.base.constant.CacheConstants; import com.ff.base.constant.Constants; @@ -9,11 +8,10 @@ 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.meitian.dto.MeiTianBetRecordResponseDTO; -import com.ff.game.api.meitian.dto.MeiTianGameDataDTO; import com.ff.game.api.request.*; import com.ff.game.domain.*; import com.ff.game.service.IGameBettingDetailsService; @@ -33,7 +31,10 @@ import org.springframework.util.ObjectUtils; import javax.annotation.Resource; import java.math.BigDecimal; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; @@ -285,7 +286,50 @@ public class FBSportsServiceImpl implements IGamesService { */ @Override public String loginWithoutRedirect(GamesLogin requestDTO) { - GetUrlRequest request = new GetUrlRequest(); + 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, @@ -314,10 +358,12 @@ public class FBSportsServiceImpl implements IGamesService { 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; + /** * 获取游戏列表 * @@ -405,35 +451,123 @@ public class FBSportsServiceImpl implements IGamesService { /** * 按时间获取投注记录 * - * @param betRecordByTimeDTO 按时间dto投注记录 + * @param requestDTO 按时间dto投注记录 * @return {@link Boolean } */ @Override - public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { - return doSyncRecordByRecordID(betRecordByTimeDTO); + 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; } - boolean doSyncRecordByRecordID(BetRecordByTimeDTO betRecordByTimeDTO) { + 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 + ); - return Boolean.FALSE; - } - - boolean doSyncRecordByDate(BetRecordByTimeDTO betRecordByTimeDTO, int daysToSubtract) { - return Boolean.FALSE; + 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 betRecordByTimeDTO 按时间dto投注记录 + * @param requestDTO 按时间dto投注记录 * @return {@link Boolean } */ @Override - public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { - doSyncRecordByDate(betRecordByTimeDTO, 0); - doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday + public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO requestDTO) { + Long lend = requestDTO.getEndTime(); + Long lstart = requestDTO.getStartTime(); + //betRecordByTimeDTO.setStartTime(lstart); + //betRecordByTimeDTO.setEndTime(lend); - return true; + 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; } @@ -456,8 +590,7 @@ public class FBSportsServiceImpl implements IGamesService { */ @Override public GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO) { - - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } /** @@ -516,23 +649,22 @@ public class FBSportsServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } - /** * 批量插入 * - * @param recordResponse 投注记录 + * @param settledOrderList 投注记录 */ - private void batchInsert(MeiTianBetRecordResponseDTO recordResponse, BetRecordByTimeDTO betRecordByTimeDTO) { + private void batchInsert(List settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) { List gameBettingDetails = new ArrayList<>(); List wagersIds = new ArrayList<>(); //数据组装 - List dataList = recordResponse.getDataList(); + List dataList = settledOrderList; if (CollectionUtils.isEmpty(dataList)) { return; } //数据转化 - for (MeiTianBetRecordResponseDTO.DataBean dataBean : dataList) { + for (OrderInfoResponse.OrderDTO dataBean : dataList) { GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder() .platform(betRecordByTimeDTO.getVendor()) .data(dataBean).build()); @@ -540,20 +672,21 @@ public class FBSportsServiceImpl implements IGamesService { bettingDetails.setId(IdUtil.getSnowflakeNextId()); gameBettingDetails.add(bettingDetails); } - wagersIds.add(dataBean.getRowID()); + 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)) { - //查询重复数据id - List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.MT.getCode()); - //用steam流清除list中与wagersIds集合相同的数据 - gameBettingDetails = gameBettingDetails.stream() - .filter(detail -> !removeWagersIds.contains(detail.getWagersId())) - .collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(gameBettingDetails)) { - gameBettingDetailsService.batchInsert(gameBettingDetails); - } + gameBettingDetailsService.batchInsert(gameBettingDetails); } - } /** @@ -566,49 +699,44 @@ public class FBSportsServiceImpl implements IGamesService { public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) { //转化类 - MeiTianBetRecordResponseDTO.DataBean dataBean = (MeiTianBetRecordResponseDTO.DataBean) gamesDataBuildDTO.getData(); -// GameSecretKeyCurrencyDTO gameSecretKey = -// gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() -// .currency(dataBean.getCurrency()) -// .platformCode(GamePlatforms.MT.getCode()).build()); - - - Member member = memberService.selectMemberByGameAccount(dataBean.getPlayerName()); + OrderInfoResponse.OrderDTO dataBean = (OrderInfoResponse.OrderDTO) gamesDataBuildDTO.getData(); + Member member = memberService.selectMemberByGameAccount(dataBean.getMerchantUserId()); if (ObjectUtils.isEmpty(member)) { return null; } - List gameDatas = redisCache.getCacheList(CacheConstants.MeiTian_GAMES); - Map dataDTOMap = gameDatas.stream().collect(Collectors.toMap(MeiTianGameDataDTO::getGameId, e -> e)); - MeiTianGameDataDTO gamesDataDTO = dataDTOMap.get(dataBean.getGameCode()); - BigDecimal originPayoffAmount = new BigDecimal(dataBean.getIncome()); // 这个值是到手的 + 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(BigDecimal.ZERO); - long gameTime = TimestampFromString.from(dataBean.getGameDate()); + 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()); + String systemCurrency = platform.getOurCurrency(dataBean.getCurrency().toString()); //数据构造 GameBettingDetails gameBettingDetails = GameBettingDetails.builder() .tenantKey(member.getTenantKey()) //保存我们的币种id .currencyCode(systemCurrency) .memberId(member.getId()) - .gameCode(dataBean.getGameCode()) - .gameType(MeiTianGameType.findSystemByCode(Integer.parseInt(dataBean.getGameType()))) - .platformCode(GamePlatforms.MT.getCode()) - .gameId(gamesDataDTO.getSystemGameId()) - .gameName(gamesDataDTO.getCnName()) + .gameCode(game.getGameCode()) + .gameType(8) // 体育 + .platformCode(GamePlatforms.FBSports.getCode()) + .gameId(game.getId()) + .gameName(game.getGameName()) .gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode()) .gameStatusType(1) // 一般下注 - .gameCurrencyCode(dataBean.getCurrency()) - .account(dataBean.getPlayerName()) - .wagersId(dataBean.getRowID()) - .wagersTime(gameTime) - .betAmount(new BigDecimal(dataBean.getBetAmount())) - .payoffTime(gameTime) + .gameCurrencyCode(dataBean.getCurrency().toString()) + .account(dataBean.getMerchantUserId()) + .wagersId(dataBean.getId()) + .wagersTime(createTime) + .betAmount(betAmount) + .payoffTime(payoffTime) .payoffAmount(originPayoffAmount.abs()) - .settlementTime(gameTime) - .turnover(new BigDecimal(dataBean.getCommissionable())) - .orderNo(dataBean.getRowID()) + .settlementTime(payoffTime) + .turnover(betAmount) + .orderNo(dataBean.getId()) .settlementStatus(SettlementStatusEnum.COMPLETED.getCode()) .build(); gameBettingDetails.setCreateBy(Constants.SYSTEM); diff --git a/ff-game/src/main/java/com/ff/utils/TimestampFromString.java b/ff-game/src/main/java/com/ff/utils/TimestampFromString.java index 0280b2c..8a3ddd6 100644 --- a/ff-game/src/main/java/com/ff/utils/TimestampFromString.java +++ b/ff-game/src/main/java/com/ff/utils/TimestampFromString.java @@ -20,4 +20,8 @@ public class TimestampFromString { return 0L; } } + + public static Long to(String timestamp) { + return Long.parseLong(timestamp); + } } From 9d88f8e08f52962de43421e100c44c32e0f32852 Mon Sep 17 00:00:00 2001 From: cengy Date: Wed, 9 Apr 2025 11:18:49 +0800 Subject: [PATCH 4/9] =?UTF-8?q?feat(fb-sports):=20=E5=AE=9E=E7=8E=B0=20FB?= =?UTF-8?q?=20=E4=BD=93=E8=82=B2=E6=95=B0=E6=8D=AE=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FB 体育类型枚举类 FBSportsType - 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录 - 优化数据处理逻辑,实现批量插入功能 -移除不必要的定时任务配置 --- .../java/com/ff/base/enums/GamePlatforms.java | 2 + .../sports/fb/impl/FBSportsServiceImpl.java | 57 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) 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 89a6973..bbe4be7 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 @@ -15,6 +15,8 @@ public enum GamePlatforms { AE("AE", "AE"), KM("KM", "KM"), PGT("PGT", "PGT"), + FBSports("FBSports", "FB体育"), + SV388("SV388", "SV388"), ; private final String code; 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 index 8cc5533..01f3dfe 100644 --- 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 @@ -415,36 +415,45 @@ public class FBSportsServiceImpl implements IGamesService { * @return {@link Boolean } */ @Override - public Boolean exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) { + public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) { GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId()); if (null == gameExchangeMoney) { throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode()); } - if (Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) { - return Boolean.TRUE; + 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(); + } } - 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())) { - return Boolean.TRUE; - } - return Boolean.FALSE; + + return ExchangeTransferStatusResponseDTO.builder() + .statusType(status) + .balance(gameExchangeMoney.getBalance()) + .coinBefore(gameExchangeMoney.getCoinBefore()) + .coinAfter(gameExchangeMoney.getCoinAfter()) + .build(); } From 6410c6bb561887b3a59f8f7d5571a1b6036fc3c5 Mon Sep 17 00:00:00 2001 From: cengy Date: Wed, 9 Apr 2025 15:14:06 +0800 Subject: [PATCH 5/9] =?UTF-8?q?feat(game):=20=E6=B7=BB=E5=8A=A0=20SV388=20?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=B9=B3=E5=8F=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SV388 游戏平台的 API 客户端和服务实现 - 添加 SV388 相关的 DTO 类和枚举定义 - 实现 SV388 的会员管理、资金转移、投注记录查询等功能 --- .../game/api/sv388/address/SV388Adrress.java | 31 + .../ff/game/api/sv388/client/SV388Client.java | 134 ++++ .../sv388/dto/SV388AETransactionResponse.java | 60 ++ .../api/sv388/dto/SV388BetRecordResponse.java | 186 ++++++ .../SV388ExchangeTransferStatusResponse.java | 56 ++ .../api/sv388/dto/SV388LoginResponse.java | 41 ++ .../sv388/dto/SV388LogoutUserResponse.java | 33 + .../game/api/sv388/dto/SV388MemberInfo.java | 64 ++ .../ff/game/api/sv388/dto/SV388Response.java | 26 + .../api/sv388/impl/SV388GamesServiceImpl.java | 587 ++++++++++++++++++ .../main/java/com/ff/game/domain/ExtInfo.java | 2 + 11 files changed, 1220 insertions(+) create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/address/SV388Adrress.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/client/SV388Client.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388AETransactionResponse.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388BetRecordResponse.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388ExchangeTransferStatusResponse.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LoginResponse.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388LogoutUserResponse.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388MemberInfo.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/dto/SV388Response.java create mode 100644 ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java 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..10043a8 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/client/SV388Client.java @@ -0,0 +1,134 @@ +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 ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByUpdateDate", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388BetRecordResponse getBetRecordByTime(@Body Map params); + + /** + * 按时间获取投注历史记录 + * + * @param params 参数 + * @return {@link SV388BetRecordResponse } + */ + @Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByTxTime", + headers = { + "Content-type: application/x-www-form-urlencoded" + }) + SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map params); + /** + * 踢出队员 + * + * @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..08e19c3 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java @@ -0,0 +1,587 @@ +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.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; + + /** + * 游戏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 betLimit = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency()); + 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.AE_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.AE.getInfo()) + .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(); + } + + + /** + * 按时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link List }<{@link GameBettingDetails }> + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + //请求参数 + Map params = this.getKey(betRecordByTimeDTO); + + String timeFrom = redisCache.getCacheObject(CacheConstants.AE_TIME_FROM); + if (StringUtils.isEmpty(timeFrom)) { + timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), 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); + + //判断是否获取成功 + if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { + //数据组装 + this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); + return Boolean.TRUE; + } else { + redisCache.deleteObject(CacheConstants.AE_TIME_FROM); + log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); + throw new BaseException(aeBetRecordResponse.getDesc()); + } + + } + + /** + * 按历史时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByHistoryTime(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.getEndTime(), 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); + + //判断是否获取成功 + if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { + //数据组装 + this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); + return Boolean.TRUE; + } else { + log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); + throw new BaseException(aeBetRecordResponse.getDesc()); + } + } + + /** + * 赠送免费局数 + * + * @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) { + log.info("GamesAEServiceImpl [kickMember] 请求参数 {}", 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 synchronized void batchInsert(SV388BetRecordResponse aeBetRecordResponse, BetRecordByTimeDTO betRecordByTimeDTO) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List dataBean = aeBetRecordResponse.getTransactions(); + + 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.AE.getInfo()); + //用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.AE_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) + .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/domain/ExtInfo.java b/ff-game/src/main/java/com/ff/game/domain/ExtInfo.java index c86d5f9..da61f05 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 @@ -14,6 +14,8 @@ public class ExtInfo implements Serializable { // 币种信息,key为其它平台的币种id,value为我们自己的币种 private Map currency; + private Map betLimit; + public String getOurCurrency(String currencyId) { return currency == null ? null : currency.get(currencyId); } From ff36a4079945613ba76dfa6587a9a46cc1d1fc4a Mon Sep 17 00:00:00 2001 From: cengy Date: Wed, 9 Apr 2025 20:05:22 +0800 Subject: [PATCH 6/9] =?UTF-8?q?feat(sports):=20=E6=96=B0=E5=A2=9E=20DB?= =?UTF-8?q?=E4=BD=93=E8=82=B2=E5=B9=B3=E5=8F=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 DB体育相关的缓存常量、数据传输对象、接口客户端等 - 实现 DB 体育平台的服务逻辑,包括用户创建、资金转账、获取会员信息等功能 - 新增延迟任务处理机制,用于处理异步任务 - 修改错误码枚举,增加新的错误类型 - 更新游戏平台枚举,添加 DB 体育选项 --- .../com/ff/base/constant/CacheConstants.java | 14 + .../java/com/ff/base/enums/ErrorCode.java | 3 +- .../java/com/ff/base/enums/GamePlatforms.java | 4 +- .../main/java/com/ff/delay/DelayService.java | 65 ++ .../src/main/java/com/ff/delay/DelayTask.java | 44 + .../meitian/impl/MeiTianGameServiceImpl.java | 93 ++- .../ff/game/api/sv388/client/SV388Client.java | 10 +- .../api/sv388/impl/SV388GamesServiceImpl.java | 31 +- .../ff/sports/db/address/DBSportsAddress.java | 32 + .../ff/sports/db/client/DBSportsClient.java | 100 +++ .../ff/sports/db/dto/CreateUserRequest.java | 30 + .../ff/sports/db/dto/CreateUserResponse.java | 19 + .../main/java/com/ff/sports/db/dto/Enums.java | 9 + .../sports/db/dto/GetMemberInfoRequest.java | 27 + .../sports/db/dto/GetMemberInfoResponse.java | 38 + .../com/ff/sports/db/dto/GetTokenRequest.java | 30 + .../ff/sports/db/dto/GetTokenResponse.java | 49 ++ .../com/ff/sports/db/dto/GetUrlRequest.java | 17 + .../com/ff/sports/db/dto/GetUrlResponse.java | 33 + .../ff/sports/db/dto/OrderFilesRequest.java | 41 + .../ff/sports/db/dto/OrderFilesResponse.java | 27 + .../ff/sports/db/dto/OrderInfoRequest.java | 26 + .../ff/sports/db/dto/OrderInfoResponse.java | 115 +++ .../sports/db/dto/TransferDetailRequest.java | 39 + .../sports/db/dto/TransferDetailResponse.java | 39 + .../ff/sports/db/dto/TransferInRequest.java | 46 ++ .../ff/sports/db/dto/TransferInResponse.java | 20 + .../ff/sports/db/dto/TransferOutRequest.java | 46 ++ .../ff/sports/db/dto/TransferOutResponse.java | 20 + .../sports/db/impl/DBSportsServiceImpl.java | 757 ++++++++++++++++++ .../sports/fb/impl/FBSportsServiceImpl.java | 2 +- 31 files changed, 1773 insertions(+), 53 deletions(-) create mode 100644 ff-game/src/main/java/com/ff/delay/DelayService.java create mode 100644 ff-game/src/main/java/com/ff/delay/DelayTask.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/address/DBSportsAddress.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/CreateUserRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/CreateUserResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/Enums.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetMemberInfoResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetTokenRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetTokenResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetUrlRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/GetUrlResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/OrderFilesResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/OrderInfoResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferDetailResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferInRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferInResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferOutRequest.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/dto/TransferOutResponse.java create mode 100644 ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java 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 27a3135..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 @@ -96,6 +96,11 @@ public class CacheConstants { * fb体育 */ public static final String FB_Sports = "fp_sports:"; + + /** + * db体育 + */ + public static final String DB_Sports = "db_sports:"; /** * pg游戏投注货币 */ @@ -125,6 +130,15 @@ public class CacheConstants { */ public static final String KM_USER_TOKEN = "km:user:token:"; + + /** + * ae时间从 + */ + public static final String SV388_TIME_FROM= "sv388:time:from"; + + public static final String SV388_GAMES = "sv388_games:"; + + } 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 29e6031..d13589b 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 @@ -35,7 +35,8 @@ public enum ErrorCode { Transfer_Out_Failure(1019, "转出失败"), Get_Member_Info_Failure(1020, "获取会员信息失败"), Transfer_Not_Exist(1021, "转帐操作不存在"), - Get_Url_Failure(1022, "获取URL失败") + Get_Url_Failure(1022, "获取URL失败"), + Miss_Config(1023, "缺少配置"), ; // 获取错误码 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 bbe4be7..e8ffc5e 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 @@ -16,8 +16,8 @@ public enum GamePlatforms { KM("KM", "KM"), PGT("PGT", "PGT"), FBSports("FBSports", "FB体育"), - SV388("SV388", "SV388"), - ; + SV388("SV388", "SV388真人"), + DBSports("DBSports", "DB体育"); private final String code; private final String info; 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/meitian/impl/MeiTianGameServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java index c01c1aa..c67b7ae 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; + /** * 获得就是成功 * @@ -301,7 +304,7 @@ public class MeiTianGameServiceImpl implements IGamesService { String merchantId = exchangeTransferMoneyRequestDTO.getAgentId(); String playerName = exchangeTransferMoneyRequestDTO.getAccount(); String coins = exchangeTransferMoneyRequestDTO.getAmount().setScale(4, RoundingMode.DOWN).toString(); - if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())){ + if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())) { MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder() .accounts(member.getGameAccount()) .agentId(exchangeTransferMoneyRequestDTO.getAgentId()) @@ -341,7 +344,7 @@ public class MeiTianGameServiceImpl implements IGamesService { //判断是否转移成功 if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) { //更新数据 - BigDecimal transAmount =new BigDecimal(coins); + BigDecimal transAmount = new BigDecimal(coins); exchangeMoney.setBalance(transAmount); exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().subtract(transAmount)); @@ -374,7 +377,7 @@ public class MeiTianGameServiceImpl implements IGamesService { //判断是否转移成功 if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) { //更新数据 - BigDecimal transAmount =new BigDecimal(coins); + BigDecimal transAmount = new BigDecimal(coins); exchangeMoney.setBalance(transAmount); exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().add(transAmount)); @@ -414,7 +417,7 @@ public class MeiTianGameServiceImpl implements IGamesService { exchangeTransferMoneyRequestDTO.getOrderId() ); Integer status = StatusType.IN_PROGRESS.getValue(); - if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode())&& "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) { + if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode()) && "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) { status = StatusType.SUCCESS.getValue(); } else { status = StatusType.FAILURE.getValue(); @@ -428,6 +431,34 @@ public class MeiTianGameServiceImpl implements IGamesService { } + class GetRecordByTimeTask extends DelayTask { + + BetRecordByTimeDTO betRecordByTimeDTO; + + public GetRecordByTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + doSyncRecordByRecordID(betRecordByTimeDTO); + } + } + + class GetRecordByHistoryTimeTask extends DelayTask { + + BetRecordByTimeDTO betRecordByTimeDTO; + + public GetRecordByHistoryTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) { + this.betRecordByTimeDTO = betRecordByTimeDTO; + } + + @Override + public void execute() { + doSyncRecordByDate(betRecordByTimeDTO, 1); + } + } + /** * 按时间获取投注记录 * @@ -436,18 +467,21 @@ public class MeiTianGameServiceImpl implements IGamesService { */ @Override public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { - return doSyncRecordByRecordID(betRecordByTimeDTO); + delayService.addTask(new GetRecordByTimeTask(betRecordByTimeDTO)); + return Boolean.TRUE; + //return doSyncRecordByRecordID(betRecordByTimeDTO); } boolean doSyncRecordByRecordID(BetRecordByTimeDTO betRecordByTimeDTO) { - String configKey = GamePlatforms.MT.getCode() + ":lastRecordID"; - String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey); - long recordID = 0; - if (lastRecordID == null || lastRecordID.isEmpty()) { + String configKey = GamePlatforms.MT.getCode() + ":lastSyncRecordID"; + long recordID = redisCache.getCacheObject(configKey); + + //String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey); + /*if (lastRecordID == null || lastRecordID.isEmpty()) { } else { recordID = Long.parseLong(lastRecordID); - } + }*/ String merchantId = betRecordByTimeDTO.getAgentId(); Map rawMap = new LinkedHashMap<>(); rawMap.put("recordID", recordID); @@ -473,7 +507,9 @@ public class MeiTianGameServiceImpl implements IGamesService { //数据插入 this.batchInsert(recordResponse, betRecordByTimeDTO); MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1); - SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey); + redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID())); + + /*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey); if (config == null) { config = new SysConfig(); config.setConfigKey(configKey); @@ -482,7 +518,7 @@ public class MeiTianGameServiceImpl implements IGamesService { } else { config.setConfigValue(dataBean.getRecordID()); sysConfigServiceImpl.updateConfig(config); - } + }*/ // 它每次返回25000条,所以需要判断,如果大于25000条,则继续拉取 if (dataList.size() >= 25000) { doSyncRecordByRecordID(betRecordByTimeDTO); @@ -498,22 +534,7 @@ public class MeiTianGameServiceImpl implements IGamesService { String date = getDateStr(daysToSubtract); String configKey = GamePlatforms.MT.getCode() + ":lastSyncDate"; - String syncDateStr = sysConfigServiceImpl.selectConfigByKey(configKey); - Map 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); @@ -541,8 +562,9 @@ public class MeiTianGameServiceImpl implements IGamesService { //数据插入 this.batchInsert(recordResponse, betRecordByTimeDTO); MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1); - syncDateMap.put(date, Long.parseLong(dataBean.getRowID())); - SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey); + //syncDateMap.put(date, Long.parseLong(dataBean.getRowID())); + redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID())); + /*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey); if (null == config) { config = new SysConfig(); config.setConfigKey(configKey); @@ -551,7 +573,7 @@ public class MeiTianGameServiceImpl implements IGamesService { } else { config.setConfigValue(JSON.toJSONString(syncDateMap)); sysConfigServiceImpl.updateConfig(config); - } + }*/ // 它每次返回25000条,所以需要判断,如果大于25000条,则继续拉取 if (dataList.size() >= 25000) { @@ -572,9 +594,10 @@ public class MeiTianGameServiceImpl implements IGamesService { */ @Override public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { - doSyncRecordByDate(betRecordByTimeDTO, 0); - doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday + //doSyncRecordByDate(betRecordByTimeDTO, 0); + //doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday + delayService.addTask(new GetRecordByHistoryTimeTask(betRecordByTimeDTO)); return true; } 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 index 10043a8..7f57520 100644 --- 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 @@ -92,11 +92,12 @@ public interface SV388Client { * @param params 参数 * @return {@link SV388BetRecordResponse } */ - @Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByUpdateDate", + @Post(url ="{fetchUrl}/fetch/gzip/getTransactionByUpdateDate", headers = { "Content-type: application/x-www-form-urlencoded" }) - SV388BetRecordResponse getBetRecordByTime(@Body Map params); + SV388BetRecordResponse getBetRecordByTime(@Body Map params, + @Var("fetchUrl") String fetchUrl); /** * 按时间获取投注历史记录 @@ -104,11 +105,12 @@ public interface SV388Client { * @param params 参数 * @return {@link SV388BetRecordResponse } */ - @Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByTxTime", + @Post(url ="{fetchUrl}/fetch/gzip/getTransactionByTxTime", headers = { "Content-type: application/x-www-form-urlencoded" }) - SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map params); + SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map params, + @Var("fetchUrl") String fetchUrl); /** * 踢出队员 * 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 index 08e19c3..a49ef4b 100644 --- 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 @@ -105,7 +105,17 @@ public class SV388GamesServiceImpl implements IGamesService { public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) { Platform platform = createMemberRequestDTO.getVendor(); - Object betLimit = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency()); + Object o = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency()); + if (!(o instanceof Map)) { + throw new ApiException(ErrorCode.Miss_Config.getCode()); + } + Map 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()); @@ -201,7 +211,7 @@ public class SV388GamesServiceImpl implements IGamesService { game.setGameId(StringUtils.addSuffix(GamePlatforms.SV388.getCode(), 1)); gameService.insertGame(game); } - return CacheConstants.AE_GAMES; + return CacheConstants.SV388_GAMES; } /** @@ -235,7 +245,7 @@ public class SV388GamesServiceImpl implements IGamesService { .currencyCode(exchangeTransferMoneyRequestDTO.getSystemCurrency()) .memberId(member.getId()) .transactionId(transactionId) - .platformCode(GamePlatforms.AE.getInfo()) + .platformCode(GamePlatforms.SV388.getCode()) .build(); exchangeMoney.setCreateBy(Constants.SYSTEM); exchangeMoney.setStatus(StatusType.IN_PROGRESS.getValue()); @@ -338,16 +348,16 @@ public class SV388GamesServiceImpl implements IGamesService { //请求参数 Map params = this.getKey(betRecordByTimeDTO); - String timeFrom = redisCache.getCacheObject(CacheConstants.AE_TIME_FROM); + String timeFrom = redisCache.getCacheObject(CacheConstants.SV388_TIME_FROM); if (StringUtils.isEmpty(timeFrom)) { - timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00"; + timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00"; } params.put("timeFrom", timeFrom); params.put("platform", GamePlatforms.SV388.getCode()); params.put("delayTime", 10000); - SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetRecordByTime(params); + SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl()); //判断是否获取成功 if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { @@ -355,7 +365,7 @@ public class SV388GamesServiceImpl implements IGamesService { this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); return Boolean.TRUE; } else { - redisCache.deleteObject(CacheConstants.AE_TIME_FROM); + redisCache.deleteObject(CacheConstants.SV388_TIME_FROM); log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); throw new BaseException(aeBetRecordResponse.getDesc()); } @@ -378,7 +388,8 @@ public class SV388GamesServiceImpl implements IGamesService { params.put("startTime", startTime); params.put("endTime", endTime); params.put("platform", /*"SEXYBCRT"*/ GamePlatforms.SV388.getCode()); - SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params); + + SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl()); //判断是否获取成功 if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { @@ -504,7 +515,7 @@ public class SV388GamesServiceImpl implements IGamesService { } if (!CollectionUtils.isEmpty(gameBettingDetails)) { //查询重复数据id - List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.AE.getInfo()); + List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.SV388.getCode()); //用steam流清除list中与wagersIds集合相同的数据 gameBettingDetails = gameBettingDetails.stream() .filter(detail -> !removeWagersIds.contains(detail.getWagersId())) @@ -516,7 +527,7 @@ public class SV388GamesServiceImpl implements IGamesService { if (StringUtils.isEmpty(timeFrom)) { timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00"; } - redisCache.setCacheObject(CacheConstants.AE_TIME_FROM, timeFrom); + redisCache.setCacheObject(CacheConstants.SV388_TIME_FROM, timeFrom); } /** 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..6fabff8 --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java @@ -0,0 +1,100 @@ +package com.ff.sports.db.client; + +import com.dtflys.forest.annotation.*; +import com.ff.sports.db.address.DBSportsAddress; +import com.ff.sports.db.dto.*; + +/** + * + * @author cengy + */ +@Address(source = DBSportsAddress.class) +public interface DBSportsClient { + /** + * 创建投注用户 + * + * @return {@link CreateUserResponse} + */ + @Post(url = "/api/v2/new/user/create") + CreateUserResponse createMember(@JSONBody CreateUserRequest request, + @Header("sign") @Var("sign") String sign, + @Header("timestamp") @Var("timestamp") long timestamp, + @Header("merchantId") @Var("merchantId") String merchantId); + + /** + * 用户金额转入到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..fd466fb --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/dto/CreateUserRequest.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.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/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..3df555c --- /dev/null +++ b/ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java @@ -0,0 +1,757 @@ +package com.ff.sports.db.impl; + +import cn.hutool.core.util.IdUtil; +import com.ff.base.constant.CacheConstants; +import com.ff.base.constant.Constants; +import com.ff.base.core.redis.RedisCache; +import com.ff.base.enums.*; +import com.ff.base.exception.base.ApiException; +import com.ff.base.exception.base.BaseException; +import com.ff.base.utils.DateUtils; +import com.ff.base.utils.StringUtils; +import com.ff.base.utils.sign.Md5Utils; +import com.ff.base.utils.uuid.IdUtils; +import com.ff.game.api.IGamesService; +import com.ff.game.api.request.*; +import com.ff.game.domain.*; +import com.ff.game.service.IGameBettingDetailsService; +import com.ff.game.service.IGameExchangeMoneyService; +import com.ff.game.service.IGameService; +import com.ff.member.domain.Member; +import com.ff.member.service.IMemberService; +import com.ff.sports.db.client.DBSportsClient; +import com.ff.sports.db.dto.*; +import com.ff.utils.TimestampFromString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + + +/** + * FB体育 + * + * @author shi + * @date 2024/10/21 + */ +@Service("DBSportsService") +@Slf4j +public class DBSportsServiceImpl implements IGamesService { + + @Resource + private RedisCache redisCache; + + @Resource + private IGameExchangeMoneyService gameExchangeMoneyService; + + @Resource + private IGameService gameService; + + + @Resource + private IMemberService memberService; + + @Resource + private DBSportsClient dbSportsClient; + + @Resource + private IGameBettingDetailsService gameBettingDetailsService; + + /** + * 获得就是成功 + * + * @param errorCode 错误代码 + * @return {@link Boolean } + */ + private Boolean isSuccess(Integer errorCode) { + return 0 == errorCode; + } + + String getSign(String bodyJsonString, String merchantId, String merchantApiSecret, long requestTimestamp) { + + String stringThatNeedsToBeSigned = bodyJsonString + "." + merchantId + "." + requestTimestamp + "." + merchantApiSecret; + String sign = Md5Utils.md5New(stringThatNeedsToBeSigned); + return sign; + } + + /** + * 创建成员 + * + * @param createMemberRequestDTO 创建成员请求dto + * @return {@link Boolean } + */ + @Override + public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) { + + long timestamp = System.currentTimeMillis(); + CreateUserRequest request = new CreateUserRequest(); + request.setMerchantUserId(createMemberRequestDTO.getAccount()); + ArrayList currencyIds = new ArrayList<>(); + currencyIds.add(Integer.parseInt(createMemberRequestDTO.getCurrency())); + request.setCurrencyIds(currencyIds); + String jsonBody = /*SortByAttributeNameASC.get(request)*/ request.toJSON(); + String sign = getSign(jsonBody, + createMemberRequestDTO.getAgentId(), + createMemberRequestDTO.getAgentKey(), + timestamp + ); + + CreateUserResponse response = dbSportsClient.createMember( + request, + sign, + timestamp, createMemberRequestDTO.getAgentId()); + if (isSuccess(response.getCode())) { + log.info("创建会员成功, account:{}->{}", createMemberRequestDTO.getAccount(), response.getData()); + return Boolean.TRUE; + } + + log.error("创建会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMessage()); + throw new ApiException(ErrorCode.Create_Member_Failure.getCode()); + } + + + /** + * 按代理id进行交换转账 + * + * @param requestDTO 外汇转账moeny dto + * @return {@link Long } + */ + @Override + @Transactional + public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO requestDTO) { + + Member member = memberService.selectMemberByGameAccount(requestDTO.getAccount()); + String transactionId = GamePlatforms.DBSports.getCode() + IdUtils.simpleUUID(); + List 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("DB体育"); + game.setCreateBy(Constants.SYSTEM); + NameInfo nameInfo = new NameInfo(); + nameInfo.setLang("zh-CN"); + nameInfo.setName("DB体育"); + game.setNameInfo(Collections.singletonList(nameInfo)); + gameService.insertGame(game); + } + /*GameName gameName = gameNameService.selectGameNameById(GAME_NAME_ID); + if (ObjectUtils.isEmpty(gameName)) { + gameNameService.insertGameName(GameName.builder() + .id(GAME_NAME_ID) + .gameId(game.getId()) + .gameName(game.getGameName()) + .langCode("zh-CN") + .createBy(Constants.SYSTEM) + .build()); + }*/ + + return CacheConstants.DB_Sports; + } + + /** + * 汇兑转移状态 + * + * @param requestDTO 兑换转账请求dto + * @return {@link Boolean } + */ + @Override + public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) { + + GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId()); + if (null == gameExchangeMoney) { + throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode()); + } + Integer status = StatusType.IN_PROGRESS.getValue(); + + if (!Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) { + TransferDetailRequest request = new TransferDetailRequest(); + request.setMerchantUserId(requestDTO.getAccount()); + request.setTransferType(Objects.equals(gameExchangeMoney.getExchangeType(), TransferType.GAMES.getCode()) + ? "IN" : "OUT"); + long timestamp = System.currentTimeMillis(); + String jsonBody = request.toJSON(); + String sign = getSign(jsonBody, + requestDTO.getAgentId(), + requestDTO.getAgentKey(), + timestamp + ); + TransferDetailResponse response = dbSportsClient.transferDetail( + request, + sign, + timestamp, + requestDTO.getAgentId() + ); + if (this.isSuccess(response.getCode())) { + status = StatusType.SUCCESS.getValue(); + } else { + status = StatusType.FAILURE.getValue(); + } + } + + return ExchangeTransferStatusResponseDTO.builder() + .statusType(status) + .balance(gameExchangeMoney.getBalance()) + .coinBefore(gameExchangeMoney.getCoinBefore()) + .coinAfter(gameExchangeMoney.getCoinAfter()) + .build(); + } + + + /** + * 按时间获取投注记录 + * + * @param requestDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO requestDTO) { + Long lend = requestDTO.getEndTime(); + Long lstart = requestDTO.getStartTime(); + //betRecordByTimeDTO.setStartTime(lstart); + //betRecordByTimeDTO.setEndTime(lend); + + OrderFilesRequest request = new OrderFilesRequest(); + request.setStartTime(lstart); + request.setEndTime(lend); + long timestamp = System.currentTimeMillis(); + String jsonBody = request.toJSON(); + String sign = getSign(jsonBody, + requestDTO.getAgentId(), + requestDTO.getAgentKey(), + timestamp + ); + OrderFilesResponse orderFilesResponse = dbSportsClient.orderFiles( + request, + sign, + timestamp, + requestDTO.getAgentId() + ); + if (this.isSuccess(orderFilesResponse.getCode())) { + for (OrderFilesResponse.FileId fileId : orderFilesResponse.getData()) { + try { + getOrderData(fileId.getFileId(), requestDTO); + } catch (Exception e) { + log.error("获取订单数据失败,fileId:{},agentId:{}", fileId, requestDTO.getAgentId()); + } + } + return true; + } + log.error("获取订单文件失败,agentId:{}", requestDTO.getAgentId()); + return false; + } + + void getOrderData(Long fileId, BetRecordByTimeDTO requestDTO) { + OrderInfoRequest request = new OrderInfoRequest(); + request.setFileId(fileId); + long timestamp = System.currentTimeMillis(); + String jsonBody = request.toJSON(); + String sign = getSign(jsonBody, + requestDTO.getAgentId(), + requestDTO.getAgentKey(), + timestamp + ); + + OrderInfoResponse orderInfoResponse = dbSportsClient.getOrderJsonData( + request, + sign, + timestamp, + requestDTO.getAgentId() + ); + if (!this.isSuccess(orderInfoResponse.getCode())) { + return; + } + List 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(game.getGameCode()) + .gameType(PlatformType.SPORTS.getCode()) // 体育 + .platformCode(GamePlatforms.DBSports.getCode()) + .gameId(game.getId()) + .gameName(game.getGameName()) + .gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode()) + .gameStatusType(1) // 一般下注 + .gameCurrencyCode(dataBean.getCurrency().toString()) + .account(dataBean.getMerchantUserId()) + .wagersId(dataBean.getId()) + .wagersTime(createTime) + .betAmount(betAmount) + .payoffTime(payoffTime) + .payoffAmount(originPayoffAmount.abs()) + .settlementTime(payoffTime) + .turnover(betAmount) + .orderNo(dataBean.getId()) + .settlementStatus(SettlementStatusEnum.COMPLETED.getCode()) + .build(); + gameBettingDetails.setCreateBy(Constants.SYSTEM); + gameBettingDetails.setCreateTime(DateUtils.getNowDate()); + return gameBettingDetails; + } +} 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 index 01f3dfe..9e84d4d 100644 --- 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 @@ -383,7 +383,7 @@ public class FBSportsServiceImpl implements IGamesService { game.setSortNo(1); //game.setPlatformId(gamePlatform.getId()); game.setPlatformCode(platform.getPlatformCode()); - game.setPlatformType(PlatformType.GAME_HALL.getCode()); + game.setPlatformType(PlatformType.SPORTS.getCode()); game.setGameCode("1"); game.setGameSourceType(String.valueOf(1)); game.setGameName("FB体育"); From b0c058345dd5b9a4cd3ea34e219bb7f5b6158776 Mon Sep 17 00:00:00 2001 From: cengy Date: Wed, 9 Apr 2025 20:25:57 +0800 Subject: [PATCH 7/9] =?UTF-8?q?refactor(ff-game):=20=E9=87=8D=E6=9E=84=20S?= =?UTF-8?q?V388=20=E6=8A=95=E6=B3=A8=E8=AE=B0=E5=BD=95=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 DelayService 和 DelayTask 机制,实现异步处理投注记录请求 -将原有的同步方法拆分为实时记录和历史记录两个异步任务- 优化了错误处理逻辑,移除了不必要的异常抛出 - 调整了参数处理和日志记录,提高了代码可读性和维护性 --- .../api/sv388/impl/SV388GamesServiceImpl.java | 82 +++++++++++++------ 1 file changed, 58 insertions(+), 24 deletions(-) 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 index a49ef4b..75ea9f0 100644 --- 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 @@ -12,6 +12,8 @@ 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; @@ -65,6 +67,9 @@ public class SV388GamesServiceImpl implements IGamesService { @Resource private IGameBettingDetailsService gameBettingDetailsService; + @Resource + private DelayService delayService; + /** * 游戏id */ @@ -337,14 +342,20 @@ public class SV388GamesServiceImpl implements IGamesService { } - /** - * 按时间获取投注记录 - * - * @param betRecordByTimeDTO 按时间dto投注记录 - * @return {@link List }<{@link GameBettingDetails }> - */ - @Override - public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + 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); @@ -353,7 +364,6 @@ public class SV388GamesServiceImpl implements IGamesService { 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); @@ -363,27 +373,16 @@ public class SV388GamesServiceImpl implements IGamesService { if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { //数据组装 this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); - return Boolean.TRUE; } else { redisCache.deleteObject(CacheConstants.SV388_TIME_FROM); log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); - throw new BaseException(aeBetRecordResponse.getDesc()); } - } - /** - * 按历史时间获取投注记录 - * - * @param betRecordByTimeDTO 按时间dto投注记录 - * @return {@link Boolean } - */ - @Override - public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + 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.getEndTime(), 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); @@ -395,13 +394,48 @@ public class SV388GamesServiceImpl implements IGamesService { if (this.getIsSuccess(aeBetRecordResponse.getStatus())) { //数据组装 this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO); - return Boolean.TRUE; } else { log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc()); - throw new BaseException(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; + } + /** * 赠送免费局数 * From fb6138f9a08a0a6803158aeeba2be4666925c480 Mon Sep 17 00:00:00 2001 From: cengy Date: Thu, 10 Apr 2025 10:20:05 +0800 Subject: [PATCH 8/9] =?UTF-8?q?refactor(game):=20=E9=87=8D=E6=9E=84=20AE?= =?UTF-8?q?=20=E5=92=8C=20SV388=20=E7=9A=84=E6=8A=95=E6=B3=A8=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 GamesAEServiceImpl 和 SV388GamesServiceImpl 类添加 DelayService 依赖 - 实现 GetRealtimeRecordTask 和 GetHistoryRecordTask 类继承 DelayTask - 重写 getBetRecordByTime 和 getBetRecordByHistoryTime 方法,使用延迟任务处理大量数据 - 优化 batchInsert 方法,增加空数据判断和日志记录 - 调整 kickMember 方法,移除不必要的日志输出 --- .../game/api/ae/impl/GamesAEServiceImpl.java | 87 ++++++++++++++----- .../api/sv388/impl/SV388GamesServiceImpl.java | 16 +++- 2 files changed, 80 insertions(+), 23 deletions(-) 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 612c597..9b3eb1e 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 */ @@ -345,15 +351,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); @@ -373,23 +384,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"; @@ -405,13 +425,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; + } + /** * 赠送免费局数 * 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 index 75ea9f0..851b650 100644 --- 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 @@ -201,7 +201,7 @@ public class SV388GamesServiceImpl implements IGamesService { //不存在这个游戏 if (ObjectUtils.isEmpty(gameList)) { Game game = new Game(); - game.setId(IdUtil.getSnowflakeNextId()); + game.setId(/*IdUtil.getSnowflakeNextId()*/GAME_ID); game.setSortNo(gameService.selectMaxSortNo(platformType, GamePlatforms.SV388.getCode()) + 1); game.setPlatformCode(platform.getPlatformCode()); game.setPlatformType(platformType); @@ -373,6 +373,9 @@ public class SV388GamesServiceImpl implements IGamesService { 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()); @@ -394,6 +397,9 @@ public class SV388GamesServiceImpl implements IGamesService { 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()); } @@ -466,7 +472,6 @@ public class SV388GamesServiceImpl implements IGamesService { */ @Override public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) { - log.info("GamesAEServiceImpl [kickMember] 请求参数 {}", kickMemberRequestDTO); Map params = this.getKey(kickMemberRequestDTO); params.put("userIds", kickMemberRequestDTO.getAccount()); XKKickMemberDTO xkKickMemberDTO = sv388Client.kickMember(params); @@ -528,12 +533,16 @@ public class SV388GamesServiceImpl implements IGamesService { * * @param aeBetRecordResponse ae下注记录响应dto */ - private synchronized void batchInsert(SV388BetRecordResponse aeBetRecordResponse, BetRecordByTimeDTO betRecordByTimeDTO) { + 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) { @@ -562,6 +571,7 @@ public class SV388GamesServiceImpl implements IGamesService { timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00"; } redisCache.setCacheObject(CacheConstants.SV388_TIME_FROM, timeFrom); + } /** From bf955ed14bf2f5ca7ecc3697f9a69a36e15cbf3b Mon Sep 17 00:00:00 2001 From: cengy Date: Thu, 10 Apr 2025 13:50:46 +0800 Subject: [PATCH 9/9] =?UTF-8?q?refactor(game):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E5=92=8C?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改游戏 ID 类型从 Long 到 String - 更新游戏创建和验证逻辑 - 调整游戏数据传输对象结构 - 优化游戏列表响应格式 --- .../game/api/ae/impl/GamesAEServiceImpl.java | 2 +- .../dg/service/impl/GamesDGServiceImpl.java | 3 +- .../api/fc/dto/ApiFCGameListResponseDTO.java | 2 +- .../game/api/fc/impl/GamesFCServiceImpl.java | 2 +- .../game/api/jili/dto/JILIGamesDataDTO.java | 2 +- .../service/impl/GamesJILIServiceImpl.java | 4 +- .../ff/game/api/km/dto/KMGameResponse.java | 2 +- .../game/api/km/impl/GamesKMServiceImpl.java | 2 +- .../api/meitian/dto/MeiTianGameDataDTO.java | 2 +- .../meitian/impl/MeiTianGameServiceImpl.java | 7 +--- .../ng/service/impl/GamesPGServiceImpl.java | 2 +- .../game/api/pgt/dto/PGTGameListResponse.java | 2 +- .../api/pgt/impl/GamesPGTServiceImpl.java | 3 +- .../game/api/pgx/dto/PGXGameListResponse.java | 2 +- .../api/pgx/impl/GamesPGXServiceImpl.java | 2 +- .../game/api/sa/impl/GamesSAServiceImpl.java | 2 +- .../api/sv388/impl/SV388GamesServiceImpl.java | 4 +- .../com/ff/game/api/xk/dto/XKGamesDTO.java | 2 +- .../xk/service/impl/GamesXKServiceImpl.java | 2 +- .../ff/game/domain/GameBettingDetails.java | 2 +- .../ff/sports/db/client/DBSportsClient.java | 14 ++++--- .../ff/sports/db/dto/CreateUserRequest.java | 24 ++++++------ .../sports/db/impl/DBSportsServiceImpl.java | 39 +++++++------------ .../sports/fb/impl/FBSportsServiceImpl.java | 2 +- 24 files changed, 59 insertions(+), 71 deletions(-) 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 9b3eb1e..9cc6c0b 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 @@ -631,7 +631,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 b4315e9..f7383c1 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 @@ -578,7 +578,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 1c4a951..2a20a3e 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 f82a544..f2c36d3 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()); } @@ -813,7 +813,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 6029bbc..189ca02 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 c67b7ae..25e3e91 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 @@ -248,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); @@ -827,10 +826,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 c1b9208..6f04a19 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 @@ -711,7 +711,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 02f89a9..48c42cf 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()); } @@ -647,6 +647,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 65c4e08..ed72b4a 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/sa/impl/GamesSAServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java index c697f95..984a0ba 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 @@ -612,7 +612,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/impl/SV388GamesServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/sv388/impl/SV388GamesServiceImpl.java index 851b650..b2ca059 100644 --- 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 @@ -201,7 +201,7 @@ public class SV388GamesServiceImpl implements IGamesService { //不存在这个游戏 if (ObjectUtils.isEmpty(gameList)) { Game game = new Game(); - game.setId(/*IdUtil.getSnowflakeNextId()*/GAME_ID); + game.setId(IdUtil.getSnowflakeNextId()); game.setSortNo(gameService.selectMaxSortNo(platformType, GamePlatforms.SV388.getCode()) + 1); game.setPlatformCode(platform.getPlatformCode()); game.setPlatformType(platformType); @@ -618,7 +618,7 @@ public class SV388GamesServiceImpl implements IGamesService { .gameCode(resultBean.getGameCode()) .gameType(PlatformType.VIDEO.getCode()) .platformCode(GamePlatforms.SV388.getCode()) - .gameId(GAME_ID) + .gameId(/*GAME_ID*/GamePlatforms.SV388.getCode() + "_" + 1) .gameName(resultBean.getGameName()) .gameStatus(gameStatus) .gameStatusType(resultBean.getSettleStatus()) 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 c916bb1..70a4f36 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 @@ -253,7 +253,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/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/sports/db/client/DBSportsClient.java b/ff-game/src/main/java/com/ff/sports/db/client/DBSportsClient.java index 6fabff8..8d58440 100644 --- 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 @@ -5,7 +5,6 @@ import com.ff.sports.db.address.DBSportsAddress; import com.ff.sports.db.dto.*; /** - * * @author cengy */ @Address(source = DBSportsAddress.class) @@ -15,11 +14,14 @@ public interface DBSportsClient { * * @return {@link CreateUserResponse} */ - @Post(url = "/api/v2/new/user/create") - CreateUserResponse createMember(@JSONBody CreateUserRequest request, - @Header("sign") @Var("sign") String sign, - @Header("timestamp") @Var("timestamp") long timestamp, - @Header("merchantId") @Var("merchantId") String merchantId); + @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,必须是正数 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 index fd466fb..e985d26 100644 --- 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 @@ -1,12 +1,9 @@ package com.ff.sports.db.dto; -import com.alibaba.fastjson2.JSON; +import com.ff.base.utils.sign.Md5Utils; import lombok.Data; import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; /** * @author cengy @@ -16,15 +13,16 @@ 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 + 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 String toJSON() { - Map map = new LinkedHashMap<>(); - map.put("currencyIds", currencyIds); - map.put("merchantUserId", merchantUserId); - map.put("oddsLevel", oddsLevel); - return JSON.toJSONString(map); + 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/impl/DBSportsServiceImpl.java b/ff-game/src/main/java/com/ff/sports/db/impl/DBSportsServiceImpl.java index 3df555c..6aa4fef 100644 --- 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 @@ -87,31 +87,22 @@ public class DBSportsServiceImpl implements IGamesService { /** * 创建成员 * - * @param createMemberRequestDTO 创建成员请求dto + * @param requestDTO 创建成员请求dto * @return {@link Boolean } */ @Override - public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) { + public Boolean createMember(CreateMemberRequestDTO requestDTO) { - 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 - ); + request.setUserName(requestDTO.getAccount()); + request.setMerchantCode(requestDTO.getAgentId()); + request.setCurrency(requestDTO.getCurrency()); + request.calcSign(requestDTO.getAgentKey()); - CreateUserResponse response = dbSportsClient.createMember( - request, - sign, - timestamp, createMemberRequestDTO.getAgentId()); + String lang = "zh"; + String requestId = IdUtils.fastUUID(); + CreateUserResponse response = dbSportsClient.createMember(request, requestId, lang); if (isSuccess(response.getCode())) { - log.info("创建会员成功, account:{}->{}", createMemberRequestDTO.getAccount(), response.getData()); return Boolean.TRUE; } @@ -388,7 +379,7 @@ public class DBSportsServiceImpl implements IGamesService { game.setPlatformType(PlatformType.SPORTS.getCode()); game.setGameCode("1"); game.setGameSourceType(String.valueOf(1)); - game.setGameName("DB体育"); + game.setGameName(GamePlatforms.DBSports.getInfo()); game.setCreateBy(Constants.SYSTEM); NameInfo nameInfo = new NameInfo(); nameInfo.setLang("zh-CN"); @@ -715,8 +706,8 @@ public class DBSportsServiceImpl implements IGamesService { if (ObjectUtils.isEmpty(member)) { return null; } - List gameList = redisCache.getCacheList(CacheConstants.DB_Sports); - Game game = gameList.get(0); + //List gameList = redisCache.getCacheList(CacheConstants.DB_Sports); + //Game game = gameList.get(0); BigDecimal originPayoffAmount = new BigDecimal(dataBean.getSettleAmount()); BigDecimal betAmount = new BigDecimal(dataBean.getStakeAmount()); @@ -731,11 +722,11 @@ public class DBSportsServiceImpl implements IGamesService { //保存我们的币种id .currencyCode(systemCurrency) .memberId(member.getId()) - .gameCode(game.getGameCode()) + .gameCode("1") .gameType(PlatformType.SPORTS.getCode()) // 体育 .platformCode(GamePlatforms.DBSports.getCode()) - .gameId(game.getId()) - .gameName(game.getGameName()) + .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()) 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 index 9e84d4d..3f5f6d9 100644 --- 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 @@ -732,7 +732,7 @@ public class FBSportsServiceImpl implements IGamesService { .gameCode(game.getGameCode()) .gameType(8) // 体育 .platformCode(GamePlatforms.FBSports.getCode()) - .gameId(game.getId()) + .gameId(game.getGameId()) .gameName(game.getGameName()) .gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode()) .gameStatusType(1) // 一般下注