Implement First Currency Game - Stock Market
Added `StockAmount` To `User` Model (Database Update Required) Reworked `ChatHub` Command Names Removed Cache On User Info Endpoints
This commit is contained in:
parent
bf1f8d5cdc
commit
41e1ad4bf0
95
qtc-net-server/Controllers/CurrencyGamesController.cs
Normal file
95
qtc-net-server/Controllers/CurrencyGamesController.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using qtc_api.Services.CurrencyGamesService;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/games")]
|
||||
[ApiController]
|
||||
public class CurrencyGamesController : ControllerBase
|
||||
{
|
||||
private IUserService _userService;
|
||||
private CurrencyGamesService _currencyGamesService;
|
||||
public CurrencyGamesController(IUserService userService, CurrencyGamesService currencyGamesService)
|
||||
{
|
||||
_userService = userService;
|
||||
_currencyGamesService = currencyGamesService;
|
||||
}
|
||||
|
||||
[HttpGet("stock-market/current-price")]
|
||||
public ActionResult<ServiceResponse<int>> GetCurrentPricePerStock()
|
||||
{
|
||||
var price = _currencyGamesService.GetCurrentPricePerStock();
|
||||
|
||||
if (price != null && price.Success)
|
||||
{
|
||||
return Ok(new ServiceResponse<int>
|
||||
{
|
||||
Success = true,
|
||||
Data = price.Data
|
||||
});
|
||||
}
|
||||
else return Ok(new ServiceResponse<int>
|
||||
{
|
||||
Success = false,
|
||||
Message = "Service Indicated Failiure"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("stock-market/buy-stock")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<UserStockActionResultDto>>> BuyStockFromMarket(int amount)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var id = claims.First().Value;
|
||||
|
||||
var user = await _userService.GetUserById(id);
|
||||
|
||||
if (user != null && user.Success && user.Data != null)
|
||||
{
|
||||
var stocks = await _currencyGamesService.BuyStock(user.Data, amount);
|
||||
|
||||
if (stocks.Success)
|
||||
{
|
||||
return Ok(new ServiceResponse<UserStockActionResultDto> { Success = true, Data = stocks.Data });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = stocks.Message });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "Identity User Not Found" });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "No Identity" });
|
||||
}
|
||||
|
||||
[HttpPost("stock-market/sell-stock")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<UserStockActionResultDto>>> SellStockToMarket(int amount)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var id = claims.First().Value;
|
||||
|
||||
var user = await _userService.GetUserById(id);
|
||||
|
||||
if (user != null && user.Success && user.Data != null)
|
||||
{
|
||||
var stocks = await _currencyGamesService.SellStock(user.Data, amount);
|
||||
|
||||
if (stocks.Success)
|
||||
{
|
||||
return Ok(new ServiceResponse<UserStockActionResultDto> { Success = true, Data = stocks.Data });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = stocks.Message });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "Identity User Not Found" });
|
||||
}
|
||||
else return Ok(new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "No Identity" });
|
||||
}
|
||||
}
|
||||
}
|
@ -20,17 +20,7 @@ namespace qtc_api.Controllers
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<List<UserInformationDto>>>> GetAllUsers()
|
||||
{
|
||||
var users = new ServiceResponse<List<UserInformationDto>>();
|
||||
|
||||
string recordId = $"Users_{DateTime.Now.ToString("yyyyMMdd_hhmm")}";
|
||||
users = await cache.GetRecordAsync<ServiceResponse<List<UserInformationDto>>>(recordId);
|
||||
|
||||
if(users == null)
|
||||
{
|
||||
users = await _userService.GetAllUsers();
|
||||
await cache.SetRecordAsync(recordId, users);
|
||||
}
|
||||
|
||||
var users = await _userService.GetAllUsers();
|
||||
return Ok(users);
|
||||
}
|
||||
|
||||
@ -38,17 +28,7 @@ namespace qtc_api.Controllers
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<UserInformationDto>>> GetUserInformation(string id)
|
||||
{
|
||||
var user = new ServiceResponse<UserInformationDto>();
|
||||
|
||||
string recordId = $"User_{id}_{DateTime.Now.ToString("yyyyMMdd_hhmm")}";
|
||||
user = await cache.GetRecordAsync<ServiceResponse<UserInformationDto>>(recordId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
user = await _userService.GetUserInformationById(id);
|
||||
await cache.SetRecordAsync(recordId, user);
|
||||
}
|
||||
|
||||
var user = await _userService.GetUserInformationById(id);
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
|
8
qtc-net-server/Dtos/User/UserStockActionResultDto.cs
Normal file
8
qtc-net-server/Dtos/User/UserStockActionResultDto.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserStockActionResultDto
|
||||
{
|
||||
public int StockAmount { get; set; }
|
||||
public int CurrencyAmount { get; set; }
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace qtc_api.Extensions
|
||||
{
|
||||
var options = new DistributedCacheEntryOptions();
|
||||
|
||||
options.AbsoluteExpirationRelativeToNow = absoluteExpireTime ?? TimeSpan.FromMinutes(5);
|
||||
options.AbsoluteExpirationRelativeToNow = absoluteExpireTime ?? TimeSpan.FromSeconds(15);
|
||||
options.SlidingExpiration = unusuedExpireTime;
|
||||
|
||||
var jsonData = JsonSerializer.Serialize(data);
|
||||
|
@ -33,7 +33,7 @@ namespace qtc_api.Hubs
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("l")]
|
||||
[HubMethodName("LoginHub")]
|
||||
public async Task LoginAsync(User user)
|
||||
{
|
||||
await Clients.All.SendAsync("rm", $"[SERVER] User {user.Username} Is Now Online");
|
||||
@ -48,86 +48,86 @@ namespace qtc_api.Hubs
|
||||
|
||||
ServerConfig serverConfig = JsonDocument.Parse(File.ReadAllText("./ServerConfig.json")).Deserialize<ServerConfig>();
|
||||
|
||||
await Clients.Client(ConnectedUsers.FirstOrDefault(e => e.ConnectionId == Context.ConnectionId)!.ConnectionId!).SendAsync("rc", serverConfig);
|
||||
await Clients.All.SendAsync("cf", "rul");
|
||||
await Clients.Client(ConnectedUsers.FirstOrDefault(e => e.ConnectionId == Context.ConnectionId)!.ConnectionId!).SendAsync("ReceiveServerConfig", serverConfig);
|
||||
await Clients.All.SendAsync("RefreshUserList");
|
||||
}
|
||||
|
||||
[HubMethodName("us")]
|
||||
[HubMethodName("UpdateStatus")]
|
||||
public async Task UpdateStatusAsync(User user, int status)
|
||||
{
|
||||
var statusDto = new UserStatusDto { Id = user.Id, Status = status };
|
||||
|
||||
await _userService.UpdateStatus(statusDto);
|
||||
|
||||
await Clients.All.SendAsync("cf", "rul");
|
||||
await Clients.All.SendAsync("RefreshUserList");
|
||||
}
|
||||
|
||||
[HubMethodName("jl")]
|
||||
public async Task JoinHubAsync(User user)
|
||||
[HubMethodName("JoinLobby")]
|
||||
public async Task JoinLobbyAsync(User user)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, "LOBBY");
|
||||
|
||||
await Clients.Group("LOBBY").SendAsync("rm", $"[SERVER] User {user.Username} Has Joined The Lobby");
|
||||
await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Joined The Lobby");
|
||||
Log($"User {user.Username} Has Joined The Lobby");
|
||||
}
|
||||
|
||||
[HubMethodName("ll")]
|
||||
[HubMethodName("LeaveLobby")]
|
||||
public async Task LeaveLobbyAsync(User user)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "LOBBY");
|
||||
|
||||
await Clients.Group("LOBBY").SendAsync("rm", $"[SERVER] User {user.Username} Has Left The Lobby");
|
||||
await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Left The Lobby");
|
||||
Log($"User {user.Username} Has Left The Lobby");
|
||||
}
|
||||
|
||||
[HubMethodName("jr")]
|
||||
[HubMethodName("JoinRoom")]
|
||||
public async Task JoinRoomAsync(User user, Room room)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, room.Id);
|
||||
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] User {user.Username} Has Joined The Room");
|
||||
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Joined {room.Name}");
|
||||
Log($"User {user.Username} Has Joined {room.Name}");
|
||||
}
|
||||
|
||||
[HubMethodName("lr")]
|
||||
[HubMethodName("LeaveRoom")]
|
||||
public async Task LeaveRoomAsync(User user, Room room)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, room.Id);
|
||||
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] User {user.Username} Has Left The Room");
|
||||
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Left {room.Name}");
|
||||
Log($"User {user.Username} Has Left {room.Name}");
|
||||
}
|
||||
|
||||
[HubMethodName("hdr")]
|
||||
[HubMethodName("HandleDeletedRoom")]
|
||||
public async Task HandleDeletedRoomAsync(Room room)
|
||||
{
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] This Room Has Been Deleted By An Administrator.");
|
||||
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[SERVER] This Room Has Been Deleted By An Administrator.");
|
||||
await Clients.Group(room.Id).SendAsync("cf", "rtl");
|
||||
|
||||
await Clients.All.SendAsync("cf", "rr");
|
||||
await Clients.All.SendAsync("RefreshRoomList");
|
||||
}
|
||||
|
||||
[HubMethodName("rcl")]
|
||||
[HubMethodName("RefreshContactsListOnUser")]
|
||||
public async Task RefreshContactsListForUser(UserInformationDto user, User execUser)
|
||||
{
|
||||
var connection = ConnectedUsers.FirstOrDefault(e => e.ConnectedUser.Id == user.Id);
|
||||
var connection2 = ConnectedUsers.FirstOrDefault(e => e.ConnectedUser.Id == execUser.Id);
|
||||
if (connection != null && connection2 != null)
|
||||
{
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("cf", "rcl");
|
||||
await Clients.Client(connection2.ConnectionId).SendAsync("cf", "rcl");
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("RefreshContactsList");
|
||||
await Clients.Client(connection2.ConnectionId).SendAsync("RefreshContactsList");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("s")]
|
||||
[HubMethodName("SendMessage")]
|
||||
public async Task SendMessageAsync(User user, Message message, bool IsLobbyMsg, Room room = null!)
|
||||
{
|
||||
if(IsLobbyMsg == true) { await Clients.Group("LOBBY").SendAsync("rm", $"[{user.Username}] {message.Content}"); return; }
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[{user.Username}] {message.Content}");
|
||||
if(IsLobbyMsg == true) { await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[{user.Username}] {message.Content}"); return; }
|
||||
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[{user.Username}] {message.Content}");
|
||||
}
|
||||
|
||||
[HubMethodName("sdm")]
|
||||
[HubMethodName("SendDirectMessage")]
|
||||
public async Task SendDirectMessageAsync(User user, UserInformationDto userToMsg, Message message)
|
||||
{
|
||||
// send direct message directly to connected user
|
||||
@ -135,7 +135,7 @@ namespace qtc_api.Hubs
|
||||
if (connection != null)
|
||||
{
|
||||
UserInformationDto userInformationDto = new UserInformationDto { Id = user.Id, Username = user.Username, Bio = user.Bio, Role = user.Role, Status = user.Status, CreatedAt = user.CreatedAt, DateOfBirth = user.DateOfBirth, ProfilePicture = user.ProfilePicture };
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("rdm", message, userInformationDto);
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("ReceiveDirectMessage", message, userInformationDto);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -159,9 +159,6 @@ namespace qtc_api.Hubs
|
||||
ConnectedUsers.Remove(connection!);
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
_logger.LogInformation(message);
|
||||
}
|
||||
private void Log(string message) => _logger.LogInformation(message);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int Status { get; set; } = 0;
|
||||
public int CurrencyAmount { get; set; } = 0;
|
||||
public int StockAmount { get; set; } = 0;
|
||||
public DateTime LastCurrencySpin { get; set; }
|
||||
|
||||
public virtual IEnumerable<RefreshToken>? RefreshTokens { get; }
|
||||
|
@ -13,6 +13,8 @@ global using qtc_api.Services.TokenService;
|
||||
global using qtc_api.Hubs;
|
||||
using qtc_api.Services.RoomService;
|
||||
using qtc_api.Services.ContactService;
|
||||
using qtc_api.Services.CurrencyGamesService;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -56,6 +58,9 @@ builder.Services.AddScoped<ITokenService, TokenService>();
|
||||
builder.Services.AddScoped<IRoomService, RoomService>();
|
||||
builder.Services.AddScoped<IContactService, ContactService>();
|
||||
|
||||
builder.Services.AddSingleton<CurrencyGamesService>();
|
||||
builder.Services.AddHostedService<CurrencyGamesService>(provider => provider.GetService<CurrencyGamesService>()!);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using var scope = app.Services.CreateScope();
|
||||
|
@ -0,0 +1,96 @@
|
||||
using qtc_api.Data;
|
||||
using qtc_api.Models;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace qtc_api.Services.CurrencyGamesService
|
||||
{
|
||||
public class CurrencyGamesService : IHostedService, IDisposable
|
||||
{
|
||||
private Timer? Timer { get; set; }
|
||||
private int CurrentPricePerStock { get; set; }
|
||||
|
||||
private IServiceScopeFactory _scopeFactory;
|
||||
private ILogger<CurrencyGamesService> logger;
|
||||
public CurrencyGamesService(IServiceScopeFactory scopeFactory, ILogger<CurrencyGamesService> logger)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
// Stock Market
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Timer = new Timer(Timer_Elapsed, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
||||
|
||||
logger.LogInformation("Stock Market Loop Started");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Timer?.Change(Timeout.Infinite, 0);
|
||||
|
||||
logger.LogInformation("Stock Market Loop Stopped");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Timer?.Dispose();
|
||||
}
|
||||
|
||||
public ServiceResponse<int> GetCurrentPricePerStock() { return new ServiceResponse<int> { Success = true, Data = CurrentPricePerStock }; }
|
||||
|
||||
public async Task<ServiceResponse<UserStockActionResultDto>> BuyStock(User user, int amount)
|
||||
{
|
||||
using(var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var userServiceScoped = scope.ServiceProvider.GetRequiredService<IUserService>();
|
||||
|
||||
int total = 0;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
total += CurrentPricePerStock;
|
||||
}
|
||||
|
||||
if (total > user.CurrencyAmount) return new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "Cannot Afford Stock(s)" };
|
||||
|
||||
var currencyResult = await userServiceScoped.RemoveCurrencyFromUser(user.Id, total);
|
||||
var stockResult = await userServiceScoped.AddStockToUser(user.Id, amount);
|
||||
|
||||
return new ServiceResponse<UserStockActionResultDto> { Success = true, Data = new UserStockActionResultDto { StockAmount = stockResult.Data, CurrencyAmount = currencyResult.Data } };
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<UserStockActionResultDto>> SellStock(User user, int amount)
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var userServiceScoped = scope.ServiceProvider.GetRequiredService<IUserService>();
|
||||
|
||||
if (amount > user.StockAmount) return new ServiceResponse<UserStockActionResultDto> { Success = false, Message = "Not Enough Stocks" };
|
||||
|
||||
int total = 0;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
total += CurrentPricePerStock;
|
||||
}
|
||||
|
||||
var currencyResult = await userServiceScoped.AddCurrencyToUser(user.Id, total, false);
|
||||
var stockResult = await userServiceScoped.RemoveStockFromUser(user.Id, amount);
|
||||
|
||||
return new ServiceResponse<UserStockActionResultDto> { Success = true, Data = new UserStockActionResultDto { StockAmount = stockResult.Data, CurrencyAmount = currencyResult.Data } };
|
||||
}
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object? state)
|
||||
{
|
||||
Random rnd = new Random();
|
||||
CurrentPricePerStock = rnd.Next(5, 200);
|
||||
logger.LogInformation($"Current Price Per Stock - {CurrentPricePerStock}");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,5 +17,7 @@ namespace qtc_api.Services.UserService
|
||||
public Task<ServiceResponse<User>> DeleteUser(string id);
|
||||
public Task<ServiceResponse<int>> AddCurrencyToUser(string id, int amount, bool isSpinClaim);
|
||||
public Task<ServiceResponse<int>> RemoveCurrencyFromUser(string id, int amount);
|
||||
public Task<ServiceResponse<int>> AddStockToUser(string id, int amount);
|
||||
public Task<ServiceResponse<int>> RemoveStockFromUser(string id, int amount);
|
||||
}
|
||||
}
|
||||
|
@ -377,6 +377,48 @@
|
||||
return response;
|
||||
}
|
||||
}
|
||||
public async Task<ServiceResponse<int>> AddStockToUser(string id, int amount)
|
||||
{
|
||||
var response = new ServiceResponse<int>();
|
||||
var user = _dataContext.Users.FirstOrDefault(e => e.Id == id);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
user.StockAmount += amount;
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
response.Success = true;
|
||||
response.Data = user.StockAmount;
|
||||
return response;
|
||||
} else
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "User Not Found";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<int>> RemoveStockFromUser(string id, int amount)
|
||||
{
|
||||
var response = new ServiceResponse<int>();
|
||||
var user = _dataContext.Users.FirstOrDefault(e => e.Id == id);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
user.StockAmount -= amount;
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
response.Success = true;
|
||||
response.Data = user.StockAmount;
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "User Not Found";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private long LongRandom(long min, long max, Random rnd)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user