添加分页功能

This commit is contained in:
milimoe 2026-05-09 17:33:16 +08:00
parent a5d91d6fc5
commit 6bd7efa8ef
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
3 changed files with 262 additions and 166 deletions

View File

@ -9,16 +9,39 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
public class CSBettingService public class CSBettingService
{ {
public static string GetEventsOverview() public static (string, int) GetEventsOverview(int page, int pageSize)
{ {
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
if (sql != null) if (sql == null) return ("数据库连接失败。", 0);
{
UpdateStatuses(sql); UpdateStatuses(sql);
sql.ExecuteDataSet("SELECT id, name, status, start_time FROM csbetting_events ORDER BY start_time DESC"); // 获取总数
if (!sql.Success || sql.DataSet.Tables.Count == 0) return "暂无赛事。"; sql.ExecuteDataSet("SELECT COUNT(*) FROM csbetting_events");
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
if (total == 0)
return ("暂无赛事。", 1);
int offset = (page - 1) * pageSize;
sql.Parameters["@page_size"] = pageSize;
sql.Parameters["@offset"] = offset;
sql.ExecuteDataSet($@"
SELECT id, name, status, start_time, end_time
FROM csbetting_events
ORDER BY
CASE status WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE 2 END,
CASE WHEN status = 2 THEN end_time END DESC,
CASE WHEN status != 2 THEN start_time END ASC
LIMIT {pageSize} OFFSET {offset}");
if (!sql.Success || sql.DataSet.Tables.Count == 0)
return ("暂无赛事。", 1);
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine($"🏆 赛事列表(第 {page}/{totalPages} 页)");
foreach (DataRow row in sql.DataSet.Tables[0].Rows) foreach (DataRow row in sql.DataSet.Tables[0].Rows)
{ {
int id = Convert.ToInt32(row["id"]); int id = Convert.ToInt32(row["id"]);
@ -27,21 +50,21 @@ namespace Oshima.FunGame.WebAPI.Services
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" }; string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
sb.AppendLine($"🏆 [{id}] {name.CreateCmdInput($" {id}")} ({statusStr})"); sb.AppendLine($"🏆 [{id}] {name.CreateCmdInput($" {id}")} ({statusStr})");
} }
return sb.ToString().TrimEnd(); return (sb.ToString().TrimEnd(), totalPages);
}
return "数据库连接失败。";
} }
public static string GetEventDetail(int eventId) public static (string, int) GetEventDetail(int eventId, int page, int pageSize)
{ {
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
if (sql != null) if (sql == null) return ("数据库连接失败。", 0);
{
UpdateStatuses(sql); UpdateStatuses(sql);
// 获取赛事信息
sql.Parameters["@id"] = eventId; sql.Parameters["@id"] = eventId;
sql.ExecuteDataSet("SELECT * FROM csbetting_events WHERE id = @id"); sql.ExecuteDataSet("SELECT * FROM csbetting_events WHERE id = @id");
if (!sql.Success || sql == null || sql.DataSet.Tables[0].Rows.Count == 0) return "赛事不存在。"; if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
return ("赛事不存在。", 0);
DataRow evt = sql.DataSet.Tables[0].Rows[0]; DataRow evt = sql.DataSet.Tables[0].Rows[0];
string name = evt["name"].ToString() ?? ""; string name = evt["name"].ToString() ?? "";
int status = Convert.ToInt32(evt["status"]); int status = Convert.ToInt32(evt["status"]);
@ -49,14 +72,35 @@ namespace Oshima.FunGame.WebAPI.Services
DateTime end = Convert.ToDateTime(evt["end_time"]); DateTime end = Convert.ToDateTime(evt["end_time"]);
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" }; string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
StringBuilder sb = new(); StringBuilder header = new();
sb.AppendLine($"赛事:{name}"); header.AppendLine($"赛事:{name}");
sb.AppendLine($"状态:{statusStr}"); header.AppendLine($"状态:{statusStr}");
sb.AppendLine($"时间:{start:yyyy/MM/dd} ~ {end:yyyy/MM/dd}"); header.AppendLine($"时间:{start:yyyy/MM/dd} ~ {end:yyyy/MM/dd}");
sb.AppendLine("比赛列表:");
// 比赛总数
sql.Parameters["@eid"] = eventId; sql.Parameters["@eid"] = eventId;
sql.ExecuteDataSet("SELECT id, team1_name, team2_name, status, bet_deadline, stage FROM csbetting_matches WHERE event_id = @eid ORDER BY start_time"); sql.ExecuteDataSet("SELECT COUNT(*) FROM csbetting_matches WHERE event_id = @eid");
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
header.AppendLine($"比赛列表(第 {page}/{totalPages} 页):");
// 分页查询比赛
int offset = (page - 1) * pageSize;
sql.Parameters["@eid"] = eventId;
sql.ExecuteDataSet($@"
SELECT id, team1_name, team2_name, status, bet_deadline, stage, start_time
FROM csbetting_matches
WHERE event_id = @eid
ORDER BY
CASE status WHEN 0 THEN 0 WHEN 1 THEN 1 ELSE 2 END,
CASE WHEN status = 2 THEN start_time END DESC,
CASE WHEN status != 2 THEN start_time END ASC
LIMIT {pageSize} OFFSET {offset}");
StringBuilder matches = new();
if (sql.Success && sql.DataSet?.Tables[0].Rows.Count > 0) if (sql.Success && sql.DataSet?.Tables[0].Rows.Count > 0)
{ {
foreach (DataRow row in sql.DataSet.Tables[0].Rows) foreach (DataRow row in sql.DataSet.Tables[0].Rows)
@ -70,12 +114,15 @@ namespace Oshima.FunGame.WebAPI.Services
string mStatusStr = mstatus switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" }; string mStatusStr = mstatus switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
string matchLabel = $"{t1} vs {t2}"; string matchLabel = $"{t1} vs {t2}";
string clickableMatch = matchLabel.CreateCmdInput($"比赛详情 {mid}"); string clickableMatch = matchLabel.CreateCmdInput($"比赛详情 {mid}");
sb.AppendLine($" [{mid}] {(stage != "" ? $"{stage} " : "")} {clickableMatch} (状态:{mStatusStr}, 截止:{deadline:MM-dd HH:mm})"); matches.AppendLine($" [{mid}] {(stage != "" ? $"{stage} " : "")} {clickableMatch} (状态:{mStatusStr}, 截止:{deadline:MM-dd HH:mm})");
} }
} }
return sb.ToString(); else
{
matches.AppendLine("暂无比赛。");
} }
return "数据库连接失败。";
return (header.ToString() + matches.ToString(), totalPages);
} }
public static string GetMatchDetail(int matchId, out int status) public static string GetMatchDetail(int matchId, out int status)
@ -293,11 +340,11 @@ namespace Oshima.FunGame.WebAPI.Services
return "数据库连接失败。"; return "数据库连接失败。";
} }
public static string GetMyBets(long uid, long mid = -1) public static (string, int) GetMyBets(long uid, long mid = -1, int page = 1, int pageSize = 10)
{ {
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
if (sql != null) if (sql == null) return ("数据库连接失败。", 0);
{
UpdateStatuses(sql); UpdateStatuses(sql);
sql.Parameters["@uid"] = uid; sql.Parameters["@uid"] = uid;
@ -307,7 +354,34 @@ namespace Oshima.FunGame.WebAPI.Services
sql.Parameters["@mid"] = mid; sql.Parameters["@mid"] = mid;
matchFilter = " AND br.match_id = @mid"; matchFilter = " AND br.match_id = @mid";
} }
sql.ExecuteDataSet($@"SELECT br.match_id, m.team1_name, m.team2_name, m.status AS match_status,
// 总数查询:不同比赛的数量
sql.ExecuteDataSet($@"
SELECT COUNT(DISTINCT br.match_id)
FROM csbetting_bet_records br
JOIN csbetting_matches m ON br.match_id = m.id
WHERE br.user_id = @uid {matchFilter}");
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
if (total == 0)
return ("你还没有任何竞猜记录。", 1);
sql.Parameters["@uid"] = uid;
matchFilter = "";
if (mid > 0)
{
sql.Parameters["@mid"] = mid;
matchFilter = " AND br.match_id = @mid";
}
int offset = (page - 1) * pageSize;
sql.Parameters["@page_size"] = pageSize;
sql.Parameters["@offset"] = offset;
// 分页聚合查询
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, GROUP_CONCAT(CONCAT(br.option_type, ':', br.option_value, ':', br.amount) ORDER BY br.id SEPARATOR '|') AS details,
SUM(br.amount) AS total_amount, SUM(br.amount) AS total_amount,
MIN(br.is_settled) AS all_settled, MIN(br.is_settled) AS all_settled,
@ -318,11 +392,18 @@ namespace Oshima.FunGame.WebAPI.Services
FROM csbetting_bet_records br FROM csbetting_bet_records br
JOIN csbetting_matches m ON br.match_id = m.id JOIN csbetting_matches m ON br.match_id = m.id
WHERE br.user_id = @uid {matchFilter} WHERE br.user_id = @uid {matchFilter}
GROUP BY br.match_id, m.team1_name, m.team2_name, m.status GROUP BY br.match_id, m.team1_name, m.team2_name, m.status, m.start_time
ORDER BY MAX(br.bet_time) DESC"); ORDER BY
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0) return "你还没有任何竞猜记录。"; MIN(br.is_settled) ASC,
CASE WHEN MIN(br.is_settled) = 0 THEN MIN(m.start_time) END ASC,
CASE WHEN MIN(br.is_settled) = 1 THEN MIN(m.start_time) END DESC
LIMIT {pageSize} OFFSET {offset}");
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
return ("你还没有任何竞猜记录。", 1);
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine($"我的竞猜(第 {page}/{totalPages} 页)");
foreach (DataRow row in sql.DataSet.Tables[0].Rows) foreach (DataRow row in sql.DataSet.Tables[0].Rows)
{ {
int matchId = Convert.ToInt32(row["match_id"]); int matchId = Convert.ToInt32(row["match_id"]);
@ -386,9 +467,7 @@ namespace Oshima.FunGame.WebAPI.Services
sb.Append($" (+{totalPayout}G)"); sb.Append($" (+{totalPayout}G)");
sb.AppendLine(); sb.AppendLine();
} }
return sb.ToString().TrimEnd(); return (sb.ToString().TrimEnd(), totalPages);
}
return "数据库连接失败。";
} }
/// <summary> /// <summary>

View File

@ -26,16 +26,20 @@ namespace Oshima.FunGame.WebAPI.Controllers
// ---------- 查询类(无需锁)---------- // ---------- 查询类(无需锁)----------
[AllowAnonymous] [AllowAnonymous]
[HttpGet("events")] [HttpGet("events")]
public BotReply GetEventsOverview() public BotReply GetEventsOverview([FromQuery] int page = 1, [FromQuery] int pageSize = 10)
{ {
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetEventsOverview() } }; var (content, totalPages) = CSBettingService.GetEventsOverview(page, pageSize);
KeyboardMessage kb = new KeyboardMessage().AddPaginationRow("赛事列表 ", page, totalPages);
return new BotReply { Markdown = new MarkdownMessage { Content = content }, Keyboard = kb };
} }
[AllowAnonymous] [AllowAnonymous]
[HttpGet("event/{eventId:int}")] [HttpGet("event/{eventId:int}")]
public BotReply GetEventDetail(int eventId) public BotReply GetEventDetail(int eventId, [FromQuery] int page = 1, [FromQuery] int pageSize = 10)
{ {
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetEventDetail(eventId) } }; var (content, totalPages) = CSBettingService.GetEventDetail(eventId, page, pageSize);
KeyboardMessage kb = new KeyboardMessage().AddPaginationRow($"赛事详情 {eventId} ", page, totalPages);
return new BotReply { Markdown = new MarkdownMessage { Content = content }, Keyboard = kb };
} }
[AllowAnonymous] [AllowAnonymous]
@ -47,16 +51,19 @@ namespace Oshima.FunGame.WebAPI.Controllers
[AllowAnonymous] [AllowAnonymous]
[HttpGet("mybets/{uid:long}")] [HttpGet("mybets/{uid:long}")]
public BotReply GetMyBets(long uid) public BotReply GetMyBets(long uid, [FromQuery] int page = 1, [FromQuery] int pageSize = 10)
{ {
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetMyBets(uid) } }; var (content, totalPages) = CSBettingService.GetMyBets(uid, -1, page, pageSize);
KeyboardMessage kb = new KeyboardMessage().AddPaginationRow("我的竞猜 ", page, totalPages);
return new BotReply { Markdown = new MarkdownMessage { Content = content }, Keyboard = kb };
} }
[AllowAnonymous] [AllowAnonymous]
[HttpGet("mybets/{uid:long}/{mid:long}")] [HttpGet("mybets/{uid:long}/{mid:long}")]
public BotReply GetMyBets(long uid, long mid) public BotReply GetMyBets(long uid, long mid)
{ {
return new BotReply { Markdown = new MarkdownMessage { Content = CSBettingService.GetMyBets(uid, mid) } }; var (content, _) = CSBettingService.GetMyBets(uid, mid, 1, int.MaxValue);
return new BotReply { Markdown = new MarkdownMessage { Content = content } };
} }
// ---------- 需要用户锁的操作 ---------- // ---------- 需要用户锁的操作 ----------

View File

@ -35,9 +35,12 @@ namespace Oshima.FunGame.WebAPI.Services
} }
// 赛事列表 // 赛事列表
if (e.Detail == "赛事列表") if (e.Detail.StartsWith("赛事列表"))
{ {
BotReply reply = BettingController.GetEventsOverview(); int page = 1;
string[] parts = e.Detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 1 && int.TryParse(parts[1], out int p)) page = p;
BotReply reply = BettingController.GetEventsOverview(page); // 需要Controller增加page参数的方法
await SendAsync(e, "CS赛事竞猜", reply); await SendAsync(e, "CS赛事竞猜", reply);
return true; return true;
} }
@ -62,7 +65,7 @@ namespace Oshima.FunGame.WebAPI.Services
Button.CreateCmdButton("📋 赛事列表", "赛事列表"), Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖")); Button.CreateCmdButton("💰 竞猜领奖", "竞猜领奖"));
reply.Keyboard = kb; reply.Keyboard = kb;
BotReply reply2 = BettingController.GetMyBets(uid, matchId); 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.Trim()}\r\n你的本场竞猜记录\r\n{reply2.Markdown.Content}"; reply.Markdown.Content = $"{reply.Markdown.Content.Trim()}\r\n你的本场竞猜记录\r\n{reply2.Markdown.Content}";
@ -79,13 +82,17 @@ namespace Oshima.FunGame.WebAPI.Services
// 赛事详情:用于查看某一赛事下的所有比赛 // 赛事详情:用于查看某一赛事下的所有比赛
if (e.Detail.StartsWith("赛事详情")) if (e.Detail.StartsWith("赛事详情"))
{ {
string detail = e.Detail.Replace("赛事详情", "").Trim(); string detail = e.Detail["赛事详情".Length..].Trim();
if (int.TryParse(detail, out int eventId)) // 解析事件ID和页码
string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1 && int.TryParse(parts[0], out int eventId))
{ {
int page = 1;
if (parts.Length > 1 && int.TryParse(parts[1], out int p)) page = p;
// 调用控制器获取详情返回BotReply // 调用控制器获取详情返回BotReply
BotReply reply = BettingController.GetEventDetail(eventId); BotReply reply = BettingController.GetEventDetail(eventId, page);
reply.Keyboard = new KeyboardMessage() reply.Keyboard ??= new KeyboardMessage();
.AppendButtons(2, reply.Keyboard.AppendButtonsWithNewRow(2,
Button.CreateCmdButton("🔍 比赛详情 ", "比赛详情 ", enter: false), Button.CreateCmdButton("🔍 比赛详情 ", "比赛详情 ", enter: false),
Button.CreateCmdButton("📋 赛事列表", "赛事列表"), Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"), Button.CreateCmdButton("📜 我的竞猜", "我的竞猜"),
@ -110,11 +117,14 @@ namespace Oshima.FunGame.WebAPI.Services
return true; return true;
} }
if (e.Detail == "我的竞猜") if (e.Detail.StartsWith("我的竞猜"))
{ {
BotReply reply = BettingController.GetMyBets(uid); int page = 1;
reply.Keyboard = new KeyboardMessage() string[] parts = e.Detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
.AppendButtons(2, if (parts.Length > 1 && int.TryParse(parts[1], 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("❓ 竞猜帮助", "竞猜帮助"));