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); }