提交
This commit is contained in:
40
Hotfix/Api/Controllers/AuthController.cs
Normal file
40
Hotfix/Api/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Fantasy;
|
||||||
|
using Fantasy.Async;
|
||||||
|
using Fantasy.Network.HTTP;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace NBF;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ServiceFilter(typeof(SceneContextFilter))]
|
||||||
|
public class AuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly Scene _scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数依赖注入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scene"></param>
|
||||||
|
public AuthController(Scene scene)
|
||||||
|
{
|
||||||
|
_scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("login")]
|
||||||
|
public async FTask<IActionResult> Login()
|
||||||
|
{
|
||||||
|
await DingTalkHelper.SendCAPTCHA("123456");
|
||||||
|
await FTask.CompletedTask;
|
||||||
|
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("code")]
|
||||||
|
public async FTask<IActionResult> SendCode()
|
||||||
|
{
|
||||||
|
await DingTalkHelper.SendCAPTCHA("123456");
|
||||||
|
await FTask.CompletedTask;
|
||||||
|
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ namespace NBF
|
|||||||
// 5. 自定义响应头
|
// 5. 自定义响应头
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
context.Response.Headers.Add("X-Game-Server", "Fantasy");
|
context.Response.Headers.Add("X-Game-Server", "NBF");
|
||||||
context.Response.Headers.Add("X-Server-Version", "1.0.0");
|
context.Response.Headers.Add("X-Server-Version", "1.0.0");
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
});
|
});
|
||||||
@@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Tokens;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Fantasy;
|
||||||
|
|
||||||
namespace NBF;
|
namespace NBF;
|
||||||
|
|
||||||
@@ -27,8 +28,8 @@ public class GameHttpServicesHandler : AsyncEventSystem<OnConfigureHttpServices>
|
|||||||
// 2. 添加全局过滤器
|
// 2. 添加全局过滤器
|
||||||
self.MvcBuilder.AddMvcOptions(options =>
|
self.MvcBuilder.AddMvcOptions(options =>
|
||||||
{
|
{
|
||||||
options.Filters.Add<GameExceptionFilter>();
|
// options.Filters.Add<GameExceptionFilter>();
|
||||||
options.Filters.Add<ModelValidationFilter>();
|
// options.Filters.Add<ModelValidationFilter>();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 配置 JWT 认证
|
// 3. 配置 JWT 认证
|
||||||
@@ -76,11 +77,11 @@ public class GameHttpServicesHandler : AsyncEventSystem<OnConfigureHttpServices>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 6. 注册游戏服务
|
// // 6. 注册游戏服务
|
||||||
self.Builder.Services.AddSingleton<IPlayerService, PlayerService>();
|
// self.Builder.Services.AddSingleton<IPlayerService, PlayerService>();
|
||||||
self.Builder.Services.AddSingleton<IGuildService, GuildService>();
|
// self.Builder.Services.AddSingleton<IGuildService, GuildService>();
|
||||||
self.Builder.Services.AddScoped<IAuthService, AuthService>();
|
// self.Builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
self.Builder.Services.AddScoped<IGameRepository, GameRepository>();
|
// self.Builder.Services.AddScoped<IGameRepository, GameRepository>();
|
||||||
|
|
||||||
Log.Info($"[HTTP] 游戏服务配置完成: Scene {self.Scene.SceneConfigId}");
|
Log.Info($"[HTTP] 游戏服务配置完成: Scene {self.Scene.SceneConfigId}");
|
||||||
|
|
||||||
63
Hotfix/Api/Helper/DingTalkHelper.cs
Normal file
63
Hotfix/Api/Helper/DingTalkHelper.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Fantasy;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NBF;
|
||||||
|
|
||||||
|
public static class DingTalkHelper
|
||||||
|
{
|
||||||
|
private static readonly HttpClient _httpClient = new HttpClient();
|
||||||
|
|
||||||
|
public static async Task SendCAPTCHA(string code)
|
||||||
|
{
|
||||||
|
DingTalkMarkdownData dingTalkTestData = new DingTalkMarkdownData
|
||||||
|
{
|
||||||
|
markdown = new DingTalkMarkdownItem
|
||||||
|
{
|
||||||
|
title = "NB验证码",
|
||||||
|
text = $"验证码:{code}"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await SendMessageAsync("23457f9c93ac0ae909e1cbf8bcfeb7e0573968ac2d4c4b2c3a961b2f0c9247cb", dingTalkTestData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通用消息发送方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <param name="message">消息对象</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static async Task<bool> SendMessageAsync(string token, DingTalkMarkdownData message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(message);
|
||||||
|
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
var response =
|
||||||
|
await _httpClient.PostAsync($"https://oapi.dingtalk.com/robot/send?access_token={token}", content);
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
Log.Info($"钉钉={responseContent}");
|
||||||
|
// 检查响应是否成功
|
||||||
|
return response.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"发送飞书消息失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class DingTalkMarkdownData
|
||||||
|
{
|
||||||
|
public string msgtype { get; set; } = "markdown";
|
||||||
|
public DingTalkMarkdownItem markdown { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class DingTalkMarkdownItem
|
||||||
|
{
|
||||||
|
public string title { get; set; }
|
||||||
|
public string text { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
using Fantasy;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace NBF.Controllers
|
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class PlayerController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly Scene _scene;
|
|
||||||
private readonly IPlayerService _playerService;
|
|
||||||
|
|
||||||
public PlayerController(Scene scene, IPlayerService playerService)
|
|
||||||
{
|
|
||||||
_scene = scene;
|
|
||||||
_playerService = playerService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取玩家信息(需要认证)
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("{playerId}")]
|
|
||||||
[Authorize(Policy = "Player")]
|
|
||||||
public async Task<IActionResult> GetPlayer(long playerId)
|
|
||||||
{
|
|
||||||
var player = await _playerService.GetPlayerAsync(playerId);
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
return NotFound(new { error = "Player not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
playerId = player.Id,
|
|
||||||
name = player.Name,
|
|
||||||
level = player.Level,
|
|
||||||
exp = player.Exp
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录接口(无需认证)
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("login")]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public async Task<IActionResult> Login([FromBody] LoginRequest request)
|
|
||||||
{
|
|
||||||
var (success, token, playerId) = await _playerService.LoginAsync(
|
|
||||||
request.Username, request.Password);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
return Unauthorized(new { error = "Invalid credentials" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
token = token,
|
|
||||||
playerId = playerId,
|
|
||||||
expiresIn = 3600
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 管理员接口(需要 Admin 角色)
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("ban/{playerId}")]
|
|
||||||
[Authorize(Policy = "Admin")]
|
|
||||||
public async Task<IActionResult> BanPlayer(long playerId, [FromBody] BanRequest request)
|
|
||||||
{
|
|
||||||
await _playerService.BanPlayerAsync(playerId, request.Reason, request.Duration);
|
|
||||||
|
|
||||||
return Ok(new { message = "Player banned successfully" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LoginRequest
|
|
||||||
{
|
|
||||||
public string Username { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BanRequest
|
|
||||||
{
|
|
||||||
public string Reason { get; set; }
|
|
||||||
public int Duration { get; set; } // 分钟
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace NBF;
|
|
||||||
|
|
||||||
public class PlayerService : IPlayerService
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using Fantasy.Async;
|
|
||||||
using Fantasy.Entitas;
|
|
||||||
using Fantasy.Network.HTTP;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Fantasy;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ServiceFilter(typeof(SceneContextFilter))]
|
|
||||||
public class HelloController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly Scene _scene;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造函数依赖注入
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scene"></param>
|
|
||||||
public HelloController(Scene scene)
|
|
||||||
{
|
|
||||||
_scene = scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("greet")]
|
|
||||||
public async FTask<IActionResult> Greet()
|
|
||||||
{
|
|
||||||
Log.Debug($"HelloController Thread.CurrentThread.ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}");
|
|
||||||
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Fantasy;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class ProductsController : ControllerBase
|
|
||||||
{
|
|
||||||
[HttpPost]
|
|
||||||
public IActionResult MiniGame ([FromBody] Product product)
|
|
||||||
{
|
|
||||||
// 假设已经保存产品数据
|
|
||||||
return Ok("Product created successfully");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Product
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Fantasy;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class UsersController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly Scene _scene;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造函数依赖注入
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scene"></param>
|
|
||||||
public UsersController(Scene scene)
|
|
||||||
{
|
|
||||||
_scene = scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{userId}")]
|
|
||||||
public IActionResult GetUser(int userId)
|
|
||||||
{
|
|
||||||
return Ok($"User ID: {userId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("register")]
|
|
||||||
public IActionResult RegisterUser([FromBody] HttpUser user)
|
|
||||||
{
|
|
||||||
return Ok("User registered successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("greet")]
|
|
||||||
public IActionResult Greet()
|
|
||||||
{
|
|
||||||
Log.Debug($"HelloController Thread.CurrentThread.ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}");
|
|
||||||
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HttpUser
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Folder Include="Api\Middlewares\" />
|
||||||
<Folder Include="Game\Condition\" />
|
<Folder Include="Game\Condition\" />
|
||||||
<Folder Include="Game\Mail\Handler\" />
|
<Folder Include="Game\Mail\Handler\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -21,4 +21,5 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScene_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F6a_003F5adfe93e_003FScene_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScene_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F6a_003F5adfe93e_003FScene_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASerializerManager_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F199e3c534d1e41cbb9a3a30bbf9ac93bde200_003F9f_003Fa4f53cf4_003FSerializerManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASerializerManager_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F199e3c534d1e41cbb9a3a30bbf9ac93bde200_003F9f_003Fa4f53cf4_003FSerializerManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASortedOneToManyList_00602_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F12_003Fcfa98068_003FSortedOneToManyList_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASortedOneToManyList_00602_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F12_003Fcfa98068_003FSortedOneToManyList_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUseExtensions_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fd455287e79b847419df3187cd3b7294f50938_003F45_003Fe5410c4e_003FUseExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWorld_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F4f_003F90b7c061_003FWorld_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWorld_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3cbbaf32e578423d8d0163d75da8233f87e00_003F4f_003F90b7c061_003FWorld_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
Reference in New Issue
Block a user