Compare commits
2
Commits
1c5e9d4cde
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f3d8bbcdd | ||
|
|
df5daf5910 |
+121
@@ -0,0 +1,121 @@
|
|||||||
|
# ==========================================
|
||||||
|
# Maven 构建产物
|
||||||
|
# ==========================================
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# IDE - IntelliJ IDEA
|
||||||
|
# ==========================================
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# IDE - Eclipse
|
||||||
|
# ==========================================
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
.factorypath
|
||||||
|
bin/
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# IDE - VS Code
|
||||||
|
# ==========================================
|
||||||
|
.vscode/
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# IDE - NetBeans
|
||||||
|
# ==========================================
|
||||||
|
nb-configuration.xml
|
||||||
|
nbproject/
|
||||||
|
nbbuild/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# Java 编译产物
|
||||||
|
# ==========================================
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
hs_err_pid*.log
|
||||||
|
replay_pid*.log
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# Spring Boot
|
||||||
|
# ==========================================
|
||||||
|
*.original
|
||||||
|
HELP.md
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 日志文件
|
||||||
|
# ==========================================
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
log/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 操作系统文件
|
||||||
|
# ==========================================
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 临时文件
|
||||||
|
# ==========================================
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.cache/
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 敏感配置 / 环境变量
|
||||||
|
# ==========================================
|
||||||
|
application-local.yml
|
||||||
|
application-local.properties
|
||||||
|
application-dev.yml
|
||||||
|
application-dev.properties
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 上传 / 资源文件
|
||||||
|
# ==========================================
|
||||||
|
uploads/
|
||||||
|
upload/
|
||||||
|
static/upload/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 测试覆盖率报告
|
||||||
|
# ==========================================
|
||||||
|
coverage/
|
||||||
|
*.coverage
|
||||||
|
*.lcov
|
||||||
|
jacoco/
|
||||||
|
test-output/
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 密钥与证书
|
||||||
|
# ==========================================
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.p12
|
||||||
|
*.jks
|
||||||
|
keystore/
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
package com.tailbet.controller;
|
package com.tailbet.controller;
|
||||||
|
|
||||||
import com.tailbet.job.GameJob;
|
import com.tailbet.job.GameJob;
|
||||||
import com.tailbet.mapper.DigitWhiteMapper;
|
|
||||||
import com.tailbet.model.dto.BetDTO;
|
import com.tailbet.model.dto.BetDTO;
|
||||||
import com.tailbet.model.dto.GameOperateDTO;
|
import com.tailbet.model.dto.GameOperateDTO;
|
||||||
import com.tailbet.model.dto.HistoryQueryDTO;
|
import com.tailbet.model.dto.HistoryQueryDTO;
|
||||||
import com.tailbet.model.dto.SetDigitDTO;
|
import com.tailbet.model.dto.SetDigitDTO;
|
||||||
import com.tailbet.model.entity.BetOrder;
|
import com.tailbet.model.entity.BetOrder;
|
||||||
import com.tailbet.model.entity.DigitWhite;
|
|
||||||
import com.tailbet.model.entity.GameRound;
|
import com.tailbet.model.entity.GameRound;
|
||||||
import com.tailbet.model.vo.DigitTrialVo;
|
import com.tailbet.model.vo.DigitTrialVo;
|
||||||
import com.tailbet.model.vo.R;
|
import com.tailbet.model.vo.R;
|
||||||
import com.tailbet.model.vo.UserContext;
|
import com.tailbet.model.vo.UserContext;
|
||||||
import com.tailbet.service.IBetService;
|
import com.tailbet.service.IBetService;
|
||||||
import com.tailbet.service.IGameService;
|
import com.tailbet.service.IGameService;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -32,7 +29,6 @@ public class GameController {
|
|||||||
private final IGameService gameService;
|
private final IGameService gameService;
|
||||||
private final IBetService betService;
|
private final IBetService betService;
|
||||||
private final GameJob gameJob;
|
private final GameJob gameJob;
|
||||||
private final DigitWhiteMapper digitWhiteMapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始游戏(群主)
|
* 开始游戏(群主)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.tailbet.job;
|
package com.tailbet.job;
|
||||||
|
|
||||||
import com.tailbet.config.OpenIMProperties;
|
import com.tailbet.config.OpenIMProperties;
|
||||||
import com.tailbet.mapper.DigitWhiteMapper;
|
|
||||||
import com.tailbet.mapper.DrawRecordMapper;
|
import com.tailbet.mapper.DrawRecordMapper;
|
||||||
import com.tailbet.mapper.GameRoundMapper;
|
import com.tailbet.mapper.GameRoundMapper;
|
||||||
|
import com.tailbet.mapper.GroupMemberMapper;
|
||||||
import com.tailbet.mapper.UserMapper;
|
import com.tailbet.mapper.UserMapper;
|
||||||
import com.tailbet.model.entity.DigitWhite;
|
|
||||||
import com.tailbet.model.entity.DrawRecord;
|
import com.tailbet.model.entity.DrawRecord;
|
||||||
import com.tailbet.model.entity.GameRound;
|
import com.tailbet.model.entity.GameRound;
|
||||||
|
import com.tailbet.model.entity.GroupMember;
|
||||||
import com.tailbet.model.entity.User;
|
import com.tailbet.model.entity.User;
|
||||||
import com.tailbet.openim.OpenIMApiClient;
|
import com.tailbet.openim.OpenIMApiClient;
|
||||||
import com.tailbet.service.IGameService;
|
import com.tailbet.service.IGameService;
|
||||||
@@ -17,6 +17,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏广播器 —— 把 OpenIM 消息发送逻辑从 {@link GameJob} 抽出
|
* 游戏广播器 —— 把 OpenIM 消息发送逻辑从 {@link GameJob} 抽出
|
||||||
@@ -48,25 +50,57 @@ public class GameBroadcaster {
|
|||||||
private final OpenIMProperties openIMProperties;
|
private final OpenIMProperties openIMProperties;
|
||||||
private final GameRoundMapper gameRoundMapper;
|
private final GameRoundMapper gameRoundMapper;
|
||||||
private final DrawRecordMapper drawRecordMapper;
|
private final DrawRecordMapper drawRecordMapper;
|
||||||
private final DigitWhiteMapper digitWhiteMapper;
|
|
||||||
private final UserMapper userMapper;
|
private final UserMapper userMapper;
|
||||||
|
private final GroupMemberMapper groupMemberMapper;
|
||||||
private final IGameService gameService;
|
private final IGameService gameService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知白名单用户(尾数试算完成)
|
* 通知当前群的白名单用户(尾数试算完成)
|
||||||
|
* <p>白名单以 {@code user.is_white=1} 为权威源,与本群成员({@code group_member})取交集后通知。
|
||||||
*/
|
*/
|
||||||
public void notifyWhiteUsers(Long roundId) {
|
public void notifyWhiteUsers(Long roundId) {
|
||||||
try {
|
try {
|
||||||
GameRound round = gameRoundMapper.selectById(roundId);
|
GameRound round = gameRoundMapper.selectById(roundId);
|
||||||
if (round == null) return;
|
if (round == null) return;
|
||||||
|
Long groupId = round.getGroupId();
|
||||||
|
|
||||||
List<DigitWhite> whites = digitWhiteMapper.selectList(new LambdaQueryWrapper<>());
|
// 1. 取本群成员用户ID集合(逻辑删除由全局配置自动过滤)
|
||||||
for (DigitWhite white : whites) {
|
Set<Long> memberIds = groupMemberMapper.selectList(
|
||||||
User user = userMapper.selectById(white.getUserId());
|
new LambdaQueryWrapper<GroupMember>()
|
||||||
if (user == null) continue;
|
.eq(GroupMember::getGroupId, groupId)
|
||||||
|
.select(GroupMember::getUserId))
|
||||||
|
.stream()
|
||||||
|
.map(GroupMember::getUserId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (memberIds.isEmpty()) {
|
||||||
|
log.debug("本局{}所属群{}无成员,跳过白名单通知", roundId, groupId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sendC2C(user.getId(), "尾数试算完成,请前往修改尾数。期号: " + round.getRoundNo());
|
// 2. 取白名单用户ID集合(user.is_white = 1)
|
||||||
log.info("通知白名单用户尾数试算完成: userId={}, roundId={}", user.getId(), roundId);
|
Set<Long> whiteIds = userMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getIsWhite, 1)
|
||||||
|
.select(User::getId))
|
||||||
|
.stream()
|
||||||
|
.map(User::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (whiteIds.isEmpty()) {
|
||||||
|
log.debug("无白名单用户,跳过通知: roundId={}", roundId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 取交集:本群成员 � 白名单
|
||||||
|
memberIds.retainAll(whiteIds);
|
||||||
|
if (memberIds.isEmpty()) {
|
||||||
|
log.debug("本局{}所属群{}无白名单成员,跳过通知", roundId, groupId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = "尾数试算完成,请前往修改尾数。期号: " + round.getRoundNo();
|
||||||
|
for (Long userId : memberIds) {
|
||||||
|
sendC2C(userId, message);
|
||||||
|
log.info("通知白名单用户尾数试算完成: userId={}, groupId={}, roundId={}", userId, groupId, roundId);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("通知白名单用户失败: roundId={}, error={}", roundId, e.getMessage(), e);
|
log.error("通知白名单用户失败: roundId={}, error={}", roundId, e.getMessage(), e);
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.tailbet.mapper;
|
|
||||||
|
|
||||||
import com.tailbet.model.entity.DigitWhite;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尾数控制白名单Mapper
|
|
||||||
*/
|
|
||||||
@Mapper
|
|
||||||
public interface DigitWhiteMapper extends BaseMapper<DigitWhite> {
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.tailbet.model.entity;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尾数控制白名单表
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@TableName("digit_white")
|
|
||||||
public class DigitWhite {
|
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户ID
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建时间
|
|
||||||
*/
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,8 @@ package com.tailbet.service.impl;
|
|||||||
|
|
||||||
|
|
||||||
import com.tailbet.mapper.UserMapper;
|
import com.tailbet.mapper.UserMapper;
|
||||||
import com.tailbet.mapper.DigitWhiteMapper;
|
import com.tailbet.mapper.GroupMemberMapper;
|
||||||
import com.tailbet.model.entity.DigitWhite;
|
import com.tailbet.model.entity.GroupMember;
|
||||||
import com.tailbet.model.entity.User;
|
import com.tailbet.model.entity.User;
|
||||||
import com.tailbet.openim.OpenIMApiClient;
|
import com.tailbet.openim.OpenIMApiClient;
|
||||||
import com.tailbet.service.IPushService;
|
import com.tailbet.service.IPushService;
|
||||||
@@ -13,6 +13,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推送服务实现
|
* 推送服务实现
|
||||||
@@ -23,30 +25,76 @@ import java.util.List;
|
|||||||
public class PushServiceImpl implements IPushService {
|
public class PushServiceImpl implements IPushService {
|
||||||
|
|
||||||
private final UserMapper userMapper;
|
private final UserMapper userMapper;
|
||||||
private final DigitWhiteMapper digitWhiteMapper;
|
private final GroupMemberMapper groupMemberMapper;
|
||||||
private final OpenIMApiClient openIMApiClient;
|
private final OpenIMApiClient openIMApiClient;
|
||||||
private final com.tailbet.config.OpenIMProperties openIMProperties;
|
private final com.tailbet.config.OpenIMProperties openIMProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推送尾数试算完成通知给白名单用户
|
* 推送尾数试算完成通知给当前群的白名单用户
|
||||||
|
* <p>白名单以 {@code user.is_white=1} 为权威源,与本群成员({@code group_member})取交集后推送。
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void pushDigitTrialComplete(Long groupId, Long roundId) {
|
public void pushDigitTrialComplete(Long groupId, Long roundId) {
|
||||||
List<DigitWhite> whites = digitWhiteMapper.selectList(new LambdaQueryWrapper<DigitWhite>());
|
// 1. 取本群成员用户ID集合
|
||||||
for (DigitWhite white : whites) {
|
Set<Long> memberIds = groupMemberMapper.selectList(
|
||||||
User user = userMapper.selectById(white.getUserId());
|
new LambdaQueryWrapper<GroupMember>()
|
||||||
if (user != null) {
|
.eq(GroupMember::getGroupId, groupId)
|
||||||
IPushService.PushMessage message = new IPushService.PushMessage();
|
.select(GroupMember::getUserId))
|
||||||
message.setType(PUSH_TYPE_DIGIT_TRIAL);
|
.stream()
|
||||||
message.setTitle("尾数试算完成");
|
.map(GroupMember::getUserId)
|
||||||
message.setContent("可前往修改尾数");
|
.collect(Collectors.toSet());
|
||||||
message.setUserId(user.getId());
|
if (memberIds.isEmpty()) {
|
||||||
message.setGroupId(groupId);
|
log.debug("群{}无成员,跳过白名单通知", groupId);
|
||||||
message.setRoundId(roundId);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 取白名单用户ID集合(user.is_white = 1)
|
||||||
|
Set<Long> whiteIds = userMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getIsWhite, 1)
|
||||||
|
.select(User::getId))
|
||||||
|
.stream()
|
||||||
|
.map(User::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (whiteIds.isEmpty()) {
|
||||||
|
log.debug("无白名单用户,跳过通知: groupId={}", groupId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 交集:本群成员 ∩ 白名单
|
||||||
|
memberIds.retainAll(whiteIds);
|
||||||
|
if (memberIds.isEmpty()) {
|
||||||
|
log.debug("群{}无白名单成员,跳过通知", groupId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPushService.PushMessage template = new IPushService.PushMessage();
|
||||||
|
template.setType(PUSH_TYPE_DIGIT_TRIAL);
|
||||||
|
template.setTitle("尾数试算完成");
|
||||||
|
template.setContent("可前往修改尾数");
|
||||||
|
template.setGroupId(groupId);
|
||||||
|
template.setRoundId(roundId);
|
||||||
|
|
||||||
|
for (Long userId : memberIds) {
|
||||||
|
IPushService.PushMessage message = cloneMessage(template);
|
||||||
|
message.setUserId(userId);
|
||||||
sendPush(message);
|
sendPush(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 克隆推送消息模板,避免多个用户共享同一对象
|
||||||
|
*/
|
||||||
|
private IPushService.PushMessage cloneMessage(IPushService.PushMessage src) {
|
||||||
|
IPushService.PushMessage dst = new IPushService.PushMessage();
|
||||||
|
dst.setType(src.getType());
|
||||||
|
dst.setTitle(src.getTitle());
|
||||||
|
dst.setContent(src.getContent());
|
||||||
|
dst.setGroupId(src.getGroupId());
|
||||||
|
dst.setRoundId(src.getRoundId());
|
||||||
|
dst.setRoundNo(src.getRoundNo());
|
||||||
|
dst.setDigit(src.getDigit());
|
||||||
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -168,14 +168,16 @@ CREATE TABLE IF NOT EXISTS draw_record (
|
|||||||
INDEX idx_group_time (group_id, create_time)
|
INDEX idx_group_time (group_id, create_time)
|
||||||
) COMMENT '开奖记录表';
|
) COMMENT '开奖记录表';
|
||||||
|
|
||||||
-- 尾数控制白名单表
|
-- 尾数控制白名单表(已废弃:白名单以 user.is_white 字段为准,digit_white 表不再被应用写入)
|
||||||
CREATE TABLE IF NOT EXISTS digit_white (
|
-- 如需彻底清理可执行:
|
||||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
-- DROP TABLE IF EXISTS `digit_white`;
|
||||||
user_id BIGINT NOT NULL UNIQUE COMMENT '用户ID',
|
-- CREATE TABLE IF NOT EXISTS digit_white (
|
||||||
deleted TINYINT DEFAULT 0 COMMENT '逻辑删除',
|
-- id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
-- user_id BIGINT NOT NULL UNIQUE COMMENT '用户ID',
|
||||||
INDEX idx_user (user_id)
|
-- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除',
|
||||||
) COMMENT '尾数控制白名单';
|
-- create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
-- INDEX idx_user (user_id)
|
||||||
|
-- ) COMMENT '尾数控制白名单';
|
||||||
|
|
||||||
-- 审计日志表
|
-- 审计日志表
|
||||||
CREATE TABLE IF NOT EXISTS audit_log (
|
CREATE TABLE IF NOT EXISTS audit_log (
|
||||||
|
|||||||
Reference in New Issue
Block a user