This commit is contained in:
Bob.Song
2026-03-27 17:55:04 +08:00
parent a924cd9c01
commit d5dafd2bcf
11 changed files with 114 additions and 200 deletions

View 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}");
}
}

View File

@@ -53,7 +53,7 @@ namespace NBF
// 5. 自定义响应头
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");
await next.Invoke();
});

View File

@@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Fantasy;
namespace NBF;
@@ -27,8 +28,8 @@ public class GameHttpServicesHandler : AsyncEventSystem<OnConfigureHttpServices>
// 2. 添加全局过滤器
self.MvcBuilder.AddMvcOptions(options =>
{
options.Filters.Add<GameExceptionFilter>();
options.Filters.Add<ModelValidationFilter>();
// options.Filters.Add<GameExceptionFilter>();
// options.Filters.Add<ModelValidationFilter>();
});
// 3. 配置 JWT 认证
@@ -76,11 +77,11 @@ public class GameHttpServicesHandler : AsyncEventSystem<OnConfigureHttpServices>
});
});
// 6. 注册游戏服务
self.Builder.Services.AddSingleton<IPlayerService, PlayerService>();
self.Builder.Services.AddSingleton<IGuildService, GuildService>();
self.Builder.Services.AddScoped<IAuthService, AuthService>();
self.Builder.Services.AddScoped<IGameRepository, GameRepository>();
// // 6. 注册游戏服务
// self.Builder.Services.AddSingleton<IPlayerService, PlayerService>();
// self.Builder.Services.AddSingleton<IGuildService, GuildService>();
// self.Builder.Services.AddScoped<IAuthService, AuthService>();
// self.Builder.Services.AddScoped<IGameRepository, GameRepository>();
Log.Info($"[HTTP] 游戏服务配置完成: Scene {self.Scene.SceneConfigId}");

View 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; }
}
}

View File

@@ -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; } // 分钟
}
}

View File

@@ -1,6 +0,0 @@
namespace NBF;
public class PlayerService : IPlayerService
{
}

View File

@@ -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}");
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -13,6 +13,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Api\Middlewares\" />
<Folder Include="Game\Condition\" />
<Folder Include="Game\Mail\Handler\" />
</ItemGroup>

View File

@@ -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_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_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>