From 7e1abc6db09a88cd89ddfbbb9a398427e0af0dcf Mon Sep 17 00:00:00 2001 From: VladimirDivak Date: Sat, 8 Jul 2023 21:28:08 +0500 Subject: [PATCH] fisr commit --- .config/dotnet-tools.json | 12 ++ Controllers/AuthController.cs | 77 ++++++++++ Controllers/GameController.cs | 86 +++++++++++ GlobalUsings.cs | 4 + Models/CityUniqueCode.cs | 17 +++ Models/Client.cs | 18 +++ Models/Tag.cs | 14 ++ Program.cs | 13 ++ .../PublishProfiles/FolderProfile.pubxml | 22 +++ Properties/launchSettings.json | 13 ++ Services/CodeGenerationService.cs | 142 ++++++++++++++++++ Services/DatabaseService.cs | 62 ++++++++ appsettings.Development.json | 8 + appsettings.json | 14 ++ 14 files changed, 502 insertions(+) create mode 100644 .config/dotnet-tools.json create mode 100644 Controllers/AuthController.cs create mode 100644 Controllers/GameController.cs create mode 100644 GlobalUsings.cs create mode 100644 Models/CityUniqueCode.cs create mode 100644 Models/Client.cs create mode 100644 Models/Tag.cs create mode 100644 Program.cs create mode 100644 Properties/PublishProfiles/FolderProfile.pubxml create mode 100644 Properties/launchSettings.json create mode 100644 Services/CodeGenerationService.cs create mode 100644 Services/DatabaseService.cs create mode 100644 appsettings.Development.json create mode 100644 appsettings.json diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..812c6cd --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "7.0.8", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs new file mode 100644 index 0000000..63ad1f6 --- /dev/null +++ b/Controllers/AuthController.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.Text; +using YandexGameServer.Models; +using YandexGameServer.Services; + +namespace YandexGameServer.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AuthController : ControllerBase + { + private readonly DatabaseService _databaseService; + private readonly CodeGenerationService _codeGenerationService; + private readonly ILogger _logger; + public AuthController(DatabaseService databaseService, ILogger logger, CodeGenerationService codeGenerationService) + { + _codeGenerationService = codeGenerationService; + _logger = logger; + _databaseService = databaseService; + } + + [HttpGet("{code:int}")] + public async Task Authorization(int code) + { + var cities = await _databaseService.GetCitiesCodesAsync(); + CityUniqueCode? cityCode = cities.Find(x => x.key == code); + + if (cityCode == null) return NotFound("Такого кода нет в базе"); + else + { + var client = new Client() { city = cityCode.cityEnum, games = new List() }; + await _databaseService.CreateAsync(client); + + return Ok(client); + } + } + + [HttpGet("admin/{code:int}")] + public async Task AdminAuthorization(int code) + { + var cities = await _databaseService.GetCitiesCodesAsync(); + CityUniqueCode? cityCode = cities.Find(x => x.adminKey == code); + + if (cityCode == null) return NotFound(); + else return Ok(cityCode); + } + + [HttpGet("secretcode/{cityId:int}")] + public async Task GetAuthorizationCodeAsync(int cityId) + { + Cities cityCode = (Cities)cityId; + + var cities = await _databaseService.GetCitiesCodesAsync(); + if (cities.Any(x => x.cityEnum == cityCode)) + { + var cityData = cities.Find(x => x.cityEnum == cityCode); + return Ok(cityData.key); + } + else return NotFound("Нет соответствующего секретного кода"); + } + + [HttpGet("all")] + public async Task GetAllCodes() + { + StringBuilder stringBuilder = new StringBuilder(); + var codes = await _databaseService.GetCitiesCodesAsync(); + foreach(var code in _codeGenerationService.citiesTranslation.Keys) + { + var data = codes.Find(x => x.cityEnum.Equals(code)); + stringBuilder.AppendLine($"{_codeGenerationService.citiesTranslation[code]} - {data.key}"); + } + + return Ok(stringBuilder.ToString()); + } + } +} diff --git a/Controllers/GameController.cs b/Controllers/GameController.cs new file mode 100644 index 0000000..0c4eb43 --- /dev/null +++ b/Controllers/GameController.cs @@ -0,0 +1,86 @@ +using Microsoft.AspNetCore.Mvc; +using YandexGameServer.Services; +using YandexGameServer.Models; +using System.Text; + +namespace YandexGameServer.Controllers +{ + public enum Games + { + Surprise, + Excursion, + MasterClass, + Entertainment, + Sport, + Kids, + GoodDeeds, + Mask, + Duck, + Butterfly + } + [ApiController] + [Route("[controller]")] + public class GameController : ControllerBase + { + private readonly DatabaseService _databaseService; + public GameController(DatabaseService databaseService) + { + _databaseService = databaseService; + } + + + [HttpGet("find")] + public async Task OnClientFindTag([FromQuery] string id, [FromQuery] Games game) + { + var clients = await _databaseService.GetClientsAsync(); + var client = clients.Find(x => x.id == id); + + client.games.Add(game); + await _databaseService.UpdateAsync(client); + + if (client.games.Count == 10) + { + client.gameFinishedDate = DateTime.UtcNow; + await _databaseService.UpdateAsync(client); + + clients = await _databaseService.GetClientsAsync(); + + var bestClients = clients + .Where(x => x.gameFinishedDate != null && x.city == client.city) + .OrderBy(x => x.gameFinishedDate) + .ToList(); + + return Ok(bestClients.IndexOf(clients.Find(x => x.id == id)) + 1); + } + else return BadRequest(); + } + + [HttpGet("best/{key:int}")] + public async Task GetBestClientsInCity(int key) + { + StringBuilder sb = new StringBuilder(); + + var cities = await _databaseService.GetCitiesCodesAsync(); + var city = cities.Find(x => x.key == key); + if (city == null) return NotFound(); + + var clients = await _databaseService.GetClientsAsync(); + + var bestClients = clients + .Where(x => x.gameFinishedDate != null && x.city == city.cityEnum) + .OrderBy(x => x.gameFinishedDate) + .ToList(); + + if(bestClients.Count == 0) + return BadRequest($"В городе {city.city} ещё нет игроков, завершивших квест"); + + sb.AppendLine($"количество игроков, завершивших квест в городе {city.city}:\n"); + foreach(var client in bestClients) + { + sb.AppendLine($"{bestClients.IndexOf(client) + 1} - закончил(а) квест {client.gameFinishedDate.Value.ToShortDateString()} в {client.gameFinishedDate.Value.ToShortTimeString()}"); + } + + return Ok(sb.ToString()); + } + } +} diff --git a/GlobalUsings.cs b/GlobalUsings.cs new file mode 100644 index 0000000..326476b --- /dev/null +++ b/GlobalUsings.cs @@ -0,0 +1,4 @@ +global using System; +global using System.Linq; +global using System.Collections; +global using System.Collections.Generic; \ No newline at end of file diff --git a/Models/CityUniqueCode.cs b/Models/CityUniqueCode.cs new file mode 100644 index 0000000..8049a7d --- /dev/null +++ b/Models/CityUniqueCode.cs @@ -0,0 +1,17 @@ +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson; +using YandexGameServer.Services; + +namespace YandexGameServer.Models +{ + [Serializable] + public class CityUniqueCode + { + [BsonId, BsonRepresentation(BsonType.ObjectId)] + public string id { get; set; } + public Cities cityEnum { get; set; } + public string city { get; set; } + public int key { get; set; } + public int adminKey { get; set; } + } +} diff --git a/Models/Client.cs b/Models/Client.cs new file mode 100644 index 0000000..93f653d --- /dev/null +++ b/Models/Client.cs @@ -0,0 +1,18 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using YandexGameServer.Controllers; +using YandexGameServer.Services; + +namespace YandexGameServer.Models +{ + [Serializable] + public class Client + { + [BsonId, BsonRepresentation(BsonType.ObjectId)] + public string id { get; set; } + public Cities city { get; set; } + public List games { get; set; } = new List(); + public DateTime? gameFinishedDate { get; set; } + public bool isAdmin { get; set; } + } +} \ No newline at end of file diff --git a/Models/Tag.cs b/Models/Tag.cs new file mode 100644 index 0000000..41a521e --- /dev/null +++ b/Models/Tag.cs @@ -0,0 +1,14 @@ +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson; + +namespace YandexGameServer.Models +{ + [Serializable] + public class Tag + { + [BsonId, BsonRepresentation(BsonType.ObjectId)] + public string id { get; set; } + public string descrition { get; set; } + public string? city { get; set; } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..240b511 --- /dev/null +++ b/Program.cs @@ -0,0 +1,13 @@ +using YandexGameServer.Services; + +var builder = WebApplication.CreateBuilder(args); +builder.WebHost.UseUrls("http://0.0.0.0:5000"); +builder.Services.AddControllers(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +var app = builder.Build(); +app.MapControllers(); +app.Services.GetService(typeof(CodeGenerationService)); + +app.Run(); \ No newline at end of file diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..015dca9 --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,22 @@ + + + + + false + false + true + Release + Any CPU + FileSystem + C:\Users\VladimirGRAFF\Desktop\yandex-game-server + FileSystem + <_TargetId>Folder + + net7.0 + linux-x64 + 765c9a4d-466f-473a-b38e-5b893db09ac8 + false + + \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..45191d4 --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://192.168.1.64:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Production" + } + } + } +} diff --git a/Services/CodeGenerationService.cs b/Services/CodeGenerationService.cs new file mode 100644 index 0000000..71dd8f1 --- /dev/null +++ b/Services/CodeGenerationService.cs @@ -0,0 +1,142 @@ +using YandexGameServer.Models; + +namespace YandexGameServer.Services +{ + public enum Cities + { + MoscowRedRose, + MoscowCity, + MoscowSkolkovo, + + SaintPetersburg, + Yekaterinburg, + Novosibirsk, + Kazan, + Innopolis, + RostovOnDon, + NizhnyNovgorod, + Simferopol, + Vladivostok, + Krasnodar, + Samara, + Chelyabinsk, + Perm, + Tula, + Sochi, + Ufa, + Krasnoyarsk, + Tumen, + Voronezh, + + Minsk, + Serbia + } + public class CodeGenerationService : IDisposable + { + private readonly DatabaseService _databaseService; + private readonly ILogger _logger; + + private CancellationTokenSource _cts; + private DateTime _triggerDate; + + public readonly Dictionary citiesTranslation = new Dictionary() + { + { Cities.MoscowRedRose, "Москва, Красная Роза" }, + { Cities.MoscowCity, "Москва, Москва-Сити" }, + { Cities.MoscowSkolkovo, "Москва, Сколково" }, + + { Cities.SaintPetersburg, "Санкт-Петербург" }, + { Cities.Yekaterinburg, "Екатеринбург" }, + { Cities.Novosibirsk, "Новосибирск" }, + { Cities.Kazan, "Казань" }, + { Cities.Innopolis, "Иннополис" }, + { Cities.RostovOnDon, "Ростов-На-Дону" }, + { Cities.NizhnyNovgorod, "Нижний Новгород" }, + { Cities.Simferopol, "Симферополь" }, + { Cities.Vladivostok, "Владивосток" }, + { Cities.Krasnodar, "Краснодар" }, + { Cities.Samara, "Самара" }, + { Cities.Chelyabinsk, "Челябинск" }, + { Cities.Perm, "Пермь" }, + { Cities.Tula, "Тула" }, + { Cities.Sochi, "Сочи" }, + { Cities.Ufa, "Уфа" }, + { Cities.Krasnoyarsk, "Красноярск" }, + { Cities.Tumen, "Тюмень" }, + { Cities.Voronezh, "Воронеж" }, + + { Cities.Minsk, "Минск" }, + { Cities.Serbia, "Сербия" } + }; + + public CodeGenerationService(DatabaseService databaseService, ILogger logger) + { + _cts = new CancellationTokenSource(); + _databaseService = databaseService; + _logger = logger; + + _triggerDate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 0, 0, 0) + TimeSpan.FromDays(1); + _logger.LogInformation($"дата и время следующей смены: {_triggerDate.ToShortDateString()}, {_triggerDate.ToShortTimeString()}"); + + Task.Run(async () => + { + var list = await _databaseService.GetCitiesCodesAsync(); + if (list.Count == 0) + { + await InitKeys(); + await UpdateCityCodes(); + } + + while (!_cts.Token.IsCancellationRequested) + { + if (DateTime.UtcNow > _triggerDate) + { + _logger.LogWarning($"смена кода ({DateTime.UtcNow.ToShortTimeString()})"); + + await UpdateCityCodes(); + _triggerDate += TimeSpan.FromDays(1); + } + await Task.Delay(1000); + } + }); + } + + public async Task InitKeys() + { + Cities cities = new Cities(); + for(int i = 0; i < citiesTranslation.Count; i++) + { + cities = (Cities)i; + await _databaseService.CreateAsync(new CityUniqueCode() { cityEnum = cities, city = citiesTranslation[cities], adminKey = new Random().Next(0, 1000000) }); + } + } + + private async Task UpdateCityCodes() + { + Random rand = new Random(); + List codesInUse = new List(); + var citiesCodes = await _databaseService.GetCitiesCodesAsync(); + + for (int i = 0; i < citiesCodes.Count; i++) + { + var randomCode = rand.Next(100000, 1000000); + while (codesInUse.Contains(randomCode)) + { + randomCode = rand.Next(100000, 1000000); + await Task.Yield(); + } + citiesCodes[i].key = randomCode; + codesInUse.Add(randomCode); + + await _databaseService.UpdateAsync(citiesCodes[i]); + } + } + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + _cts = null; + } + } +} \ No newline at end of file diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs new file mode 100644 index 0000000..d6bb65d --- /dev/null +++ b/Services/DatabaseService.cs @@ -0,0 +1,62 @@ +using MongoDB.Driver; +using YandexGameServer.Models; +using Tag = YandexGameServer.Models.Tag; + +namespace YandexGameServer.Services +{ + public class DatabaseService + { + private readonly string _connectionString; + private readonly string _databaseName; + + private readonly IMongoCollection _clientsCollection; + private readonly IMongoCollection _tagsCollection; + private readonly IMongoCollection _citiesCollection; + + public DatabaseService(IConfiguration configuration) + { + var databaseSection = configuration.GetSection("Database"); + _connectionString = databaseSection["ConnectionString"]; + _databaseName = databaseSection["DatabaseName"]; + + var mongoClient = new MongoClient(_connectionString); + var database = mongoClient.GetDatabase(_databaseName); + + _clientsCollection = database.GetCollection("clients"); + _tagsCollection = database.GetCollection("tags"); + _citiesCollection = database.GetCollection("cities-codes"); + } + + public async Task CreateAsync(T data) + { + if (data is Client) await _clientsCollection.InsertOneAsync(data as Client); + else if (data is Tag) await _tagsCollection.InsertOneAsync(data as Tag); + else if (data is CityUniqueCode) await _citiesCollection.InsertOneAsync(data as CityUniqueCode); + + else throw new NullReferenceException(); + } + + public async Task?> GetClientsAsync() => await _clientsCollection.Find(_ => true).ToListAsync(); + public async Task?> GetTagsAsync() => await _tagsCollection.Find(_ => true).ToListAsync(); + public async Task?> GetCitiesCodesAsync() => await _citiesCollection.Find(_ => true).ToListAsync(); + + public async Task UpdateAsync(T data) + { + if (data is Client) + { + Client client = data as Client; + _clientsCollection.ReplaceOne(x => x.id == client.id, client); + } + else if (data is Tag) + { + Tag tag = data as Tag; + _tagsCollection.ReplaceOne(x => x.id == tag.id, tag); + } + else if (data is CityUniqueCode) + { + CityUniqueCode cityCode = data as CityUniqueCode; + _citiesCollection.ReplaceOne(x => x.id == cityCode.id, cityCode); + } + } + } +} \ No newline at end of file diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..5991ef2 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,14 @@ +{ + "Database": { + "ConnectionString": "mongodb://yandex:yandexgame@192.168.1.64:27017", + "DatabaseName": "yandex" + }, + + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file