Compare commits

...

15 Commits

Author SHA1 Message Date
shi 3236b4ab55 refactor(game): 重构游戏平台交易ID生成逻辑
- 移除 CreateOrderServiceImpl 中的 getTransactionId 方法
- 在每个游戏平台的实现类中添加 getTransactionId 方法,具体实现如下:
  - GamesAEServiceImpl
  - GamesDGServiceImpl
  - GamesFCServiceImpl
  - GamesJILIServiceImpl
  - GamesKMServiceImpl - GamesPGServiceImpl
  - GamesPGTServiceImpl
  - GamesPGXServiceImpl
  - GamesSAServiceImpl
- 更新 DBSportsServiceImpl 和 FBSportsServiceImpl 中的交易ID生成逻辑
- 重构后的交易ID生成逻辑更清晰,每个平台有自己的实现方式
2025-04-11 15:20:45 +08:00
shi 265b626b6e Merge branch 'main-meitian' into main-pgt 2025-04-11 11:03:29 +08:00
shi 537b23a420 refactor(ff-game): 重构 FB 和 SV388 平台的转账逻辑
- 移除 FBSports 和 SV388 平台的重复代码
- 优化交易 ID 生成逻辑,支持不同平台的格式
-调整 CreateOrderServiceImpl 中的平台判断逻辑
- 重构 FBSportsServiceImpl 和 SV388GamesServiceImpl 中的转账方法
2025-04-11 11:03:24 +08:00
liaoyong 0ce67f08ab feat(sports): 重构 DB体育接口
- 修改了 CreateUserRequest 和 CreateUserResponse 的字段名称
- 重写了 DBSportsClient 接口中的方法
- 更新了 DBSportsServiceImpl 中的实现逻辑
- 新增了 GetBetListRequest 和 GetBetListResponse 类
-调整了 GetMemberInfoRequest 的结构
2025-04-11 10:59:27 +08:00
shi 9fa259268e Merge branch 'main-meitian' into main-pgt
# Conflicts:
#	ff-base/src/main/java/com/ff/base/enums/ErrorCode.java
#	ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java
2025-04-11 10:56:12 +08:00
liaoyong bf955ed14b refactor(game): 重构游戏相关接口和数据结构
- 修改游戏 ID 类型从 Long 到 String
- 更新游戏创建和验证逻辑
- 调整游戏数据传输对象结构
- 优化游戏列表响应格式
2025-04-10 13:50:46 +08:00
liaoyong fb6138f9a0 refactor(game): 重构 AE 和 SV388 的投注记录获取逻辑
- 为 GamesAEServiceImpl 和 SV388GamesServiceImpl 类添加 DelayService 依赖
- 实现 GetRealtimeRecordTask 和 GetHistoryRecordTask 类继承 DelayTask
- 重写 getBetRecordByTime 和 getBetRecordByHistoryTime 方法,使用延迟任务处理大量数据
- 优化 batchInsert 方法,增加空数据判断和日志记录
- 调整 kickMember 方法,移除不必要的日志输出
2025-04-10 10:20:05 +08:00
liaoyong b0c058345d refactor(ff-game): 重构 SV388 投注记录获取逻辑
- 引入 DelayService 和 DelayTask 机制,实现异步处理投注记录请求
-将原有的同步方法拆分为实时记录和历史记录两个异步任务- 优化了错误处理逻辑,移除了不必要的异常抛出
- 调整了参数处理和日志记录,提高了代码可读性和维护性
2025-04-09 20:25:57 +08:00
liaoyong ff36a40799 feat(sports): 新增 DB体育平台支持
- 添加 DB体育相关的缓存常量、数据传输对象、接口客户端等
- 实现 DB 体育平台的服务逻辑,包括用户创建、资金转账、获取会员信息等功能
- 新增延迟任务处理机制,用于处理异步任务
- 修改错误码枚举,增加新的错误类型
- 更新游戏平台枚举,添加 DB 体育选项
2025-04-09 20:05:22 +08:00
liaoyong 6410c6bb56 feat(game): 添加 SV388 游戏平台支持
- 新增 SV388 游戏平台的 API 客户端和服务实现
- 添加 SV388 相关的 DTO 类和枚举定义
- 实现 SV388 的会员管理、资金转移、投注记录查询等功能
2025-04-09 15:14:06 +08:00
liaoyong 9d88f8e08f feat(fb-sports): 实现 FB 体育数据对接
- 新增 FB 体育类型枚举类 FBSportsType
- 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录
- 优化数据处理逻辑,实现批量插入功能
-移除不必要的定时任务配置
2025-04-09 11:18:49 +08:00
liaoyong 59dc248c3e Merge remote-tracking branch 'origin/main-pgt' into main-meitian
# Conflicts:
#	ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java
#	ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java
#	ff-game/src/main/resources/mapper/game/GameMapper.xml
2025-04-09 09:16:20 +08:00
liaoyong 8ec1afd875 feat(fb-sports): 实现 FB 体育数据对接
- 新增 FB 体育类型枚举类 FBSportsType
- 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录
- 优化数据处理逻辑,实现批量插入功能
-移除不必要的定时任务配置
2025-04-08 17:11:31 +08:00
liaoyong b087305ba1 test(fb): 修改游戏 ID 生成逻辑
- 将静态常量 GAME_ID 修改为 11111
- 在创建新游戏时,将游戏 ID 设置为 GAME_ID,而不是使用 Snowflake算法生成的随机 ID
2025-04-08 13:36:57 +08:00
liaoyong 72810d4d0e feat(fb): 新增 FB 体育平台支持
- 添加 FB 体育相关的数据结构和接口定义
- 实现 FB 体育平台的会员创建、资金转账、获取会员信息等功能
- 集成 FB 体育平台的 URL 获取和登录逻辑
- 为 FB 体育平台添加错误码定义
-优化游戏列表获取逻辑,支持 FB 体育游戏数据同步
2025-04-08 13:33:18 +08:00
91 changed files with 5062 additions and 247 deletions

View File

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

View File

@ -20,8 +20,8 @@ public enum ErrorCode {
CURRENCY_NOT_EXIST(1004, "游戏平台不支持的货币"),
GAME_NOT_EXIST(1005, "游戏不存在"),
CURRENCY_EXCHANGE(1006, "不支持币种的汇率"),
FREQUENT_INTERFACE_REQUESTS (1007, "接口请求频繁"),
BALANCE_TRANSFER_FAILED (1008, "余额转移失败"),
FREQUENT_INTERFACE_REQUESTS(1007, "接口请求频繁"),
BALANCE_TRANSFER_FAILED(1008, "余额转移失败"),
LANG_NOT_EXIST(1009, "游戏平台不支持的语言"),
ORDER_NOT_EXIST(1010, "订单不存在"),
PLAYERS_ARE_PLAYING(1011, "玩家游玩中"),
@ -30,7 +30,14 @@ public enum ErrorCode {
ACCOUNT_NOT_ONLINE(1014, "账号不在线"),
FREQUENT_BALANCE_TRANSFER(1015, "当前游戏账号余额转移频繁"),
PLATFORM_NOT_METHODS(1016, "游戏平台不支持的方法"),
DUPLICATE_ORDER_ID (1017, "重复的订单id"),
Create_Member_Failure(1017, "创建会员失败"),
Transfer_In_Failure(1018, "转入失败"),
Transfer_Out_Failure(1019, "转出失败"),
Get_Member_Info_Failure(1020, "获取会员信息失败"),
Transfer_Not_Exist(1021, "转帐操作不存在"),
Get_Url_Failure(1022, "获取URL失败"),
Miss_Config(1023, "缺少配置"),
DUPLICATE_ORDER_ID (1024, "重复的订单id"),
;
// 获取错误码

View File

@ -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<Integer> 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<String> system = Stream.of(FBSportsType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(FBSportsType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

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

View File

@ -38,11 +38,10 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static String DAY_END_TIME = "23:59:59";
public static final String ISO_8601_FORMAT= "yyyy-MM-dd'T'HH:mm:ss";
public static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
public static final String ISO_8601_FORMAT_Z= "yyyy-MM-dd'T'HH:mm:ss'Z'";
public static final String ISO_8601_FORMAT_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/**
@ -213,8 +212,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
*
*
* @param timestamp
* @param format yyyy-MM-dd'T'HH:mm:ssXXX
* @param timeZone GMT+8UTC
* @param format yyyy-MM-dd'T'HH:mm:ssXXX
* @param timeZone GMT+8UTC
* @return
*/
public static String convertTimestampToFormattedDate(long timestamp, String format, String timeZone) {
@ -251,7 +250,6 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
}
/**
* LocalDate ==> Date
*/
@ -943,4 +941,38 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static boolean isBetween(Long value, Long minValue, Long maxValue) {
return value >= minValue && value <= maxValue;
}
/**
*
*
* @param dateString "yyyy-MM-dd'T'HH:mm:ss.SSS"
* @param timezone UTCAsia/Shanghai
* @return
* @throws Exception
*/
public static long convertToMillisWithTimezone(String dateString, String timezone) {
try {
// 设置日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
// 设置解析时使用的时区
sdf.setTimeZone(TimeZone.getTimeZone(timezone));
// 解析日期字符串为 Date 对象
Date date = sdf.parse(dateString);
// 获取项目默认时区
TimeZone defaultTimeZone = TimeZone.getDefault();
// 使用默认时区的 Calendar 计算
Calendar calendar = Calendar.getInstance(defaultTimeZone);
calendar.setTime(date);
// 返回该时区对应的毫秒时间戳
return calendar.getTimeInMillis();
} catch (Exception e) {
return 0;
}
}
}

View File

@ -16,6 +16,11 @@
<dependencies>
<!--<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.4.10</version> &lt;!&ndash; 请检查最新版本 &ndash;&gt;
</dependency>-->
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -83,9 +83,6 @@ public class ApiGameController extends BaseController {
@Resource
private ITenantGameQuotaService tenantGameQuotaService;
@Resource
private ITenantGameQuotaFlowService tenantGameQuotaFlowService;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;

View File

@ -289,14 +289,11 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
ApiException.notNull(tenantPlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
// 获取用户信息
Member member = memberService.selectMemberByAccount(gameBalanceExchange.getAccount(), gameBalanceExchange.getCurrencyCode(), gameBalanceExchange.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
// 检查用户是否存在,否则抛出异常
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
@ -326,6 +323,9 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
.agentKey(gameBalanceExchange.getAgentKey())
.build();
balanceRequestAmount = iGamesService.getMemberInfo(gamesBaseRequestDTO).getBalance();
if (balanceRequestAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode());
}
}
@ -390,12 +390,10 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
// 如果是扣账操作,首先处理假额度
String falseTenantQuotaType = QuotaUtils.getFalseTenantQuota(gameBalanceExchange.getPlatformCode(), gameBalanceExchange.getCurrencyCode());
TenantGameQuota falseTenantQuota = selectTenantGameQuotaByTenantKey(tenantSecretKey.getTenantKey(), falseTenantQuotaType);
balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED)));
balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED)));
if (falseTenantQuota.getBalance().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal falseQuotaBalance = falseTenantQuota.getBalance();
if (falseQuotaBalance.compareTo(balanceRequestAmount) >= 0) {
// 假额度足够扣除本次所需金额

View File

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

View File

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

View File

@ -52,6 +52,14 @@ public interface IGamesService {
*/
String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO);
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO);
/**
* id

View File

@ -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
*/
@ -212,10 +218,27 @@ public class GamesAEServiceImpl implements IGamesService {
game.setNameInfo(Collections.singletonList(nameInfo));
game.setGameId(StringUtils.addSuffix(GamePlatforms.AE.getCode(), 1));
gameService.insertGame(game);
}else {
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("AE大厅");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.updateGame(game);
}
return CacheConstants.AE_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.AE.getCode() + IdUtils.simpleUUID();
}
/**
* id
*
@ -319,15 +342,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<String, Object> params = this.getKey(betRecordByTimeDTO);
@ -347,23 +375,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<String, Object> params = this.getKey(betRecordByTimeDTO);
String startTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
@ -379,13 +416,40 @@ public class GamesAEServiceImpl implements IGamesService {
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
//数据组装
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
return Boolean.TRUE;
if (aeBetRecordResponse.getTransactions().size() >= 20000) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
}
// return Boolean.TRUE;
} else {
log.error("GamesAEServiceImpl [getBetRecordByHistoryTime] 获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
throw new BaseException(aeBetRecordResponse.getDesc());
// throw new BaseException(aeBetRecordResponse.getDesc());
}
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link List }<{@link GameBettingDetails }>
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
@ -558,7 +622,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())

View File

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

View File

@ -0,0 +1,106 @@
package com.ff.game.api.db.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.db.address.DBSportsAddress;
import com.ff.game.api.db.dto.*;
/**
* @author cengy
*/
@Address(source = DBSportsAddress.class)
public interface DBSportsClient {
/**
*
*
* @return {@link CreateUserResponse}
*/
@Post(url = "/api/user/create",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
CreateUserResponse createMember(@Body CreateUserRequest request,
@Header("requestId") @Var("requestId") String requestId);
/**
* ()
*
* @param request
* @param requestId
* @return
*/
@Post(url = "/api/user/login",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
LoginResponse login(@Body LoginRequest request,
@Header("requestId") String requestId);
/**
* DB0.01
*
* @param request
* @return {@link TransferResponse}
*/
@Post(url = "/api/fund/transfer",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferResponse transferIn(@Body TransferRequest request,
@Header("requestId") @Var("requestId") String requestId
);
@Post(url = "/api/fund/transfer",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferResponse transferOut(@Body TransferRequest request,
@Header("requestId") String requestId
);
@Post(url = "/api/fund/checkBalance",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
GetMemberInfoResponse getMemberInfo(@Body GetMemberInfoRequest request,
@Header("requestId") String requestId);
/**
* ID
*/
@Post(url = "/api/fund/getTransferRecord",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferDetailResponse transferDetail(@Body TransferDetailRequest request,
@Header("requestId") String requestId);
/**
*
*
* @param request
* @param requestId
* @return
*/
@Post(value = "/api/user/kickOutUser",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
KickUserResponse kickUser(@Body KickUserRequest request,
@Header("requestId") String requestId);
/**
* Json
*/
@Post(url = "/api/bet/queryBetListV2")
GetBetListResponse getBetList(@Body GetBetListRequest request,
@Header("requestId") String requestId);
}

View File

@ -0,0 +1,28 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class CreateUserRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName; // 用户名(可以包含但是不要等同于特殊字符或者空格长度控制在30个字符以下)
private String merchantCode; // 商户code
private String timestamp = System.currentTimeMillis() + ""; // 13位时间戳
private String currency; // 币种
private String nickname; // N 昵称
private String agentId; // N 信用网(代理id)
private String signature; // 签名 signature =MD5(MD5(userName +”&”+ merchantCode +”&”+ timestamp) + ”&”+ key)
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(userName + "&" + merchantCode + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,26 @@
package com.ff.game.api.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 status;
private String msg;
private String code;
private Long serverTime;
private UserDTO data;
@Data
public static class UserDTO {
private String userId;
}
}

View File

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

View File

@ -0,0 +1,93 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class GetBetListRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private String userName;
/**
* 13
*/
private String startTime;
/**
* 13
*/
private String endTime;
/**
*
*/
private String merchantCode;
/**
* ID
*/
private Integer sportId;
/**
* ID
*/
private Long tournamentId;
/**
*
* 0:
* 1:
* 2:
* 3:
* 4:
* 5:
*/
private Integer settleStatus;
/**
* 1
*/
private Integer pageNum;
/**
* 1000
*/
private Integer pageSize;
/**
* 13
*/
private String timestamp;
/**
*
* 1:
* 2:
*/
private Integer orderBy;
/**
* "en"
*/
private String language;
/**
*
*/
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + startTime + "&" + endTime + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,194 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetBetListResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
// 当前页码
private Integer pageNum;
// 每页条数
private Integer pageSize;
// 总条数
private Integer totalCount;
// 注单列表
private List<OrderItemDTO> list;
@Data
public static class OrderItemDTO {
// 用户名
private String userName;
// 商户编码
private String merchantCode;
// 订单ID
private String orderNo;
// 订单状态,具体见参数字段映射
private Integer orderStatus;
// 投注时间13位时间戳
private Long createTime;
// 订单更新时间13位时间戳
private Long modifyTime;
// 实际投注金额
private String orderAmount;
// 注单项数量
private Integer betCount;
// 结算时间13位时间戳
private Long settleTime;
// 结算金额
private Double settleAmount;
// 提前结算投注金额
private Double preBetAmount;
// 盈利金额
private Double profitAmount;
// 注单结算结果
// 2:走水3:输4:赢5:赢半6:输半7:赛事取消8:赛事延期
private Integer outcome;
// 串关类型
private Integer seriesType;
// 串关值
private String seriesValue;
// 结算次数
private Integer settleTimes;
// 设备类型1-H52-PC3-Android4-IOS
private String deviceType;
// 移动设备标识
private String deviceImei;
// 用户IP地址
private String ip;
// 币种
private String currency;
// 汇率
private BigDecimal exchangeRate;
// 最大中奖金额
private Double maxWinAmount;
// VIP等级
private Integer vipLevel;
// 投注前余额
private BigDecimal beforeTransfer;
// 投注后余额
private BigDecimal afterTransfer;
// 有效投注金额
private BigDecimal validOrderAmount;
// 注单详情列表
private List<DetailItemDTO> detailList;
}
@Data
public static class DetailItemDTO {
// 投注项编号
private Long betNo;
// 投注项ID
private Long playOptionsId;
// 赛事ID
private Long matchId;
// 比赛开始时间13位时间戳
private Long beginTime;
// 注单金额
private Double betAmount;
// 联赛名称
private String matchName;
// 比赛对阵
private String matchInfo;
// 投注类型:
// 1早盘2滚球盘3冠军盘4虚拟赛事5电竞赛事
private Integer matchType;
// 赛种ID
private Integer sportId;
// 玩法ID
private Integer playId;
// 投注项(如主客队)
private String playOptions;
// 游戏名称
private String sportName;
// 联赛ID
private Long tournamentId;
// 投注项名称
private String playOptionName;
// 玩法名称
private String playName;
// 盘口类型
private String marketType;
// 盘口值
private String marketValue;
// 让球值
private BigDecimal handicap;
// 结算比分(我方处理后所得,数据商可能未提供)
private String settleScore;
// 基准分
private String scoreBenchmark;
// 当前赔率(欧洲盘表示)
private BigDecimal oddsValue;
// 注单结算结果:
// 0:无结果2:走水3:输4:赢5:赢一半6:输一半,
// 7:赛事取消8:赛事延期11:比赛延迟12:比赛中断,
// 13:未知15:比赛放弃16:异常盘口17:未知状态,
// 18:比赛取消19:比赛延期
private String betResult;
// 最终赔率(按盘口类型)
private BigDecimal oddFinally;
}
}

View File

@ -0,0 +1,36 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class GetMemberInfoRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ,
*/
private String userName;
/**
*
*/
private String merchantCode;
/**
* Long(13)
*/
private String timestamp = System.currentTimeMillis() + "";
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,27 @@
package com.ff.game.api.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 status;
private String msg;
private String code;
private Long serverTime;
private MemberInfoDTO data;
@Data
public static class MemberInfoDTO {
private BigDecimal balance;
private String userName;
}
}

View File

@ -0,0 +1,24 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class KickUserRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String merchantCode;
private String timestamp = System.currentTimeMillis() + "";
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,20 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class KickUserResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
}

View File

@ -0,0 +1,57 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class LoginRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String merchantCode;
private String timestamp = System.currentTimeMillis() + "";
private String signature;
// 不能传错,用户可能受到设备类型相关风控措施的错误限制,进而影响正常用户货量,并产生客诉
private String terminal; // 终端类型【电脑传值pc】【移动设备传值mobile】 注这个参数传值必须是pc或者mobile
private BigDecimal balance; // 用户余额,N
private String currency; // 币种(见参数映射:支持币种 )会员首次登录,必须填写币种参数,否则注册失败
private String callbackUrl; // 玩家会话失效跳转的商户地址url(非必传)
private String stoken; // 非必传字段(商户方用户会话)
// 非必传字段(支持体育游戏其他端的跳转)
// C端⽀持跳转的游戏如果C端⽀持跳转多个游戏
// 则使⽤逗号区分。 具体字段可查看参数字段映射第6条 参数字段映射
private String jumpsupport;
private String jumpfrom; // 非必传字段,同上
private String agentId; // 信用网(代理id),N
// 用户语种:
//zh中文
//en英文
//vi越南语
//tw中文繁体
//th泰语
//ms马来语
//ad印尼语
//ko韩语
//mya:缅甸语
//pt:葡萄牙语
//es:西班牙语
//非必传字段
private String language;
private String ip; // 非必传字段 说明商户端在该字段上传入用户登陆时端ip
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + terminal + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,34 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class LoginResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private LoginRespDTO data;
@Data
public static class LoginRespDTO {
private String domain; // 体育游戏前端URL
private String token; // 带有登录状态的token
private String apiDomain;// 赛事API的域名(若没有对接赛事API为空)
private String imgDomain; // 静态资源域名
private String loginUrl; // 登录体育URL可直接跳转
private String userId; // 用户id
private String url; // 参加活动的商户使用,客户端域名和参数拼接后提供商户使用
private List<String> loginUrlArr; // loginUrl数组多个域名调试使用
}
}

View File

@ -0,0 +1,38 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferDetailRequest implements Serializable {
/**
* ,N
*/
private String userName;
/**
* code
*/
private String merchantCode;
/**
* id(19)
*/
private String transferId;
private String timestamp = System.currentTimeMillis() + "";
/**
*
*/
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + transferId + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,39 @@
package com.ff.game.api.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 TransferDetailDTO data;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
@Data
public static class TransferDetailDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String transferId; // 交易id
private String merchantCode; // 商户代码
private Long userId; // 用户id
private Integer transferType; // 交易类型 1加款2扣款
private BigDecimal amount; // 交易金额
private BigDecimal beforeTransfer; // 转账前余额
private Integer afterTransfer; // 转账后余额
private Integer status; // 转账成功与否(0:失败1:成功)
private String mag; // 转账模式 1免转2转账
private Integer transferMode; // 转账涉及订单(transferMode为2时该字段为空)
private Long createTime; // 交易时间
}
}

View File

@ -0,0 +1,28 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferRequest implements Serializable {
private String userName;
private String merchantCode;
private int transferType; // 1加款 2扣款
private String amount; // 金额小数2位
private String transferId; // 交易的讯息号唯一标示不可重复19位长度的字符串
private String timestamp = String.valueOf(System.currentTimeMillis());
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + transferType + "&" + amount + "&" + transferId + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,25 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private TransferDTO data;
@Data
public static class TransferDTO {
private String userName;
}
}

View File

@ -0,0 +1,608 @@
package com.ff.game.api.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.db.dto.*;
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.game.api.db.client.DBSportsClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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;
/**
* DB
*
* @author cengy
* @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(String errorCode) {
return "0000".equals(errorCode);
}
String getSign(String bodyJsonString, String merchantId, String merchantApiSecret, long requestTimestamp) {
String stringThatNeedsToBeSigned = bodyJsonString + "." + merchantId + "." + requestTimestamp + "." + merchantApiSecret;
String sign = Md5Utils.md5New(stringThatNeedsToBeSigned);
return sign;
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO requestDTO) {
CreateUserRequest request = new CreateUserRequest();
request.setUserName(requestDTO.getAccount());
request.setMerchantCode(requestDTO.getAgentId());
request.setCurrency(requestDTO.getCurrency());
request.buildSignature(requestDTO.getAgentKey());
//String lang = requestDTO.getLan
String requestId = IdUtils.fastUUID();
CreateUserResponse response = dbSportsClient.createMember(request, requestId);
if (isSuccess(response.getCode())) {
return Boolean.TRUE;
}
log.error("创建会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMsg());
throw new ApiException(ErrorCode.Create_Member_Failure.getCode());
}
/**
* id
*
* @param requestDTO
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO requestDTO) {
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeId());
// 转入
if (requestDTO.getTransferType().equals(TransferType.GAMES.getCode())) {
TransferRequest request = new TransferRequest();
request.setUserName(requestDTO.getAccount());
request.setTransferType(1);
request.setTransferId(requestDTO.getTransactionId());
request.setMerchantCode(requestDTO.getAgentId());
request.setAmount(requestDTO.getAmount().toString());
request.buildSignature(requestDTO.getAgentKey());
TransferResponse response = dbSportsClient.transferIn(
request, IdUtils.fastUUID()
);
if (isSuccess(response.getCode())) {
GetMemberInfoRequest queryMemberRequest = new GetMemberInfoRequest();
queryMemberRequest.setUserName(requestDTO.getAccount());
queryMemberRequest.setMerchantCode(requestDTO.getAgentId());
queryMemberRequest.buildSignature(requestDTO.getAgentKey());
String requestId = IdUtils.fastUUID();
try {
GetMemberInfoResponse queryMemberResponse = dbSportsClient.getMemberInfo(queryMemberRequest, requestId);
if (this.isSuccess(queryMemberResponse.getCode())) {
BigDecimal transAmount = requestDTO.getAmount();
BigDecimal afterAmount = queryMemberResponse.getData().getBalance();
BigDecimal beforeAmount = afterAmount.subtract(transAmount);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(beforeAmount);
exchangeMoney.setCoinAfter(afterAmount);
exchangeMoney.setCurrencyBefore(beforeAmount);
exchangeMoney.setCurrencyAfter(afterAmount);
}
} catch (Exception e) {
log.error("查询会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMsg(), e);
}
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
throw new ApiException(ErrorCode.Transfer_In_Failure.getCode());
}
} else {
// 获取第三方钱包余额
MemberInfoRequestDTO memberInfoRequestDTO = MemberInfoRequestDTO.builder()
.accounts(requestDTO.getAccount())
.agentId(requestDTO.getAgentId())
.agentKey(requestDTO.getAgentKey())
.build();
BigDecimal balance = this.getMemberInfo(memberInfoRequestDTO).getBalance();
if (balance.compareTo(BigDecimal.ZERO) <= 0) {
throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode());
}
TransferRequest request = new TransferRequest();
request.setUserName(requestDTO.getAccount());
request.setTransferType(2); // 转出
request.setTransferId(requestDTO.getTransactionId());
request.setMerchantCode(requestDTO.getAgentId());
request.setAmount(/*requestDTO.getAmount().toString()*/ balance.toString());
request.buildSignature(requestDTO.getAgentKey());
TransferResponse response = dbSportsClient
.transferOut(
request, IdUtils.fastUUID()
);
//判断是否转移成功
if (this.isSuccess(response.getCode())) {
BigDecimal transAmount = balance;
BigDecimal beforeAmount = balance;
BigDecimal afterAmount = BigDecimal.ZERO;
//更新数据
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(beforeAmount);
exchangeMoney.setCoinAfter(afterAmount);
exchangeMoney.setCurrencyBefore(beforeAmount);
exchangeMoney.setCurrencyAfter(afterAmount);
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
throw new ApiException(ErrorCode.Transfer_Out_Failure.getCode());
}
}
return exchangeMoney.getId();
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.DBSports.getInfo(), 11);
}
/**
*
*
* @param requestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO requestDTO) {
GetMemberInfoRequest request = new GetMemberInfoRequest();
request.setUserName(requestDTO.getAccounts());
request.setMerchantCode(requestDTO.getAgentId());
request.buildSignature(requestDTO.getAgentKey());
String requestId = IdUtils.fastUUID();
GetMemberInfoResponse response = dbSportsClient.getMemberInfo(request, requestId);
//判断是否获取成功
if (this.isSuccess(response.getCode())) {
return MemberInfoResponseDTO.builder()
.status(GameMemberStatus.UNKNOWN.getCode())
.balance(response.getData().getBalance())
.account(requestDTO.getAccounts())
.build();
}
throw new ApiException(ErrorCode.Get_Member_Info_Failure.getCode());
}
/**
*
*
* @param requestDTO
* @return {@link String }
*/
@Override
public String loginWithoutRedirect(GamesLogin requestDTO) {
LoginRequest request = new LoginRequest();
request.setUserName(requestDTO.getAccount());
request.setMerchantCode(requestDTO.getAgentId());
if (requestDTO.getPlatform().equalsIgnoreCase("web")) {
request.setTerminal("pc");
} else {
request.setTerminal("mobile");
}
request.setLanguage(requestDTO.getLang());
request.setCurrency(requestDTO.getCurrency());
request.buildSignature(requestDTO.getAgentKey());
LoginResponse response = dbSportsClient.login(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode())) {
LoginResponse.LoginRespDTO respDTO = response.getData();
String loginURL = respDTO.getLoginUrl();
if (StringUtils.isEmpty(loginURL)) {
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
return loginURL;
}
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
@Transactional
@Override
public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {
Platform platform = gamesBaseRequestDTO.getVendor();
Game condition = new Game();
condition.setPlatformCode(platform.getPlatformCode());
condition.setPlatformType(PlatformType.SPORTS.getCode());
List<Game> gameList = gameService.selectGameList(condition);
//不存在这个游戏
if (ObjectUtils.isEmpty(gameList)) {
Game game = new Game();
game.setId(IdUtil.getSnowflakeNextId());
game.setSortNo(1);
game.setPlatformCode(platform.getPlatformCode());
game.setPlatformType(PlatformType.SPORTS.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName(GamePlatforms.DBSports.getInfo());
game.setCreateBy(Constants.SYSTEM);
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("DB体育");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.insertGame(game);
}
/*GameName gameName = gameNameService.selectGameNameById(GAME_NAME_ID);
if (ObjectUtils.isEmpty(gameName)) {
gameNameService.insertGameName(GameName.builder()
.id(GAME_NAME_ID)
.gameId(game.getId())
.gameName(game.getGameName())
.langCode("zh-CN")
.createBy(Constants.SYSTEM)
.build());
}*/
return CacheConstants.DB_Sports;
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) {
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId());
if (null == gameExchangeMoney) {
throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode());
}
Integer status = StatusType.IN_PROGRESS.getValue();
if (!Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) {
TransferDetailRequest request = new TransferDetailRequest();
request.setMerchantCode(requestDTO.getAgentId());
request.setTransferId(gameExchangeMoney.getOrderId());
request.buildSignature(requestDTO.getAgentKey());
TransferDetailResponse response = dbSportsClient.transferDetail(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode()) && response.getData().getStatus() == 1) {
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) {
this.getRealtimeRecord(requestDTO, 1);
return Boolean.TRUE;
}
void getRealtimeRecord(BetRecordByTimeDTO requestDTO, int pageNum) {
GetBetListRequest request = new GetBetListRequest();
request.setStartTime(String.valueOf(requestDTO.getStartTime()));
request.setEndTime(String.valueOf(requestDTO.getEndTime()));
request.setMerchantCode(requestDTO.getAgentId());
request.setPageNum(pageNum);
request.setPageSize(1000);
request.buildSignature(requestDTO.getAgentKey());
GetBetListResponse response = dbSportsClient.getBetList(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode())) {
List<GetBetListResponse.OrderItemDTO> list = response.getList();
if (CollectionUtils.isEmpty(list)) {
return;
}
this.batchInsert(list, requestDTO);
getRealtimeRecord(requestDTO, ++pageNum);
}
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO requestDTO) {
return Boolean.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) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param kickMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) {
KickUserRequest request = new KickUserRequest();
request.setUserName(kickMemberRequestDTO.getAccount());
request.setMerchantCode(kickMemberRequestDTO.getAgentId());
request.buildSignature(kickMemberRequestDTO.getAgentKey());
KickUserResponse kickUserResponse = dbSportsClient.kickUser(request, IdUtils.fastUUID());
if (this.isSuccess(kickUserResponse.getCode())) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param cancelFreeSpinRequestDTO
* @return {@link Boolean }
*/
@Override
public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param gameDemoLoginRequestDTO dto
* @return {@link GameDemoLoginResponseDTO }
*/
@Override
public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param settledOrderList
*/
private void batchInsert(List<GetBetListResponse.OrderItemDTO> settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<GetBetListResponse.OrderItemDTO> dataList = settledOrderList;
if (CollectionUtils.isEmpty(dataList)) {
return;
}
//数据转化
for (GetBetListResponse.OrderItemDTO dataBean : dataList) {
if (dataBean.getOrderStatus() != 1) { // 只关心结算的
continue;
}
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.getOrderNo());
}
if (CollectionUtils.isEmpty(gameBettingDetails)) {
return;
}
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService
.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.DBSports.getCode());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
gameBettingDetailsService.batchInsert(gameBettingDetails);
}
}
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
@Override
public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
//转化类
GetBetListResponse.OrderItemDTO dataBean = (GetBetListResponse.OrderItemDTO) gamesDataBuildDTO.getData();
Member member = memberService.selectMemberByGameAccount(dataBean.getUserName());
if (ObjectUtils.isEmpty(member)) {
return null;
}
//List<Game> gameList = redisCache.getCacheList(CacheConstants.DB_Sports);
//Game game = gameList.get(0);
BigDecimal originPayoffAmount = BigDecimal.valueOf(dataBean.getSettleAmount());
BigDecimal betAmount = new BigDecimal(dataBean.getOrderAmount());
int compareResult = originPayoffAmount.compareTo(betAmount);
long payoffTime = dataBean.getSettleTime();
long createTime = dataBean.getCreateTime();
Platform platform = gamesDataBuildDTO.getPlatform();
String systemCurrency = platform.getOurCurrency(dataBean.getCurrency());
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(systemCurrency)
.memberId(member.getId())
.gameCode("1")
.gameType(PlatformType.SPORTS.getCode()) // 体育
.platformCode(GamePlatforms.DBSports.getCode())
.gameId(GamePlatforms.DBSports.getCode() + "_1")
.gameName(GamePlatforms.DBSports.getInfo())
.gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode())
.gameStatusType(1) // 一般下注
.gameCurrencyCode(dataBean.getCurrency())
.account(dataBean.getUserName())
.wagersId(dataBean.getOrderNo())
.wagersTime(createTime)
.betAmount(betAmount)
.payoffTime(payoffTime)
.payoffAmount(originPayoffAmount.abs())
.settlementTime(payoffTime)
.turnover(betAmount)
.orderNo(dataBean.getOrderNo())
.settlementStatus(SettlementStatusEnum.COMPLETED.getCode())
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -204,10 +204,28 @@ public class GamesDGServiceImpl implements IGamesService {
game.setNameInfo(Collections.singletonList(new NameInfo("真人棋牌", "zh-CN")));
game.setGameId(StringUtils.addSuffix(GamePlatforms.DG.getCode(), 1));
gameService.insertGame(game);
}else {
game.setNameInfo(Collections.singletonList(new NameInfo("真人棋牌", "zh-CN")));
gameService.updateGame(game);
}
return CacheConstants.DG_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.DG.getInfo() + IdUtils.simpleUUID();
}
/**
* id
*
@ -553,7 +571,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())

View File

@ -6,20 +6,24 @@ import com.ff.base.exception.base.ApiException;
import com.ff.base.manager.AsyncManager;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.game.api.IGamesService;
import com.ff.game.api.exchange.AbstractStepProcessor;
import com.ff.game.api.exchange.StepProcessorFactory;
import com.ff.game.api.exchange.StepProcessorService;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.api.request.ExchangeTransferStatusRequestDTO;
import com.ff.game.api.request.TransactionIdRequestDTO;
import com.ff.game.domain.GameExchangeMoney;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.member.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
/**
* impl
@ -33,11 +37,12 @@ public class CreateOrderServiceImpl extends AbstractStepProcessor {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private StepProcessorFactory stepProcessorFactory;
@Autowired
private Map<String, IGamesService> gamesService;
/**
*
*
@ -56,10 +61,10 @@ public class CreateOrderServiceImpl extends AbstractStepProcessor {
* @return boolean
*/
@Override
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
gameExchangeMoney.setTransactionId(this.getTransactionId(GamePlatforms.getByCode(gameExchangeMoney.getPlatformCode()),gameExchangeMoney.getExchangeType(),gameExchangeMoney.getGameAccount()));
String transactionId = gamesService.get(gameExchangeMoney.getPlatformCode()+Constants.SERVICE).getTransactionId(TransactionIdRequestDTO.builder().exchangeType(gameExchangeMoney.getExchangeType()).gameAccount(gameExchangeMoney.getGameAccount()).build());
gameExchangeMoney.setTransactionId(transactionId);
gameExchangeMoney.setCreateBy(Constants.SYSTEM);
gameExchangeMoney.setStatus(StatusType.IN_PROGRESS.getValue());
gameExchangeMoney.setStep(GameExchangeStep.CREATE_ORDER.getCode());
@ -67,45 +72,6 @@ public class CreateOrderServiceImpl extends AbstractStepProcessor {
return gameExchangeMoneyService.insertGameExchangeMoney(gameExchangeMoney) > 0;
}
/**
* id
*
* @return {@link String }
*/
private String getTransactionId(GamePlatforms gamePlatforms,Integer exchangeType ,String account) {
switch (gamePlatforms) {
case AE:
return GamePlatforms.AE.getCode() + IdUtils.simpleUUID();
case JILI:
return GamePlatforms.JILI.getInfo() + IdUtils.simpleUUID();
case XK:
return GamePlatforms.XK.getCode() + IdUtils.simpleUUID();
case PG:
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PG.getCode(), 32);
case PGX:
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PGX.getInfo(), 17);
case FC:
return gameExchangeMoneyService.getTransactionId(GamePlatforms.FC.getInfo(), 30);
case DG:
return GamePlatforms.DG.getInfo() + IdUtils.simpleUUID();
case MT:
return GamePlatforms.MT.getCode() + IdUtils.simpleUUID();
case SA:
//判断是转入还是转出
String transactionId = "OUT" + DateUtils.dateTimeNow() + account;
if (!TransferType.ALL.getCode().equals(exchangeType)) {
transactionId = "IN" + DateUtils.dateTimeNow() + account;
}
return transactionId;
case KM:
return GamePlatforms.KM.getInfo() + IdUtils.simpleUUID();
case PGT:
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PGT.getInfo(), 17);
}
throw new ApiException(ErrorCode.PLATFORM_NOT_EXIST.getCode());
}
/**

View File

@ -0,0 +1,30 @@
package com.ff.game.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.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* <a href="https://doc.newsportspro.com/apidoc_data.html#%E5%85%A5%E5%8F%82"></a>
*
* @author cengy
*/
@Component
public class FBSportsAddress implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.FBSports.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("https", apiBaseUrl, 443, "fb/data");
}
}

View File

@ -0,0 +1,102 @@
package com.ff.game.api.fb.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.fb.address.FBSportsAddress;
import com.ff.game.api.fb.dto.*;
/**
* <a href="https://doc.newsportspro.com/apidoc_data.html"></a><br/>
* <a href="https://doc.newsportspro.com/h5_pc_doc.html"></a>
*
* @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);
/**
* FB0.01
*
* @param request
* @param sign
* @param timestamp
* @param merchantId
* @return {@link TransferInResponse}
*/
@Post(url = "/api/v2/new/transfer/in")
TransferInResponse transferIn(@JSONBody TransferInRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/new/transfer/out")
TransferOutResponse transferOut(@JSONBody TransferOutRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/new/user/detail")
GetMemberInfoResponse getMemberInfo(@JSONBody GetMemberInfoRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* /
*/
@Post(url = "/api/v2/transfer/detail")
TransferDetailResponse transferDetail(@JSONBody TransferDetailRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
@Post(url = "/api/v2/service/domain/list")
GetUrlResponse getUrl(@JSONBody GetUrlRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* FBFB5
* IDID
*/
@Post(url = "/api/v2/order/file/ids")
OrderFilesResponse orderFiles(@JSONBody OrderFilesRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* Json
*/
@Post(url = "/api/v2/order/list")
OrderInfoResponse getOrderJsonData(@JSONBody OrderInfoRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
/**
* apptokenurltokenapp
*
* @param request
* @param sign
* @param timestamp
* @param merchantId
* @return
*/
@Post(url = "/api/v2/token/get")
GetTokenResponse getToken(@JSONBody GetTokenRequest request,
@Header("sign") @Var("sign") String sign,
@Header("timestamp") @Var("timestamp") long timestamp,
@Header("merchantId") @Var("merchantId") String merchantId);
}

View File

@ -0,0 +1,30 @@
package com.ff.game.api.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<Integer> currencyIds = null; // 币种id集合 , see enum: currency
private Integer oddsLevel = null; // 赔率级别,不传则为默认, see enum: user_odds_level_enum
public String toJSON() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("currencyIds", currencyIds);
map.put("merchantUserId", merchantUserId);
map.put("oddsLevel", oddsLevel);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,19 @@
package com.ff.game.api.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;
}

View File

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

View File

@ -0,0 +1,27 @@
package com.ff.game.api.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<String, Object> map = new LinkedHashMap<>();
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,38 @@
package com.ff.game.api.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<Wallet> wallets; // 钱包集合
private Integer oddsLevel;// 赔率级别 , see enum: user_odds_level_enum
}
@Data
public static class Wallet implements Serializable{
private static final long serialVersionUID = 1L;
private Integer currencyType; // 币种类型 , see enum: currency_type
private BigDecimal balance; // 余额
private Integer currencyId; // 币种id , see enum: currency
}
}

View File

@ -0,0 +1,30 @@
package com.ff.game.api.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;
// 平台类型pch5, mobile , see enum: plat_form_enum
private String platForm;
// 客户端用户ip地址尽可能提供我们用于风控
private String ip; // 可选
public String toJSON() {
Map<String, String> map = new LinkedHashMap<>();
map.put("merchantUserId", merchantUserId);
map.put("platForm", platForm);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,49 @@
package com.ff.game.api.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<Domain> domains; // 全部服务器地址信息
private String themeBgColor; // 主题背景色
private String themeFgColor; // 主题前景色
private Integer userId; // FB用户ID
}
@Data
public static class ServerInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String apiServerAddress; // app接口服务地址
private String apiEmbeddedServerAddress; // app内嵌网页地址
private String pushServerAddress; // 推送服务地址
private String pcAddress; // PC投注网站地址
private String h5Address; // h5投注网站地址
private String virtualAddress; // 虚拟体育投注网站地址
private String virtualMatchVideoAddress; // 虚拟赛事视频地址
private String ouH5Address; // 欧版h5地址
private String ouPcAddress; // 欧版pc地址
}
@Data
public static class Domain {
private Integer type; // 域名类型
private List<String> domains; // 域名集合
}
}

View File

@ -0,0 +1,17 @@
package com.ff.game.api.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 "{}";
}
}

View File

@ -0,0 +1,33 @@
package com.ff.game.api.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<UrlDTO> data;
private Integer code;
@Data
public static class UrlDTO implements Serializable {
private static final long serialVersionUID = 1L;
private int type; //域名类型1API2PUSH3H54PC5IMAGE , see enum: domain_type_enum
private List<DomainDTO> domainList; // 域名集合
}
@Data
public static class DomainDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String domain; // 域名
private int weight; // 权限值
}
}

View File

@ -0,0 +1,41 @@
package com.ff.game.api.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<String, Object> map = new LinkedHashMap<>();
map.put("endTime", endTime);
map.put("startTime", startTime);
return JSON.toJSONString(map);
// String endTimeStr = endTime ; // 转换为字符串
// String startTimeStr = startTime; // 转换为字符串
//
// String json = "{" +
// "\"endTime\": \"" + endTimeStr + "\", " +
// "\"startTime\": \"" + startTimeStr + "\"" +
// "}";
// return json;
}
}

View File

@ -0,0 +1,27 @@
package com.ff.game.api.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<FileId> data;
@Data
public static class FileId implements Serializable {
private static final long serialVersionUID = 1L;
private Long fileId;
}
}

View File

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

View File

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

View File

@ -0,0 +1,39 @@
package com.ff.game.api.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<String, Object> map = new LinkedHashMap<>();
map.put("businessId", businessId);
map.put("merchantUserId", merchantUserId);
map.put("transferType", transferType);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,39 @@
package com.ff.game.api.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
}
}

View File

@ -0,0 +1,46 @@
package com.ff.game.api.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<String, Object> map = new LinkedHashMap<>();
map.put("amount", amount);
map.put("businessId", businessId);
map.put("currencyId", currencyId);
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,20 @@
package com.ff.game.api.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;
}

View File

@ -0,0 +1,46 @@
package com.ff.game.api.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<String, Object> map = new LinkedHashMap<>();
map.put("amount", amount);
map.put("businessId", businessId);
map.put("currencyId", currencyId);
map.put("merchantUserId", merchantUserId);
return JSON.toJSONString(map);
}
}

View File

@ -0,0 +1,20 @@
package com.ff.game.api.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;
}

View File

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

View File

@ -37,7 +37,7 @@ public class ApiFCGameListResponseDTO {
/**
* id
*/
private Long systemGameId;
private String systemGameId;
/**
* id
*/

View File

@ -283,9 +283,14 @@ public class GamesFCServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(NameInfo.builder().lang("zh-CN").name(gameDetails.getGameNameOfChinese()).build());
nameInfos.add(NameInfo.builder().lang("en-US").name(gameDetails.getGameNameOfEnglish()).build());
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gameDetails.setSystemGameId(game.getId());
gameDetails.setSystemGameId(game.getGameId());
gameDetails.setGameId(gameIdKey);
gameDetailsList.add(gameDetails);
}
@ -366,6 +371,19 @@ public class GamesFCServiceImpl implements IGamesService {
return exchangeMoney.getId();
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.FC.getInfo(), 30);
}
/**
*
*

View File

@ -22,7 +22,7 @@ public class JILIGamesDataDTO {
/**
*id
*/
private Long systemGameId;
private String systemGameId;
/**
*

View File

@ -252,8 +252,13 @@ public class GamesJILIServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(gamesDataDTO.getName().getZhCN(), "zh-CN"));
nameInfos.add(new NameInfo(gamesDataDTO.getName().getEnUS(), "en-US"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gamesDataDTO.setSystemGameId(game.getId());
gamesDataDTO.setSystemGameId(game.getGameId());
}
@ -269,6 +274,17 @@ public class GamesJILIServiceImpl implements IGamesService {
return CacheConstants.JILI_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.JILI.getInfo() + IdUtils.simpleUUID();
}
/**
* id
*
@ -788,7 +804,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())

View File

@ -91,7 +91,7 @@ public class KMGameResponse {
/**
* id
*/
private Long systemGameId;
private String systemGameId;
/**
*

View File

@ -301,12 +301,30 @@ public class GamesKMServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(gamesDataDTO.getName(), "zh-CN"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gamesDataDTO.setSystemGameId(game.getId());
gamesDataDTO.setSystemGameId(game.getGameId());
}
return gameList.getGames();
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.KM.getInfo() + IdUtils.simpleUUID();
}
/**
* id
*

View File

@ -22,7 +22,7 @@ public class MeiTianGameDataDTO {
/**
*id
*/
private Long systemGameId;
private String systemGameId;
private String gameId;
private String cnName;
private String enName;

View File

@ -10,14 +10,14 @@ import com.ff.base.core.redis.RedisCache;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.system.domain.SysConfig;
import com.ff.base.system.service.impl.SysConfigServiceImpl;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.delay.DelayService;
import com.ff.delay.DelayTask;
import com.ff.game.api.IGamesService;
import com.ff.game.api.km.dto.KMBalanceTransferStatusResponseDTO;
import com.ff.game.api.meitian.client.MeiTianClient;
import com.ff.game.api.meitian.dto.*;
import com.ff.game.api.request.*;
@ -80,6 +80,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
@Autowired
private SysConfigServiceImpl sysConfigServiceImpl;
@Autowired
private DelayService delayService;
/**
*
*
@ -244,9 +247,13 @@ public class MeiTianGameServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(gamesDataDTO.getCnName(), "zh-CN"));
nameInfos.add(new NameInfo(gamesDataDTO.getEnName(), "en-US"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gamesDataDTO.setSystemGameId(game.getId());
gamesDataDTO.setSystemGameId(game.getGameId());
}
redisCache.deleteObject(CacheConstants.MeiTian_GAMES);
@ -256,6 +263,17 @@ public class MeiTianGameServiceImpl implements IGamesService {
return CacheConstants.MeiTian_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.MT.getCode() + IdUtils.simpleUUID();
}
/**
* id
*
@ -276,7 +294,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
String merchantId = exchangeTransferMoneyRequestDTO.getAgentId();
String playerName = exchangeTransferMoneyRequestDTO.getAccount();
String coins = exchangeTransferMoneyRequestDTO.getAmount().setScale(4, RoundingMode.DOWN).toString();
if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())){
if (exchangeTransferMoneyRequestDTO.getTransferType().equals(TransferType.ALL.getCode())) {
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(exchangeTransferMoneyRequestDTO.getAgentId())
@ -316,7 +334,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
//判断是否转移成功
if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) {
//更新数据
BigDecimal transAmount =new BigDecimal(coins);
BigDecimal transAmount = new BigDecimal(coins);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().subtract(transAmount));
@ -349,7 +367,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
//判断是否转移成功
if (this.isSuccess(exchangeMoneyResponse.getErrorCode())) {
//更新数据
BigDecimal transAmount =new BigDecimal(coins);
BigDecimal transAmount = new BigDecimal(coins);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(exchangeMoneyResponse.getBalance().add(transAmount));
@ -389,7 +407,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
exchangeTransferMoneyRequestDTO.getOrderId()
);
Integer status = StatusType.IN_PROGRESS.getValue();
if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode())&& "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) {
if (this.isSuccess(meiTianBalanceTransferStatusResponseDTO.getResultCode()) && "1".equals(meiTianBalanceTransferStatusResponseDTO.getStatus())) {
status = StatusType.SUCCESS.getValue();
} else {
status = StatusType.FAILURE.getValue();
@ -403,6 +421,34 @@ public class MeiTianGameServiceImpl implements IGamesService {
}
class GetRecordByTimeTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRecordByTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
doSyncRecordByRecordID(betRecordByTimeDTO);
}
}
class GetRecordByHistoryTimeTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRecordByHistoryTimeTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
doSyncRecordByDate(betRecordByTimeDTO, 1);
}
}
/**
*
*
@ -411,18 +457,21 @@ public class MeiTianGameServiceImpl implements IGamesService {
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
return doSyncRecordByRecordID(betRecordByTimeDTO);
delayService.addTask(new GetRecordByTimeTask(betRecordByTimeDTO));
return Boolean.TRUE;
//return doSyncRecordByRecordID(betRecordByTimeDTO);
}
boolean doSyncRecordByRecordID(BetRecordByTimeDTO betRecordByTimeDTO) {
String configKey = GamePlatforms.MT.getCode() + ":lastRecordID";
String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey);
long recordID = 0;
if (lastRecordID == null || lastRecordID.isEmpty()) {
String configKey = GamePlatforms.MT.getCode() + ":lastSyncRecordID";
long recordID = redisCache.getCacheObject(configKey);
//String lastRecordID = sysConfigServiceImpl.selectConfigByKey(configKey);
/*if (lastRecordID == null || lastRecordID.isEmpty()) {
} else {
recordID = Long.parseLong(lastRecordID);
}
}*/
String merchantId = betRecordByTimeDTO.getAgentId();
Map<String, Object> rawMap = new LinkedHashMap<>();
rawMap.put("recordID", recordID);
@ -448,7 +497,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
//数据插入
this.batchInsert(recordResponse, betRecordByTimeDTO);
MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1);
SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID()));
/*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
if (config == null) {
config = new SysConfig();
config.setConfigKey(configKey);
@ -457,7 +508,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
} else {
config.setConfigValue(dataBean.getRecordID());
sysConfigServiceImpl.updateConfig(config);
}
}*/
// 它每次返回25000条所以需要判断如果大于25000条则继续拉取
if (dataList.size() >= 25000) {
doSyncRecordByRecordID(betRecordByTimeDTO);
@ -473,22 +524,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
String date = getDateStr(daysToSubtract);
String configKey = GamePlatforms.MT.getCode() + ":lastSyncDate";
String syncDateStr = sysConfigServiceImpl.selectConfigByKey(configKey);
Map<String, Long> syncDateMap = new HashMap<>();
long recordID = 0;
if (syncDateStr == null || syncDateStr.isEmpty()) {
} else {
syncDateMap = JSON.parseObject(syncDateStr, Map.class);
}
if (syncDateMap.containsKey(date)) {
recordID = syncDateMap.get(date);
if (syncDateMap.size() > 10) {
syncDateMap.clear();
syncDateMap.put(date, recordID);
}
} else {
syncDateMap.put(date, recordID);
}
long recordID = redisCache.getCacheObject(configKey);
String merchantId = betRecordByTimeDTO.getAgentId();
Map<String, Object> rawMap = new LinkedHashMap<>();
rawMap.put("rowID", recordID);
@ -516,8 +552,9 @@ public class MeiTianGameServiceImpl implements IGamesService {
//数据插入
this.batchInsert(recordResponse, betRecordByTimeDTO);
MeiTianBetRecordResponseDTO.DataBean dataBean = dataList.get(dataList.size() - 1);
syncDateMap.put(date, Long.parseLong(dataBean.getRowID()));
SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
//syncDateMap.put(date, Long.parseLong(dataBean.getRowID()));
redisCache.setCacheObject(configKey, Long.parseLong(dataBean.getRowID()));
/*SysConfig config = sysConfigServiceImpl.getByConfigKey(configKey);
if (null == config) {
config = new SysConfig();
config.setConfigKey(configKey);
@ -526,7 +563,7 @@ public class MeiTianGameServiceImpl implements IGamesService {
} else {
config.setConfigValue(JSON.toJSONString(syncDateMap));
sysConfigServiceImpl.updateConfig(config);
}
}*/
// 它每次返回25000条所以需要判断如果大于25000条则继续拉取
if (dataList.size() >= 25000) {
@ -547,9 +584,10 @@ public class MeiTianGameServiceImpl implements IGamesService {
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
doSyncRecordByDate(betRecordByTimeDTO, 0);
doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday
//doSyncRecordByDate(betRecordByTimeDTO, 0);
//doSyncRecordByDate(betRecordByTimeDTO, 1); // yesterday
delayService.addTask(new GetRecordByHistoryTimeTask(betRecordByTimeDTO));
return true;
}
@ -779,10 +817,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);

View File

@ -13,6 +13,7 @@ import com.ff.base.utils.DateUtils;
import com.ff.base.utils.SleepUtil;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.ng.client.NGClient;
@ -258,6 +259,12 @@ public class GamesPGServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(apiGameInfoResponseDTO.getGameName().get("zh-hans"), "zh-CN"));
nameInfos.add(new NameInfo(apiGameInfoResponseDTO.getGameName().get("zh-hant"), "zh-TW"));
nameInfos.add(new NameInfo(apiGameInfoResponseDTO.getGameName().get("en"), "en-US"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
apiGameInfoResponseDTO.setSystemGameId(game.getId());
@ -273,6 +280,19 @@ public class GamesPGServiceImpl implements IGamesService {
return CacheConstants.PG_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PG.getCode(), 32);
}
/**
* id
*
@ -298,10 +318,14 @@ public class GamesPGServiceImpl implements IGamesService {
.agentKey(exchangeTransferMoneyRequestDTO.getAgentKey())
.currency(exchangeTransferMoneyRequestDTO.getCurrency())
.build();
MemberInfoResponseDTO memberInfo = this.getMemberInfo(gamesBaseRequestDTO);
//判断是不是转出
if (NGTransferType.TRANSFER_OUT.getValue().equals(type)) {
MemberInfoResponseDTO memberInfo = this.getMemberInfo(gamesBaseRequestDTO);
exchangeTransferMoneyRequestDTO.setAmount(memberInfo.getBalance());
if (exchangeTransferMoneyRequestDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode());
}
}
@ -322,6 +346,20 @@ public class GamesPGServiceImpl implements IGamesService {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
ExchangeTransferStatusRequestDTO exchangeTransferStatusRequestDTO = new ExchangeTransferStatusRequestDTO();
exchangeTransferStatusRequestDTO.setAccount(exchangeTransferMoneyRequestDTO.getAccount());
exchangeTransferStatusRequestDTO.setCurrency(exchangeTransferMoneyRequestDTO.getCurrency());
exchangeTransferStatusRequestDTO.setOrderId(exchangeTransferMoneyRequestDTO.getTransactionId());
exchangeTransferStatusRequestDTO.setAgentId(exchangeTransferMoneyRequestDTO.getAgentId());
exchangeTransferStatusRequestDTO.setAgentKey(exchangeTransferMoneyRequestDTO.getAgentKey());
ExchangeTransferStatusResponseDTO statusResponseDTO = this.exchangeTransferStatus(exchangeTransferStatusRequestDTO);
//更新钱
exchangeMoney.setBalance(statusResponseDTO.getBalance());
exchangeMoney.setCoinBefore(statusResponseDTO.getCoinBefore());
exchangeMoney.setCoinAfter(statusResponseDTO.getCoinAfter());
exchangeMoney.setCurrencyBefore(exchangeMoney.getCoinBefore());
exchangeMoney.setCurrencyAfter(exchangeMoney.getCoinAfter());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
@ -341,6 +379,7 @@ public class GamesPGServiceImpl implements IGamesService {
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) {
SleepUtil.sleep(1000);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("playerId", exchangeTransferMoneyRequestDTO.getAccount());
@ -359,7 +398,7 @@ public class GamesPGServiceImpl implements IGamesService {
.build();
ApiExchangeTransferStatusResponseDTO apiNGResponseDTOData = apiNGResponseDTO.getData();
if (!ObjectUtils.isEmpty(apiNGResponseDTOData)) {
transferStatusResponseDTO.setBalance(apiNGResponseDTOData.getAmount());
transferStatusResponseDTO.setBalance(apiNGResponseDTOData.getAmount().abs());
transferStatusResponseDTO.setCoinBefore(NumberUtil.sub(apiNGResponseDTOData.getAmount(), apiNGResponseDTOData.getAfterBalance()).abs());
transferStatusResponseDTO.setCoinAfter(apiNGResponseDTOData.getAfterBalance());
}
@ -685,7 +724,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)

View File

@ -83,7 +83,7 @@ public class PGTGameListResponse {
/**
* id
*/
private Long systemGameId;
private String systemGameId;
/**
*

View File

@ -15,6 +15,7 @@ import com.ff.base.utils.DateUtils;
import com.ff.base.utils.JsonUtil;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Base64;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.fc.dto.ApiFCGameListResponseDTO;
@ -243,8 +244,12 @@ public class GamesPGTServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(NameInfo.builder().lang("en-US").name(gameIdKey.getName()).build());
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gameIdKey.setSystemGameId(game.getId());
gameIdKey.setSystemGameId(game.getGameId());
}
@ -256,7 +261,16 @@ public class GamesPGTServiceImpl implements IGamesService {
}
return CacheConstants.PGT_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PGT.getInfo(), 17);
}
/**
* id
*
@ -619,6 +633,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())

View File

@ -79,13 +79,13 @@ public class PGXBetHistoryResponse {
* () GMT/UTC +0
*/
@JsonProperty("start_time")
private Date startTime;
private String startTime;
/**
* (String) GMT/UTC +0
*/
@JsonProperty("end_time")
private Date endTime;
private String endTime;
/**
* (String) GMT/UTC +0

View File

@ -48,7 +48,7 @@ public class PGXGameListResponse {
/**
* id
*/
private Long systemGameId;
private String systemGameId;
/** 游戏代码 (字符串类型) */
@JsonProperty("gameCode")

View File

@ -251,8 +251,12 @@ public class GamesPGXServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(gamesDataDTO.getGameName(), "en-US"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gamesDataDTO.setSystemGameId(game.getId());
gamesDataDTO.setSystemGameId(game.getGameId());
}
@ -430,6 +434,21 @@ public class GamesPGXServiceImpl implements IGamesService {
}
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.PGX.getInfo(), 17);
}
/**
*
*
@ -585,6 +604,7 @@ public class GamesPGXServiceImpl implements IGamesService {
payoffAmount = NumberUtil.sub(payout, resultBean.getBet()).negate();
gameStatus = GameStatus.FAIL.getCode();
}
long endTime = DateUtils.convertToMillisWithTimezone(resultBean.getEndTime(), "UTC");
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
@ -602,11 +622,11 @@ public class GamesPGXServiceImpl implements IGamesService {
.gameCurrencyCode(/*currencyDTO.getCurrency()*/gamesDataBuildDTO.getCurrencyCode())
.account(resultBean.getMember())
.wagersId(String.valueOf(resultBean.getId()))
.wagersTime(resultBean.getStartTime().getTime())
.wagersTime(DateUtils.convertToMillisWithTimezone(resultBean.getStartTime(),"UTC"))
.betAmount(resultBean.getBet())
.payoffTime(resultBean.getEndTime().getTime())
.payoffTime(endTime)
.payoffAmount(payoffAmount)
.settlementTime(resultBean.getEndTime().getTime())
.settlementTime(endTime)
.turnover(resultBean.getTurnover())
.settlementStatus(PGXBetRecordStatus.findSystemCodeByCode(resultBean.getStatus()))
.build();

View File

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

View File

@ -0,0 +1,25 @@
package com.ff.game.api.request;
import com.ff.base.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* iddto
*
* @author shi
* @date 2025/04/11
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TransactionIdRequestDTO {
/** 转出类型 1游戏商转入到用户全部转出 2 用户转移到游戏商 3 游戏商转移额度到平台商 */
private Integer exchangeType;
/** 游戏账号 */
private String gameAccount;
}

View File

@ -10,6 +10,7 @@ import com.ff.base.exception.base.ApiException;
import com.ff.base.system.service.ISysConfigService;
import com.ff.base.utils.*;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.*;
@ -240,11 +241,34 @@ public class GamesSAServiceImpl implements IGamesService {
game.setGameId(StringUtils.addSuffix(GamePlatforms.SA.getCode(), 1));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}else {
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo("真人棋牌", "zh-CN"));;
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
return CacheConstants.SA_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
//判断是转入还是转出
String transactionId = "OUT" + DateUtils.dateTimeNow() + transactionIdRequestDTO.getGameAccount();
if (!TransferType.ALL.getCode().equals(transactionIdRequestDTO.getExchangeType())) {
transactionId = "IN" + DateUtils.dateTimeNow() + transactionIdRequestDTO.getGameAccount();
}
return transactionId;
}
/**
* id
*
@ -582,7 +606,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)

View File

@ -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, "");
}
}

View File

@ -0,0 +1,136 @@
package com.ff.game.api.sv388.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.jili.dto.JILIExchangeMoneyResponseDTO;
import com.ff.game.api.jili.dto.JILIKickMemberAllDTO;
import com.ff.game.api.jili.dto.JILIKickMemberDTO;
import com.ff.game.api.sv388.address.SV388Adrress;
import com.ff.game.api.sv388.dto.*;
import com.ff.game.api.xk.dto.XKKickMemberAllDTO;
import com.ff.game.api.xk.dto.XKKickMemberDTO;
import java.util.Map;
/**
* xk
*
* @author shi
* @date 2025/02/10
*/
@Address(source = SV388Adrress.class)
public interface SV388Client {
/**
*
*
* @param params
* @return {@link String }
*/
@Post(url ="/wallet/createMember",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388Response createMember(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link SV388MemberInfo }
*/
@Post(url ="/wallet/getBalance",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388MemberInfo getMemberInfo(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link SV388LoginResponse }
*/
@Post("/wallet/login")
SV388LoginResponse loginWithoutRedirect(@Body Map<String, Object> params);
/**
* id
*
* @param params
* @return {@link JILIExchangeMoneyResponseDTO }
*/
@Post(url ="/wallet/deposit",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388AETransactionResponse deposit(@Body Map<String, Object> params);
@Post(url ="/wallet/withdraw",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388AETransactionResponse withdraw(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link SV388ExchangeTransferStatusResponse }
*/
@Post(url ="/wallet/checkTransferOperation",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388ExchangeTransferStatusResponse exchangeTransferStatus(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link SV388BetRecordResponse }
*/
@Post(url ="{fetchUrl}/fetch/gzip/getTransactionByUpdateDate",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388BetRecordResponse getBetRecordByTime(@Body Map<String, Object> params,
@Var("fetchUrl") String fetchUrl);
/**
*
*
* @param params
* @return {@link SV388BetRecordResponse }
*/
@Post(url ="{fetchUrl}/fetch/gzip/getTransactionByTxTime",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
SV388BetRecordResponse getBetHistoryRecordByTime(@Body Map<String, Object> params,
@Var("fetchUrl") String fetchUrl);
/**
*
*
* @param params
* @return {@link JILIKickMemberDTO }
*/
@Post(url ="/wallet/logout",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
XKKickMemberDTO kickMember(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link JILIKickMemberAllDTO }
*/
@Get("/kickMemberAll")
XKKickMemberAllDTO kickMemberAll(@JSONBody Map<String, Object> params);
}

View File

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

View File

@ -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<Transaction> 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;
}
}

View File

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

View File

@ -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<String> extension;
}

View File

@ -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<String> logoutUsers;
/**
*
*/
@JsonProperty("count")
private Integer count;
}

View File

@ -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<UserInfo> 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;
}
}

View File

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

View File

@ -0,0 +1,636 @@
package com.ff.game.api.sv388.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.ff.base.constant.CacheConstants;
import com.ff.base.constant.Constants;
import com.ff.base.core.redis.RedisCache;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.JsonUtil;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.delay.DelayService;
import com.ff.delay.DelayTask;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.*;
import com.ff.game.api.sv388.client.SV388Client;
import com.ff.game.api.sv388.dto.*;
import com.ff.game.api.xk.dto.XKKickMemberDTO;
import com.ff.game.domain.*;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* AE impl
*
* @author shi
* @date 2024/11/12
*/
@Service("SV388Service")
@Slf4j
public class SV388GamesServiceImpl implements IGamesService {
@Resource
private RedisCache redisCache;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameService gameService;
@Resource
private IMemberService memberService;
@Resource
private SV388Client sv388Client;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
@Resource
private DelayService delayService;
/**
* id
*/
private static final Long GAME_ID = 1122L;
/**
*
*
* @param errorCode
* @return {@link Boolean }
*/
private Boolean getIsSuccess(Integer errorCode) {
return 0 == errorCode;
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link Map }<{@link String }, {@link Object }>
*/
private Map<String, Object> getKey(GamesBaseRequestDTO gamesBaseRequestDTO) {
Map<String, Object> params = new LinkedHashMap<>();
params.put("cert", gamesBaseRequestDTO.getAgentKey());
params.put("agentId", gamesBaseRequestDTO.getAgentId());
return params;
}
/**
*
*
* @param createMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
Platform platform = createMemberRequestDTO.getVendor();
Object o = platform.getExtInfo().getBetLimit().get(createMemberRequestDTO.getSystemCurrency());
if (!(o instanceof Map)) {
throw new ApiException(ErrorCode.Miss_Config.getCode());
}
Map<String, Object> betLimit = new HashMap<>();
betLimit.put(GamePlatforms.SV388.getCode(), new HashMap<String, Object>() {{
put("LIVE", new HashMap<String, Object>((Map<String, ?>) o) {{
remove("@type");
}});
}}
);
Map<String, Object> params = this.getKey(createMemberRequestDTO);
params.put("userId", createMemberRequestDTO.getAccount());
params.put("currency", createMemberRequestDTO.getCurrency());
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<String, Object> 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<String, Object> 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<Game> gameList = gameService.selectGameList(condition);
Platform platform = gamesBaseRequestDTO.getVendor();
//不存在这个游戏
if (CollectionUtils.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);
}else {
for (Game game : gameList) {
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("SV388真人");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.updateGame(game);
}
}
return CacheConstants.SV388_GAMES;
}
/**
* id
*
* @param exchangeTransferMoneyRequestDTO moeny dto
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) {
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeId());
Map<String, Object> params = this.getKey(exchangeTransferMoneyRequestDTO);
SV388AETransactionResponse deposit = null;
try {
if (TransferType.GAMES.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) {
params.put("userId", exchangeTransferMoneyRequestDTO.getAccount());
params.put("txCode", exchangeTransferMoneyRequestDTO.getTransactionId());
params.put("transferAmount", exchangeTransferMoneyRequestDTO.getAmount());
deposit = sv388Client.deposit(params);
} else {
params.put("userId", exchangeTransferMoneyRequestDTO.getAccount());
params.put("txCode", exchangeTransferMoneyRequestDTO.getTransactionId());
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();
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.SV388.getCode() + IdUtils.simpleUUID();
}
/**
*
*
* @param exchangeTransferMoneyRequestDTO dto
* @return {@link ExchangeTransferStatusResponseDTO }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) {
Map<String, Object> paramsMap = this.getKey(exchangeTransferMoneyRequestDTO);
paramsMap.put("txCode", exchangeTransferMoneyRequestDTO.getOrderId());
SV388ExchangeTransferStatusResponse exchangeTransferStatusResponse = sv388Client.exchangeTransferStatus(paramsMap);
Integer status = StatusType.IN_PROGRESS.getValue();
if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "1".equals(exchangeTransferStatusResponse.getTxStatus())) {
status = StatusType.SUCCESS.getValue();
} else if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "0".equals(exchangeTransferStatusResponse.getTxStatus())) {
status = StatusType.FAILURE.getValue();
} else if ("1017".equals(exchangeTransferStatusResponse.getStatus())) {
status = StatusType.FAILURE.getValue();
}
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeMoneyId());
//更新
BigDecimal coinBefore;
if (TransferType.GAMES.getCode().equals(exchangeMoney.getExchangeType())) {
coinBefore = NumberUtil.sub(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount());
} else {
coinBefore = NumberUtil.add(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount());
}
return ExchangeTransferStatusResponseDTO.builder()
.statusType(status)
.balance(exchangeTransferStatusResponse.getTransferAmount())
.coinBefore(coinBefore)
.coinAfter(exchangeTransferStatusResponse.getBalance())
.build();
}
class GetRealtimeRecordTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRealtimeRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
getRealtimeRecord(betRecordByTimeDTO);
}
}
void getRealtimeRecord(BetRecordByTimeDTO betRecordByTimeDTO) {
//请求参数
Map<String, Object> params = this.getKey(betRecordByTimeDTO);
String timeFrom = redisCache.getCacheObject(CacheConstants.SV388_TIME_FROM);
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
}
params.put("timeFrom", timeFrom);
params.put("platform", GamePlatforms.SV388.getCode());
params.put("delayTime", 10000);
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl());
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
//数据组装
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
if (aeBetRecordResponse.getTransactions().size() >= 20000) {
delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO));
}
} else {
redisCache.deleteObject(CacheConstants.SV388_TIME_FROM);
log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
}
}
void getHistoryRecord(BetRecordByTimeDTO betRecordByTimeDTO) {
Map<String, Object> params = this.getKey(betRecordByTimeDTO);
String startTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
String endTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
params.put("startTime", startTime);
params.put("endTime", endTime);
params.put("platform", /*"SEXYBCRT"*/ GamePlatforms.SV388.getCode());
SV388BetRecordResponse aeBetRecordResponse = sv388Client.getBetHistoryRecordByTime(params, betRecordByTimeDTO.getVendor().getUrlInfo().getBetUrl());
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
//数据组装
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
if (aeBetRecordResponse.getTransactions().size() >= 20000) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
}
} else {
log.error("获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
}
}
class GetHistoryRecordTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetHistoryRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
getHistoryRecord(betRecordByTimeDTO);
}
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link List }<{@link GameBettingDetails }>
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
* @param createFreeSpinRequest
* @return {@link Boolean }
*/
@Override
public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param getGameDetailRequestDTO dto
* @return {@link GetGameDetailResponseDTO }
*/
@Override
public GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param kickMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) {
Map<String, Object> 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 dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param cancelFreeSpinRequestDTO
* @return {@link Boolean }
*/
@Override
public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param gameDemoLoginRequestDTO dto
* @return {@link GameDemoLoginResponseDTO }
*/
@Override
public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param aeBetRecordResponse aedto
*/
private void batchInsert(SV388BetRecordResponse aeBetRecordResponse, BetRecordByTimeDTO betRecordByTimeDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<SV388BetRecordResponse.Transaction> dataBean = aeBetRecordResponse.getTransactions();
if (CollectionUtils.isEmpty(dataBean)) {
return;
}
String timeFrom = null;
//数据转化
for (SV388BetRecordResponse.Transaction bean : dataBean) {
GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder()
.platform(betRecordByTimeDTO.getVendor())
.data(bean).build());
if (!ObjectUtils.isEmpty(bettingDetails)) {
bettingDetails.setId(IdUtil.getSnowflakeNextId());
gameBettingDetails.add(bettingDetails);
}
wagersIds.add(bean.getPlatform() + bean.getPlatformTxId());
timeFrom = bean.getUpdateTime();
}
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.SV388.getCode());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
gameBettingDetailsService.batchInsert(gameBettingDetails);
}
}
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00";
}
redisCache.setCacheObject(CacheConstants.SV388_TIME_FROM, timeFrom);
}
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
@Override
public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
//转化类
SV388BetRecordResponse.Transaction resultBean = (SV388BetRecordResponse.Transaction) gamesDataBuildDTO.getData();
Member member = memberService.selectMemberByGameAccount(resultBean.getUserId());
if (ObjectUtils.isEmpty(member)) {
return null;
}
// 判断输赢
Integer gameStatus = GameStatus.FLAT.getCode();
BigDecimal payoffAmount = BigDecimal.ZERO;
if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) > 0) {
gameStatus = GameStatus.WIN.getCode();
payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount());
} else if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) < 0) {
gameStatus = GameStatus.FAIL.getCode();
payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount()).abs();
}
//结算状态
int settlementStatus = SettlementStatusEnum.REVOKED.getCode();
if (resultBean.getTxStatus() == 1) {
settlementStatus = SettlementStatusEnum.COMPLETED.getCode();
}
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(gamesDataBuildDTO.getPlatform().getOurCurrency(resultBean.getCurrency()))
.memberId(member.getId())
.gameCode(resultBean.getGameCode())
.gameType(PlatformType.VIDEO.getCode())
.platformCode(GamePlatforms.SV388.getCode())
.gameId(/*GAME_ID*/GamePlatforms.SV388.getCode() + "_" + 1)
.gameName(resultBean.getGameName())
.gameStatus(gameStatus)
.gameStatusType(resultBean.getSettleStatus())
.gameCurrencyCode(resultBean.getCurrency())
.account(resultBean.getUserId())
.wagersId(resultBean.getPlatform() + resultBean.getPlatformTxId())
.wagersTime(resultBean.getBetTime().getTime())
.betAmount(resultBean.getRealBetAmount())
.payoffTime(resultBean.getTxTime().getTime())
.payoffAmount(payoffAmount)
.betContent(resultBean.getGameInfo())
.settlementTime(resultBean.getTxTime().getTime())
.turnover(resultBean.getTurnover())
.orderNo(String.valueOf(resultBean.getRoundId()))
.settlementStatus(settlementStatus)
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -43,7 +43,7 @@ public class XKGamesDTO {
/**
*id
*/
private Long systemGameId;
private String systemGameId;
/**
* jp
*/

View File

@ -197,7 +197,16 @@ public class GamesXKServiceImpl implements IGamesService {
throw new BaseException(xkLoginWithoutRedirectResponseDTO.getMsg());
}
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.XK.getCode() + IdUtils.simpleUUID();
}
/**
*
@ -251,8 +260,13 @@ public class GamesXKServiceImpl implements IGamesService {
gameService.insertGame(game);
} else {
game = games.get(0);
List<NameInfo> nameInfos = new ArrayList<>();
nameInfos.add(new NameInfo(gamesDataDTO.getName(), "zh-CN"));
game.setNameInfo(nameInfos);
gameService.updateGame(game);
}
gamesDataDTO.setSystemGameId(game.getId());
gamesDataDTO.setSystemGameId(game.getGameId());
}
redisCache.deleteObject(CacheConstants.XK_GAMES);

View File

@ -19,6 +19,8 @@ public class ExtInfo implements Serializable {
*/
private Map<String, Long> timeout;
private Map<String, Object> betLimit;
public String getOurCurrency(String currencyId) {
return currency == null ? null : currency.get(currencyId);
}

View File

@ -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 字典")

View File

@ -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;
// }
//}

View File

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

View File

@ -1,7 +0,0 @@
package com.ff.sports.fb;
/**
* @author cengy
*/
public class A {
}

View File

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

View File

@ -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<String, Object> 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);
}
}

View File

@ -0,0 +1,27 @@
package com.ff.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author cengy
*/
public class TimestampFromString {
public static final String PATTERN_DATE = "yyyy-MM-dd HH:mm:ss";
public static Long from(String date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(PATTERN_DATE);
try {
Date parse = simpleDateFormat.parse(date);
return parse.getTime();
} catch (ParseException e) {
return 0L;
}
}
public static Long to(String timestamp) {
return Long.parseLong(timestamp);
}
}