diff --git a/accompany-base/accompany-core/src/main/java/com/accompany/common/constant/GameConstant.java b/accompany-base/accompany-core/src/main/java/com/accompany/common/constant/GameConstant.java index e9bcc7e20..f22f260c6 100644 --- a/accompany-base/accompany-core/src/main/java/com/accompany/common/constant/GameConstant.java +++ b/accompany-base/accompany-core/src/main/java/com/accompany/common/constant/GameConstant.java @@ -1,20 +1,40 @@ package com.accompany.common.constant; +import com.accompany.common.utils.StringUtils; + public class GameConstant { public enum GameChannel { - LEADERCC( "灵"), + LEADERCC( "灵", "game_gold_log"), - BAISHUN( "百顺") + BAISHUN( "百顺", "bai_shun_game_record"), ; private String desc; + private String tableName; - GameChannel(String desc) { + GameChannel(String desc, String tableName) { this.desc = desc; + this.tableName = tableName; } public String getDesc() { return desc; } + + public String getTableName() { + return tableName; + } + + public static GameChannel getByChannel(String channel) { + if (StringUtils.isEmpty(channel)) { + return null; + } + for (GameChannel value : values()) { + if (channel.equals(value.name())) { + return value; + } + } + return null; + } } } diff --git a/accompany-base/accompany-sharding/accompany-sharding-sdk/src/main/java/com/accompany/sharding/vo/GameDataTotalVo.java b/accompany-base/accompany-sharding/accompany-sharding-sdk/src/main/java/com/accompany/sharding/vo/GameDataTotalVo.java index f3308beb4..789d5eabf 100644 --- a/accompany-base/accompany-sharding/accompany-sharding-sdk/src/main/java/com/accompany/sharding/vo/GameDataTotalVo.java +++ b/accompany-base/accompany-sharding/accompany-sharding-sdk/src/main/java/com/accompany/sharding/vo/GameDataTotalVo.java @@ -13,16 +13,16 @@ public class GameDataTotalVo { private String statDate; @ApiModelProperty("新用户参与人数") - private Integer newUserJoinNum; + private Integer newUsersCount; @ApiModelProperty("新用户参与人数") - private Integer totalJoinNum; + private Integer totalUsersCount; @ApiModelProperty("投入") - private BigDecimal totalBet; + private BigDecimal payGold; @ApiModelProperty("支出") - private BigDecimal totalWin; + private BigDecimal winGold; @ApiModelProperty("剩余") private BigDecimal totalRemain; diff --git a/accompany-base/accompany-sharding/accompany-sharding-service/src/main/java/com/accompany/sharding/config/ShardingSphereConfig.java b/accompany-base/accompany-sharding/accompany-sharding-service/src/main/java/com/accompany/sharding/config/ShardingSphereConfig.java index 66809df9b..7862dfb2f 100644 --- a/accompany-base/accompany-sharding/accompany-sharding-service/src/main/java/com/accompany/sharding/config/ShardingSphereConfig.java +++ b/accompany-base/accompany-sharding/accompany-sharding-service/src/main/java/com/accompany/sharding/config/ShardingSphereConfig.java @@ -206,7 +206,9 @@ public class ShardingSphereConfig { } private AlgorithmConfiguration getGameGoldLogShardingAlgorithmConfiguration() { - return getMonthShardingAlgorithmConfiguration(); + AlgorithmConfiguration algorithmConfiguration = getMonthShardingAlgorithmConfiguration(); + return algorithmConfiguration; + } private AlgorithmConfiguration getBaiShunGameRecordShardingAlgorithmConfiguration() { diff --git a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/model/game/GameDayStatData.java b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/model/game/GameDayStatData.java index 04f6c1828..0bfad6c00 100644 --- a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/model/game/GameDayStatData.java +++ b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/model/game/GameDayStatData.java @@ -33,10 +33,22 @@ public class GameDayStatData implements Serializable { * 游戏id */ private Integer gameId; + /** + * 分区id + */ + private Integer partitionId; /** * 用户uid */ private Long uid; + /** + * 总参与人数 + */ + private Integer totalUsersCount; + /** + * 新用户参与人数 + */ + private Integer newUsersCount; /** * h5小游戏支出 */ diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/mybatismapper/game/GameDayStatDataMapper.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/mybatismapper/game/GameDayStatDataMapper.java index bf00473cd..4ddbd3247 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/mybatismapper/game/GameDayStatDataMapper.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/mybatismapper/game/GameDayStatDataMapper.java @@ -1,15 +1,46 @@ package com.accompany.business.mybatismapper.game; import com.accompany.business.model.game.GameDayStatData; +import com.accompany.sharding.vo.GameDataTotalVo; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; import java.util.List; /** * 游戏日统计表 Mapper 接口 * - * @author + * @author * @since 2025-03-04 */ public interface GameDayStatDataMapper extends BaseMapper { + /** + * 统计单天,分区,游戏,用户分组数据(仅限) + * + * @param splitMonth + * @param beginDate + * @param endDate + * @return + */ + List statLeaderccDayList(@Param("splitMonth") String splitMonth, @Param("beginDate") Date beginDate, @Param("endDate") Date endDate); + + List statBaiShunDayList(@Param("splitMonth") String splitMonth, @Param("beginDate") Date beginDate, @Param("endDate") Date endDate); + + /** + * 统计当天数据(仅限) + * + * @param gameId + * @param beginTime + * @param endTime + * @param partitionId + */ + GameDataTotalVo selectTodayList(@Param("channel") String channel, @Param("tableName") String tableName, @Param("gameId") String gameId, + @Param("beginTime") Date beginTime, @Param("endTime") Date endTime, @Param("partitionId") Integer partitionId); + + + IPage selectGroupDateList(IPage iPage, @Param("channel") String channel, @Param("gameId") String gameId, + @Param("beginTime") Date beginTime, @Param("endTime") Date endTime, @Param("partitionId") Integer partitionId); } diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/GameDayStatDataService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/GameDayStatDataService.java index e84c83404..7943bf269 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/GameDayStatDataService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/GameDayStatDataService.java @@ -7,6 +7,7 @@ import com.accompany.sharding.vo.GameDataTotalVo; import com.baomidou.mybatisplus.extension.service.IService; import java.util.Date; +import java.util.List; /** * 游戏日统计表 服务类 @@ -18,4 +19,13 @@ public interface GameDayStatDataService extends IService { PageResult totalList( String channel, String gameId, String startDate, String endDate, Integer partitionId, Integer pageNo, Integer pageSize); + + /** + * + * @param channel + * @param statTime 传2号,统计1号的数据 + * @return + */ + void statDayList(String channel, Date statTime); + } diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/impl/GameDayStatDataServiceImpl.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/impl/GameDayStatDataServiceImpl.java index d785a1978..98ada1e89 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/impl/GameDayStatDataServiceImpl.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/game/impl/GameDayStatDataServiceImpl.java @@ -1,50 +1,179 @@ package com.accompany.business.service.game.impl; +import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import com.accompany.business.model.game.GameDayStatData; import com.accompany.business.mybatismapper.game.GameDayStatDataMapper; import com.accompany.business.service.game.GameDayStatDataService; +import com.accompany.business.service.game.GameService; import com.accompany.common.constant.GameConstant; import com.accompany.common.result.PageResult; import com.accompany.core.exception.AdminServiceException; import com.accompany.sharding.vo.GameDataTotalVo; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.apache.shardingsphere.infra.hint.HintManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; +import javax.sql.DataSource; +import java.math.RoundingMode; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.Date; +import java.util.List; /** * 游戏日统计表 服务实现类 * - * @author + * @author * @since 2025-03-04 */ +@Slf4j @Service public class GameDayStatDataServiceImpl extends ServiceImpl implements GameDayStatDataService { + @Autowired + @Qualifier("shardingDataSource") + private DataSource shardingDataSource; + + @Autowired + private GameService gameService; @Override public PageResult totalList( - String channel, String gameId, String startDate, String endDate, Integer partitionId, Integer pageNo, Integer pageSize) { - if (StringUtils.isEmpty(channel)) { + String channel, String gameId, String startDate, String endDate, Integer partitionId, Integer pageNo, Integer pageSize) { + GameConstant.GameChannel byChannel = GameConstant.GameChannel.getByChannel(channel); + if (byChannel == null) { throw new AdminServiceException("请选择第三方名称"); } - Date beginTime = null, endTime = null; + Date beginTime, endTime; + Date now = new Date(); if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate)) { beginTime = DateUtil.parseDateTime(startDate); endTime = DateUtil.parseDateTime(endDate); } else { - endTime = new Date(); - beginTime = DateUtil.offsetDay(endTime, -7); + endTime = DateUtil.endOfDay(now); + beginTime = DateUtil.offsetDay(endTime, -30); + } + IPage gameDayStatDataIPage = baseMapper.selectGroupDateList(new Page<>(pageNo, pageSize), channel, gameId, beginTime, endTime, partitionId); + + List records = gameDayStatDataIPage.getRecords(); + + //是否包含今天 今天的单独查 + Boolean containsToday = beginTime.before(now) && endTime.after(now); + GameDataTotalVo todayData; + if (containsToday) { + String splitMonth = DateUtil.format(now, DatePattern.SIMPLE_MONTH_PATTERN); + todayData = this.selectDayData(channel, splitMonth, gameId, DateUtil.beginOfDay(now), DateUtil.endOfDay(now), partitionId); + if (todayData != null) { + todayData.setStatDate(DateUtil.formatDate(now)); + records.add(0, todayData); + } + } + if (CollectionUtils.isNotEmpty(records)) { + for (GameDataTotalVo record : records) { + record.setTotalRemain(record.getPayGold().subtract(record.getWinGold())); + record.setBetRate(record.getTotalRemain().divide(record.getPayGold(), 4, RoundingMode.DOWN)); + } + } + return new PageResult<>(gameDayStatDataIPage); + } + + @Override + public void statDayList(String channel, Date statTime) { + log.info("GameDayStatDataServiceImpl.statDayList-begin channel:{}, statTime:{}", channel, statTime); + Date endDate = DateUtil.beginOfDay(statTime); + Date beginDate = DateUtil.offsetDay(endDate, -1); + String splitMonth = DateUtil.format(beginDate, DatePattern.SIMPLE_MONTH_PATTERN); + List gameDayStatDatas = null; + if (GameConstant.GameChannel.LEADERCC.name().equals(channel)) { + gameDayStatDatas = baseMapper.statLeaderccDayList(splitMonth, beginDate, endDate); + } else if (GameConstant.GameChannel.BAISHUN.name().equals(channel)) { + gameDayStatDatas = baseMapper.statBaiShunDayList(splitMonth, beginDate, endDate); + } + if (CollectionUtils.isEmpty(gameDayStatDatas)) { + return; + } + for (GameDayStatData gameDayStatData : gameDayStatDatas) { + gameDayStatData.setStatDate(beginDate); + gameDayStatData.setCreateTime(statTime); + } + baseMapper.insert(gameDayStatDatas); + log.info("GameDayStatDataServiceImpl.statDayList-end channel:{}, statTime:{}", channel, statTime); + } + + + private GameDataTotalVo selectDayData(String channel, String splitMonth, String gameId, Date beginTime, Date endTime, Integer partitionId) { + StringBuilder querySql = new StringBuilder(" SELECT\n" + + " DATE(g.create_time) AS statDate,\n" + + " COUNT(DISTINCT u.uid) AS totalUsersCount,\n" + + " COUNT(\n" + + " DISTINCT\n" + + " CASE\n" + + " WHEN u.create_time >= DATE(g.create_time) - INTERVAL 1 DAY\n" + + " AND u.create_time < DATE(g.create_time) THEN g.uid END) AS newUsersCount,"); + if(GameConstant.GameChannel.LEADERCC.name().equals(channel)){ + querySql.append(" IFNULL(sum(if(g.`type` = 1, g.`coin`, '0')), 0) payGold,\n" + + " IFNULL(sum(if(g.`type` = 2, g.`coin`, '0')), 0) winGold\n" + + " FROM game_gold_log g"); + } else { + querySql.append(" ifnull(abs(sum(g.if(currency_diff < 0, g.`currency_diff`, '0'))), 0) payGold,\n" + + " ifnull(sum(if(g.currency_diff > 0, g.`currency_diff`, '0')), 0) winGold\n" + + " FROM bai_shun_game_record g"); + } + String tableName = GameConstant.GameChannel.getByChannel(channel).getTableName(); + querySql.append(" WHERE\n" + + " g.create_time >= ?\n" + + " AND g.create_time < ?"); + List params = new ArrayList<>(); + params.add(DateUtil.formatDateTime(beginTime)); + params.add(DateUtil.formatDateTime(endTime)); + if (StringUtils.isNotEmpty(gameId) && !gameService.ALL.equals(gameId)) { + querySql.append(" AND g.game_id = ?"); + params.add(gameId); + } + if (partitionId != null) { + querySql.append(" AND u.partition_id = ?"); + params.add(partitionId.toString()); } - if (GameConstant.GameChannel.LEADERCC.name().equals(channel)) { - return null; - } else if (GameConstant.GameChannel.BAISHUN.name().equals(channel)) { - return null; + try (HintManager hintManager = HintManager.getInstance(); + Connection conn = shardingDataSource.getConnection(); + PreparedStatement preparedStatement = conn.prepareStatement(querySql.toString())) { + hintManager.addTableShardingValue(tableName, splitMonth); + int i = 1; + for (String param : params) { + preparedStatement.setString(i++, param); + } + try (ResultSet rs = preparedStatement.executeQuery()) { + List result = new ArrayList<>(); + while (rs.next()) { + GameDataTotalVo vo = new GameDataTotalVo(); + vo.setStatDate(rs.getString("statDate")); + vo.setTotalUsersCount(rs.getInt("totalUsersCount")); + vo.setNewUsersCount(rs.getInt("newUsersCount")); + vo.setPayGold(rs.getBigDecimal("payGold")); + vo.setWinGold(rs.getBigDecimal("winGold")); + result.add(vo); + } + if (CollectionUtils.isNotEmpty(result)) { + return result.get(0); + } + return null; + } + } catch (SQLException e) { + e.printStackTrace(); } return null; - } } diff --git a/accompany-business/accompany-business-service/src/main/resources/accompany/sqlmappers/GameDayStatDataMapper.xml b/accompany-business/accompany-business-service/src/main/resources/accompany/sqlmappers/GameDayStatDataMapper.xml index 55ef5fbb8..43ae0c24c 100644 --- a/accompany-business/accompany-business-service/src/main/resources/accompany/sqlmappers/GameDayStatDataMapper.xml +++ b/accompany-business/accompany-business-service/src/main/resources/accompany/sqlmappers/GameDayStatDataMapper.xml @@ -2,4 +2,121 @@ + + + + + + + + IFNULL(sum(if(g.`type` = 1, g.`coin`, '0')), 0) payGold, + IFNULL(sum(if(g.`type` = 2, g.`coin`, '0')), 0) winGold + FROM game_gold_log g + + + ifnull(abs(sum(g.if(currency_diff < 0, g.`currency_diff`, '0'))), 0) payGold, + ifnull(sum(if(g.currency_diff > 0, g.`currency_diff`, '0')), 0) winGold + FROM bai_shun_game_record g + + + + + + + diff --git a/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/game/GameController.java b/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/game/GameController.java index d3c07eaa6..ea042b090 100644 --- a/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/game/GameController.java +++ b/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/game/GameController.java @@ -1,7 +1,9 @@ package com.accompany.business.controller.game; +import cn.hutool.core.date.DateUtil; import com.accompany.business.config.LeaderccMiniGameConfig; import com.accompany.business.param.neteasepush.MD5Utils; +import com.accompany.business.service.game.GameDayStatDataService; import com.accompany.business.service.game.GameService; import com.accompany.business.vo.game.GameConsumeRequestVO; import com.accompany.business.vo.game.GameResponseVO; @@ -18,6 +20,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Date; + @Slf4j @RestController @RequestMapping("/game") @@ -27,6 +31,8 @@ public class GameController { private GameService gameService; @Autowired private LeaderccMiniGameConfig leaderccMiniGameConfig; + @Autowired + private GameDayStatDataService gameDayStatDataService; @PostMapping("/gold") public GameResponseVO changeGold(@RequestBody @Validated GameConsumeRequestVO param){ @@ -76,6 +82,11 @@ public class GameController { return gameService.changeGold(param); } + @PostMapping("/statData") + public void statData(String channel, String statTime) { + gameDayStatDataService.statDayList(channel, DateUtil.parseDateTime(statTime)); + } + public static void main(String[] args) { String token = "b7b68181-4005-441b-affb-3b20cd75519e"; String gameId = "1"; diff --git a/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/game/GameDayStatDataTask.java b/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/game/GameDayStatDataTask.java new file mode 100644 index 000000000..1d3815e5b --- /dev/null +++ b/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/game/GameDayStatDataTask.java @@ -0,0 +1,23 @@ +package com.accompany.scheduler.task.game; + +import com.accompany.business.service.game.GameDayStatDataService; +import com.accompany.common.constant.GameConstant; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Component +public class GameDayStatDataTask { + + @Autowired + private GameDayStatDataService gameDayStatDataService; + + @Scheduled(cron = "0 10 0 * * *") + public void statDayList() { + for (GameConstant.GameChannel gameChannel : GameConstant.GameChannel.values()) { + gameDayStatDataService.statDayList(gameChannel.name(), new Date()); + } + } +}