mirror of
https://github.com/oshima-studios/OshimaGameModule.git
synced 2026-06-04 19:42:13 +00:00
文本和算法更新
This commit is contained in:
parent
471f897d31
commit
699b80f5a5
@ -2,183 +2,6 @@
|
||||
|
||||
namespace Oshima.FunGame.WebAPI.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 赛事状态
|
||||
/// </summary>
|
||||
public enum EventStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 未开始
|
||||
/// </summary>
|
||||
Upcoming,
|
||||
/// <summary>
|
||||
/// 进行中
|
||||
/// </summary>
|
||||
InProgress,
|
||||
/// <summary>
|
||||
/// 已结束
|
||||
/// </summary>
|
||||
Completed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比赛状态
|
||||
/// </summary>
|
||||
public enum MatchStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 未开始
|
||||
/// </summary>
|
||||
Scheduled,
|
||||
/// <summary>
|
||||
/// 正在进行
|
||||
/// </summary>
|
||||
Live,
|
||||
/// <summary>
|
||||
/// 已结束
|
||||
/// </summary>
|
||||
Finished
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 投注选项类型
|
||||
/// </summary>
|
||||
public enum BetOptionType
|
||||
{
|
||||
/// <summary>
|
||||
/// 队伍1胜
|
||||
/// </summary>
|
||||
Team1Win,
|
||||
/// <summary>
|
||||
/// 队伍2胜
|
||||
/// </summary>
|
||||
Team2Win,
|
||||
/// <summary>
|
||||
/// 精确比分
|
||||
/// </summary>
|
||||
Score,
|
||||
/// <summary>
|
||||
/// 赛事MVP
|
||||
/// </summary>
|
||||
MVP
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CS 队伍
|
||||
/// </summary>
|
||||
public class CSTeam
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string LogoUrl { get; set; } = "";
|
||||
public List<string> Players { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 赛事(Event)
|
||||
/// </summary>
|
||||
public class CSEvent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public EventStatus Status { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime EndTime { get; set; }
|
||||
public List<CSMatch> Matches { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// MVP 候选人列表(玩家 OpenId 或游戏内 UID)
|
||||
/// </summary>
|
||||
public List<long> MVPCandidates { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单场比赛
|
||||
/// </summary>
|
||||
public class CSMatch
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int EventId { get; set; }
|
||||
public int Team1Id { get; set; }
|
||||
public int Team2Id { get; set; }
|
||||
public MatchStatus Status { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public string Stage { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 竞猜截止时间
|
||||
/// </summary>
|
||||
public DateTime BetDeadline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 比赛结果(如 “16:14” 或 “2:1”)
|
||||
/// </summary>
|
||||
///
|
||||
public string? Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获胜方(1=Team1,2=Team2,0=平局/未定)
|
||||
/// </summary>
|
||||
public int Winner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支持的投注选项类型(常规包含 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>
|
||||
/// 用户投注记录
|
||||
/// </summary>
|
||||
public class BetRecord
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public int MatchId { get; set; }
|
||||
public BetOptionType OptionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 投注选项内容(如 “Team1Win” 或具体比分 “16:14”)
|
||||
/// </summary>
|
||||
public string OptionValue { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 投注金币
|
||||
/// </summary>
|
||||
public long Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 投注时间
|
||||
/// </summary>
|
||||
public DateTime BetTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已结算
|
||||
/// </summary>
|
||||
public bool IsSettled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结算金额(含本金),未结算为 null
|
||||
/// </summary>
|
||||
public long? Payout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结算备注
|
||||
/// </summary>
|
||||
public string? ResultNote { get; set; }
|
||||
}
|
||||
|
||||
public class CreateEventRequest
|
||||
{
|
||||
[JsonPropertyName("uid")]
|
||||
@ -225,6 +48,9 @@ namespace Oshima.FunGame.WebAPI.Model
|
||||
|
||||
[JsonPropertyName("team2_win_odds")]
|
||||
public decimal? Team2WinOdds { get; set; }
|
||||
|
||||
[JsonPropertyName("team1_win_probability")]
|
||||
public decimal? Team1WinProbability { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateMatchRequest
|
||||
@ -249,5 +75,8 @@ namespace Oshima.FunGame.WebAPI.Model
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[JsonPropertyName("team1_win_probability")]
|
||||
public decimal? Team1WinProbability { get; set; }
|
||||
}
|
||||
}
|
||||
@ -233,28 +233,27 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
}
|
||||
sb.AppendLine($"{t1} vs {t2}".CreateCmdInput($"比赛详情 {matchId}"));
|
||||
sb.AppendLine($"开赛:{start:yyyy/MM/dd HH:mm}");
|
||||
sb.AppendLine($"竞猜截止:{deadline:yyyy/MM/dd HH:mm}");
|
||||
sb.AppendLine($"预测截止:{deadline:yyyy/MM/dd HH:mm}");
|
||||
sb.AppendLine($"状态:{statusStr}");
|
||||
if (status == 0)
|
||||
{
|
||||
sb.AppendLine($"可用选项:");
|
||||
if (available.Contains("team1_win")) sb.AppendLine($" - 队伍1 {t1} 胜 (x {team1Odds})");
|
||||
if (available.Contains("team2_win")) sb.AppendLine($" - 队伍2 {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)
|
||||
{
|
||||
string winnerName = winner switch { 1 => t1, 2 => t2, 3 => result, _ => "待定" };
|
||||
sb.AppendLine($"胜者:{winnerName}");
|
||||
if (winner != 3) sb.AppendLine($"结果:{result}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
sb.AppendLine($"> 📝 {description}\r\n");
|
||||
}
|
||||
|
||||
if (status == 0) sb.AppendLine($"可用选项:");
|
||||
else sb.AppendLine($"该比赛已截止预测。");
|
||||
if (available.Contains("team1_win")) sb.AppendLine($" - 队伍1 {t1} 胜 (x {team1Odds})");
|
||||
if (available.Contains("team2_win")) sb.AppendLine($" - 队伍2 {t2} 胜 (x {team2Odds})");
|
||||
if (available.Contains("score")) sb.AppendLine($" - 精确比分 (x 4)");
|
||||
if (available.Contains("mvp")) sb.AppendLine($" - 赛事MVP (x 3.5)");
|
||||
if (status == 2)
|
||||
{
|
||||
string winnerName = winner switch { 1 => t1, 2 => t2, 3 => result, _ => "待定" };
|
||||
sb.AppendLine($"胜者:{winnerName}");
|
||||
if (winner != 3) sb.AppendLine($"结果:{result}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
return "数据库连接失败。";
|
||||
@ -277,13 +276,15 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
DataRow row = sql.DataSet.Tables[0].Rows[0];
|
||||
int status = Convert.ToInt32(row["status"]);
|
||||
DateTime deadline = Convert.ToDateTime(row["bet_deadline"]);
|
||||
if (status != 0 || DateTime.Now > deadline)
|
||||
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
||||
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
||||
if (status == 2 || DateTime.Now > deadline)
|
||||
{
|
||||
error = "当前比赛已结束或非投注期。";
|
||||
error = "当前比赛已结束或非可预测阶段。";
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- 单场比赛投注上限检查 ---
|
||||
// --- 单场比赛助力上限检查 ---
|
||||
long alreadyBet = 0;
|
||||
long totalBet = amount;
|
||||
sql.Parameters["@uid"] = uid;
|
||||
@ -296,44 +297,54 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
}
|
||||
if (totalBet > 5000)
|
||||
{
|
||||
error = $"本场比赛你的投注总额不能超过 5000 {General.GameplayEquilibriumConstant.InGameCurrency}(已投 {alreadyBet})。";
|
||||
error = $"本场比赛你的助力总额不能超过 5000 {General.GameplayEquilibriumConstant.InGameCurrency}(已助力 {alreadyBet})。";
|
||||
return false;
|
||||
}
|
||||
|
||||
string available = row["available_options"]?.ToString() ?? "[]";
|
||||
int optionType;
|
||||
string optionValue = option;
|
||||
decimal oddsAtBet = 0;
|
||||
if (option == "team1" && available.Contains("team1_win"))
|
||||
{
|
||||
optionType = 1;
|
||||
oddsAtBet = team1Odds;
|
||||
}
|
||||
else if (option == "team2" && available.Contains("team2_win"))
|
||||
{
|
||||
optionType = 2;
|
||||
oddsAtBet = team2Odds;
|
||||
}
|
||||
else if (option.StartsWith("score:") && available.Contains("score"))
|
||||
{
|
||||
optionType = 3;
|
||||
optionValue = option.Replace("score:", "").Trim();
|
||||
oddsAtBet = 4.0m;
|
||||
}
|
||||
else if (option.StartsWith("mvp:") && available.Contains("mvp"))
|
||||
{
|
||||
optionType = 4;
|
||||
optionValue = option.Replace("mvp:", "").Trim();
|
||||
oddsAtBet = 3.5m;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "无效的投注选项。";
|
||||
error = "无效的预测选项。";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 写入投注记录
|
||||
// 写入预测记录
|
||||
sql.Parameters["@uid"] = uid;
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.Parameters["@otype"] = optionType;
|
||||
sql.Parameters["@oval"] = optionValue;
|
||||
sql.Parameters["@amt"] = amount;
|
||||
sql.Parameters["@odds"] = oddsAtBet;
|
||||
sql.Parameters["@time"] = DateTime.Now;
|
||||
sql.Execute("INSERT INTO csbetting_bet_records (user_id, match_id, option_type, option_value, amount, bet_time) VALUES (@uid, @mid, @otype, @oval, @amt, @time)");
|
||||
sql.Execute("INSERT INTO csbetting_bet_records (user_id, match_id, option_type, option_value, amount, odds_at_bet, bet_time) VALUES (@uid, @mid, @otype, @oval, @amt, @odds, @time)");
|
||||
if (!sql.Success)
|
||||
{
|
||||
error = "投注记录写入失败。";
|
||||
error = "预测记录写入失败。";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -347,75 +358,102 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql != null)
|
||||
{
|
||||
UpdateStatuses(sql);
|
||||
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.ExecuteDataSet("SELECT * FROM csbetting_matches WHERE id = @mid");
|
||||
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
|
||||
return "比赛不存在。";
|
||||
DataRow row = sql.DataSet.Tables[0].Rows[0];
|
||||
string available = row["available_options"]?.ToString() ?? "[]";
|
||||
bool isMvp = available.Contains("mvp", StringComparison.CurrentCultureIgnoreCase);
|
||||
int status = Convert.ToInt32(row["status"]);
|
||||
if (status == 2)
|
||||
return "比赛已结算。";
|
||||
|
||||
int winTeam = 3;
|
||||
if (!isMvp)
|
||||
try
|
||||
{
|
||||
if (winner == "team1") winTeam = 1;
|
||||
else if (winner == "team2") winTeam = 2;
|
||||
else return "请指定获胜方为 team1 或 team2。MVP 赛事获胜方请直接指定选手 ID。";
|
||||
}
|
||||
sql.NewTransaction();
|
||||
|
||||
// 更新比赛结果
|
||||
sql.Parameters["@res"] = result;
|
||||
sql.Parameters["@win"] = winTeam;
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.Execute("UPDATE csbetting_matches SET status=2, result=@res, winner=@win WHERE id=@mid");
|
||||
UpdateStatuses(sql);
|
||||
|
||||
// 获取所有未结算投注
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
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)
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.ExecuteDataSet("SELECT * FROM csbetting_matches WHERE id = @mid");
|
||||
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
|
||||
return "比赛不存在。";
|
||||
DataRow row = sql.DataSet.Tables[0].Rows[0];
|
||||
string available = row["available_options"]?.ToString() ?? "[]";
|
||||
bool isMvp = available.Contains("mvp", StringComparison.CurrentCultureIgnoreCase);
|
||||
int status = Convert.ToInt32(row["status"]);
|
||||
if (status == 2)
|
||||
return "比赛已结算。";
|
||||
|
||||
int winTeam = 3;
|
||||
if (!isMvp)
|
||||
{
|
||||
long betId = Convert.ToInt64(bet["id"]);
|
||||
int otype = Convert.ToInt32(bet["option_type"]);
|
||||
string ovalue = bet["option_value"].ToString() ?? "";
|
||||
long amount = Convert.ToInt64(bet["amount"]);
|
||||
if (winner == "team1") winTeam = 1;
|
||||
else if (winner == "team2") winTeam = 2;
|
||||
else return "请指定获胜方为 team1 或 team2。MVP 赛事获胜方请直接指定选手 ID。";
|
||||
}
|
||||
|
||||
bool win = false;
|
||||
if (otype == 1 && winTeam == 1) win = true;
|
||||
else if (otype == 2 && winTeam == 2) win = true;
|
||||
else if (otype == 3 && ovalue.Replace(":", ":").Equals(result, StringComparison.CurrentCultureIgnoreCase)) win = true;
|
||||
else if (otype == 4 && ovalue.Equals(result, StringComparison.CurrentCultureIgnoreCase)) win = true;
|
||||
|
||||
long payout = 0;
|
||||
string note = "未中奖";
|
||||
if (win)
|
||||
// 更新比赛结果
|
||||
sql.Parameters["@res"] = result;
|
||||
sql.Parameters["@win"] = winTeam;
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
sql.Execute("UPDATE csbetting_matches SET status=2, result=@res, winner=@win WHERE id=@mid");
|
||||
if (sql.Success)
|
||||
{
|
||||
// 获取所有未结算助力
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
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)
|
||||
{
|
||||
double odds = otype switch
|
||||
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)
|
||||
{
|
||||
1 => (double)team1Odds,
|
||||
2 => (double)team2Odds,
|
||||
3 => 4,
|
||||
4 => 3.5,
|
||||
_ => 2.5
|
||||
};
|
||||
payout = (long)(amount * odds);
|
||||
note = "中奖";
|
||||
long betId = Convert.ToInt64(bet["id"]);
|
||||
int otype = Convert.ToInt32(bet["option_type"]);
|
||||
string ovalue = bet["option_value"].ToString() ?? "";
|
||||
long amount = Convert.ToInt64(bet["amount"]);
|
||||
decimal oddsAtBet = Convert.ToDecimal(bet["odds_at_bet"]);
|
||||
|
||||
bool win = false;
|
||||
if (otype == 1 && winTeam == 1) win = true;
|
||||
else if (otype == 2 && winTeam == 2) win = true;
|
||||
else if (otype == 3 && ovalue.Replace(":", ":").Equals(result, StringComparison.CurrentCultureIgnoreCase)) win = true;
|
||||
else if (otype == 4 && ovalue.Equals(result, StringComparison.CurrentCultureIgnoreCase)) win = true;
|
||||
|
||||
long payout = 0;
|
||||
string note = "未中奖";
|
||||
if (win)
|
||||
{
|
||||
if (oddsAtBet <= 0)
|
||||
{
|
||||
oddsAtBet = otype switch
|
||||
{
|
||||
1 => team1Odds,
|
||||
2 => team2Odds,
|
||||
3 => 4,
|
||||
4 => 3.5m,
|
||||
_ => 2.5m
|
||||
};
|
||||
}
|
||||
payout = (long)(amount * oddsAtBet);
|
||||
note = "中奖";
|
||||
}
|
||||
sql.Parameters["@payout"] = payout;
|
||||
sql.Parameters["@note"] = note;
|
||||
sql.Parameters["@bid"] = betId;
|
||||
sql.Execute("UPDATE csbetting_bet_records SET is_settled=1, payout=@payout, result_note=@note WHERE id=@bid");
|
||||
if (!sql.Success)
|
||||
{
|
||||
sql.Rollback();
|
||||
throw sql.LastException ?? new Milimoe.FunGame.SQLQueryException();
|
||||
}
|
||||
}
|
||||
}
|
||||
sql.Parameters["@payout"] = payout;
|
||||
sql.Parameters["@note"] = note;
|
||||
sql.Parameters["@bid"] = betId;
|
||||
sql.Execute("UPDATE csbetting_bet_records SET is_settled=1, payout=@payout, result_note=@note WHERE id=@bid");
|
||||
sql.Commit();
|
||||
return $"比赛 {matchId} 结算完成。";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Rollback();
|
||||
throw sql.LastException ?? new Milimoe.FunGame.SQLQueryException();
|
||||
}
|
||||
}
|
||||
return $"比赛 {matchId} 结算完成。";
|
||||
catch (Exception e)
|
||||
{
|
||||
sql.Rollback();
|
||||
return $"比赛 {matchId} 结算失败,发生异常:{e.Message}";
|
||||
}
|
||||
}
|
||||
return "数据库连接失败。";
|
||||
}
|
||||
@ -451,7 +489,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (page > totalPages) page = totalPages;
|
||||
if (page < 1) page = 1;
|
||||
if (total == 0)
|
||||
return ("你还没有任何竞猜记录。", 1);
|
||||
return ("你还没有任何预测记录。", 1);
|
||||
}
|
||||
|
||||
// 构建分页SQL片段
|
||||
@ -466,7 +504,8 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
sql.Parameters["@uid"] = uid;
|
||||
sql.ExecuteDataSet($@"
|
||||
SELECT br.match_id, m.team1_name, m.team2_name, m.status AS match_status, m.start_time,
|
||||
GROUP_CONCAT(CONCAT(br.option_type, ':', br.option_value, ':', br.amount) ORDER BY br.id SEPARATOR '|') AS details,
|
||||
MAX(m.team1_win_odds) AS team1_odds, MAX(m.team2_win_odds) AS team2_odds,
|
||||
GROUP_CONCAT(CONCAT(br.option_type, ':', br.option_value, ':', br.amount, ':', br.odds_at_bet) ORDER BY br.id SEPARATOR '|') AS details,
|
||||
SUM(br.amount) AS total_amount,
|
||||
MIN(br.is_settled) AS all_settled,
|
||||
SUM(CASE WHEN br.is_settled = 1 AND br.payout > 0 AND br.is_claimed = 1 THEN 1 ELSE 0 END) AS claimed_count,
|
||||
@ -484,17 +523,19 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{limitClause}");
|
||||
|
||||
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
|
||||
return ("你还没有任何竞猜记录。", 1);
|
||||
return ("你还没有任何预测记录。", 1);
|
||||
|
||||
StringBuilder sb = new();
|
||||
if (mid > 0) sb.Append("你的本场竞猜记录:\r\n> ");
|
||||
else sb.Append($"我的竞猜{(paged && totalPages > 1 ? $"(第 {page}/{totalPages} 页)" : "")}\r\n> ");
|
||||
if (mid > 0) sb.Append("你的本场预测记录:\r\n> ");
|
||||
else sb.Append($"我的预测{(paged && totalPages > 1 ? $"(第 {page}/{totalPages} 页)" : "")}\r\n> ");
|
||||
foreach (DataRow row in sql.DataSet.Tables[0].Rows)
|
||||
{
|
||||
int matchId = Convert.ToInt32(row["match_id"]);
|
||||
string t1 = row["team1_name"].ToString() ?? "";
|
||||
string t2 = row["team2_name"].ToString() ?? "";
|
||||
int matchStatus = Convert.ToInt32(row["match_status"]);
|
||||
decimal t1Odds = Convert.ToDecimal(row["team1_odds"]);
|
||||
decimal t2Odds = Convert.ToDecimal(row["team2_odds"]);
|
||||
long totalAmount = Convert.ToInt64(row["total_amount"]);
|
||||
long totalPayout = Convert.ToInt64(row["total_payout"]);
|
||||
int allSettled = Convert.ToInt32(row["all_settled"]);
|
||||
@ -502,7 +543,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
int unclaimedCount = Convert.ToInt32(row["unclaimed_count"]);
|
||||
int lostCount = Convert.ToInt32(row["lost_count"]);
|
||||
|
||||
// 解析投注详情
|
||||
// 解析助力详情
|
||||
string detailsStr = row["details"].ToString() ?? "";
|
||||
string[]? parts = detailsStr.Split('|');
|
||||
List<string> summary = [];
|
||||
@ -514,6 +555,18 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
int otype = int.Parse(items[0]);
|
||||
string ovalue = items[1];
|
||||
long oamount = long.Parse(items[2]);
|
||||
decimal odds = decimal.Parse(items[3]);
|
||||
if (odds <= 0)
|
||||
{
|
||||
odds = otype switch
|
||||
{
|
||||
1 => t1Odds,
|
||||
2 => t2Odds,
|
||||
3 => 4m,
|
||||
4 => 3.5m,
|
||||
_ => 2.5m
|
||||
};
|
||||
}
|
||||
string optStr = otype switch
|
||||
{
|
||||
1 => $"{t1}胜",
|
||||
@ -522,7 +575,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
4 => $"MVP {ovalue}",
|
||||
_ => ovalue
|
||||
};
|
||||
summary.Add($"{optStr} {oamount}G");
|
||||
summary.Add($"{optStr} {oamount}G [x {odds}]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,7 +599,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
string matchLabel = $"{t1} vs {t2}".CreateCmdInput($"比赛详情 {matchId}");
|
||||
sb.Append($"[比赛{matchId}] {matchLabel} | ");
|
||||
sb.Append($"投注:{totalAmount}G ({detailLine}) | ");
|
||||
sb.Append($"助力:{totalAmount}G ({detailLine}) | ");
|
||||
sb.Append($"状态:{statusLine}");
|
||||
if (totalPayout > 0)
|
||||
sb.Append($" (+{totalPayout}G)");
|
||||
@ -574,7 +627,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
// 对于已经过了开始时间但还没有 winner 且状态为 1 的,可保留为进行中;实际上只要 winner 为 null,状态应为 1(进行中)
|
||||
// 如果有结果但 winner 不为 null,管理员应该已经手动结算,状态会设为 2,这里不做额外修改。
|
||||
// 安全起见,只更新未开始的,以及当比赛时间已过且无 winner 时自动变成进行中。
|
||||
// 如果需要自动结束(比如时间过长),可再添加规则,但竞猜系统通常由管理员手动结算结束。
|
||||
// 如果需要自动结束(比如时间过长),可再添加规则,但预测系统通常由管理员手动结算结束。
|
||||
// 这里只做基础更新。
|
||||
}
|
||||
|
||||
@ -640,7 +693,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, 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, out string error, out long? newMatchId)
|
||||
{
|
||||
error = "";
|
||||
newMatchId = null;
|
||||
@ -670,8 +723,34 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
decimal t1Odds = team1WinOdds ?? 2.50m;
|
||||
decimal t2Odds = team2WinOdds ?? 2.50m;
|
||||
// 处理默认奖励率或基于胜率计算
|
||||
decimal t1Odds, t2Odds;
|
||||
if (team1WinProbability.HasValue)
|
||||
{
|
||||
decimal prob = team1WinProbability.Value;
|
||||
try
|
||||
{
|
||||
(t1Odds, t2Odds) = CalculateOdds(prob);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = e.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (team1WinOdds.HasValue && team2WinOdds.HasValue)
|
||||
{
|
||||
t1Odds = team1WinOdds.Value;
|
||||
t2Odds = team2WinOdds.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认双方各 2.0
|
||||
t1Odds = 2.0m;
|
||||
t2Odds = 2.0m;
|
||||
}
|
||||
|
||||
// 校验奖励率大于0
|
||||
if (t1Odds <= 0 || t2Odds <= 0)
|
||||
{
|
||||
error = "奖励率必须大于0。";
|
||||
@ -704,7 +783,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 管理员提前结束竞猜(将未开始比赛标记为进行中)
|
||||
/// 管理员提前结束预测(将未开始比赛标记为进行中)
|
||||
/// </summary>
|
||||
public static bool CloseBetting(int matchId, out string message)
|
||||
{
|
||||
@ -728,7 +807,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
int status = Convert.ToInt32(sql.DataSet.Tables[0].Rows[0]["status"]);
|
||||
if (status != 0)
|
||||
{
|
||||
message = "该比赛已开始或已结束,无需关闭投注。";
|
||||
message = "该比赛已开始或已结束,无需关闭预测。";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -739,7 +818,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
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} 的竞猜已提前关闭。";
|
||||
message = $"[比赛{matchId}] {matchlabel} 的预测已提前关闭。";
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -775,30 +854,53 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
string available = sql.DataSet.Tables[0].Rows[0]["available_options"]?.ToString() ?? "[]";
|
||||
bool hasSpecial = available.Contains("score", StringComparison.OrdinalIgnoreCase) || available.Contains("mvp", StringComparison.OrdinalIgnoreCase);
|
||||
decimal? newTeam1Odds = request.Team1WinOdds;
|
||||
decimal? newTeam2Odds = request.Team2WinOdds;
|
||||
|
||||
// 校验奖励率逻辑(同创建比赛)
|
||||
if ((request.Team1WinOdds.HasValue || request.Team2WinOdds.HasValue) && hasSpecial)
|
||||
if (request.Team1WinProbability.HasValue)
|
||||
{
|
||||
error = "比赛包含比分或MVP选项时,不能修改猜胜者奖励率。";
|
||||
return false;
|
||||
if (hasSpecial)
|
||||
{
|
||||
error = "比赛包含比分或MVP选项,不能修改猜胜者赔率。";
|
||||
return false;
|
||||
}
|
||||
decimal prob = request.Team1WinProbability.Value;
|
||||
try
|
||||
{
|
||||
(newTeam1Odds, newTeam2Odds) = CalculateOdds(prob);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = e.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((request.Team1WinOdds.HasValue && request.Team1WinOdds <= 0) ||
|
||||
(request.Team2WinOdds.HasValue && request.Team2WinOdds <= 0))
|
||||
else
|
||||
{
|
||||
error = "奖励率必须大于0。";
|
||||
return false;
|
||||
if ((newTeam1Odds.HasValue || newTeam2Odds.HasValue) && hasSpecial)
|
||||
{
|
||||
error = "比赛包含比分或MVP选项时,不能修改猜胜者奖励率。";
|
||||
return false;
|
||||
}
|
||||
if ((newTeam1Odds.HasValue && newTeam1Odds <= 0) ||
|
||||
(newTeam2Odds.HasValue && newTeam2Odds <= 0))
|
||||
{
|
||||
error = "奖励率必须大于0。";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 构建动态UPDATE
|
||||
StringBuilder setClause = new();
|
||||
if (request.Team1WinOdds.HasValue)
|
||||
if (newTeam1Odds.HasValue)
|
||||
{
|
||||
sql.Parameters["@t1od"] = request.Team1WinOdds.Value;
|
||||
sql.Parameters["@t1od"] = newTeam1Odds.Value;
|
||||
setClause.Append("team1_win_odds = @t1od, ");
|
||||
}
|
||||
if (request.Team2WinOdds.HasValue)
|
||||
if (newTeam2Odds.HasValue)
|
||||
{
|
||||
sql.Parameters["@t2od"] = request.Team2WinOdds.Value;
|
||||
sql.Parameters["@t2od"] = newTeam2Odds.Value;
|
||||
setClause.Append("team2_win_odds = @t2od, ");
|
||||
}
|
||||
if (request.StartTime.HasValue)
|
||||
@ -836,5 +938,21 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static (decimal oddsA, decimal oddsB) CalculateOdds(decimal team1WinProbability, decimal margin = 0.08m)
|
||||
{
|
||||
if (team1WinProbability <= 0 || team1WinProbability >= 1)
|
||||
throw new ArgumentException("胜率必须大于0且小于1。");
|
||||
|
||||
if (margin < 0) margin = 0.08m;
|
||||
|
||||
decimal probB = 1m - team1WinProbability;
|
||||
decimal adj = 1m + margin;
|
||||
|
||||
decimal oddsA = Math.Round(1m / (team1WinProbability * adj), 2);
|
||||
decimal oddsB = Math.Round(1m / (probB * adj), 2);
|
||||
|
||||
return (oddsA, oddsB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,3 +71,12 @@ ALTER TABLE `csbetting_matches`
|
||||
-- 为比赛表添加描述字段
|
||||
ALTER TABLE `csbetting_matches`
|
||||
ADD COLUMN `description` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '比赛描述信息' AFTER `team2_win_odds`;
|
||||
|
||||
-- 将队伍赔率的默认值改为 2.00
|
||||
ALTER TABLE `csbetting_matches`
|
||||
MODIFY COLUMN `team1_win_odds` DECIMAL(5,2) NOT NULL DEFAULT 2.00 COMMENT '队伍1胜赔率',
|
||||
MODIFY COLUMN `team2_win_odds` DECIMAL(5,2) NOT NULL DEFAULT 2.00 COMMENT '队伍2胜赔率';
|
||||
|
||||
-- 为投注记录表增加投注时的赔率字段,确保结算公平
|
||||
ALTER TABLE `csbetting_bet_records`
|
||||
ADD COLUMN `odds_at_bet` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '投注时的赔率' AFTER `amount`;
|
||||
|
||||
@ -20,7 +20,6 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
private ILogger<CSBettingController> Logger { get; set; } = logger;
|
||||
|
||||
private const string noSaved = "你还没有创建存档!请发送【创建存档】创建。";
|
||||
private const string refused = "暂时无法使用此指令。";
|
||||
private const string busy = "服务器繁忙,请稍后再试。";
|
||||
|
||||
// ---------- 查询类(无需锁)----------
|
||||
@ -61,20 +60,18 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
[HttpGet("match/{matchId:int}")]
|
||||
public BotReply GetMatchDetail(int matchId)
|
||||
{
|
||||
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetMatchDetail(matchId, out int status) + (status == 0 ? $"竞猜指令:{"竞猜".CreateCmdInput()} <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n👇🏻 点击下方按钮快速竞猜" : "")} };
|
||||
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetMatchDetail(matchId, out int status) + (status == 0 ? $"预测指令:{"预测".CreateCmdInput()} <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n👇🏻 点击下方按钮快速预测" : "")} };
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("mybets/{uid:long}")]
|
||||
public BotReply GetMyBets(long uid, [FromQuery] int page = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
var (content, totalPages) = CSBettingService.GetMyBets(uid, -1, page, pageSize);
|
||||
KeyboardMessage kb = new();
|
||||
if (totalPages > 1) kb = new KeyboardMessage().AddPaginationRow("我的竞猜 ", page, totalPages);
|
||||
if (totalPages > 1) kb = new KeyboardMessage().AddPaginationRow("我的预测 ", page, totalPages);
|
||||
return new BotReply { Markdown = new MarkdownMessage { Content = content }, Keyboard = kb };
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("mybets/{uid:long}/{mid:long}")]
|
||||
public BotReply GetMyBets(long uid, long mid)
|
||||
{
|
||||
@ -107,7 +104,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;
|
||||
}
|
||||
@ -122,7 +119,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
{
|
||||
user.Inventory.Credits -= (int)amount;
|
||||
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
|
||||
md.Content = $"投注成功!{amount} {General.GameplayEquilibriumConstant.InGameCurrency}已扣除。";
|
||||
md.Content = $"{"助力".CreateCmdInput($"比赛详情 {matchId}")}成功!{amount} {General.GameplayEquilibriumConstant.InGameCurrency}已扣除。";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -266,7 +263,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, out string error, out long? newId))
|
||||
request.StartTime, request.BetDeadline, request.AvailableOptions, request.Team1WinOdds, request.Team2WinOdds, request.Team1WinProbability, out string error, out long? newId))
|
||||
{
|
||||
md.Content = $"比赛创建成功!新比赛ID:{newId}";
|
||||
}
|
||||
|
||||
@ -10,43 +10,43 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
public async Task<bool> HandleCSBettingInput(IBotMessage e, long uid)
|
||||
{
|
||||
if (e.Detail == "竞猜帮助")
|
||||
if (e.Detail == "预测帮助" || e.Detail == "赛事帮助")
|
||||
{
|
||||
e.UseNotice = false;
|
||||
BotReply reply = new()
|
||||
{
|
||||
Markdown = new MarkdownMessage
|
||||
{
|
||||
Content = "🎮 CS赛事竞猜帮助:\r\n"
|
||||
Content = "🎮 CS赛事预测帮助:\r\n"
|
||||
+ $"✨ {"赛事列表".CreateCmdInput()} - 查看所有赛事\r\n"
|
||||
+ $"✨ {"比赛列表".CreateCmdInput()} - 查看所有比赛\r\n"
|
||||
+ $"✨ {"创建存档".CreateCmdInput()} - 创建存档后可竞猜\r\n"
|
||||
+ $"✨ {"我的竞猜".CreateCmdInput()} - 查看我的投注记录\r\n"
|
||||
+ $"✨ {"竞猜领奖".CreateCmdInput()} - 领取竞猜奖励\r\n"
|
||||
+ $"✨ {"比赛详情".CreateCmdInput()} - 查看单场比赛并投注"
|
||||
+ $"✨ {"创建存档".CreateCmdInput()} - 创建存档后可预测\r\n"
|
||||
+ $"✨ {"我的预测".CreateCmdInput()} - 查看我的预测记录\r\n"
|
||||
+ $"✨ {"预测领奖".CreateCmdInput()} - 领取预测奖励\r\n"
|
||||
+ $"✨ {"比赛详情".CreateCmdInput()} - 查看单场比赛并预测"
|
||||
},
|
||||
Keyboard = new KeyboardMessage()
|
||||
.AppendButtons(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("⚙️ 创建存档", "创建存档"),
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"))
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖"),
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"))
|
||||
};
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 赛事列表
|
||||
if (e.Detail.StartsWith("赛事列表"))
|
||||
if (e.Detail.StartsWith("赛事") || e.Detail.StartsWith("赛事列表"))
|
||||
{
|
||||
int page = 1;
|
||||
string detail = e.Detail.Replace("赛事列表", "").Trim();
|
||||
string detail = e.Detail.Replace("赛事", "").Replace("赛事列表", "").Trim();
|
||||
System.Text.RegularExpressions.Match match = GetFirstNumber().Match(detail);
|
||||
if (match.Success && int.TryParse(match.Value, out int p)) page = p;
|
||||
BotReply reply = BettingController.GetEventsOverview(page);
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -63,8 +63,8 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
reply.Keyboard.AppendButtonsWithNewRow(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"));
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"));
|
||||
await SendAsync(e, "比赛赛程", reply);
|
||||
return true;
|
||||
}
|
||||
@ -76,31 +76,31 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
BotReply reply = BettingController.GetMatchDetail(matchId);
|
||||
KeyboardMessage kb = new();
|
||||
// 构建投注键盘(填充“竞猜 <matchId> <选项> ”)
|
||||
// 构建预测键盘(填充“预测 <matchId> <选项> ”)
|
||||
if (reply.Markdown?.Content?.Contains("可用选项") ?? false)
|
||||
{
|
||||
kb.AppendButtons(2,
|
||||
Button.CreateCmdButton("⚔️ 队伍1胜", $"竞猜 {matchId} team1 1000", enter: false),
|
||||
Button.CreateCmdButton("🛡️ 队伍2胜", $"竞猜 {matchId} team2 1000", enter: false),
|
||||
Button.CreateCmdButton("🎯 精确比分", $"竞猜 {matchId} score:", enter: false),
|
||||
Button.CreateCmdButton("🏆 MVP", $"竞猜 {matchId} mvp:", enter: false));
|
||||
Button.CreateCmdButton("⚔️ 队伍1胜", $"预测 {matchId} team1 1000", enter: false),
|
||||
Button.CreateCmdButton("🛡️ 队伍2胜", $"预测 {matchId} team2 1000", enter: false),
|
||||
Button.CreateCmdButton("🎯 精确比分", $"预测 {matchId} score:", enter: false),
|
||||
Button.CreateCmdButton("🏆 MVP", $"预测 {matchId} mvp:", enter: false));
|
||||
}
|
||||
kb.AppendButtonsWithNewRow(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"));
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖"),
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"));
|
||||
reply.Keyboard = kb;
|
||||
BotReply reply2 = BettingController.GetMyBets(uid, mid: matchId);
|
||||
if (reply.Markdown != null && reply.Markdown.Content != null && !(reply2.Markdown?.Content?.Equals("你还没有任何竞猜记录。") ?? true))
|
||||
if (reply.Markdown != null && reply.Markdown.Content != null && !(reply2.Markdown?.Content?.Equals("你还没有任何预测记录。") ?? true))
|
||||
{
|
||||
reply.Markdown.Content += (reply.Markdown.Content.Contains("点击下方按钮快速竞猜") ? "\r\n" : "") + reply2.Markdown.Content;
|
||||
reply.Markdown.Content += (reply.Markdown.Content.Contains("点击下方按钮快速预测") ? "\r\n" : "") + reply2.Markdown.Content;
|
||||
}
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendAsync(e, "CS赛事竞猜", "格式:比赛详情 <比赛ID>");
|
||||
await SendAsync(e, "CS赛事预测", "格式:比赛详情 <比赛ID>");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -121,9 +121,9 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
reply.Keyboard.AppendButtonsWithNewRow(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖"));
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖"));
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -137,74 +137,74 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
.AppendButtons(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"),
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"))
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"),
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"))
|
||||
};
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Detail.StartsWith("我的竞猜"))
|
||||
if (e.Detail.StartsWith("我的预测"))
|
||||
{
|
||||
int page = 1;
|
||||
string detail = e.Detail.Replace("我的竞猜", "").Trim();
|
||||
string detail = e.Detail.Replace("我的预测", "").Trim();
|
||||
System.Text.RegularExpressions.Match match = GetFirstNumber().Match(detail);
|
||||
if (match.Success && int.TryParse(match.Value, out int p)) page = p;
|
||||
BotReply reply = BettingController.GetMyBets(uid, page);
|
||||
reply.Keyboard ??= new KeyboardMessage();
|
||||
reply.Keyboard.AppendButtonsWithNewRow(2,
|
||||
Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖", enter: true),
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖", enter: true),
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"));
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"));
|
||||
if (reply.Markdown?.Content?.Contains("创建存档") ?? false)
|
||||
{
|
||||
reply.Keyboard.AppendButtons(2, Button.CreateCmdButton("⚙️ 创建存档", "创建存档"));
|
||||
}
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Detail == "竞猜领奖")
|
||||
if (e.Detail == "预测领奖")
|
||||
{
|
||||
BotReply reply = BettingController.ClaimRewards(uid);
|
||||
reply.Keyboard = new KeyboardMessage()
|
||||
.AppendButtons(2,
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"));
|
||||
if (reply.Markdown?.Content?.Contains("创建存档") ?? false)
|
||||
{
|
||||
reply.Keyboard.AppendButtons(2, Button.CreateCmdButton("⚙️ 创建存档", "创建存档"));
|
||||
}
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Detail.StartsWith("竞猜"))
|
||||
if (e.Detail.StartsWith("预测"))
|
||||
{
|
||||
string detail = e.Detail.Replace("竞猜", "").Trim();
|
||||
string detail = e.Detail.Replace("预测", "").Trim();
|
||||
string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 3 || !int.TryParse(parts[0], out int mid) || !long.TryParse(parts[^1], out long amt))
|
||||
{
|
||||
await SendAsync(e, "CS赛事竞猜", $"格式:竞猜 <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n选项:team1 / team2 / score:2:0 / mvp:选手UID");
|
||||
await SendAsync(e, "CS赛事预测", $"格式:预测 <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n选项:team1 / team2 / score:2:0 / mvp:选手UID");
|
||||
return true;
|
||||
}
|
||||
string option = string.Join(" ", parts[1..^1]).ToLower();
|
||||
|
||||
BotReply reply = BettingController.PlaceBet(uid, mid, option, amt);
|
||||
|
||||
// 根据控制器返回的消息判断投注结果(简单判断是否包含"成功")
|
||||
// 根据控制器返回的消息判断预测结果(简单判断是否包含"成功")
|
||||
bool success = reply.Markdown?.Content?.Contains("成功") ?? false;
|
||||
|
||||
KeyboardMessage kb = new();
|
||||
// 成功与失败通用的按钮
|
||||
kb.AppendButtons(2,
|
||||
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("❓ 竞猜帮助", "竞猜帮助"));
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"));
|
||||
|
||||
if (reply.Markdown?.Content?.Contains("创建存档") ?? false)
|
||||
{
|
||||
@ -215,12 +215,12 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (success)
|
||||
{
|
||||
kb.AppendButtonsWithNewRow(2,
|
||||
Button.CreateCmdButton("🔄 再次投注", e.Detail, enter: false),
|
||||
Button.CreateCmdButton("🔄 再次预测", e.Detail, enter: false),
|
||||
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {mid}"));
|
||||
}
|
||||
|
||||
reply.Keyboard = kb;
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -243,10 +243,10 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("⚙️ 继续结算", "结算比赛 ", enter: false));
|
||||
await SendAsync(e, "CS赛事竞猜", reply);
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
}
|
||||
else
|
||||
await SendAsync(e, "CS赛事竞猜", "格式:结算比赛 <比赛ID> winner=team1 result=2:0");
|
||||
await SendAsync(e, "CS赛事预测", "格式:结算比赛 <比赛ID> winner=team1 result=2:0");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -303,10 +303,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
// 指令:创建比赛 <赛事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
|
||||
// 示例:创建比赛 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
|
||||
// 指令:创建比赛 <赛事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
|
||||
// 示例(提供自定义奖励率):创建比赛 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
|
||||
// 示例(提供队伍1的胜率自动计算奖励率):创建比赛 1 Vitality FaZe Final 2026-05-12 20:00 2026-05-12 19:55 prob=0.6
|
||||
if (e.Detail.StartsWith("创建比赛"))
|
||||
{
|
||||
e.UseNotice = false;
|
||||
@ -321,7 +322,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (parts.Length < 7) // 最少需要 eventId, team1, team2, stage, start date, start time, deadline date, deadline time
|
||||
{
|
||||
await SendAsync(e, "创建比赛",
|
||||
"格式:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> <投注截止时间> [选项列表(逗号分隔)]\r\n" +
|
||||
"格式:创建比赛 <赛事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 ,比分和MVP选项只允许独立添加:score,mvp");
|
||||
@ -344,7 +345,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
// 截止时间(parts[6] + parts[7] 如果存在)
|
||||
if (parts.Length < 8)
|
||||
{
|
||||
await SendAsync(e, "创建比赛", "投注截止时间需要完整日期和时间,示例:2026-03-05 13:55");
|
||||
await SendAsync(e, "创建比赛", "预测截止时间需要完整日期和时间,示例:2026-03-05 13:55");
|
||||
return true;
|
||||
}
|
||||
string deadlineDate = parts[6];
|
||||
@ -359,7 +360,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
// 解析剩余参数:选项和奖励率
|
||||
List<string> optionParts = [];
|
||||
decimal? team1Odds = null, team2Odds = null;
|
||||
decimal? team1Odds = null, team2Odds = null, team1WinProbability = null;
|
||||
for (int i = 8; i < parts.Length; i++)
|
||||
{
|
||||
string segment = parts[i];
|
||||
@ -373,6 +374,16 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (decimal.TryParse(segment[11..], out decimal odds2))
|
||||
team2Odds = odds2;
|
||||
}
|
||||
else if (segment.StartsWith("prob="))
|
||||
{
|
||||
if (decimal.TryParse(segment[5..], out decimal prob))
|
||||
team1WinProbability = prob;
|
||||
else
|
||||
{
|
||||
await SendAsync(e, "创建比赛", "胜率值无效,应为0~1之间的小数。");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
optionParts.Add(segment);
|
||||
@ -391,25 +402,26 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
BetDeadline = deadlineDt,
|
||||
AvailableOptions = options,
|
||||
Team1WinOdds = team1Odds,
|
||||
Team2WinOdds = team2Odds
|
||||
Team2WinOdds = team2Odds,
|
||||
Team1WinProbability = team1WinProbability
|
||||
});
|
||||
await SendAsync(e, "创建比赛", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 指令:关闭投注 <比赛ID>
|
||||
if (e.Detail.StartsWith("关闭投注") || e.Detail.StartsWith("结束竞猜"))
|
||||
// 指令:关闭预测 <比赛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, "关闭投注", "你没有权限执行此操作。");
|
||||
await SendAsync(e, "关闭预测", "你没有权限执行此操作。");
|
||||
return true;
|
||||
}
|
||||
|
||||
string detail = e.Detail
|
||||
.Replace("关闭投注", "")
|
||||
.Replace("结束竞猜", "")
|
||||
.Replace("关闭预测", "")
|
||||
.Replace("结束预测", "")
|
||||
.Trim();
|
||||
if (int.TryParse(detail, out int matchId))
|
||||
{
|
||||
@ -418,11 +430,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
.AppendButtons(2,
|
||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}"));
|
||||
await SendAsync(e, "关闭投注", reply);
|
||||
await SendAsync(e, "关闭预测", reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendAsync(e, "关闭投注", "格式:关闭投注 <比赛ID>");
|
||||
await SendAsync(e, "关闭预测", "格式:关闭预测 <比赛ID>");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -475,6 +487,10 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (decimal.TryParse(val, out decimal t2od)) request.Team2WinOdds = t2od;
|
||||
else { await SendAsync(e, "修改比赛", "t2od 值无效。"); return true; }
|
||||
break;
|
||||
case "prob":
|
||||
if (decimal.TryParse(val, out decimal prob)) request.Team1WinProbability = prob;
|
||||
else { await SendAsync(e, "修改比赛", "prob 值无效。"); return true; }
|
||||
break;
|
||||
case "st":
|
||||
if (DateTime.TryParse(val, out DateTime st)) request.StartTime = st;
|
||||
else { await SendAsync(e, "修改比赛", "开始时间格式错误,请用 yyyy-MM-dd HH:mm。"); return true; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user