From 6971daf9a7382f9fd39d5045d5aa34438c01ef1f Mon Sep 17 00:00:00 2001
From: Fdily <2949549024@qq.com>
Date: Tue, 14 Jul 2026 18:32:14 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4roll=E6=97=A5=E5=88=9B?=
=?UTF-8?q?=E5=BB=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
doc/官方日Roll与机器人管理.sql | 67 +++++
ruoyi-ui/src/api/skins/ttRoll/api.js | 53 +++-
ruoyi-ui/src/views/skins/robotUser/index.vue | 209 +++++++++++++
ruoyi-ui/src/views/skins/ttRoll/index.vue | 21 +-
.../views/skins/ttRoll/officialRollConfig.vue | 279 ++++++++++++++++++
ruoyi-ui/src/views/skins/ttuser/index.vue | 41 ---
.../entity/roll/TtOfficialRollConfig.java | 92 ++++++
.../com/ruoyi/domain/entity/roll/TtRoll.java | 3 +
.../TtOfficialRollConfigController.java | 88 ++++++
.../admin/controller/TtRollController.java | 7 +-
.../mapper/TtOfficialRollConfigMapper.java | 9 +
.../service/TtOfficialRollConfigService.java | 14 +
.../impl/TtOfficialRollConfigServiceImpl.java | 78 +++++
.../admin/service/impl/TtUserServiceImpl.java | 7 +-
.../ruoyi/admin/task/OfficialRollTask.java | 201 +++++++++++++
.../playingmethod/scheduled/fightTask.java | 2 +-
16 files changed, 1119 insertions(+), 52 deletions(-)
create mode 100644 doc/官方日Roll与机器人管理.sql
create mode 100644 ruoyi-ui/src/views/skins/robotUser/index.vue
create mode 100644 ruoyi-ui/src/views/skins/ttRoll/officialRollConfig.vue
create mode 100644 skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtOfficialRollConfig.java
create mode 100644 skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOfficialRollConfigController.java
create mode 100644 skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOfficialRollConfigMapper.java
create mode 100644 skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOfficialRollConfigService.java
create mode 100644 skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOfficialRollConfigServiceImpl.java
create mode 100644 skins-service/service-admin/src/main/java/com/ruoyi/admin/task/OfficialRollTask.java
diff --git a/doc/官方日Roll与机器人管理.sql b/doc/官方日Roll与机器人管理.sql
new file mode 100644
index 0000000..2b8f956
--- /dev/null
+++ b/doc/官方日Roll与机器人管理.sql
@@ -0,0 +1,67 @@
+-- ============================================================
+-- 官方日Roll定时任务 + 机器人管理页面 一次性数据库脚本
+-- 目标库:ruoyi 业务库(与 tt_roll / tt_user / sys_menu / sys_job 同库)
+-- 说明:执行一次即可;执行后请【重启后端】,随后在"系统监控-定时任务"可见"官方日Roll生成"。
+-- ============================================================
+
+-- ------------------------------------------------------------
+-- 1) 官方周期Roll房配置表
+-- ------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tt_official_roll_config` (
+ `id` int NOT NULL AUTO_INCREMENT COMMENT '配置ID',
+ `config_name` varchar(64) NOT NULL DEFAULT '' COMMENT '配置标识',
+ `jackpot_id` int DEFAULT NULL COMMENT '奖池ID(tt_roll_jackpot.jackpot_id)',
+ `period_type` varchar(16) NOT NULL DEFAULT 'DAILY' COMMENT '周期类型 DAILY每日/WEEKLY每周',
+ `room_name` varchar(64) NOT NULL DEFAULT '' COMMENT '生成的房间名称',
+ `description` varchar(255) DEFAULT '' COMMENT '房间描述',
+ `roll_password` varchar(32) DEFAULT '' COMMENT '房间密码,为空则无密码',
+ `min_recharge` decimal(10,2) DEFAULT '0.00' COMMENT '充值门槛,0为无门槛',
+ `people_num` int DEFAULT '50' COMMENT '人数上限',
+ `robot_num` int DEFAULT '0' COMMENT '每次自动加入的机器人数量,0则不加',
+ `cdk_count` int DEFAULT '0' COMMENT '每次自动创建房间生成的CDK数量,0则不启用CDK',
+ `sort_by` int DEFAULT '0' COMMENT '排序',
+ `status` char(1) DEFAULT '0' COMMENT '状态 0启用 1停用',
+ `last_generate_time` datetime DEFAULT NULL COMMENT '上次生成时间',
+ `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ `del_flag` char(1) DEFAULT '0' COMMENT '删除标志 0存在 2删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='官方周期Roll房配置' ROW_FORMAT=DYNAMIC;
+
+-- 若 tt_official_roll_config 表此前已创建(无 cdk_count 列),执行下句补列;全新执行本脚本已含该列,可跳过:
+-- ALTER TABLE `tt_official_roll_config` ADD COLUMN `cdk_count` int DEFAULT '0' COMMENT '每次自动创建房间生成的CDK数量,0则不启用CDK';
+
+-- ------------------------------------------------------------
+-- 2) tt_roll 增加 official_config_id 列(标记房间由哪条配置生成,用于每日幂等)
+-- 注意:MySQL 不支持 ADD COLUMN IF NOT EXISTS,若该列已存在请跳过本句。
+-- ------------------------------------------------------------
+ALTER TABLE `tt_roll`
+ ADD COLUMN `official_config_id` int DEFAULT NULL COMMENT '官方周期Roll配置ID(tt_official_roll_config.id),手动创建为NULL';
+
+-- ------------------------------------------------------------
+-- 3) 注册定时任务到"系统监控-定时任务":每天10点创建官方日Roll房并自动加入机器人
+-- ------------------------------------------------------------
+SET @jobId = (SELECT IFNULL(MAX(job_id), 100) + 1 FROM sys_job);
+INSERT INTO `sys_job`(`job_id`,`job_name`,`job_group`,`invoke_target`,`cron_expression`,`misfire_policy`,`concurrent`,`status`,`create_by`,`create_time`,`remark`)
+VALUES (@jobId, '官方日Roll生成', 'DEFAULT', 'officialRollTask.generateDailyRolls()', '0 0 10 * * ?', '3', '1', '0', 'admin', NOW(), '每天10点为启用的日Roll配置自动创建官方Roll房并加入机器人');
+
+-- ------------------------------------------------------------
+-- 4) 菜单:机器人管理(挂"用户管理"下)、日Roll配置(挂"Roll房设置"下)
+-- 父菜单按名称查找;若你的父菜单名称不同,请调整下面 menu_name 的匹配值。
+-- 若父菜单未找到(@xxxId 为 NULL),菜单将挂到顶级(parent_id=0),可在"菜单管理"里再拖动调整。
+-- ------------------------------------------------------------
+SET @userMgrId = (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE menu_name = '用户管理' AND menu_type = 'M' ORDER BY menu_id LIMIT 1) t);
+SET @rollMgrId = (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE menu_name = 'Roll房设置' AND menu_type = 'M' ORDER BY menu_id LIMIT 1) t);
+SET @menuBase = (SELECT IFNULL(MAX(menu_id), 2000) FROM sys_menu);
+
+INSERT INTO `sys_menu`(`menu_id`,`menu_name`,`parent_id`,`order_num`,`path`,`component`,`query`,`is_frame`,`is_cache`,`menu_type`,`visible`,`status`,`perms`,`icon`,`create_by`,`create_time`)
+VALUES (@menuBase + 1, '机器人管理', IFNULL(@userMgrId, 0), 5, 'robotUser', 'skins/robotUser/index', '', 1, 0, 'C', '0', '0', 'admin:robot:list', 'user', 'admin', NOW());
+
+INSERT INTO `sys_menu`(`menu_id`,`menu_name`,`parent_id`,`order_num`,`path`,`component`,`query`,`is_frame`,`is_cache`,`menu_type`,`visible`,`status`,`perms`,`icon`,`create_by`,`create_time`)
+VALUES (@menuBase + 2, '日Roll配置', IFNULL(@rollMgrId, 0), 9, 'officialRollConfig', 'skins/ttRoll/officialRollConfig', '', 1, 0, 'C', '0', '0', 'admin:officialRollConfig:list', 'time', 'admin', NOW());
+
+-- ============================================================
+-- 完成。若为非 admin 角色,请在"角色管理"里为角色勾选以上两个菜单权限。
+-- ============================================================
diff --git a/ruoyi-ui/src/api/skins/ttRoll/api.js b/ruoyi-ui/src/api/skins/ttRoll/api.js
index ec6fd38..1d2926a 100644
--- a/ruoyi-ui/src/api/skins/ttRoll/api.js
+++ b/ruoyi-ui/src/api/skins/ttRoll/api.js
@@ -225,11 +225,12 @@ export function generateRollCdk(data) {
});
}
-/** 查询Roll房CDK列表 */
-export function getRollCdkList(rollId) {
+/** 查询Roll房CDK列表(分页) */
+export function getRollCdkList(rollId, query) {
return request({
url: `/admin/roll/cdk/list/${rollId}`,
- method: 'get'
+ method: 'get',
+ params: query
});
}
@@ -240,3 +241,49 @@ export function deleteRollCdk(cdkId) {
method: 'delete'
});
}
+
+// ---- 官方周期Roll配置(日Roll/周Roll 自动创建) ----
+
+// 官方周期Roll配置-列表
+export function officialRollConfigList(query) {
+ return request({
+ url: '/admin/officialRollConfig/list',
+ method: 'get',
+ params: query
+ });
+}
+
+// 官方周期Roll配置-新增
+export function officialRollConfigAdd(data) {
+ return request({
+ url: '/admin/officialRollConfig',
+ method: 'post',
+ data: data
+ });
+}
+
+// 官方周期Roll配置-编辑
+export function officialRollConfigChange(data) {
+ return request({
+ url: '/admin/officialRollConfig',
+ method: 'put',
+ data: data
+ });
+}
+
+// 官方周期Roll配置-删除
+export function officialRollConfigDel(id) {
+ return request({
+ url: `/admin/officialRollConfig/${id}`,
+ method: 'delete'
+ });
+}
+
+// 官方周期Roll配置-启用/停用
+export function officialRollConfigChangeStatus(data) {
+ return request({
+ url: '/admin/officialRollConfig/changeStatus',
+ method: 'put',
+ data: data
+ });
+}
diff --git a/ruoyi-ui/src/views/skins/robotUser/index.vue b/ruoyi-ui/src/views/skins/robotUser/index.vue
new file mode 100644
index 0000000..a7f1081
--- /dev/null
+++ b/ruoyi-ui/src/views/skins/robotUser/index.vue
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 生成机器人
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ VIP{{ scope.row.vipLevel }}
+
+
+
+ {{ parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}:{s}") }}
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ 要生成的机器人数量(每次最多10个)
+
+
+
+
+
+
+
+
+
+
diff --git a/ruoyi-ui/src/views/skins/ttRoll/index.vue b/ruoyi-ui/src/views/skins/ttRoll/index.vue
index 8b7f7fa..e788fd7 100644
--- a/ruoyi-ui/src/views/skins/ttRoll/index.vue
+++ b/ruoyi-ui/src/views/skins/ttRoll/index.vue
@@ -318,6 +318,14 @@
+
@@ -401,7 +409,12 @@ export default {
cdkList: [],
cdkLoading: false,
cdkGenCount: 10,
- currentCdkRollId: null
+ currentCdkRollId: null,
+ cdkTotal: 0,
+ cdkQueryParams: {
+ pageNum: 1,
+ pageSize: 10
+ }
};
},
mounted() {
@@ -577,14 +590,16 @@ export default {
// 打开CDK管理弹窗
handleCdk(row) {
this.currentCdkRollId = row.id;
+ this.cdkQueryParams.pageNum = 1;
this.cdkDialogVisible = true;
this.loadCdkList();
},
// 加载CDK列表
loadCdkList() {
this.cdkLoading = true;
- getRollCdkList(this.currentCdkRollId).then(res => {
- this.cdkList = res.data || [];
+ getRollCdkList(this.currentCdkRollId, this.cdkQueryParams).then(res => {
+ this.cdkList = res.rows || [];
+ this.cdkTotal = res.total || 0;
this.cdkLoading = false;
});
},
diff --git a/ruoyi-ui/src/views/skins/ttRoll/officialRollConfig.vue b/ruoyi-ui/src/views/skins/ttRoll/officialRollConfig.vue
new file mode 100644
index 0000000..aceb120
--- /dev/null
+++ b/ruoyi-ui/src/views/skins/ttRoll/officialRollConfig.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+ {{ jackpotName(scope.row.jackpotId) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 每天自动创建房间后加入的机器人数量,为0则不加机器人
+
+
+
+
+
+
+
+
+ 为空表示无密码
+
+
+
+
+
+ 填0则不启用CDK模式,使用普通密码
+
+
+
+
+ 启用
+ 停用
+
+
+
+
+
+
+
+
+
diff --git a/ruoyi-ui/src/views/skins/ttuser/index.vue b/ruoyi-ui/src/views/skins/ttuser/index.vue
index c6936a7..789ae98 100644
--- a/ruoyi-ui/src/views/skins/ttuser/index.vue
+++ b/ruoyi-ui/src/views/skins/ttuser/index.vue
@@ -71,14 +71,6 @@
v-hasPermi="['admin:user:export']"
>导出
-
- 生成机器人
-
@@ -114,21 +106,6 @@
确 定
-
-
-
-
-
-
-
- 要生成的账号数量
-
-
-
-
{
- this.getList();
- (this.newNumber = null), (this.addRobot = false);
- const blob = new Blob([res], {
- type: "text/plain;charset=utf-8"
- });
- const objectURL = URL.createObjectURL(blob);
- const aTag = document.createElement("a");
- aTag.href = objectURL;
- aTag.download = `账号数据_${new Date().getTime()}.txt`;
- aTag.click();
- URL.revokeObjectURL(objectURL);
- });
- },
/** 查询注册用户列表 */
getList() {
this.loading = true;
diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtOfficialRollConfig.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtOfficialRollConfig.java
new file mode 100644
index 0000000..4c2f8e1
--- /dev/null
+++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtOfficialRollConfig.java
@@ -0,0 +1,92 @@
+package com.ruoyi.domain.entity.roll;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 官方周期Roll房配置模板。
+ * 每条配置指定奖池、周期(日/周)、充值门槛、人数、机器人数量等,
+ * 由 OfficialRollTask 定时按周期自动创建 tt_roll 房间并自动加入机器人。
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+@Builder
+@TableName(value = "tt_official_roll_config")
+public class TtOfficialRollConfig implements Serializable {
+
+ @TableField(exist = false)
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+
+ @Excel(name = "配置标识")
+ private String configName;
+
+ @Excel(name = "奖池ID")
+ private Integer jackpotId;
+
+ /** 周期类型: DAILY=每日, WEEKLY=每周 */
+ @Excel(name = "周期类型")
+ private String periodType;
+
+ @Excel(name = "房间名称")
+ private String roomName;
+
+ @Excel(name = "房间描述")
+ private String description;
+
+ private String rollPassword;
+
+ @Excel(name = "充值门槛")
+ private BigDecimal minRecharge;
+
+ @Excel(name = "人数上限")
+ private Integer peopleNum;
+
+ /** 每天(每周期)自动加入的机器人数量 */
+ @Excel(name = "机器人数量")
+ private Integer robotNum;
+
+ /** 每次自动创建房间生成的CDK数量,0则不启用CDK(使用普通密码) */
+ @Excel(name = "CDK数量")
+ private Integer cdkCount;
+
+ @Excel(name = "排序依据")
+ private Integer sortBy;
+
+ /** 状态: 0=启用, 1=停用 */
+ @Excel(name = "状态")
+ private String status;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date lastGenerateTime;
+
+ private String createBy;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date createTime;
+
+ private String updateBy;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date updateTime;
+
+ @TableField(select = false)
+ private String delFlag;
+}
diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java
index 1f80056..4c97ff3 100644
--- a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java
+++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java
@@ -82,4 +82,7 @@ public class TtRoll implements Serializable {
@TableField(select = false)
private String delFlag;
+ // 官方周期Roll配置ID(tt_official_roll_config.id),标记该房由哪条配置生成,手动创建的房间为NULL
+ private Integer officialConfigId;
+
}
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOfficialRollConfigController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOfficialRollConfigController.java
new file mode 100644
index 0000000..796c49c
--- /dev/null
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOfficialRollConfigController.java
@@ -0,0 +1,88 @@
+package com.ruoyi.admin.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.admin.service.TtOfficialRollConfigService;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.page.PageDataInfo;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Api(tags = "管理端 官方周期Roll配置")
+@RestController
+@RequestMapping("/admin/officialRollConfig")
+public class TtOfficialRollConfigController extends BaseController {
+
+ private final TtOfficialRollConfigService configService;
+
+ public TtOfficialRollConfigController(TtOfficialRollConfigService configService) {
+ this.configService = configService;
+ }
+
+ @ApiOperation("配置列表")
+ @GetMapping("/list")
+ public PageDataInfo list(
+ @RequestParam(value = "configName", required = false) String configName,
+ @RequestParam(value = "periodType", required = false) String periodType,
+ @RequestParam(value = "status", required = false) String status) {
+ startPage();
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ if (StringUtils.isNotEmpty(configName)) wrapper.like(TtOfficialRollConfig::getConfigName, configName);
+ if (StringUtils.isNotEmpty(periodType)) wrapper.eq(TtOfficialRollConfig::getPeriodType, periodType);
+ if (StringUtils.isNotEmpty(status)) wrapper.eq(TtOfficialRollConfig::getStatus, status);
+ wrapper.orderByAsc(TtOfficialRollConfig::getSortBy).orderByDesc(TtOfficialRollConfig::getCreateTime);
+ List configList = configService.list(wrapper);
+ return getPageData(configList);
+ }
+
+ @ApiOperation("配置详情")
+ @GetMapping("/{id}")
+ public R getInfo(@PathVariable("id") Integer id) {
+ return R.ok(configService.getById(id));
+ }
+
+ @ApiOperation("新增配置")
+ @PostMapping
+ public AjaxResult add(@RequestBody TtOfficialRollConfig config) {
+ config.setCreateBy(getUsername());
+ config.setCreateTime(DateUtils.getNowDate());
+ return configService.addConfig(config);
+ }
+
+ @ApiOperation("编辑配置")
+ @PutMapping
+ public AjaxResult edit(@RequestBody TtOfficialRollConfig config) {
+ config.setUpdateBy(getUsername());
+ config.setUpdateTime(DateUtils.getNowDate());
+ return configService.editConfig(config);
+ }
+
+ @ApiOperation("删除配置")
+ @DeleteMapping("/{id}")
+ public AjaxResult remove(@PathVariable("id") Integer id) {
+ return configService.removeById(id) ? AjaxResult.success() : AjaxResult.error("删除失败");
+ }
+
+ @ApiOperation("启用/停用配置")
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody TtOfficialRollConfig config) {
+ if (config.getId() == null) return AjaxResult.error("配置ID不能为空");
+ if (!"0".equals(config.getStatus()) && !"1".equals(config.getStatus())) {
+ return AjaxResult.error("状态不合法");
+ }
+ TtOfficialRollConfig update = new TtOfficialRollConfig();
+ update.setId(config.getId());
+ update.setStatus(config.getStatus());
+ update.setUpdateBy(getUsername());
+ update.setUpdateTime(DateUtils.getNowDate());
+ return configService.updateById(update) ? AjaxResult.success() : AjaxResult.error("操作失败");
+ }
+}
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java
index 66332f7..955ce9f 100644
--- a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java
@@ -201,12 +201,13 @@ public class TtRollController extends BaseController {
return success("生成成功");
}
- /** 查看Roll房所有CDK列表 */
+ /** 查看Roll房所有CDK列表(分页) */
@ApiOperation("查看Roll房CDK列表")
@GetMapping("/cdk/list/{rollId}")
- public AjaxResult getCdkList(@PathVariable("rollId") Integer rollId) {
+ public PageDataInfo getCdkList(@PathVariable("rollId") Integer rollId) {
+ startPage();
List list = rollCdkService.listByRollId(rollId);
- return success(list);
+ return getPageData(list);
}
/** 删除指定CDK */
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOfficialRollConfigMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOfficialRollConfigMapper.java
new file mode 100644
index 0000000..7fe95af
--- /dev/null
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOfficialRollConfigMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.admin.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface TtOfficialRollConfigMapper extends BaseMapper {
+}
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOfficialRollConfigService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOfficialRollConfigService.java
new file mode 100644
index 0000000..8f11ffc
--- /dev/null
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOfficialRollConfigService.java
@@ -0,0 +1,14 @@
+package com.ruoyi.admin.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
+
+public interface TtOfficialRollConfigService extends IService {
+
+ /** 新增配置(含校验) */
+ AjaxResult addConfig(TtOfficialRollConfig config);
+
+ /** 编辑配置(含校验) */
+ AjaxResult editConfig(TtOfficialRollConfig config);
+}
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOfficialRollConfigServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOfficialRollConfigServiceImpl.java
new file mode 100644
index 0000000..42ce524
--- /dev/null
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOfficialRollConfigServiceImpl.java
@@ -0,0 +1,78 @@
+package com.ruoyi.admin.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.admin.mapper.TtOfficialRollConfigMapper;
+import com.ruoyi.admin.service.TtOfficialRollConfigService;
+import com.ruoyi.admin.service.TtRollJackpotService;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
+import com.ruoyi.domain.entity.roll.TtRollJackpot;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+@Service
+public class TtOfficialRollConfigServiceImpl
+ extends ServiceImpl
+ implements TtOfficialRollConfigService {
+
+ /** 周期类型:每日 */
+ public static final String PERIOD_DAILY = "DAILY";
+
+ @Autowired
+ private TtRollJackpotService rollJackpotService;
+
+ @Override
+ public AjaxResult addConfig(TtOfficialRollConfig config) {
+ AjaxResult check = validate(config);
+ if (check != null) return check;
+ normalize(config);
+ return save(config) ? AjaxResult.success() : AjaxResult.error("新增失败");
+ }
+
+ @Override
+ public AjaxResult editConfig(TtOfficialRollConfig config) {
+ if (config.getId() == null) return AjaxResult.error("配置ID不能为空");
+ AjaxResult check = validate(config);
+ if (check != null) return check;
+ return updateById(config) ? AjaxResult.success() : AjaxResult.error("修改失败");
+ }
+
+ /**
+ * 基本校验:配置名、房间名、奖池存在、周期合法、人数>0、门槛/机器人数非负;返回 null 表示通过
+ */
+ private AjaxResult validate(TtOfficialRollConfig config) {
+ if (config == null) return AjaxResult.error("参数不能为空");
+ if (StringUtils.isEmpty(config.getConfigName())) return AjaxResult.error("配置标识不能为空");
+ if (StringUtils.isEmpty(config.getRoomName())) return AjaxResult.error("房间名称不能为空");
+ if (config.getJackpotId() == null) return AjaxResult.error("请选择奖池");
+ TtRollJackpot jackpot = rollJackpotService.getById(config.getJackpotId());
+ if (jackpot == null) return AjaxResult.error("指定的奖池不存在");
+ String period = config.getPeriodType();
+ if (!PERIOD_DAILY.equals(period)) {
+ return AjaxResult.error("周期类型不合法,仅支持 DAILY");
+ }
+ if (config.getPeopleNum() == null || config.getPeopleNum() <= 0) {
+ return AjaxResult.error("人数上限必须大于0");
+ }
+ if (config.getMinRecharge() != null && config.getMinRecharge().compareTo(BigDecimal.ZERO) < 0) {
+ return AjaxResult.error("充值门槛不能为负数");
+ }
+ if (config.getRobotNum() != null && config.getRobotNum() < 0) {
+ return AjaxResult.error("机器人数量不能为负数");
+ }
+ return null;
+ }
+
+ /**
+ * 归一化默认值
+ */
+ private void normalize(TtOfficialRollConfig config) {
+ if (config.getMinRecharge() == null) config.setMinRecharge(BigDecimal.ZERO);
+ if (config.getRobotNum() == null) config.setRobotNum(0);
+ if (config.getSortBy() == null) config.setSortBy(0);
+ if (StringUtils.isEmpty(config.getStatus())) config.setStatus("0");
+ }
+}
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java
index 4210bfa..d22a31f 100644
--- a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java
@@ -132,7 +132,12 @@ public class TtUserServiceImpl extends ServiceImpl impleme
wrapper.like(TtUser::getUserName, ttUserBody.getUserName());
if (StringUtils.isNotNull(ttUserBody.getNickName()))
wrapper.like(TtUser::getNickName, ttUserBody.getNickName());
- if (StringUtils.isNotNull(ttUserBody.getUserType())) wrapper.eq(TtUser::getUserType, ttUserBody.getUserType());
+ if (StringUtils.isNotEmpty(ttUserBody.getUserType())) {
+ wrapper.eq(TtUser::getUserType, ttUserBody.getUserType());
+ } else {
+ // 用户列表默认排除机器人(userType=03),机器人在独立的机器人管理页显式传 userType=03 查询
+ wrapper.and(w -> w.isNull(TtUser::getUserType).or().ne(TtUser::getUserType, "03"));
+ }
if (StringUtils.isNotEmpty(ttUserBody.getPhoneNumber()))
wrapper.likeRight(TtUser::getPhoneNumber, ttUserBody.getPhoneNumber());
if (StringUtils.isNotEmpty(ttUserBody.getStatus())) wrapper.eq(TtUser::getStatus, ttUserBody.getStatus());
diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/OfficialRollTask.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/OfficialRollTask.java
new file mode 100644
index 0000000..7b3a27d
--- /dev/null
+++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/OfficialRollTask.java
@@ -0,0 +1,201 @@
+package com.ruoyi.admin.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.admin.domain.body.AddRobotBody;
+import com.ruoyi.admin.service.TtOfficialRollConfigService;
+import com.ruoyi.admin.service.TtRollCdkService;
+import com.ruoyi.admin.service.TtRollService;
+import com.ruoyi.admin.service.TtUserService;
+import com.ruoyi.admin.service.impl.TtOfficialRollConfigServiceImpl;
+import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
+import com.ruoyi.domain.entity.roll.TtRoll;
+import com.ruoyi.domain.entity.sys.TtUser;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 官方周期Roll房定时创建任务。
+ *
+ * 注册到 RuoYi 系统监控-定时任务(由 sys_job 统一调度,不加 @Scheduled):
+ * - 任务名称:官方日Roll生成 调用目标:officialRollTask.generateDailyRolls() 默认cron:0 0 10 * * ?(每天10:00)
+ *
+ * 复用官方房(roll_type=0)语义,直接创建 tt_roll,并按配置的机器人数量自动加入机器人。
+ */
+@Slf4j
+@Component("officialRollTask")
+public class OfficialRollTask {
+
+ /** 官方房类型 */
+ private static final String ROLL_TYPE_OFFICIAL = "0";
+ /** 未开奖状态 */
+ private static final String ROLL_STATUS_OPEN = "0";
+ /** 官方房创建者ID(0=官方) */
+ private static final Integer OFFICIAL_USER_ID = 0;
+ /** 机器人用户类型 */
+ private static final String ROBOT_USER_TYPE = "03";
+
+ @Autowired
+ private TtOfficialRollConfigService configService;
+
+ @Autowired
+ private TtRollService rollService;
+
+ @Autowired
+ private TtUserService userService;
+
+ @Autowired
+ private TtRollCdkService rollCdkService;
+
+ /**
+ * 每天为所有启用的 DAILY 配置创建当日官方roll房并自动加入机器人。
+ * 充值统计起点=当天0点(今日充值),开奖=当日23:59:59。
+ */
+ public void generateDailyRolls() {
+ Date start = todayStart();
+ Date end = todayEnd();
+ generate(TtOfficialRollConfigServiceImpl.PERIOD_DAILY, start, end);
+ }
+
+ private void generate(String periodType, Date rechargeStartTime, Date endTime) {
+ log.info("[官方周期Roll] 开始生成 period={}, start={}, end={}", periodType, rechargeStartTime, endTime);
+
+ LambdaQueryWrapper query = Wrappers.lambdaQuery();
+ query.eq(TtOfficialRollConfig::getStatus, "0")
+ .eq(TtOfficialRollConfig::getPeriodType, periodType);
+ List configs = configService.list(query);
+
+ if (configs == null || configs.isEmpty()) {
+ log.info("[官方周期Roll] 无启用的 {} 配置,跳过", periodType);
+ return;
+ }
+
+ int created = 0;
+ int skipped = 0;
+ for (TtOfficialRollConfig config : configs) {
+ try {
+ // 幂等:同一配置 + 同一充值统计起点 至多生成一个房间
+ long exists = rollService.count(Wrappers.lambdaQuery()
+ .eq(TtRoll::getOfficialConfigId, config.getId())
+ .eq(TtRoll::getRechargeStartTime, rechargeStartTime));
+ if (exists > 0) {
+ skipped++;
+ log.info("[官方周期Roll] 配置{}({})本周期房间已存在,跳过", config.getId(), config.getConfigName());
+ continue;
+ }
+
+ TtRoll roll = TtRoll.builder()
+ .jackpotId(config.getJackpotId())
+ .userId(OFFICIAL_USER_ID)
+ .rollType(ROLL_TYPE_OFFICIAL)
+ .rollName(config.getRoomName())
+ .description(config.getDescription())
+ .endTime(endTime)
+ .peopleNum(config.getPeopleNum())
+ .rollPassword(config.getRollPassword())
+ .minRecharge(config.getMinRecharge() != null ? config.getMinRecharge() : BigDecimal.ZERO)
+ .rechargeStartTime(rechargeStartTime)
+ .sortBy(config.getSortBy())
+ .rollStatus(ROLL_STATUS_OPEN)
+ .officialConfigId(config.getId())
+ .createBy("system")
+ .createTime(new Date())
+ .build();
+ rollService.save(roll);
+
+ // 自动加入机器人:数量取配置值,且不超过可用机器人总数;addRobot 会自动跳过该房已有机器人
+ addRobots(roll.getId(), config);
+
+ // 按配置数量为房间生成CDK(0则不生成,使用普通密码)
+ generateCdks(roll.getId(), config);
+
+ // 回写上次生成时间(幂等辅助/可观测)
+ TtOfficialRollConfig update = new TtOfficialRollConfig();
+ update.setId(config.getId());
+ update.setLastGenerateTime(new Date());
+ configService.updateById(update);
+
+ created++;
+ log.info("[官方周期Roll] 配置{}({}) 已生成房间 rollId={},起点={},开奖={}",
+ config.getId(), config.getConfigName(), roll.getId(), rechargeStartTime, endTime);
+ } catch (Exception e) {
+ log.error("[官方周期Roll] 配置{}生成房间失败:{}", config.getId(), e.getMessage(), e);
+ }
+ }
+
+ log.info("[官方周期Roll] 生成完成 period={}, 配置数={}, 新建={}, 跳过={}",
+ periodType, configs.size(), created, skipped);
+ }
+
+ /**
+ * 为指定房间按配置数量加入机器人。数量取 min(配置机器人数, 可用机器人总数),避免"可用机器人数量不足"导致一个都不加。
+ */
+ private void addRobots(Integer rollId, TtOfficialRollConfig config) {
+ Integer robotNum = config.getRobotNum();
+ if (rollId == null || robotNum == null || robotNum <= 0) {
+ return;
+ }
+ try {
+ long robotTotal = userService.count(new QueryWrapper().eq("user_type", ROBOT_USER_TYPE));
+ int toAdd = (int) Math.min(robotNum.longValue(), robotTotal);
+ if (toAdd <= 0) {
+ log.warn("[官方周期Roll] 配置{}需要{}个机器人,但用户表无可用机器人,跳过加入", config.getId(), robotNum);
+ return;
+ }
+ AddRobotBody body = new AddRobotBody();
+ body.setRollId(rollId);
+ body.setRobotNum(toAdd);
+ rollService.addRobot(body);
+ log.info("[官方周期Roll] 配置{} 房间{} 已尝试加入{}个机器人", config.getId(), rollId, toAdd);
+ } catch (Exception e) {
+ log.error("[官方周期Roll] 配置{} 房间{} 加入机器人失败:{}", config.getId(), rollId, e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 按配置数量为房间生成CDK;0或空则不生成(使用普通密码)。
+ */
+ private void generateCdks(Integer rollId, TtOfficialRollConfig config) {
+ Integer cdkCount = config.getCdkCount();
+ if (rollId == null || cdkCount == null || cdkCount <= 0) {
+ return;
+ }
+ try {
+ rollCdkService.generateCdks(rollId, cdkCount);
+ log.info("[官方周期Roll] 配置{} 房间{} 已生成{}个CDK", config.getId(), rollId, cdkCount);
+ } catch (Exception e) {
+ log.error("[官方周期Roll] 配置{} 房间{} 生成CDK失败:{}", config.getId(), rollId, e.getMessage(), e);
+ }
+ }
+
+ /** 今天0点 */
+ private Date todayStart() {
+ Calendar c = Calendar.getInstance();
+ clearTime(c);
+ return c.getTime();
+ }
+
+ /** 今天23:59:59 */
+ private Date todayEnd() {
+ Calendar c = Calendar.getInstance();
+ c.set(Calendar.HOUR_OF_DAY, 23);
+ c.set(Calendar.MINUTE, 59);
+ c.set(Calendar.SECOND, 59);
+ c.set(Calendar.MILLISECOND, 0);
+ return c.getTime();
+ }
+
+ private void clearTime(Calendar c) {
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ }
+}
diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java
index 174eb4e..cd63990 100644
--- a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java
+++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java
@@ -50,7 +50,7 @@ public class fightTask {
private Executor customThreadPoolExecutor;
// 定时更新超时未结束的对局
- @Scheduled(cron = "0/6 * * * * ?")
+ //@Scheduled(cron = "0/6 * * * * ?")
private void refreshDayTask() {
log.info("定时更新超时未结束的对局");
// Timestamp now = new Timestamp(System.currentTimeMillis());