feat(platform): 新增 DBSports体育平台支持

- 添加 DBSports 体育平台的接口和实现类
- 新增相关 DTO 类和枚举类
- 实现会员创建、资金转账、获取会员信息等功能
- 添加投注记录获取和处理相关方法
main-pp
liaoyong 2025-04-12 10:31:09 +08:00
parent 32f68276fb
commit af1253e96a
52 changed files with 2934 additions and 8 deletions

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>ff</artifactId>
<groupId>com.ff</groupId>
<version>0.0.1</version>
</parent>
<artifactId>xk-client</artifactId>
<version>0.0.1</version>
<name>xk-client</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.ff</groupId>
<artifactId>ff-domain</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>xk-client</finalName>
</build>
</project>

View File

@ -0,0 +1,153 @@
package com.ff.client;
import com.ff.dto.*;
import java.util.List;
/**
*
*
* @author shi
* @date 2024/10/21
*/
public interface IGamesService {
/**
*
*
* @param createMemberRequestDTO dto
* @return {@link Boolean }
*/
Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO);
/**
*
*
* @param memberInfoRequestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO);
/**
*
*
* @param gamesLogin
* @return {@link String }
*/
String loginWithoutRedirect(GamesLogin gamesLogin);
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO);
/**
* id
*
* @param exchangeTransferMoneyRequestDTO moeny dto
* @return {@link Long }
*/
Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO);
/**
*
*
* @param exchangeTransferMoneyRequestDTO dto
* @return {@link ExchangeTransferStatusResponseDTO }
*/
ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO);
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO);
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO);
/**
*
*
* @param createFreeSpinRequest
* @return {@link Boolean }
*/
Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest);
/**
*
*
* @param getGameDetailRequestDTO dto
* @return {@link GetGameDetailResponseDTO }
*/
GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO);
/**
*
*
* @param kickMemberRequestDTO dto
* @return {@link Boolean }
*/
Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO);
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO);
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO);
/**
*
*
* @param cancelFreeSpinRequestDTO
* @return {@link Boolean }
*/
Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO);
/**
*
*
* @param gameDemoLoginRequestDTO dto
* @return {@link GameDemoLoginResponseDTO }
*/
GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO);
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO);
}

View File

@ -0,0 +1,35 @@
package com.ff.dto;
import lombok.Data;
/**
* dto
*
* @author shi
* @date 2024/10/22
*/
@Data
public class BetRecordByTimeDTO extends GamesBaseRequestDTO {
/**
*
*/
private Long startTime;
/**
*
*/
private Long endTime;
/**
*
*/
private Integer page;
/**
*
*/
private Integer pageLimit;
/**
* id
*/
private Integer gameId;
}

View File

@ -0,0 +1,23 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
*
*
* @author shi
* @date 2024/11/11
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class CancelFreeSpinRequestDTO extends GamesBaseRequestDTO {
/**
* id
*/
private String referenceId;
}

View File

@ -0,0 +1,69 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.math.BigDecimal;
import java.util.List;
/**
* dto
*
* @author shi
* @date 2024/11/11
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class CreateFreeSpinRequestDTO extends GamesBaseRequestDTO {
/**
* (JILI )
*/
private String account;
/**
* 使
*/
private String currency;
/**
* , 50
*/
private String referenceId;
/**
*
*
*
*/
private Long freeSpinValidity;
/**
*
*/
private Integer numberOfRounds;
/**
* 使 ID, ; id
* 200
*/
private List<Long> gameIds;
/**
* ;
* , 使
*/
private BigDecimal betValue;
/**
* 使
* , 使
*/
private Long startTime;
}

View File

@ -0,0 +1,38 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.Map;
/**
* dto
*
* @author shi
* @date 2024/10/22
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class CreateMemberRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private String account;
/**
* ae
*/
private Map<String, Map<String, Map<String, Object>>> betLimit;
/**
* 0 1
*/
private Integer platformType;
}

View File

@ -0,0 +1,58 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.math.BigDecimal;
/**
*
*
* @author shi
* @date 2024/10/22
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class ExchangeTransferMoneyRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private String account;
/**
*
*/
private String tenantKey;
/**
* id
*/
private String orderId;
/**
*
*/
private BigDecimal amount;
/**
*
*/
private BigDecimal quota;
/**
*
* 1: ( amount
* )
* 2:
* 3:
*/
private Integer transferType;
}

View File

@ -0,0 +1,36 @@
package com.ff.dto;
import lombok.Data;
/**
* dto
*
* @author shi
* @date 2025/03/12
*/
@Data
public class ExchangeTransferStatusRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private String account;
/**
*
*/
private String currency;
/**
* ID
*/
private String orderId;
/**
* id
*/
private Long gameExchangeMoneyId;
}

View File

@ -0,0 +1,45 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
*
*
* @author shi
* @date 2025/04/08
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ExchangeTransferStatusResponseDTO {
/**
* StatusType
*/
private Integer statusType;
/**
*
*/
private BigDecimal balance;
/**
* ()
*/
private BigDecimal coinBefore;
/**
* ()
*/
private BigDecimal coinAfter;
}

View File

@ -0,0 +1,183 @@
package com.ff.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ff.base.core.domain.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.math.BigDecimal;
/**
* ff_game_betting_details
*
* @author shi
* @date 2025-02-10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GameBettingDetails extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* key
*/
private String tenantKey;
/**
*
*/
private String currencyCode;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long memberId;
/**
* id
*/
private String gameCode;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private String gameId;
/**
* ff_game_type
*/
private Integer gameType;
/**
*
*/
private String platformCode;
/**
*
*/
private String gameName;
/**
* 1: 2: 3: 4
*/
private Integer gameStatus;
/**
*
* 1
* 9
* 11
* 12
* 17 ()
* 18 ()
* 19
* 28
* DG
* 1:2:
* AE
*
* 0
*
* Resettle / Unsettle / Voidsettle / Unvoidsettle: 1
* Voidbet: -1
*/
private Integer gameStatusType;
/**
*
*/
private String gameCurrencyCode;
/**
*
*/
private String account;
/**
*
*/
private String round;
/**
*
*/
private String table;
/**
*
*/
private String seat;
/**
* text
*/
private String betContent;
/**
*
*/
private String wagersId;
/**
* (Unix )
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long wagersTime;
/**
*
*/
private BigDecimal betAmount;
/**
* (Unix )
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long payoffTime;
/**
*
*/
private BigDecimal payoffAmount;
/**
* (Unix )
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long settlementTime;
/**
* 1
*/
private BigDecimal turnover;
/**
* id
*/
private String orderNo;
/**
* 1 2 3 4
*/
private Integer settlementStatus;
}

View File

@ -0,0 +1,34 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* dto
*
* @author shi
* @date 2025/04/03
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GameDemoLoginRequestDTO extends GamesBaseRequestDTO{
/**
*
*/
private String lang;
/**
*
*/
private String gameType;
/**
* id
*/
private String gameId;
}

View File

@ -0,0 +1,23 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* dto
*
* @author shi
* @date 2025/04/03
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GameDemoLoginResponseDTO {
/**
*
*/
private String url;
}

View File

@ -0,0 +1,106 @@
package com.ff.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ff.base.core.domain.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* ff_game_free_record
*
* @author shi
* @date 2025-02-13
*/
@Data
@AllArgsConstructor
@SuperBuilder
@NoArgsConstructor
public class GameFreeRecord extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
*
*/
private String currencyCode;
/**
*
*/
private String platformCode;
/**
*
*/
private String referenceId;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long memberId;
/**
*
*/
private String memberAccount;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long gameId;
/**
* 使
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long sendTime;
/**
*
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long expiredTime;
/**
*
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long freeUpdateTime;
/**
*
*/
private String sendGame;
/**
*
*/
private Integer sendAmount;
/**
* 使
*/
private Integer usedAmount;
/**
* 使
*/
private Integer unusedAmount;
/**
* 1 0
*/
private Integer freeStatus;
}

View File

@ -0,0 +1,51 @@
package com.ff.dto;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
/**
* dto
*
* @author shi
* @date 2024/10/22
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GamesBaseRequestDTO implements Serializable {
private static final long serialVersionUID = 5139311242800113436L;
/**
* id
*/
private String agentId;
/**
*
*/
private String agentKey;
/**
*
*/
private String query;
/**
*
*/
private String currency;
private Platform vendor;
private KeyInfo keyInfo;
private String systemCurrency;
}

View File

@ -0,0 +1,46 @@
package com.ff.dto;
import com.ff.game.domain.Platform;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* dto
*
* @author shi
* @date 2024/10/22
*/
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Data
public class GamesDataBuildDTO
{
/**
*
*/
private Object data;
/**
*
*/
private String systemCurrencyCode;
/**
*
*/
private String currencyCode;
/**
* id
*/
private String agentId;
private Platform platform;
}

View File

@ -0,0 +1,57 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.Map;
/**
*
*
* @author shi
* @date 2024/10/22
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GamesLogin extends GamesBaseRequestDTO{
/**
*
*/
private String account;
/**
* ( GameList GameId)
*/
private String gameId;
/**
* UI ,
*/
private String lang;
/**
* md5
*/
private String homeUrl;
/**
* md5 web app
*/
private String platform;
/**
* md5 , 1
*/
private Integer disableFullScreen;
/**
*
*/
private String gameType;
/**
* ae
*/
private Map<String, Map<String, Map<String, Object>>> betLimit;
}

View File

@ -0,0 +1,19 @@
package com.ff.dto;
import lombok.Data;
/**
* dashflowdto
*
* @author shi
* @date 2024/11/11
*/
@Data
public class GetFreeSpinDashflowRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private Long startTime;
}

View File

@ -0,0 +1,28 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* dto
*
* @author shi
* @date 2024/11/12
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GetGameDetailRequestDTO extends GamesBaseRequestDTO {
/**
* id
*/
private String wagersId;
/**
*
*/
private String lang;
}

View File

@ -0,0 +1,25 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* dto
*
* @author shi
* @date 2024/11/12
*/
@NoArgsConstructor
@Data
@AllArgsConstructor
@Builder
public class GetGameDetailResponseDTO {
/**
* url
*/
private String url;
}

View File

@ -0,0 +1,28 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import javax.validation.constraints.NotNull;
/**
* dto
*
* @author shi
* @date 2024/11/12
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class KickMemberAllDTO extends GamesBaseRequestDTO {
/**
* id
*/
private String gameId;
}

View File

@ -0,0 +1,25 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* dto
*
* @author shi
* @date 2024/11/12
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class KickMemberRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private String account;
}

View File

@ -0,0 +1,25 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
*
*
* @author shi
* @date 2024/10/22
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class MemberInfoRequestDTO extends GamesBaseRequestDTO {
/**
*
*/
private String accounts;
}

View File

@ -0,0 +1,41 @@
package com.ff.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.math.BigDecimal;
/**
* dto
*
* @author shi
* @date 2024/10/30
*/
@NoArgsConstructor
@Data
@AllArgsConstructor
@SuperBuilder
public class MemberInfoResponseDTO {
/**
*
*/
private String account;
/**
*
*/
private BigDecimal balance;
/**
*
* 1: 线
* 2:
* 3:
* 4
*/
private Integer status;
}

View File

@ -15,6 +15,14 @@
<dependencies>
<dependency>
<groupId>com.ff</groupId>
<artifactId>xk-client</artifactId>
</dependency>
<dependency>
<groupId>com.ff</groupId>
<artifactId>comp-redis</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>

View File

@ -0,0 +1,24 @@
package com.ff.service;
import com.ff.game.domain.Platform;
import com.ff.redis.GRedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author cengy
*/
@Service
public class PlatformManager {
@Autowired
GRedisCache gRedisCache;
public Platform get(String platformCode) {
return gRedisCache.getCacheObject(getCacheKey(platformCode));
}
private String getCacheKey(String configKey) {
return "platform:" + configKey;
}
}

View File

@ -0,0 +1,12 @@
package com.ff.utils;
/**
* @author cengy
*/
public class AdjustTimestampByMinuteOffset {
public static long get(long timestampMillis, int minuteOffset) {
// 将分钟转换为毫秒,并加到时间戳上
return timestampMillis + minuteOffset * 60 * 1000L;
}
}

View File

@ -0,0 +1,13 @@
package com.ff.utils;
import java.time.Instant;
/**
* @author cengy
*/
public class CurrentMillis {
public static Long get() {
return Instant.now().toEpochMilli();
}
}

View File

@ -9,5 +9,5 @@ import lombok.Data;
public class TaskParam {
// 定时任务的参数平台code, 拉多少分钟前的
private String platformCode;
private long ago;
private int ago;
}

View File

@ -1,20 +1,74 @@
package com.ff.xxljob.task;
import com.ff.client.IGamesService;
import com.ff.dto.BetRecordByTimeDTO;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.service.PlatformManager;
import com.ff.utils.AdjustTimestampByMinuteOffset;
import com.ff.utils.CurrentMillis;
import com.ff.xxljob.fetch.TaskParam;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author cengy
*/
@Component
@Slf4j
public class FetchHistoryOrderListTask extends FetchTask {
@Autowired(required = false)
private Map<String, IGamesService> clientServiceMap = new HashMap<>();
@Autowired
private PlatformManager platformManager;
@XxlJob("fetchHistoryOrderListTask")
@Override
public void doExecute(TaskParam taskParam) {
//捞取指定分钟前的数据
Platform platform = platformManager.get(taskParam.getPlatformCode());
if (null == platform) {
return;
}
int agoMin = taskParam.getAgo();
//捞取指定分钟前的数据
Long endTime = CurrentMillis.get();
Long startTime = AdjustTimestampByMinuteOffset.get(endTime, agoMin);
List<KeyInfo> keyData = platform.getKeyInfo();
for (KeyInfo keyInfo : keyData) {
try {
BetRecordByTimeDTO betRecordByTimeDTO = new BetRecordByTimeDTO();
betRecordByTimeDTO.setStartTime(startTime);
betRecordByTimeDTO.setEndTime(endTime);
betRecordByTimeDTO.setPage(1);
betRecordByTimeDTO.setPageLimit(1000);
betRecordByTimeDTO.setAgentId(keyInfo.getCode());
betRecordByTimeDTO.setAgentKey(keyInfo.getKey());
betRecordByTimeDTO.setKeyInfo(keyInfo);
betRecordByTimeDTO.setVendor(platform);
betRecordByTimeDTO.setSystemCurrency(keyInfo.getCurrency());
betRecordByTimeDTO.setCurrency(platform.getCurrencyInfo().get(keyInfo.getCurrency()));
clientServiceMap.get(platform.getPlatformCode() + "Service").getBetRecordByHistoryTime(betRecordByTimeDTO);
} catch (Exception e) {
log.error("同步平台投注记录失败platformCode:{}", platform.getPlatformCode(), e);
}
if (platform.isMultiAgent()) {
break;
}
}
}
}

View File

@ -1,20 +1,76 @@
package com.ff.xxljob.task;
import com.ff.client.IGamesService;
import com.ff.dto.BetRecordByTimeDTO;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.service.PlatformManager;
import com.ff.utils.AdjustTimestampByMinuteOffset;
import com.ff.utils.CurrentMillis;
import com.ff.xxljob.fetch.TaskParam;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author cengy
*/
@Component
@Slf4j
public class FetchOrderListTask extends FetchTask {
@Autowired(required = false)
private Map<String, IGamesService> clientServiceMap = new HashMap<>();
@Autowired
private PlatformManager platformManager;
@XxlJob("fetchOrderListTask")
@Override
public void doExecute(TaskParam taskParam) {
Platform platform = platformManager.get(taskParam.getPlatformCode());
if (null == platform) {
return;
}
int agoMin = taskParam.getAgo();
//捞取指定分钟前的数据
Long endTime = CurrentMillis.get();
Long startTime = AdjustTimestampByMinuteOffset.get(endTime, agoMin);
List<KeyInfo> keyData = platform.getKeyInfo();
for (KeyInfo keyInfo : keyData) {
try {
BetRecordByTimeDTO betRecordByTimeDTO = new BetRecordByTimeDTO();
betRecordByTimeDTO.setStartTime(startTime);
betRecordByTimeDTO.setEndTime(endTime);
betRecordByTimeDTO.setPage(1);
betRecordByTimeDTO.setPageLimit(1000);
betRecordByTimeDTO.setAgentId(keyInfo.getCode());
betRecordByTimeDTO.setAgentKey(keyInfo.getKey());
betRecordByTimeDTO.setKeyInfo(keyInfo);
betRecordByTimeDTO.setVendor(platform);
betRecordByTimeDTO.setSystemCurrency(keyInfo.getCurrency());
betRecordByTimeDTO.setCurrency(platform.getCurrencyInfo().get(keyInfo.getCurrency()));
clientServiceMap.get(platform.getPlatformCode() + "Service").getBetRecordByTime(betRecordByTimeDTO);
} catch (Exception e) {
log.error("同步平台投注记录失败platformCode:{}", platform.getPlatformCode(), e);
}
if (platform.isMultiAgent()) {
break;
}
}
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>ff</artifactId>
<groupId>com.ff</groupId>
<version>0.0.1</version>
</parent>
<artifactId>xk-dbsports</artifactId>
<version>0.0.1</version>
<name>xk-dbsports</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>xk-dbsports</finalName>
</build>
</project>

View File

@ -0,0 +1,32 @@
package com.ff.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.sun.media.jfxmediaimpl.platform.PlatformManager;
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 PlatformManager 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,110 @@
package com.ff.client;
import com.ff.dto.CreateUserResponse;
import com.ff.dto.TransferResponse;
import com.ff.sports.db.address.DBSportsAddress;
/**
* @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",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
GetBetListResponse getBetList(@Body GetBetListRequest request,
@Header("requestId") String requestId);
}

View File

@ -0,0 +1,28 @@
package com.ff.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.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.dto;
/**
* @author cengy
*/
public class Enums {
}

View File

@ -0,0 +1,93 @@
package com.ff.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 = String.valueOf(System.currentTimeMillis());
/**
*
* 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,199 @@
package com.ff.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 DataDTO data;
@Data
public static class DataDTO {
// 当前页码
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.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,26 @@
package com.ff.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @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.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.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.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.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.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.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.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.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,616 @@
package com.ff.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.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.GameDemoLoginResponseDTO;
import com.ff.game.api.request.GetGameDetailResponseDTO;
import com.ff.game.api.request.MemberInfoResponseDTO;
import com.ff.game.domain.GameBettingDetails;
import com.ff.game.domain.GameFreeRecord;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import com.ff.sports.db.client.DBSportsClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 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) {
Member member = memberService.selectMemberByGameAccount(requestDTO.getAccount());
String transactionId = GamePlatforms.DBSports.getCode() + IdUtils.simpleUUID();
List<GameExchangeMoney> gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList(
GameExchangeMoney.builder()
.tenantKey(requestDTO.getTenantKey())
.orderId(requestDTO.getOrderId())
.build()
);
Assert.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), "订单号重复");
//获取下一个自增id
GameExchangeMoney exchangeMoney = GameExchangeMoney
.builder()
.orderId(requestDTO.getOrderId())
.tenantKey(requestDTO.getTenantKey())
.quota(requestDTO.getQuota())
.balance(requestDTO.getAmount())
.exchangeType(requestDTO.getTransferType())
.currencyCode(requestDTO.getSystemCurrency())
.memberId(member.getId())
.transactionId(transactionId)
.platformCode(GamePlatforms.DBSports.getCode())
.build();
exchangeMoney.setCreateBy(Constants.SYSTEM);
//接口限制限制50字符
exchangeMoney.setTransactionId(transactionId);
// 转入
if (requestDTO.getTransferType().equals(TransferType.GAMES.getCode())) {
TransferRequest request = new TransferRequest();
request.setUserName(requestDTO.getAccount());
request.setTransferType(1);
request.setTransferId(requestDTO.getOrderId());
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.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS
gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney);
} else {
throw new ApiException(ErrorCode.Transfer_In_Failure.getCode());
}
} else {
// 获取第三方钱包余额
MemberInfoRequestDTO memberInfoRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(requestDTO.getAgentId())
.agentKey(requestDTO.getAgentKey())
.build();
BigDecimal balance = this.getMemberInfo(memberInfoRequestDTO).getBalance();
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.getOrderId());
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.setStatus(StatusType.SUCCESS.getValue()); // SUCCESS
gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney);
} else {
throw new ApiException(ErrorCode.Transfer_Out_Failure.getCode());
}
}
return exchangeMoney.getId();
}
/**
*
*
* @param requestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO requestDTO) {
GetMemberInfoRequest request = new GetMemberInfoRequest();
request.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())*/ "1744333220000");
request.setEndTime(/*String.valueOf(requestDTO.getEndTime())*/ "1744335020000");
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.getData().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;
}
}

11
pom.xml
View File

@ -214,14 +214,13 @@
<dependency>
<groupId>com.ff</groupId>
<artifactId>ff-service</artifactId>
<artifactId>comp-redis</artifactId>
<version>${ff.version}</version>
</dependency>
<dependency>
<groupId>com.ff</groupId>
<artifactId>comp-redis</artifactId>
<version>0.0.1</version>
<artifactId>xk-client</artifactId>
<version>${ff.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -231,6 +230,10 @@
<module>ff-fetcher</module>
<module>ff-bean/ff-domain</module>
<module>ff-bean/comp-redis</module>
<module>ff-bean/xk-client</module>
<!-- platform module -->
<module>ff-platform/xk-dbsports</module>
</modules>
<packaging>pom</packaging>

View File

@ -29,10 +29,11 @@ if [ $? -eq 0 ]; then
mkdir -p ${basePath}/dumps
# 6. 拷贝构建产物
cp ff-game/target/xk-*.jar ${basePath}/
cp -v ff-game/target/xk-*.jar ${basePath}/
# 7. 拷贝配置文件
cp ff-game/src/main/resources/application-prod.yml ${basePath}/config
cp -v ff-game/src/main/resources/config/**/*.properties ${basePath}/config/
cp -v ff-game/src/main/resources/application-prod.properties ${basePath}/
# 8. 拷贝启动脚本
cp ./start.sh ${basePath}/

View File

@ -3,6 +3,8 @@ setlocal
set targetName=xk-admin
set basePath=\opt\server\dist\%targetName%
set resourcePath=ff-game\src\main\resources
:: 1. 切换到项目根目录(当前脚本所在目录的上两级)
cd /d %~dp0
@ -42,7 +44,11 @@ for /f %%f in ('dir /b ff-game\target\xk-*.jar') do (
:: 7. 拷贝配置文件
echo 拷贝配置文件...
copy "ff-game\src\main\resources\application-prod.yml" "%basePath%\config\"
rem Copy all .properties files recursively
for /r "%sourcePath%\config" %%f in (*.properties) do (
copy "%%f" "%targetPath%\"
)
copy "%sourcePath%\application-prod.properties" "%basePath%\"
:: 8. 拷贝启动脚本
echo 拷贝启动脚本...