mirror of
https://github.com/oshima-studios/OshimaGameModule.git
synced 2026-06-04 19:42:13 +00:00
优化+新功能+BUG修复
This commit is contained in:
parent
3a8477efae
commit
9dfd61885b
@ -43,6 +43,11 @@ namespace Oshima.FunGame.OshimaModules.Regions
|
||||
|
||||
}
|
||||
|
||||
public virtual bool AddGoodsToStore(string storeName, List<Goods> goodsList, bool addToNextRefreshGoods = true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder = new();
|
||||
|
||||
@ -187,6 +187,46 @@ namespace Oshima.FunGame.OshimaModules.Regions
|
||||
storeTemplate.SaveConfig();
|
||||
}
|
||||
|
||||
public override bool AddGoodsToStore(string storeName, List<Goods> goodsList, bool addToNextRefreshGoods = true)
|
||||
{
|
||||
EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo");
|
||||
storeTemplate.LoadConfig();
|
||||
|
||||
Store? store = storeTemplate.Get(storeName);
|
||||
store ??= storeName switch
|
||||
{
|
||||
"dokyo_forge" => CreateNewForgeStore(),
|
||||
"dokyo_horseracing" => CreateNewHorseRacingStore(),
|
||||
"dokyo_cooperative" => CreateNewCooperativeStore(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (store is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long maxKey = store.Goods.Count > 0 ? store.Goods.Keys.Max() : 0;
|
||||
long newKey = maxKey + 1;
|
||||
|
||||
foreach (Goods goods in goodsList)
|
||||
{
|
||||
goods.Id = newKey;
|
||||
store.Goods[newKey] = goods;
|
||||
|
||||
if (addToNextRefreshGoods)
|
||||
{
|
||||
store.NextRefreshGoods[newKey] = goods;
|
||||
}
|
||||
|
||||
newKey++;
|
||||
}
|
||||
|
||||
storeTemplate.Add(storeName, store);
|
||||
storeTemplate.SaveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Store CreateNewForgeStore()
|
||||
{
|
||||
Store store = new("锻造积分商店")
|
||||
|
||||
@ -81,5 +81,134 @@ namespace Oshima.FunGame.WebAPI.Model
|
||||
|
||||
[JsonPropertyName("result")]
|
||||
public string? Result { get; set; }
|
||||
|
||||
[JsonPropertyName("stage")]
|
||||
public string? Stage { get; set; }
|
||||
}
|
||||
|
||||
public class BettingEvent
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public int Status { get; set; } // 0=未开始 1=进行中 2=已结束
|
||||
|
||||
[JsonPropertyName("start_time")]
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
[JsonPropertyName("end_time")]
|
||||
public DateTime EndTime { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class BettingMatch
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("event_id")]
|
||||
public int EventId { get; set; }
|
||||
|
||||
[JsonPropertyName("stage")]
|
||||
public string? Stage { get; set; }
|
||||
|
||||
[JsonPropertyName("team1_name")]
|
||||
public string Team1Name { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("team1_logo")]
|
||||
public string? Team1Logo { get; set; }
|
||||
|
||||
[JsonPropertyName("team2_name")]
|
||||
public string Team2Name { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("team2_logo")]
|
||||
public string? Team2Logo { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public int Status { get; set; } // 0=未开始 1=进行中 2=已结束
|
||||
|
||||
[JsonPropertyName("start_time")]
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
[JsonPropertyName("bet_deadline")]
|
||||
public DateTime BetDeadline { get; set; }
|
||||
|
||||
[JsonPropertyName("result")]
|
||||
public string? Result { get; set; }
|
||||
|
||||
[JsonPropertyName("winner")]
|
||||
public int? Winner { get; set; } // 1=team1, 2=team2, null=未定
|
||||
|
||||
[JsonPropertyName("available_options")]
|
||||
public string AvailableOptions { get; set; } = "[]"; // JSON 数组字符串
|
||||
|
||||
[JsonPropertyName("team1_win_odds")]
|
||||
public decimal Team1WinOdds { get; set; }
|
||||
|
||||
[JsonPropertyName("team2_win_odds")]
|
||||
public decimal Team2WinOdds { get; set; }
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class BettingBetRecord
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("user_id")]
|
||||
public long UserId { get; set; }
|
||||
|
||||
[JsonPropertyName("match_id")]
|
||||
public int MatchId { get; set; }
|
||||
|
||||
[JsonPropertyName("option_type")]
|
||||
public int OptionType { get; set; } // 1=team1胜 2=team2胜 3=精确比分 4=MVP
|
||||
|
||||
[JsonPropertyName("option_value")]
|
||||
public string OptionValue { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("amount")]
|
||||
public long Amount { get; set; }
|
||||
|
||||
[JsonPropertyName("odds_at_bet")]
|
||||
public decimal OddsAtBet { get; set; }
|
||||
|
||||
[JsonPropertyName("bet_time")]
|
||||
public DateTime BetTime { get; set; }
|
||||
|
||||
[JsonPropertyName("is_settled")]
|
||||
public bool IsSettled { get; set; }
|
||||
|
||||
[JsonPropertyName("payout")]
|
||||
public long? Payout { get; set; }
|
||||
|
||||
[JsonPropertyName("is_claimed")]
|
||||
public bool IsClaimed { get; set; }
|
||||
|
||||
[JsonPropertyName("result_note")]
|
||||
public string? ResultNote { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@ -151,7 +151,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
|
||||
sql.ExecuteDataSet($@"
|
||||
SELECT m.id, m.team1_name, m.team2_name, m.status, m.start_time, m.stage,
|
||||
e.name AS event_name, e.id AS event_id
|
||||
e.name AS event_name, e.id AS event_id, m.result
|
||||
FROM csbetting_matches m
|
||||
LEFT JOIN csbetting_events e ON m.event_id = e.id
|
||||
ORDER BY
|
||||
@ -176,8 +176,9 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
string stage = row["stage"]?.ToString() ?? "";
|
||||
string eventName = row["event_name"]?.ToString() ?? "";
|
||||
long eventId = Convert.ToInt64(row["event_id"]);
|
||||
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
||||
|
||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => $"已结束 | {result}", _ => "未知" };
|
||||
string matchLabel = $"{t1} vs {t2}".CreateCmdInput($"比赛详情 {id}");
|
||||
|
||||
sb.Append($"[{id}] {matchLabel}");
|
||||
@ -222,6 +223,22 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
eventName = rowEvent["name"].ToString() ?? "";
|
||||
}
|
||||
|
||||
// 查询各选项统计
|
||||
sql.Parameters["@mid"] = matchId;
|
||||
DataSet stats = sql.ExecuteDataSet(@"SELECT option_type,
|
||||
COUNT(*) AS totalRecord, SUM(amount) AS totalAmount
|
||||
FROM csbetting_bet_records WHERE match_id = @mid GROUP BY option_type");
|
||||
|
||||
// 将统计存入字典便于查找
|
||||
Dictionary<int, (int, long)> statDict = [];
|
||||
if (sql.Success && stats.Tables[0].Rows.Count > 0)
|
||||
{
|
||||
foreach (DataRow srow in stats.Tables[0].Rows)
|
||||
{
|
||||
statDict[Convert.ToInt32(srow["option_type"])] = (Convert.ToInt32(srow["totalRecord"]), Convert.ToInt64(srow["totalAmount"]));
|
||||
}
|
||||
}
|
||||
|
||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine($"比赛 #{matchId}");
|
||||
@ -241,27 +258,44 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
sb.AppendLine($"> 📝 {description}\r\n");
|
||||
}
|
||||
|
||||
if (status == 0) sb.AppendLine($"可用选项:");
|
||||
DateTime now = DateTime.Now;
|
||||
bool canBet = (status == 0 || status == 1) && now < deadline;
|
||||
if (canBet) sb.AppendLine($"可用选项:");
|
||||
else sb.AppendLine($"该比赛已截止预测。");
|
||||
|
||||
string GetStatString(int opt)
|
||||
{
|
||||
if (statDict.TryGetValue(opt, out var stat) && stat.Item1 > 0)
|
||||
{
|
||||
return $" 👥 {stat.Item1} 🔥 {stat.Item2}";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
string statText = "";
|
||||
if (available.Contains("team1_win"))
|
||||
{
|
||||
sb.AppendLine($" - {t1} 胜 (x {team1Odds})");
|
||||
if (status == 0) kb.AppendButtons(2, Button.CreateCmdButton($"⚔️ {t1} 胜", $"预测 {matchId} team1 1000", enter: false));
|
||||
statText = GetStatString(1);
|
||||
sb.AppendLine($" - {t1} 胜 (x {team1Odds}){statText}");
|
||||
if (canBet) kb.AppendButtons(2, Button.CreateCmdButton($"⚔️ {t1} 胜", $"预测 {matchId} team1 1000", enter: false));
|
||||
}
|
||||
if (available.Contains("team2_win"))
|
||||
{
|
||||
sb.AppendLine($" - {t2} 胜 (x {team2Odds})");
|
||||
if (status == 0) kb.AppendButtons(2, Button.CreateCmdButton($"🛡️ {t2} 胜", $"预测 {matchId} team2 1000", enter: false));
|
||||
statText = GetStatString(2);
|
||||
sb.AppendLine($" - {t2} 胜 (x {team2Odds}){statText}");
|
||||
if (canBet) kb.AppendButtons(2, Button.CreateCmdButton($"🛡️ {t2} 胜", $"预测 {matchId} team2 1000", enter: false));
|
||||
}
|
||||
if (available.Contains("score"))
|
||||
{
|
||||
sb.AppendLine($" - 精确比分 (x 4)");
|
||||
if (status == 0) kb.AppendButtons(2, Button.CreateCmdButton("🎯 精确比分", $"预测 {matchId} score:", enter: false));
|
||||
statText = GetStatString(3);
|
||||
sb.AppendLine($" - 精确比分 (x 4){statText}");
|
||||
if (canBet) kb.AppendButtons(2, Button.CreateCmdButton("🎯 精确比分", $"预测 {matchId} score:", enter: false));
|
||||
}
|
||||
if (available.Contains("mvp"))
|
||||
{
|
||||
sb.AppendLine($" - 赛事MVP (x 3.5)");
|
||||
if (status == 0) kb.AppendButtons(2, Button.CreateCmdButton("🏆 MVP", $"预测 {matchId} mvp:", enter: false));
|
||||
statText = GetStatString(4);
|
||||
sb.AppendLine($" - 赛事MVP (x 3.5){statText}");
|
||||
if (canBet) kb.AppendButtons(2, Button.CreateCmdButton("🏆 MVP", $"预测 {matchId} mvp:", enter: false));
|
||||
}
|
||||
if (status == 2)
|
||||
{
|
||||
@ -270,7 +304,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
if (winner != 3) sb.AppendLine($"结果:{result}");
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
if (canBet)
|
||||
{
|
||||
sb.AppendLine($"预测指令:{"预测".CreateCmdInput()} <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n👇🏻 点击下方按钮快速预测");
|
||||
}
|
||||
@ -316,9 +350,9 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
alreadyBet = Convert.ToInt64(sql.DataSet.Tables[0].Rows[0]["total"] ?? 0L);
|
||||
totalBet += alreadyBet;
|
||||
}
|
||||
if (totalBet > 5000)
|
||||
if (totalBet > 10000)
|
||||
{
|
||||
error = $"本场比赛你的助力总额不能超过 5000 {General.GameplayEquilibriumConstant.InGameCurrency}(已助力 {alreadyBet})。";
|
||||
error = $"本场比赛你的助力总额不能超过 10000 {General.GameplayEquilibriumConstant.InGameCurrency}(已助力 {alreadyBet})。";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -919,6 +953,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
sql.Parameters["@result"] = (object?)request.Result ?? "";
|
||||
setClause.Append("result = @result, ");
|
||||
}
|
||||
if (request.Stage != null)
|
||||
{
|
||||
sql.Parameters["@stage"] = (object?)request.Stage ?? "";
|
||||
setClause.Append("stage = @stage, ");
|
||||
}
|
||||
|
||||
if (setClause.Length == 0)
|
||||
{
|
||||
|
||||
@ -28,7 +28,6 @@ CREATE TABLE IF NOT EXISTS `csbetting_events` (
|
||||
`status` tinyint NOT NULL DEFAULT '0' COMMENT '赛事状态:0=未开始,1=进行中,2=已结束',
|
||||
`start_time` datetime NOT NULL COMMENT '赛事开始时间',
|
||||
`end_time` datetime NOT NULL COMMENT '赛事结束时间',
|
||||
`mvp_candidates` json DEFAULT NULL COMMENT 'MVP候选人UID列表,如 [1001, 1002]',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
|
||||
267
OshimaWebAPI/Controllers/BettingController.cs
Normal file
267
OshimaWebAPI/Controllers/BettingController.cs
Normal file
@ -0,0 +1,267 @@
|
||||
using System.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Milimoe.FunGame.Core.Api.Utility;
|
||||
using Oshima.FunGame.WebAPI.Model;
|
||||
|
||||
namespace Oshima.FunGame.WebAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class BettingController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取赛事列表(分页 + 可选状态过滤)
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("events")]
|
||||
public ActionResult<IEnumerable<BettingEvent>> GetEvents(
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
[FromQuery] int? status = null)
|
||||
{
|
||||
using var sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null) return StatusCode(500, "数据库连接失败");
|
||||
sql.ClearParametersAfterExecute = false;
|
||||
|
||||
// 动态条件
|
||||
string where = status.HasValue ? "WHERE status = @status" : "";
|
||||
if (status.HasValue) sql.Parameters["@status"] = status.Value;
|
||||
|
||||
// 总数
|
||||
sql.ExecuteDataSet($"SELECT COUNT(*) FROM csbetting_events {where}");
|
||||
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
|
||||
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
if (page < 1) page = 1;
|
||||
|
||||
int offset = (page - 1) * pageSize;
|
||||
sql.ExecuteDataSet($@"
|
||||
SELECT * FROM csbetting_events {where}
|
||||
ORDER BY start_time DESC
|
||||
LIMIT {pageSize} OFFSET {offset}");
|
||||
|
||||
if (!sql.Success || sql.DataSet.Tables.Count == 0)
|
||||
return Ok(new { data = Array.Empty<BettingEvent>(), page, totalPages, total });
|
||||
|
||||
var list = new List<BettingEvent>();
|
||||
foreach (DataRow row in sql.DataSet.Tables[0].Rows)
|
||||
{
|
||||
list.Add(MapEvent(row));
|
||||
}
|
||||
|
||||
return Ok(new { data = list, page, totalPages, total });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个赛事详情
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("events/{id:int}")]
|
||||
public ActionResult<BettingEvent> GetEvent(int id)
|
||||
{
|
||||
using var sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null) return StatusCode(500, "数据库连接失败");
|
||||
sql.ClearParametersAfterExecute = false;
|
||||
|
||||
sql.Parameters["@id"] = id;
|
||||
DataRow? row = sql.ExecuteDataRow("SELECT * FROM csbetting_events WHERE id = @id");
|
||||
if (row == null) return NotFound();
|
||||
|
||||
return Ok(MapEvent(row));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取比赛列表(分页 + 可选过滤)
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("matches")]
|
||||
public ActionResult<IEnumerable<BettingMatch>> GetMatches(
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
[FromQuery] int? eventId = null,
|
||||
[FromQuery] int? status = null)
|
||||
{
|
||||
using var sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null) return StatusCode(500, "数据库连接失败");
|
||||
sql.ClearParametersAfterExecute = false;
|
||||
|
||||
var conditions = new List<string>();
|
||||
if (eventId.HasValue)
|
||||
{
|
||||
conditions.Add("event_id = @eventId");
|
||||
sql.Parameters["@eventId"] = eventId.Value;
|
||||
}
|
||||
if (status.HasValue)
|
||||
{
|
||||
conditions.Add("status = @status");
|
||||
sql.Parameters["@status"] = status.Value;
|
||||
}
|
||||
string where = conditions.Count > 0 ? "WHERE " + string.Join(" AND ", conditions) : "";
|
||||
|
||||
sql.ExecuteDataSet($"SELECT COUNT(*) FROM csbetting_matches {where}");
|
||||
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
|
||||
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
if (page < 1) page = 1;
|
||||
|
||||
int offset = (page - 1) * pageSize;
|
||||
sql.ExecuteDataSet($@"
|
||||
SELECT * FROM csbetting_matches {where}
|
||||
ORDER BY start_time DESC
|
||||
LIMIT {pageSize} OFFSET {offset}");
|
||||
|
||||
if (!sql.Success || sql.DataSet.Tables.Count == 0)
|
||||
return Ok(new { data = Array.Empty<BettingMatch>(), page, totalPages, total });
|
||||
|
||||
var list = new List<BettingMatch>();
|
||||
foreach (DataRow row in sql.DataSet.Tables[0].Rows)
|
||||
{
|
||||
list.Add(MapMatch(row));
|
||||
}
|
||||
|
||||
return Ok(new { data = list, page, totalPages, total });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个比赛详情
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("matches/{id:int}")]
|
||||
public ActionResult<BettingMatch> GetMatch(int id)
|
||||
{
|
||||
using var sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null) return StatusCode(500, "数据库连接失败");
|
||||
sql.ClearParametersAfterExecute = false;
|
||||
|
||||
sql.Parameters["@id"] = id;
|
||||
DataRow? row = sql.ExecuteDataRow("SELECT * FROM csbetting_matches WHERE id = @id");
|
||||
if (row == null) return NotFound();
|
||||
|
||||
return Ok(MapMatch(row));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取投注记录列表(管理员专用)
|
||||
/// </summary>
|
||||
[Authorize(AuthenticationSchemes = "CustomBearer")]
|
||||
[HttpGet("bets")]
|
||||
public ActionResult<IEnumerable<BettingBetRecord>> GetBets(
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
[FromQuery] long? userId = null,
|
||||
[FromQuery] int? matchId = null,
|
||||
[FromQuery] bool? isSettled = null)
|
||||
{
|
||||
// 简易管理员检查(可替换为实际鉴权)
|
||||
if (!User.IsInRole("Admin") && !User.IsInRole("Operator"))
|
||||
return Forbid();
|
||||
|
||||
using var sql = Factory.OpenFactory.GetSQLHelper();
|
||||
if (sql == null) return StatusCode(500, "数据库连接失败");
|
||||
sql.ClearParametersAfterExecute = false;
|
||||
|
||||
var conditions = new List<string>();
|
||||
if (userId.HasValue)
|
||||
{
|
||||
conditions.Add("user_id = @uid");
|
||||
sql.Parameters["@uid"] = userId.Value;
|
||||
}
|
||||
if (matchId.HasValue)
|
||||
{
|
||||
conditions.Add("match_id = @mid");
|
||||
sql.Parameters["@mid"] = matchId.Value;
|
||||
}
|
||||
if (isSettled.HasValue)
|
||||
{
|
||||
conditions.Add("is_settled = @settled");
|
||||
sql.Parameters["@settled"] = isSettled.Value ? 1 : 0;
|
||||
}
|
||||
string where = conditions.Count > 0 ? "WHERE " + string.Join(" AND ", conditions) : "";
|
||||
|
||||
sql.ExecuteDataSet($"SELECT COUNT(*) FROM csbetting_bet_records {where}");
|
||||
int total = sql.Success ? Convert.ToInt32(sql.DataSet.Tables[0].Rows[0][0]) : 0;
|
||||
int totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
if (page < 1) page = 1;
|
||||
|
||||
int offset = (page - 1) * pageSize;
|
||||
sql.ExecuteDataSet($@"
|
||||
SELECT * FROM csbetting_bet_records {where}
|
||||
ORDER BY bet_time DESC
|
||||
LIMIT {pageSize} OFFSET {offset}");
|
||||
|
||||
if (!sql.Success || sql.DataSet.Tables.Count == 0)
|
||||
return Ok(new { data = Array.Empty<BettingBetRecord>(), page, totalPages, total });
|
||||
|
||||
var list = new List<BettingBetRecord>();
|
||||
foreach (DataRow row in sql.DataSet.Tables[0].Rows)
|
||||
{
|
||||
list.Add(MapBetRecord(row));
|
||||
}
|
||||
|
||||
return Ok(new { data = list, page, totalPages, total });
|
||||
}
|
||||
|
||||
// ---------- 映射辅助方法 ----------
|
||||
private static BettingEvent MapEvent(DataRow row)
|
||||
{
|
||||
return new BettingEvent
|
||||
{
|
||||
Id = Convert.ToInt32(row["id"]),
|
||||
Name = row["name"].ToString() ?? "",
|
||||
Status = Convert.ToInt32(row["status"]),
|
||||
StartTime = Convert.ToDateTime(row["start_time"]),
|
||||
EndTime = Convert.ToDateTime(row["end_time"]),
|
||||
CreatedAt = Convert.ToDateTime(row["created_at"]),
|
||||
UpdatedAt = Convert.ToDateTime(row["updated_at"])
|
||||
};
|
||||
}
|
||||
|
||||
private static BettingMatch MapMatch(DataRow row)
|
||||
{
|
||||
return new BettingMatch
|
||||
{
|
||||
Id = Convert.ToInt32(row["id"]),
|
||||
EventId = Convert.ToInt32(row["event_id"]),
|
||||
Stage = row["stage"]?.ToString(),
|
||||
Team1Name = row["team1_name"].ToString() ?? "",
|
||||
Team1Logo = row["team1_logo"]?.ToString(),
|
||||
Team2Name = row["team2_name"].ToString() ?? "",
|
||||
Team2Logo = row["team2_logo"]?.ToString(),
|
||||
Status = Convert.ToInt32(row["status"]),
|
||||
StartTime = Convert.ToDateTime(row["start_time"]),
|
||||
BetDeadline = Convert.ToDateTime(row["bet_deadline"]),
|
||||
Result = row["result"]?.ToString(),
|
||||
Winner = row["winner"] != DBNull.Value ? Convert.ToInt32(row["winner"]) : null,
|
||||
AvailableOptions = row["available_options"].ToString() ?? "[]",
|
||||
Team1WinOdds = Convert.ToDecimal(row["team1_win_odds"]),
|
||||
Team2WinOdds = Convert.ToDecimal(row["team2_win_odds"]),
|
||||
Description = row["description"]?.ToString(),
|
||||
CreatedAt = Convert.ToDateTime(row["created_at"]),
|
||||
UpdatedAt = Convert.ToDateTime(row["updated_at"])
|
||||
};
|
||||
}
|
||||
|
||||
private static BettingBetRecord MapBetRecord(DataRow row)
|
||||
{
|
||||
return new BettingBetRecord
|
||||
{
|
||||
Id = Convert.ToInt64(row["id"]),
|
||||
UserId = Convert.ToInt64(row["user_id"]),
|
||||
MatchId = Convert.ToInt32(row["match_id"]),
|
||||
OptionType = Convert.ToInt32(row["option_type"]),
|
||||
OptionValue = row["option_value"].ToString() ?? "",
|
||||
Amount = Convert.ToInt64(row["amount"]),
|
||||
OddsAtBet = Convert.ToDecimal(row["odds_at_bet"]),
|
||||
BetTime = Convert.ToDateTime(row["bet_time"]),
|
||||
IsSettled = Convert.ToInt32(row["is_settled"]) == 1,
|
||||
Payout = row["payout"] != DBNull.Value ? Convert.ToInt64(row["payout"]) : null,
|
||||
IsClaimed = Convert.ToInt32(row["is_claimed"]) == 1,
|
||||
ResultNote = row["result_note"]?.ToString(),
|
||||
CreatedAt = Convert.ToDateTime(row["created_at"]),
|
||||
UpdatedAt = Convert.ToDateTime(row["updated_at"])
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +162,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
long total = CSBettingService.ClaimRewards(uid);
|
||||
if (total > 0)
|
||||
{
|
||||
user.Inventory.Credits += (int)total;
|
||||
user.Inventory.Credits += total;
|
||||
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
|
||||
md.Content = $"领取成功!获得 {total} {General.GameplayEquilibriumConstant.InGameCurrency}。";
|
||||
}
|
||||
|
||||
@ -8577,6 +8577,79 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("systemstoreaddgoods")]
|
||||
public BotReply SystemStoreAddGoods([FromQuery] long? uid = null, [FromQuery] long region = 0, [FromQuery] string storeName = "", [FromQuery] string name = "", [FromQuery] double price = 0, [FromQuery] int stock = -1, [FromQuery] int quota = 0, [FromQuery] bool addToNextRefreshGoods = true)
|
||||
{
|
||||
long userid = uid ?? Convert.ToInt64("10" + Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 11));
|
||||
|
||||
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
|
||||
|
||||
if (pc.Count > 0)
|
||||
{
|
||||
User user = FunGameService.GetUser(pc);
|
||||
|
||||
string msg = "";
|
||||
if (user.IsAdmin)
|
||||
{
|
||||
if (FunGameConstant.PlayerRegions.FirstOrDefault(r => r.Id == region) is OshimaRegion or)
|
||||
{
|
||||
if (FunGameConstant.Items.FirstOrDefault(i => i.Name == name) is Item item)
|
||||
{
|
||||
Item newItem = item.Copy();
|
||||
Goods g = new()
|
||||
{
|
||||
Name = newItem.Name,
|
||||
Description = newItem.Description,
|
||||
Stock = stock,
|
||||
Quota = quota,
|
||||
};
|
||||
g.Items.Add(newItem);
|
||||
if (price == 0)
|
||||
{
|
||||
(int min, int max) = (0, 0);
|
||||
if (FunGameConstant.PriceRanges.TryGetValue(item.QualityType, out (int Min, int Max) range))
|
||||
{
|
||||
(min, max) = (range.Min, range.Max);
|
||||
}
|
||||
price = Random.Shared.Next(min, max);
|
||||
}
|
||||
newItem.Price = price;
|
||||
g.SetPrice(General.GameplayEquilibriumConstant.InGameCurrency, price);
|
||||
|
||||
if (or.AddGoodsToStore(storeName, [g], addToNextRefreshGoods))
|
||||
{
|
||||
msg = $"添加成功,请查看商店!";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"添加失败,商店可能不存在。";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"目标物品不存在,请重新输入。";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"未知地区,请重新输入。{"世界地图".CreateCmdInput()}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"你没有权限使用此指令!";
|
||||
}
|
||||
|
||||
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
FunGameService.ReleaseUserSemaphoreSlim(userid);
|
||||
return noSaved;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("forgeitemcreate")]
|
||||
public BotReply ForgeItem_Create([FromQuery] long uid = -1, [FromBody] Dictionary<string, int>? materials = null)
|
||||
{
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
Markdown = new MarkdownMessage
|
||||
{
|
||||
Content = "🎮 CS赛事预测帮助:\r\n"
|
||||
Content = "🎮 赛事预测系统帮助:\r\n"
|
||||
+ $"✨ {"赛事列表".CreateCmdInput()} - 查看所有赛事\r\n"
|
||||
+ $"✨ {"比赛列表".CreateCmdInput()} - 查看所有比赛\r\n"
|
||||
+ $"✨ {"创建存档".CreateCmdInput()} - 创建存档后可预测\r\n"
|
||||
@ -35,7 +35,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖"),
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"))
|
||||
};
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -75,11 +75,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
reply.Markdown.Content += "\r\n" + reply2.Markdown.Content;
|
||||
}
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendAsync(e, "CS赛事预测", "格式:比赛详情 <比赛ID>");
|
||||
await SendAsync(e, "赛事预测", "格式:比赛详情 <比赛ID>");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -102,7 +102,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"),
|
||||
Button.CreateCmdButton("💰 预测领奖", "预测领奖"));
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -119,7 +119,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Button.CreateCmdButton("❓ 预测帮助", "预测帮助"),
|
||||
Button.CreateCmdButton("📜 我的预测", "我的预测"))
|
||||
};
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -132,7 +132,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
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, "赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
reply.Keyboard.AppendButtons(2, Button.CreateCmdButton("⚙️ 创建存档", "创建存档"));
|
||||
}
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -169,7 +169,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
{
|
||||
reply.Keyboard.AppendButtons(2, Button.CreateCmdButton("⚙️ 创建存档", "创建存档"));
|
||||
}
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
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, "赛事预测", $"格式:预测 <比赛ID> <选项> <{General.GameplayEquilibriumConstant.InGameCurrency}数>\r\n选项:team1 / team2 / score:2:0 / mvp:选手UID");
|
||||
return true;
|
||||
}
|
||||
string option = string.Join(" ", parts[1..^1]).ToLower();
|
||||
@ -211,7 +211,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
}
|
||||
|
||||
reply.Keyboard = kb;
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -235,10 +235,10 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||
Button.CreateCmdButton("⚙️ 继续结算", "结算比赛 ", enter: false),
|
||||
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {mid}"));
|
||||
await SendAsync(e, "CS赛事预测", reply);
|
||||
await SendAsync(e, "赛事预测", reply);
|
||||
}
|
||||
else
|
||||
await SendAsync(e, "CS赛事预测", "格式:结算比赛 <比赛ID> winner=team1 result=2:0");
|
||||
await SendAsync(e, "赛事预测", "格式:结算比赛 <比赛ID> winner=team1 result=2:0");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -2144,6 +2144,63 @@ namespace Oshima.FunGame.WebAPI.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
if (e.Detail.StartsWith("添加商品", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
string detail = e.Detail["添加商品".Length..].Trim();
|
||||
|
||||
// 匹配必需参数:地区ID(数字),商店名称(可带双引号),物品名称(可带双引号),以及剩余可选参数部分
|
||||
string pattern = @"^(?<region>\d+)\s+(?<storeName>(""[^""]*""|\S+))\s+(?<itemName>(""[^""]*""|\S+))\s*(?<options>.*)$";
|
||||
Match match = Regex.Match(detail, pattern);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
long region = long.Parse(match.Groups["region"].Value);
|
||||
string storeName = match.Groups["storeName"].Value.Trim('"');
|
||||
string itemName = match.Groups["itemName"].Value.Trim('"');
|
||||
string optionsStr = match.Groups["options"].Value.Trim();
|
||||
|
||||
// 默认值
|
||||
double price = 0;
|
||||
int stock = -1;
|
||||
int quota = 0;
|
||||
bool addToNextRefreshGoods = true;
|
||||
|
||||
// 解析可选键值对
|
||||
if (!string.IsNullOrEmpty(optionsStr))
|
||||
{
|
||||
string optionPattern = @"(?<key>\w+)=(?<value>[^\s]+)";
|
||||
foreach (Match opt in Regex.Matches(optionsStr, optionPattern))
|
||||
{
|
||||
string key = opt.Groups["key"].Value.ToLower();
|
||||
string val = opt.Groups["value"].Value;
|
||||
switch (key)
|
||||
{
|
||||
case "price":
|
||||
_ = double.TryParse(val, out price);
|
||||
break;
|
||||
case "stock":
|
||||
_ = int.TryParse(val, out stock);
|
||||
break;
|
||||
case "quota":
|
||||
_ = int.TryParse(val, out quota);
|
||||
break;
|
||||
case "refresh":
|
||||
// 支持 是/否 或 true/false 或 1/0
|
||||
addToNextRefreshGoods = val == "1" ||
|
||||
bool.TryParse(val, out bool b) && b ||
|
||||
val.Equals("是", StringComparison.OrdinalIgnoreCase) ||
|
||||
val.Equals("yes", StringComparison.OrdinalIgnoreCase);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BotReply reply = Controller.SystemStoreAddGoods(uid, region, storeName, itemName, price, stock, quota, addToNextRefreshGoods);
|
||||
await SendAsync(e, "添加商品", reply);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (e.Detail.StartsWith("查地区", StringComparison.CurrentCultureIgnoreCase) || e.Detail.StartsWith("查询地区", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
string detail = e.Detail.Replace("查地区", "").Replace("查询地区", "").Trim();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user