From 9d3fdfe328106d754d873467e61d54a97e24a224 Mon Sep 17 00:00:00 2001 From: milimoe Date: Mon, 25 May 2026 22:50:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OshimaServers/Model/CSBettingModels.cs | 3 + OshimaServers/Model/QQBot.cs | 10 ++ OshimaServers/Service/CSBettingService.cs | 38 ++++- .../Controllers/CSBettingController.cs | 15 +- .../Services/CSBettingInputHandler.cs | 149 ++++++++++++++---- OshimaWebAPI/Services/RainBOTService.cs | 8 +- 6 files changed, 182 insertions(+), 41 deletions(-) diff --git a/OshimaServers/Model/CSBettingModels.cs b/OshimaServers/Model/CSBettingModels.cs index 63e2a37..f0aab6f 100644 --- a/OshimaServers/Model/CSBettingModels.cs +++ b/OshimaServers/Model/CSBettingModels.cs @@ -51,6 +51,9 @@ namespace Oshima.FunGame.WebAPI.Model [JsonPropertyName("team1_win_probability")] public decimal? Team1WinProbability { get; set; } + + [JsonPropertyName("betting_enabled")] + public bool? BettingEnabled { get; set; } } public class UpdateMatchRequest diff --git a/OshimaServers/Model/QQBot.cs b/OshimaServers/Model/QQBot.cs index 4e3a857..fb2fc4e 100644 --- a/OshimaServers/Model/QQBot.cs +++ b/OshimaServers/Model/QQBot.cs @@ -85,6 +85,7 @@ namespace Oshima.FunGame.OshimaServers.Model public long FunGameUID { get; set; } public bool UseNotice { get; set; } public string ImageUrl { get; set; } + public bool SendProactive { get; set; } } public class BotReply @@ -267,6 +268,9 @@ namespace Oshima.FunGame.OshimaServers.Model [JsonIgnore] public string ImageUrl { get; set; } = ""; + + [JsonIgnore] + public bool SendProactive { get; set; } } public class C2CMessage : IBotMessage @@ -303,6 +307,9 @@ namespace Oshima.FunGame.OshimaServers.Model public string OpenId => Author.UserOpenId; public bool IsGroup => false; public string AuthorOpenId => Author.UserOpenId; + + [JsonIgnore] + public bool SendProactive { get; set; } } public class GroupAtMessage : IBotMessage @@ -342,6 +349,9 @@ namespace Oshima.FunGame.OshimaServers.Model public string OpenId => GroupOpenId; public bool IsGroup => true; public string AuthorOpenId => Author.MemberOpenId; + + [JsonIgnore] + public bool SendProactive { get; set; } } public class MediaResponse diff --git a/OshimaServers/Service/CSBettingService.cs b/OshimaServers/Service/CSBettingService.cs index 9d5299b..70b5a99 100644 --- a/OshimaServers/Service/CSBettingService.cs +++ b/OshimaServers/Service/CSBettingService.cs @@ -324,6 +324,35 @@ namespace Oshima.FunGame.WebAPI.Services return "数据库连接失败。"; } + public static bool GetMatchTimes(int matchId, out DateTime startTime, out DateTime betDeadline, out int status, out string error) + { + startTime = DateTime.MinValue; + betDeadline = DateTime.MinValue; + status = -1; + error = ""; + + using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); + if (sql == null) + { + error = "数据库连接失败。"; + return false; + } + + sql.Parameters["@mid"] = matchId; + sql.ExecuteDataSet("SELECT start_time, bet_deadline, status FROM csbetting_matches WHERE id = @mid"); + if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0) + { + error = "比赛不存在。"; + return false; + } + + DataRow row = sql.DataSet.Tables[0].Rows[0]; + startTime = Convert.ToDateTime(row["start_time"]); + betDeadline = Convert.ToDateTime(row["bet_deadline"]); + status = Convert.ToInt32(row["status"]); + return true; + } + public static bool PlaceBet(long uid, int matchId, string option, long amount, out string error) { error = ""; @@ -770,7 +799,7 @@ namespace Oshima.FunGame.WebAPI.Services } // 创建比赛 - public static bool CreateMatch(int eventId, string team1Name, string team2Name, string stage, DateTime startTime, DateTime betDeadline, string availableOptions, decimal? team1WinOdds, decimal? team2WinOdds, decimal? team1WinProbability, out string error, out long? newMatchId) + public static bool CreateMatch(int eventId, string team1Name, string team2Name, string stage, DateTime startTime, DateTime betDeadline, string availableOptions, decimal? team1WinOdds, decimal? team2WinOdds, decimal? team1WinProbability, bool? enableBet, out string error, out long? newMatchId) { error = ""; newMatchId = null; @@ -822,9 +851,7 @@ namespace Oshima.FunGame.WebAPI.Services } else { - // 默认双方各 2.0 - t1Odds = 2.0m; - t2Odds = 2.0m; + (t1Odds, t2Odds) = CalculateOdds(0.5M); } // 校验奖励率大于0 @@ -843,9 +870,10 @@ namespace Oshima.FunGame.WebAPI.Services sql.Parameters["@opts"] = optionsJson; sql.Parameters["@t1_odds"] = t1Odds; sql.Parameters["@t2_odds"] = t2Odds; + sql.Parameters["@betting_enabled"] = (enableBet ?? false) ? 1 : 0; sql.Execute(@"INSERT INTO csbetting_matches (event_id, team1_name, team2_name, stage, start_time, bet_deadline, available_options, team1_win_odds, team2_win_odds, status) - VALUES (@eid, @t1, @t2, @stage, @start, @deadline, @opts, @t1_odds, @t2_odds, 0)"); + VALUES (@eid, @t1, @t2, @stage, @start, @deadline, @opts, @t1_odds, @t2_odds, 0, @betting_enabled)"); if (sql.Success) { diff --git a/OshimaWebAPI/Controllers/CSBettingController.cs b/OshimaWebAPI/Controllers/CSBettingController.cs index 1c4b03c..cbc9323 100644 --- a/OshimaWebAPI/Controllers/CSBettingController.cs +++ b/OshimaWebAPI/Controllers/CSBettingController.cs @@ -79,6 +79,19 @@ namespace Oshima.FunGame.WebAPI.Controllers return new BotReply { Markdown = new MarkdownMessage { Content = content } }; } + [HttpGet("match/time/{mid:int}")] + public BotReply GetMatchTimes(int mid) + { + MarkdownMessage md = new() { Content = busy }; + BotReply reply = new() { Markdown = md }; + if (CSBettingService.GetMatchTimes(mid, out DateTime startTime, out DateTime betDeadline, out int status, out string error)) + { + md.Content = $"比赛开始时间:{startTime:yyyy-MM-dd HH:mm:ss}\n预测截止时间:{betDeadline:yyyy-MM-dd HH:mm:ss}\n当前状态:{(status == 0 ? "未开始" : status == 1 ? "进行中" : "已结束")}"; + } + else md.Content = error; + return reply; + } + // ---------- 需要用户锁的操作 ---------- /// @@ -264,7 +277,7 @@ namespace Oshima.FunGame.WebAPI.Controllers } if (CSBettingService.CreateMatch(request.EventId, request.Team1Name, request.Team2Name, request.Stage, - request.StartTime, request.BetDeadline, request.AvailableOptions, request.Team1WinOdds, request.Team2WinOdds, request.Team1WinProbability, out string error, out long? newId)) + request.StartTime, request.BetDeadline, request.AvailableOptions, request.Team1WinOdds, request.Team2WinOdds, request.Team1WinProbability, request.BettingEnabled, out string error, out long? newId)) { md.Content = $"比赛创建成功!新比赛ID:{newId}"; reply.Keyboard = new KeyboardMessage().AppendButtons(1, Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {newId}")); diff --git a/OshimaWebAPI/Services/CSBettingInputHandler.cs b/OshimaWebAPI/Services/CSBettingInputHandler.cs index 84e8a35..e2065ff 100644 --- a/OshimaWebAPI/Services/CSBettingInputHandler.cs +++ b/OshimaWebAPI/Services/CSBettingInputHandler.cs @@ -409,6 +409,21 @@ namespace Oshima.FunGame.WebAPI.Services return true; } } + bool enableBet = true; + if (paramDict.TryGetValue("be", out string? be)) + { + if (be == "0" || be == "1") enableBet = be == "1"; + else + { + await SendAsync(e, "创建比赛", "enabled 值必须为 0 或 1。"); + return true; + } + } + // 如果队伍包含TBD,则默认不可预测 + if (team1 == "TBD" || team2 == "TBD") + { + enableBet = false; + } BotReply reply = BettingController.CreateMatch(new CreateMatchRequest { @@ -422,42 +437,13 @@ namespace Oshima.FunGame.WebAPI.Services AvailableOptions = options, Team1WinOdds = team1Odds, Team2WinOdds = team2Odds, - Team1WinProbability = team1WinProbability + Team1WinProbability = team1WinProbability, + BettingEnabled = enableBet }); await SendAsync(e, "创建比赛", reply); return true; } - // 指令:关闭预测 <比赛ID> - if (e.Detail.StartsWith("关闭预测") || e.Detail.StartsWith("结束预测")) - { - e.UseNotice = false; - if (!FunGameConstant.UserIdAndUsername.TryGetValue(uid, out User? user) || (!user.IsAdmin && !user.IsOperator)) - { - await SendAsync(e, "关闭预测", "你没有权限执行此操作。"); - return true; - } - - string detail = e.Detail - .Replace("关闭预测", "") - .Replace("结束预测", "") - .Trim(); - if (int.TryParse(detail, out int matchId)) - { - BotReply reply = BettingController.CloseBetting(uid, matchId); - reply.Keyboard = new KeyboardMessage() - .AppendButtons(2, - Button.CreateCmdButton("📋 赛事列表", "赛事列表"), - Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}")); - await SendAsync(e, "关闭预测", reply); - } - else - { - await SendAsync(e, "关闭预测", "格式:关闭预测 <比赛ID>"); - } - return true; - } - // 指令:修改比赛 <比赛ID> [参数=值 ...] if (e.Detail.StartsWith("修改比赛")) { @@ -581,6 +567,107 @@ namespace Oshima.FunGame.WebAPI.Services return true; } + if (e.Detail.StartsWith("推迟比赛")) + { + e.UseNotice = false; + if (!FunGameConstant.UserIdAndUsername.TryGetValue(uid, out User? user) || (!user.IsAdmin && !user.IsOperator)) + { + await SendAsync(e, "修改比赛", "你没有权限执行此操作。"); + return true; + } + + string detail = e.Detail.Replace("推迟比赛", "").Trim(); + string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length < 2 || !int.TryParse(parts[0], out int matchId) || !int.TryParse(parts[1], out int minutes) || minutes <= 0) + { + await SendAsync(e, "修改比赛", "格式:推迟比赛 <比赛ID> <分钟数>\n示例:推迟比赛 123 30"); + return true; + } + + // 获取当前比赛时间 + if (!CSBettingService.GetMatchTimes(matchId, out DateTime oldStart, out DateTime oldDeadline, out int status, out string error)) + { + await SendAsync(e, "修改比赛", error); + return true; + } + + if (status == 2) + { + await SendAsync(e, "修改比赛", "比赛已结束,无法推迟。"); + return true; + } + + DateTime newStart = oldStart.AddMinutes(minutes); + DateTime newDeadline = oldDeadline.AddMinutes(minutes); + + UpdateMatchRequest request = new() + { + MatchId = matchId, + StartTime = newStart, + BetDeadline = newDeadline + }; + + BotReply reply = BettingController.UpdateMatch(request); + reply.Keyboard = new KeyboardMessage() + .AppendButtons(2, + Button.CreateCmdButton("📋 赛事列表", "赛事列表"), + Button.CreateCmdButton("📅 比赛列表", "比赛列表"), + Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}")); + await SendAsync(e, "修改比赛", reply); + return true; + } + + // 提前比赛 + if (e.Detail.StartsWith("提前比赛")) + { + e.UseNotice = false; + if (!FunGameConstant.UserIdAndUsername.TryGetValue(uid, out User? user) || (!user.IsAdmin && !user.IsOperator)) + { + await SendAsync(e, "修改比赛", "你没有权限执行此操作。"); + return true; + } + + string detail = e.Detail.Replace("提前比赛", "").Trim(); + string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length < 2 || !int.TryParse(parts[0], out int matchId) || !int.TryParse(parts[1], out int minutes) || minutes <= 0) + { + await SendAsync(e, "修改比赛", "格式:提前比赛 <比赛ID> <分钟数>\n示例:提前比赛 123 30"); + return true; + } + + // 获取当前比赛时间 + if (!CSBettingService.GetMatchTimes(matchId, out DateTime oldStart, out DateTime oldDeadline, out int status, out string error)) + { + await SendAsync(e, "修改比赛", error); + return true; + } + + if (status == 2) + { + await SendAsync(e, "修改比赛", "比赛已结束,无法推迟。"); + return true; + } + + DateTime newStart = oldStart.AddMinutes(-minutes); + DateTime newDeadline = oldDeadline.AddMinutes(-minutes); + + UpdateMatchRequest request = new() + { + MatchId = matchId, + StartTime = newStart, + BetDeadline = newDeadline + }; + + BotReply reply = BettingController.UpdateMatch(request); + reply.Keyboard = new KeyboardMessage() + .AppendButtons(2, + Button.CreateCmdButton("📋 赛事列表", "赛事列表"), + Button.CreateCmdButton("📅 比赛列表", "比赛列表"), + Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}")); + await SendAsync(e, "修改比赛", reply); + return true; + } + return false; } diff --git a/OshimaWebAPI/Services/RainBOTService.cs b/OshimaWebAPI/Services/RainBOTService.cs index 714a25c..5f3d72a 100644 --- a/OshimaWebAPI/Services/RainBOTService.cs +++ b/OshimaWebAPI/Services/RainBOTService.cs @@ -63,12 +63,12 @@ namespace Oshima.FunGame.WebAPI.Services else if (msg.IsGroup) { content = "\r\n" + content.Trim(); - await Service.SendGroupMessageAsync(msg.OpenId, content, msgType, media, msg.Id, msgSeq); + await Service.SendGroupMessageAsync(msg.OpenId, content, msgType, media, msg.SendProactive ? null : msg.Id, msgSeq); } else { content = content.Trim(); - await Service.SendC2CMessageAsync(msg.OpenId, content, msgType, media, msg.Id, msgSeq); + await Service.SendC2CMessageAsync(msg.OpenId, content, msgType, media, msg.SendProactive ? null : msg.Id, msgSeq); } await CheckOfflineNotice(msg); } @@ -83,11 +83,11 @@ namespace Oshima.FunGame.WebAPI.Services } else if (msg.IsGroup) { - await Service.SendGroupMarkdownAsync(msg.OpenId, mdMsg, kbMsg, msg.Id, msgSeq); + await Service.SendGroupMarkdownAsync(msg.OpenId, mdMsg, kbMsg, msg.SendProactive ? null : msg.Id, msgSeq); } else { - await Service.SendC2CMarkdownAsync(msg.OpenId, mdMsg, kbMsg, msg.Id, msgSeq); + await Service.SendC2CMarkdownAsync(msg.OpenId, mdMsg, kbMsg, msg.SendProactive ? null : msg.Id, msgSeq); } await CheckOfflineNotice(msg); }