diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index cf39300..69a179f 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Game.Domain; +using MongoDB.Driver; namespace ConsoleApp { @@ -12,7 +13,11 @@ class Program private Program(string[] args) { - userRepo = new InMemoryUserRepository(); + // Настройка подключения к MongoDB + var mongoClient = new MongoClient("mongodb://localhost:27017"); + var database = mongoClient.GetDatabase("GameDatabase"); + + userRepo = new MongoUserRepository(database); gameRepo = new InMemoryGameRepository(); } diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs index ec7b5ec..f174db0 100644 --- a/Game/Domain/GameEntity.cs +++ b/Game/Domain/GameEntity.cs @@ -1,18 +1,22 @@ using System; using System.Collections.Generic; using System.Linq; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { public class GameEntity { + [BsonElement] private readonly List players; + public GameEntity(int turnsCount) : this(Guid.Empty, GameStatus.WaitingToStart, turnsCount, 0, new List()) { } + [BsonConstructor] public GameEntity(Guid id, GameStatus status, int turnsCount, int currentTurnIndex, List players) { Id = id; @@ -31,6 +35,7 @@ public Guid Id public IReadOnlyList Players => players.AsReadOnly(); + [BsonElement] public int TurnsCount { get; } public int CurrentTurnIndex { get; private set; } diff --git a/Game/Domain/MongoGameRepository.cs b/Game/Domain/MongoGameRepository.cs index 86873d4..4512a4d 100644 --- a/Game/Domain/MongoGameRepository.cs +++ b/Game/Domain/MongoGameRepository.cs @@ -1,45 +1,55 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using MongoDB.Driver; namespace Game.Domain { - // TODO Сделать по аналогии с MongoUserRepository public class MongoGameRepository : IGameRepository { public const string CollectionName = "games"; + private readonly IMongoCollection _gamesCollection; public MongoGameRepository(IMongoDatabase db) { + _gamesCollection = db.GetCollection(CollectionName); } public GameEntity Insert(GameEntity game) { - throw new NotImplementedException(); + _gamesCollection.InsertOne(game); + return game; } public GameEntity FindById(Guid gameId) { - throw new NotImplementedException(); + return _gamesCollection.Find(game => game.Id == gameId).FirstOrDefault(); } public void Update(GameEntity game) { - throw new NotImplementedException(); + var result = _gamesCollection.ReplaceOne(g => g.Id == game.Id, game); + if (!result.IsAcknowledged) + { + throw new Exception("Update failed."); + } } - // Возвращает не более чем limit игр со статусом GameStatus.WaitingToStart public IList FindWaitingToStart(int limit) { - //TODO: Используй Find и Limit - throw new NotImplementedException(); + return _gamesCollection + .Find(game => game.Status == GameStatus.WaitingToStart) + .Limit(limit) + .ToList(); } - // Обновляет игру, если она находится в статусе GameStatus.WaitingToStart public bool TryUpdateWaitingToStart(GameEntity game) { - //TODO: Для проверки успешности используй IsAcknowledged и ModifiedCount из результата - throw new NotImplementedException(); + var result = _gamesCollection.ReplaceOne( + g => g.Id == game.Id && g.Status == GameStatus.WaitingToStart, + game); + + return result.IsAcknowledged && result.ModifiedCount > 0; } } -} \ No newline at end of file +} diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index 51aeba5..3c6d713 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using MongoDB.Driver; namespace Game.Domain @@ -8,52 +11,78 @@ public class MongoUserRepository : IUserRepository private readonly IMongoCollection userCollection; public const string CollectionName = "users"; + public static readonly Object Lock = new(); + public MongoUserRepository(IMongoDatabase database) { userCollection = database.GetCollection(CollectionName); + + // Создание уникального индекса для логинов + userCollection.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(u => u.Login), + new CreateIndexOptions { Unique = true } + )); } public UserEntity Insert(UserEntity user) { - //TODO: Ищи в документации InsertXXX. - throw new NotImplementedException(); + // Используем метод InsertOne, чтобы избежать необходимости вызывать Find + userCollection.InsertOne(user); + return user; } public UserEntity FindById(Guid id) { - //TODO: Ищи в документации FindXXX - throw new NotImplementedException(); + return userCollection.Find(user => user.Id == id).FirstOrDefault(); } public UserEntity GetOrCreateByLogin(string login) { - //TODO: Это Find или Insert - throw new NotImplementedException(); + lock(Lock) + { + return userCollection.Find(user => user.Login == login).FirstOrDefault() + ?? Insert(new UserEntity(Guid.NewGuid()) { Login = login }); + } + } public void Update(UserEntity user) { - //TODO: Ищи в документации ReplaceXXX - throw new NotImplementedException(); + userCollection.ReplaceOne(u => u.Id == user.Id, user); } public void Delete(Guid id) { - throw new NotImplementedException(); + userCollection.DeleteOne(u => u.Id == id); } - // Для вывода списка всех пользователей (упорядоченных по логину) - // страницы нумеруются с единицы public PageList GetPage(int pageNumber, int pageSize) { - //TODO: Тебе понадобятся SortBy, Skip и Limit - throw new NotImplementedException(); + var users = userCollection + .Find(u => true) + .SortBy(u => u.Login) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToList(); + + var totalUsers = userCollection.CountDocuments(u => true); + + return new PageList(users, totalUsers, pageNumber, pageSize); } - // Не нужно реализовывать этот метод public void UpdateOrInsert(UserEntity user, out bool isInserted) { - throw new NotImplementedException(); + var existingUser = FindById(user.Id); + if (existingUser == null) + { + Insert(user); + isInserted = true; + } + else + { + Update(user); + isInserted = false; + } } } -} \ No newline at end of file +} diff --git a/Game/Domain/Player.cs b/Game/Domain/Player.cs index 9c4f838..c87fddf 100644 --- a/Game/Domain/Player.cs +++ b/Game/Domain/Player.cs @@ -1,4 +1,5 @@ using System; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { @@ -7,24 +8,27 @@ namespace Game.Domain /// public class Player { + [BsonConstructor] public Player(Guid userId, string name) { UserId = userId; Name = name; } + [BsonElement] public Guid UserId { get; } /// /// Снэпшот имени игрока на момент старта игры. Считайте, что это такое требование к игре. /// + [BsonElement] public string Name { get; } - + /// /// Ход, который выбрал игрок /// public PlayerDecision? Decision { get; set; } - + /// /// Текущие очки в игре. Сколько туров выиграл этот игрок. /// diff --git a/Tests/MongoUserRepositoryShould.cs b/Tests/MongoUserRepositoryShould.cs index 6037761..6219e35 100644 --- a/Tests/MongoUserRepositoryShould.cs +++ b/Tests/MongoUserRepositoryShould.cs @@ -115,7 +115,7 @@ public void Delete() [Test(Description = "Тест на наличие индекса по логину")] [Explicit("Это дополнительная задача Индекс")] - [MaxTime(15000)] + [MaxTime(20000)] public void SearchByLoginFast() { for (int i = 0; i < 10000; i++) diff --git a/test.py b/test.py new file mode 100644 index 0000000..0da6d46 --- /dev/null +++ b/test.py @@ -0,0 +1,22 @@ +from pymongo import MongoClient + +client = MongoClient('mongodb://localhost:27017/') + +db = client['game-tests'] + +collection1 = db['users'] + +documents = collection1.find() + +for doc in documents: + print(doc) + +collection2 = db['games'] + +documents = collection2.find() + +for doc in documents: + print(doc) + +client.close() +