mirror of
https://github.com/oshima-studios/OshimaGameModule.git
synced 2026-06-04 19:42:13 +00:00
添加支持率
This commit is contained in:
parent
187630233b
commit
226159bb53
@ -125,6 +125,16 @@ namespace Oshima.FunGame.WebAPI.Model
|
||||
/// 支持的投注选项类型(常规包含 Team1Win/Team2Win,总决赛增加 Score)
|
||||
/// </summary>
|
||||
public List<BetOptionType> AvailableOptions { get; set; } = [BetOptionType.Team1Win, BetOptionType.Team2Win];
|
||||
|
||||
/// <summary>
|
||||
/// 队伍1胜赔率,默认2.5
|
||||
/// </summary>
|
||||
public decimal Team1WinOdds { get; set; } = 2.50m;
|
||||
|
||||
/// <summary>
|
||||
/// 队伍2胜赔率,默认2.5
|
||||
/// </summary>
|
||||
public decimal Team2WinOdds { get; set; } = 2.50m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -208,5 +218,11 @@ namespace Oshima.FunGame.WebAPI.Model
|
||||
|
||||
[JsonPropertyName("available_options")]
|
||||
public string AvailableOptions { get; set; } = "team1_win,team2_win";
|
||||
|
||||
[JsonPropertyName("team1_win_odds")]
|
||||
public decimal? Team1WinOdds { get; set; }
|
||||
|
||||
[JsonPropertyName("team2_win_odds")]
|
||||
public decimal? Team2WinOdds { get; set; }
|
||||
}
|
||||
}
|
||||
@ -147,6 +147,8 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
string available = row["available_options"]?.ToString() ?? "[]";
|
||||
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
||||
long winner = row["winner"] != DBNull.Value ? Convert.ToInt64(row["winner"]) : 0;
|
||||
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
||||
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
||||
|
||||
string eventName = "";
|
||||
sql.Parameters["@eid"] = eventId;
|
||||
@ -172,9 +174,9 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (status == 0)
|
||||
{
|
||||
sb.AppendLine($"可用选项:");
|
||||
if (available.Contains("team1_win")) sb.AppendLine($" - {t1}胜 (x 2.5)");
|
||||
if (available.Contains("team2_win")) sb.AppendLine($" - {t2}胜 (x 2.5)");
|
||||
if (available.Contains("score")) sb.AppendLine($" - 精确比分 (x 3.5)");
|
||||
if (available.Contains("team1_win")) sb.AppendLine($" - {t1}胜 (x {team1Odds})");
|
||||
if (available.Contains("team2_win")) sb.AppendLine($" - {t2}胜 (x {team2Odds})");
|
||||
if (available.Contains("score")) sb.AppendLine($" - 精确比分 (x 4)");
|
||||
if (available.Contains("mvp")) sb.AppendLine($" - 赛事MVP (x 3.5)");
|
||||
}
|
||||
else if (status == 2)
|
||||
@ -308,6 +310,8 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
sql.ExecuteDataSet("SELECT * FROM csbetting_bet_records WHERE match_id=@mid AND is_settled=0");
|
||||
if (sql.Success && sql.DataSet.Tables[0].Rows.Count > 0)
|
||||
{
|
||||
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
||||
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
||||
foreach (DataRow bet in sql.DataSet.Tables[0].Rows)
|
||||
{
|
||||
long betId = Convert.ToInt64(bet["id"]);
|
||||
@ -325,7 +329,14 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
string note = "未中奖";
|
||||
if (win)
|
||||
{
|
||||
double odds = otype switch { 2 or 3 => 3.5, _ => 2.5 };
|
||||
double odds = otype switch
|
||||
{
|
||||
1 => (double)team1Odds,
|
||||
2 => (double)team2Odds,
|
||||
3 => 4,
|
||||
4 => 3.5,
|
||||
_ => 2.5
|
||||
};
|
||||
payout = (long)(amount * odds);
|
||||
note = "中奖";
|
||||
}
|
||||
@ -559,7 +570,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
}
|
||||
|
||||
// 创建比赛
|
||||
public static bool CreateMatch(int eventId, string team1Name, string team2Name, string stage, DateTime startTime, DateTime betDeadline, string availableOptions, 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, out string error, out long? newMatchId)
|
||||
{
|
||||
error = "";
|
||||
newMatchId = null;
|
||||
@ -581,6 +592,22 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
.Where(s => s.Length > 0)];
|
||||
string optionsJson = System.Text.Json.JsonSerializer.Serialize(options);
|
||||
|
||||
// 如果比赛包含比分或 MVP 选项,不允许自定义猜胜者赔率
|
||||
bool hasSpecialOption = options.Contains("score") || options.Contains("mvp");
|
||||
if ((team1WinOdds.HasValue || team2WinOdds.HasValue) && hasSpecialOption)
|
||||
{
|
||||
error = "比赛包含比分或MVP选项时,不能自定义猜胜者赔率。";
|
||||
return false;
|
||||
}
|
||||
|
||||
decimal t1Odds = team1WinOdds ?? 2.50m;
|
||||
decimal t2Odds = team2WinOdds ?? 2.50m;
|
||||
if (t1Odds <= 0 || t2Odds <= 0)
|
||||
{
|
||||
error = "赔率必须大于0。";
|
||||
return false;
|
||||
}
|
||||
|
||||
sql.Parameters["@eid"] = eventId;
|
||||
sql.Parameters["@t1"] = team1Name;
|
||||
sql.Parameters["@t2"] = team2Name;
|
||||
@ -588,9 +615,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
sql.Parameters["@start"] = startTime;
|
||||
sql.Parameters["@deadline"] = betDeadline;
|
||||
sql.Parameters["@opts"] = optionsJson;
|
||||
sql.Parameters["@t1_odds"] = t1Odds;
|
||||
sql.Parameters["@t2_odds"] = t2Odds;
|
||||
sql.Execute(@"INSERT INTO csbetting_matches
|
||||
(event_id, team1_name, team2_name, stage, start_time, bet_deadline, available_options, status)
|
||||
VALUES (@eid, @t1, @t2, @stage, @start, @deadline, @opts, 0)");
|
||||
(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)");
|
||||
|
||||
if (sql.Success)
|
||||
{
|
||||
@ -603,5 +632,48 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
error = "数据库连接失败。";
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 管理员提前结束竞猜(将未开始比赛标记为进行中)
|
||||
/// </summary>
|
||||
public static bool CloseBetting(int matchId, out string message)
|
||||
{
|
||||
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null)
|
||||
{
|
||||
message = "数据库连接失败。";
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateStatuses(sql);
|
||||
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.ExecuteDataSet("SELECT status, team1_name, team2_name FROM csbetting_matches WHERE id = @mid");
|
||||
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
|
||||
{
|
||||
message = "比赛不存在。";
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = Convert.ToInt32(sql.DataSet.Tables[0].Rows[0]["status"]);
|
||||
if (status != 0)
|
||||
{
|
||||
message = "该比赛已开始或已结束,无需关闭投注。";
|
||||
return false;
|
||||
}
|
||||
|
||||
sql.Execute("UPDATE csbetting_matches SET status = 1 WHERE id = @mid");
|
||||
if (sql.Success)
|
||||
{
|
||||
string t1 = sql.DataSet.Tables[0].Rows[0]["team1_name"].ToString() ?? "";
|
||||
string t2 = sql.DataSet.Tables[0].Rows[0]["team2_name"].ToString() ?? "";
|
||||
string matchlabel = $"{t1} vs {t2}".CreateCmdInput($"比赛详情 {matchId}");
|
||||
message = $"[比赛{matchId}] {matchlabel} 的竞猜已提前关闭。";
|
||||
return true;
|
||||
}
|
||||
|
||||
message = "更新比赛状态失败。";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,3 +60,10 @@ CREATE TABLE IF NOT EXISTS `csbetting_matches` (
|
||||
KEY `idx_start_time` (`start_time`),
|
||||
CONSTRAINT `fk_matches_event` FOREIGN KEY (`event_id`) REFERENCES `csbetting_events` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CS比赛表';
|
||||
|
||||
-- PATCH
|
||||
|
||||
-- 为比赛表添加猜胜者赔率字段
|
||||
ALTER TABLE `csbetting_matches`
|
||||
ADD COLUMN `team1_win_odds` DECIMAL(5,2) NOT NULL DEFAULT 2.50 COMMENT '队伍1胜赔率' AFTER `available_options`,
|
||||
ADD COLUMN `team2_win_odds` DECIMAL(5,2) NOT NULL DEFAULT 2.50 COMMENT '队伍2胜赔率' AFTER `team1_win_odds`;
|
||||
|
||||
@ -94,7 +94,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
// 校验金额
|
||||
if (amount < 1000)
|
||||
{
|
||||
md.Content = "最低投注额为 1000 {General.GameplayEquilibriumConstant.InGameCurrency}。";
|
||||
md.Content = $"最低投注额为 1000 {General.GameplayEquilibriumConstant.InGameCurrency}。";
|
||||
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
|
||||
return reply;
|
||||
}
|
||||
@ -253,7 +253,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
}
|
||||
|
||||
if (CSBettingService.CreateMatch(request.EventId, request.Team1Name, request.Team2Name, request.Stage,
|
||||
request.StartTime, request.BetDeadline, request.AvailableOptions, out string error, out long? newId))
|
||||
request.StartTime, request.BetDeadline, request.AvailableOptions, request.Team1WinOdds, request.Team2WinOdds, out string error, out long? newId))
|
||||
{
|
||||
md.Content = $"比赛创建成功!新比赛ID:{newId}";
|
||||
}
|
||||
@ -269,5 +269,32 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("close-betting")]
|
||||
public BotReply CloseBetting([FromQuery] long uid, [FromQuery] int matchId)
|
||||
{
|
||||
MarkdownMessage md = new() { Content = busy };
|
||||
BotReply reply = new() { Markdown = md };
|
||||
try
|
||||
{
|
||||
if (!FunGameConstant.UserIdAndUsername.TryGetValue(uid, out User? admin) || (!admin.IsAdmin && !admin.IsOperator))
|
||||
{
|
||||
md.Content = "你没有权限执行此操作。";
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (CSBettingService.CloseBetting(matchId, out string msg))
|
||||
{
|
||||
md.Content = msg;
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "CloseBetting 异常");
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +143,10 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
.AppendButtons(2,
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"));
|
||||
if (reply.Markdown?.Content?.Contains("创建存档") ?? false)
|
||||
{
|
||||
reply.Keyboard.AppendButtons(2, Button.CreateCmdButton("⚙️ 创建存档", "创建存档"));
|
||||
}
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
return true;
|
||||
}
|
||||
@ -268,7 +272,8 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
// 指令:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> <投注截止时间> [选项列表(逗号分隔)]
|
||||
// 示例:创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00 2026-03-05 13:55
|
||||
// 示例:创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 2026-04-02 17:55 team1_win,team2_win,score
|
||||
// 示例:创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 2026-04-02 17:55 team1_win,team2_win
|
||||
// 示例:创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 2026-04-02 17:55 team1_win=2.5 team2_win=2.5 team1_win,team2_win
|
||||
if (e.Detail.StartsWith("创建比赛"))
|
||||
{
|
||||
e.UseNotice = false;
|
||||
@ -286,7 +291,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
"格式:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> <投注截止时间> [选项列表(逗号分隔)]\r\n" +
|
||||
"时间格式:yyyy-MM-dd HH:mm(开始/截止各占两段)\r\n" +
|
||||
"示例:创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00 2026-03-05 13:55\r\n" +
|
||||
"选项默认 team1_win,team2_win ,可额外添加 score,mvp");
|
||||
"选项默认 team1_win,team2_win ,比分和MVP选项只允许独立添加:score,mvp");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -319,11 +324,28 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
string options = "team1_win,team2_win";
|
||||
if (parts.Length > 8)
|
||||
// 解析剩余参数:选项和赔率
|
||||
List<string> optionParts = [];
|
||||
decimal? team1Odds = null, team2Odds = null;
|
||||
for (int i = 8; i < parts.Length; i++)
|
||||
{
|
||||
options = string.Join(",", parts[8..]); // 剩余部分视为选项列表
|
||||
string segment = parts[i];
|
||||
if (segment.StartsWith("team1_win="))
|
||||
{
|
||||
if (decimal.TryParse(segment[11..], out decimal odds1))
|
||||
team1Odds = odds1;
|
||||
}
|
||||
else if (segment.StartsWith("team2_win="))
|
||||
{
|
||||
if (decimal.TryParse(segment[11..], out decimal odds2))
|
||||
team2Odds = odds2;
|
||||
}
|
||||
else
|
||||
{
|
||||
optionParts.Add(segment);
|
||||
}
|
||||
}
|
||||
string options = optionParts.Count > 0 ? string.Join(",", optionParts) : "team1_win,team2_win";
|
||||
|
||||
BotReply reply = BettingController.CreateMatch(new CreateMatchRequest
|
||||
{
|
||||
@ -334,12 +356,44 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Stage = stage,
|
||||
StartTime = startDt,
|
||||
BetDeadline = deadlineDt,
|
||||
AvailableOptions = options
|
||||
AvailableOptions = options,
|
||||
Team1WinOdds = team1Odds,
|
||||
Team2WinOdds = team2Odds
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user