Lots of additions and restructuring

This commit is contained in:
2021-10-30 21:58:43 -04:00
parent 9cf11e982f
commit 20cac8c378
37 changed files with 4465 additions and 117 deletions

View File

@@ -1,5 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using TOOHUCardAPI.Data.Models;
using TOOHUCardAPI.Data.Models.ModelBuilders;
using TOOHUCardAPI.Data.Seeders;
namespace TOOHUCardAPI.Data
{
@@ -8,6 +13,16 @@ namespace TOOHUCardAPI.Data
public DbSet<User> Users { get; set; }
public DbSet<Card> Cards { get; set; }
private readonly IEnumerable<ISeeder> _seeders = new List<ISeeder>
{
new CardSeeder()
};
private readonly IEnumerable<IModelBuilder> _modelBuilders = new List<IModelBuilder>
{
new CardLevelModelBuilder()
};
public AppDbContext(DbContextOptions options) : base(options)
{
}
@@ -15,7 +30,14 @@ namespace TOOHUCardAPI.Data
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var builder in _modelBuilders)
{
builder.BuildModel(modelBuilder);
}
foreach (var seeder in _seeders)
{
seeder.SeedData(modelBuilder);
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

View File

@@ -22,4 +22,9 @@ namespace TOOHUCardAPI.Data
{
public override AbstractResponse response => new NotFirstWinResponse();
}
public class InvalidCardException : TooHooException
{
public override AbstractResponse response => new InvalidCardResponse();
}
}

View File

@@ -8,7 +8,7 @@ namespace TOOHUCardAPI.Data.Models
[Key]
public string ItemCode { get; set; }
public string CardName { get; set; }
public Rarity Rarity { get; set; }
public Rarity Quality { get; set; }
public bool HasVoice { get; set; }
public bool HasPortrait { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace TOOHUCardAPI.Data.Models
{
public class CardLevel
{
public string UserSteamId { get; set; }
public string CardItemCode { get; set; }
public Card Card { get; set; }
public int Level { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace TOOHUCardAPI.Data.Models.ModelBuilders
{
public class CardLevelModelBuilder : IModelBuilder
{
public ModelBuilder BuildModel(ModelBuilder builder)
{
EntityTypeBuilder<CardLevel> cardLevelBuilder = builder.Entity<CardLevel>();
cardLevelBuilder.HasKey(cl => new {cl.UserSteamId, cl.CardItemCode});
return builder;
}
}
}

View File

@@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace TOOHUCardAPI.Data.Models.ModelBuilders
{
public interface IModelBuilder
{
public ModelBuilder BuildModel(ModelBuilder builder);
}
}

View File

@@ -19,7 +19,7 @@ namespace TOOHUCardAPI.Data.Models
public bool Ban { get; set; }
public int Point { get; set; }
public DateTime LastFirstWin { get; set; }
public string LevelList { get; set; }
public List<EncodedCardGroup> EncodedCardGroups { get; set; }
public List<CardLevel> CardLevels { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using TOOHUCardAPI.Data.Models;
namespace TOOHUCardAPI.Data.Repositories
{
public class CardRepository
{
private readonly AppDbContext _context;
public CardRepository(AppDbContext context)
{
_context = context;
}
public async Task<Card> GetCardByItemCode(string itemCode, bool allowNull = false)
{
Card card = await _context.Cards.FirstOrDefaultAsync(card => card.ItemCode == itemCode);
if (card == null && !allowNull)
{
throw new InvalidCardException();
}
return card;
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using TOOHUCardAPI.Data.Models;
@@ -20,46 +21,56 @@ namespace TOOHUCardAPI.Data.Repositories
private IQueryable<User> GetAllUsersQuery()
{
return _appDbContext.Users.Include(u => u.EncodedCardGroups).AsQueryable();
return _appDbContext.Users
.Include(u => u.EncodedCardGroups)
.Include(u => u.CardLevels)
.ThenInclude(cl => cl.Card)
.AsSingleQuery()
.AsQueryable();
}
public User GetUser(string steamId)
public async Task<User> GetUser(string steamId, bool allowNull = false)
{
return GetAllUsersQuery().FirstOrDefault(user => user.SteamId.Equals(steamId));
User user = await GetAllUsersQuery().FirstOrDefaultAsync(user => user.SteamId.Equals(steamId));
if (user == null && !allowNull)
{
throw new InvalidUserException();
}
return user;
}
public User CreateUser(string steamId)
public async Task<User> CreateUser(string steamId)
{
User user = new User()
{
SteamId = steamId,
Vip = true,
EndTime = DateTime.MaxValue,
LevelList = string.Empty,
PetLevel = 1,
EncodedCardGroups = new List<EncodedCardGroup>()
};
_appDbContext.Users.Add(user);
_appDbContext.SaveChanges();
await _appDbContext.Users.AddAsync(user);
await _appDbContext.SaveChangesAsync();
return user;
}
public User UpdateUser(User user)
public async Task<User> UpdateUser(User user)
{
var trackedUser = GetAllUsersQuery().FirstOrDefault(u => u.SteamId == user.SteamId);
if (trackedUser == default)
{
return trackedUser;
return null;
}
_appDbContext.Update(user);
_appDbContext.SaveChanges();
await _appDbContext.SaveChangesAsync();
return user;
}
public User GetOrCreateUser(string steamId)
public async Task<User> GetOrCreateUser(string steamId)
{
User user = GetUser(steamId) ?? CreateUser(steamId);
User user = await GetUser(steamId, true) ?? await CreateUser(steamId);
return user;
}
}

View File

@@ -0,0 +1,604 @@
{
"item_0026": {
"quality": 3,
"cardname": "rin",
"hasVoice": true,
"hasPortrait": true
},
"item_0043": {
"quality": 2,
"cardname": "hatate",
"hasVoice": true,
"hasPortrait": true
},
"item_0014": {
"quality": 2,
"cardname": "merlin",
"hasVoice": true,
"hasPortrait": true
},
"item_0007": {
"quality": 1,
"cardname": "hanadayousei",
"hasVoice": false,
"hasPortrait": false
},
"item_2002": {
"quality": 4,
"cardname": "item_2002",
"hasVoice": false,
"hasPortrait": false
},
"item_0055": {
"quality": 3,
"cardname": "suika",
"hasVoice": true,
"hasPortrait": true
},
"item_0002": {
"quality": 2,
"cardname": "nazrin",
"hasVoice": true,
"hasPortrait": true
},
"item_0061": {
"quality": 3,
"cardname": "keine",
"hasVoice": false,
"hasPortrait": true
},
"item_0069": {
"quality": 2,
"cardname": "toramaru",
"hasVoice": false,
"hasPortrait": true
},
"item_0042": {
"quality": 3,
"cardname": "aya",
"hasVoice": false,
"hasPortrait": true
},
"item_1011": { "quality": 1, "cardname": "BonusEgg" },
"item_0015": {
"quality": 2,
"cardname": "rumia",
"hasVoice": true,
"hasPortrait": true
},
"item_0030": {
"quality": 4,
"cardname": "remilia",
"hasVoice": false,
"hasPortrait": true
},
"item_0028": {
"quality": 4,
"cardname": "reimu",
"hasVoice": true,
"hasPortrait": true
},
"item_0045": {
"quality": 3,
"cardname": "kagerou",
"hasVoice": false,
"hasPortrait": true
},
"item_0019": {
"quality": 3,
"cardname": "marisa",
"hasVoice": true,
"hasPortrait": true
},
"item_0075": {
"quality": 3,
"cardname": "clownpiece",
"hasVoice": false,
"hasPortrait": true
},
"item_0050": {
"quality": 3,
"cardname": "nue",
"hasVoice": false,
"hasPortrait": true
},
"item_0044": {
"quality": 2,
"cardname": "momiji",
"hasVoice": true,
"hasPortrait": true
},
"item_0011": {
"quality": 2,
"cardname": "letty",
"hasVoice": true,
"hasPortrait": true
},
"item_1003": { "quality": 1, "cardname": "BonusEgg" },
"item_0022": {
"quality": 3,
"cardname": "sakuya",
"hasVoice": true,
"hasPortrait": true
},
"item_0003": {
"quality": 2,
"cardname": "minoriko",
"hasVoice": true,
"hasPortrait": true
},
"item_2022": {
"quality": 3,
"cardname": "item_2022",
"hasVoice": false,
"hasPortrait": false
},
"item_2020": {
"quality": 4,
"cardname": "item_2020",
"hasVoice": false,
"hasPortrait": false
},
"item_2016": {
"quality": 3,
"cardname": "item_2016",
"hasVoice": false,
"hasPortrait": false
},
"item_2018": {
"quality": 2,
"cardname": "item_2018",
"hasVoice": false,
"hasPortrait": false
},
"item_2006": {
"quality": 2,
"cardname": "item_2006",
"hasVoice": false,
"hasPortrait": false
},
"item_2019": {
"quality": 2,
"cardname": "item_2019",
"hasVoice": false,
"hasPortrait": false
},
"item_2015": {
"quality": 3,
"cardname": "item_2015",
"hasVoice": false,
"hasPortrait": false
},
"item_2014": {
"quality": 4,
"cardname": "item_2014",
"hasVoice": false,
"hasPortrait": false
},
"item_0009": {
"quality": 2,
"cardname": "cirno",
"hasVoice": false,
"hasPortrait": true
},
"item_0038": {
"quality": 2,
"cardname": "chen",
"hasVoice": false,
"hasPortrait": true
},
"item_0074": {
"quality": 4,
"cardname": "hecatia",
"hasVoice": false,
"hasPortrait": true
},
"item_0080": {
"quality": 4,
"cardname": "shinki",
"hasVoice": false,
"hasPortrait": true
},
"item_0051": {
"quality": 4,
"cardname": "byakuren",
"hasVoice": false,
"hasPortrait": true
},
"item_1012": { "quality": 2, "cardname": "BonusEgg" },
"item_0029": {
"quality": 4,
"cardname": "daiyousei",
"hasVoice": true,
"hasPortrait": true
},
"item_0073": {
"quality": 4,
"cardname": "junko",
"hasVoice": false,
"hasPortrait": true
},
"item_0063": {
"quality": 2,
"cardname": "kisume",
"hasVoice": false,
"hasPortrait": true
},
"item_2007": {
"quality": 2,
"cardname": "item_2007",
"hasVoice": false,
"hasPortrait": false
},
"item_0025": {
"quality": 3,
"cardname": "youmu",
"hasVoice": true,
"hasPortrait": true
},
"item_0004": {
"quality": 1,
"cardname": "mugiyousei",
"hasVoice": false,
"hasPortrait": false
},
"item_0096": {
"quality": 3,
"cardname": "seiga",
"hasVoice": false,
"hasPortrait": true
},
"item_0034": {
"quality": 2,
"cardname": "meirin",
"hasVoice": false,
"hasPortrait": true
},
"item_2011": {
"quality": 3,
"cardname": "item_2011",
"hasVoice": false,
"hasPortrait": false
},
"item_2010": {
"quality": 3,
"cardname": "item_2010",
"hasVoice": false,
"hasPortrait": false
},
"item_2008": {
"quality": 2,
"cardname": "item_2008",
"hasVoice": false,
"hasPortrait": false
},
"item_0036": {
"quality": 4,
"cardname": "yukari",
"hasVoice": false,
"hasPortrait": true
},
"item_0018": {
"quality": 2,
"cardname": "mystia",
"hasVoice": true,
"hasPortrait": true
},
"item_2009": {
"quality": 3,
"cardname": "item_2009",
"hasVoice": false,
"hasPortrait": false
},
"item_0046": {
"quality": 3,
"cardname": "sanae",
"hasVoice": false,
"hasPortrait": true
},
"item_2017": {
"quality": 3,
"cardname": "item_2017",
"hasVoice": false,
"hasPortrait": false
},
"item_2005": {
"quality": 2,
"cardname": "item_2005",
"hasVoice": false,
"hasPortrait": false
},
"item_0052": {
"quality": 4,
"cardname": "miko",
"hasVoice": true,
"hasPortrait": true
},
"item_0023": {
"quality": 3,
"cardname": "reisen",
"hasVoice": true,
"hasPortrait": true
},
"item_0049": {
"quality": 3,
"cardname": "minamitsu",
"hasVoice": true,
"hasPortrait": true
},
"item_2004": {
"quality": 2,
"cardname": "item_2004",
"hasVoice": false,
"hasPortrait": false
},
"item_2003": {
"quality": 3,
"cardname": "item_2003",
"hasVoice": false,
"hasPortrait": false
},
"item_0056": {
"quality": 2,
"cardname": "star",
"hasVoice": true,
"hasPortrait": true
},
"item_0006": {
"quality": 1,
"cardname": "hourainingyou",
"hasVoice": false,
"hasPortrait": false
},
"item_0057": {
"quality": 2,
"cardname": "sunny",
"hasVoice": true,
"hasPortrait": true
},
"item_0095": {
"quality": 3,
"cardname": "futo",
"hasVoice": false,
"hasPortrait": true
},
"item_2001": {
"quality": 3,
"cardname": "item_2001",
"hasVoice": false,
"hasPortrait": false
},
"item_1013": { "quality": 3, "cardname": "BonusEgg" },
"item_0097": {
"quality": 2,
"cardname": "yoshika",
"hasVoice": false,
"hasPortrait": true
},
"item_0027": {
"quality": 3,
"cardname": "utsuho",
"hasVoice": false,
"hasPortrait": true
},
"item_0068": {
"quality": 4,
"cardname": "komachi",
"hasVoice": false,
"hasPortrait": true
},
"item_0047": {
"quality": 4,
"cardname": "kanako",
"hasVoice": true,
"hasPortrait": true
},
"item_0058": {
"quality": 2,
"cardname": "luna",
"hasVoice": true,
"hasPortrait": true
},
"item_2013": {
"quality": 4,
"cardname": "item_2013",
"hasVoice": false,
"hasPortrait": false
},
"item_0024": {
"quality": 3,
"cardname": "yuyuko",
"hasVoice": true,
"hasPortrait": true
},
"item_0093": {
"quality": 2,
"cardname": "kyouko",
"hasVoice": true,
"hasPortrait": true
},
"item_0005": {
"quality": 1,
"cardname": "shanghainingyou",
"hasVoice": false,
"hasPortrait": false
},
"item_1006": { "quality": 4, "cardname": "BonusEgg" },
"item_0064": {
"quality": 4,
"cardname": "shikieiki",
"hasVoice": false,
"hasPortrait": true
},
"item_0092": {
"quality": 2,
"cardname": "medicine",
"hasVoice": true,
"hasPortrait": true
},
"item_0017": {
"quality": 2,
"cardname": "iku",
"hasVoice": false,
"hasPortrait": true
},
"item_0013": {
"quality": 2,
"cardname": "lunasa",
"hasVoice": true,
"hasPortrait": true
},
"item_0091": {
"quality": 2,
"cardname": "hina",
"hasVoice": false,
"hasPortrait": true
},
"item_1004": { "quality": 2, "cardname": "BonusEgg" },
"item_0040": {
"quality": 3,
"cardname": "mokou",
"hasVoice": true,
"hasPortrait": true
},
"item_0088": {
"quality": 2,
"cardname": "sizuha",
"hasVoice": true,
"hasPortrait": true
},
"item_2012": {
"quality": 4,
"cardname": "item_2012",
"hasVoice": false,
"hasPortrait": false
},
"item_0010": {
"quality": 2,
"cardname": "kogasa",
"hasVoice": true,
"hasPortrait": true
},
"item_0062": {
"quality": 2,
"cardname": "inaba",
"hasVoice": false,
"hasPortrait": true
},
"item_0001": {
"quality": 2,
"cardname": "lily",
"hasVoice": false,
"hasPortrait": true
},
"item_1005": { "quality": 3, "cardname": "BonusEgg" },
"item_0060": {
"quality": 2,
"cardname": "wriggle",
"hasVoice": false,
"hasPortrait": true
},
"item_0059": {
"quality": 3,
"cardname": "alice",
"hasVoice": true,
"hasPortrait": true
},
"item_0054": {
"quality": 3,
"cardname": "yuugi",
"hasVoice": true,
"hasPortrait": true
},
"item_0032": {
"quality": 4,
"cardname": "flandre",
"hasVoice": true,
"hasPortrait": true
},
"item_0033": {
"quality": 2,
"cardname": "koakuma",
"hasVoice": true,
"hasPortrait": true
},
"item_0041": {
"quality": 3,
"cardname": "kaguya",
"hasVoice": true,
"hasPortrait": true
},
"item_0039": {
"quality": 4,
"cardname": "eirin",
"hasVoice": true,
"hasPortrait": true
},
"item_0037": {
"quality": 3,
"cardname": "ran",
"hasVoice": true,
"hasPortrait": true
},
"item_0035": {
"quality": 4,
"cardname": "yuuka",
"hasVoice": true,
"hasPortrait": true
},
"item_0048": {
"quality": 3,
"cardname": "suwako",
"hasVoice": false,
"hasPortrait": true
},
"item_0016": {
"quality": 2,
"cardname": "satori",
"hasVoice": true,
"hasPortrait": true
},
"item_0053": {
"quality": 3,
"cardname": "kokoro",
"hasVoice": true,
"hasPortrait": true
},
"item_0008": {
"quality": 1,
"cardname": "maidyousei",
"hasVoice": false,
"hasPortrait": false
},
"item_0012": {
"quality": 2,
"cardname": "lyrica",
"hasVoice": true,
"hasPortrait": true
},
"item_0031": {
"quality": 4,
"cardname": "koishi",
"hasVoice": true,
"hasPortrait": true
},
"item_0094": {
"quality": 3,
"cardname": "soga",
"hasVoice": true,
"hasPortrait": true
},
"item_0021": {
"quality": 3,
"cardname": "patchouli",
"hasVoice": true,
"hasPortrait": true
},
"item_0020": {
"quality": 3,
"cardname": "tenshi",
"hasVoice": false,
"hasPortrait": true
},
"item_1014": { "quality": 4, "cardname": "BonusEgg" }
}

View File

@@ -0,0 +1,25 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TOOHUCardAPI.Data.Models;
namespace TOOHUCardAPI.Data.Seeders
{
public class CardSeeder : ISeeder
{
public ModelBuilder SeedData(ModelBuilder builder)
{
var dataFile = System.IO.File.ReadAllText(ISeeder.SeedDataDirectory + @"CardData.json");
JObject jsonifiedData = JObject.Parse(dataFile);
var cardObjects = jsonifiedData.Properties().Select(i =>
{
var obj = i.Value.ToObject<JObject>();
obj["ItemCode"] = i.Name;
return obj;
}).Select(obj => obj.ToObject<Card>());
builder.Entity<Card>().HasData(cardObjects);
return builder;
}
}
}

View File

@@ -0,0 +1,10 @@
using Microsoft.EntityFrameworkCore;
namespace TOOHUCardAPI.Data.Seeders
{
public interface ISeeder
{
public static string SeedDataDirectory = @"Data/SeedData/";
public ModelBuilder SeedData(ModelBuilder builder);
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using MethodMap = System.Collections.Generic.Dictionary<string, System.Reflection.MethodInfo>;
namespace TOOHUCardAPI.Data.Services
{
public class MethodMapService
{
public static readonly Dictionary<Type, MethodMap> MethodMapByType = new();
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using TOOHUCardAPI.Data.Enums;
using TOOHUCardAPI.Data.Models;
using TOOHUCardAPI.Data.Repositories;
using TOOHUCardAPI.DTO;
@@ -11,77 +13,80 @@ namespace TOOHUCardAPI.Data.Services
{
private readonly ILogger<StoreService> _logger;
private readonly UserRepository _userRepository;
private readonly CardRepository _cardRepository;
public StoreService(ILogger<StoreService> logger, UserRepository userRepository)
public StoreService(ILogger<StoreService> logger, UserRepository userRepository, CardRepository cardRepository)
{
_logger = logger;
_userRepository = userRepository;
_cardRepository = cardRepository;
}
private void AddPoints(int points, User user)
private async Task AddPoints(int points, User user)
{
user.Point = Math.Max(0, user.Point + points);
_userRepository.UpdateUser(user);
await _userRepository.UpdateUser(user);
_logger.LogInformation($"User with steamid {user.SteamId} was given {points} points. New total: {user.Point}");
}
public async Task<int> LevelUpCard(string steamId, string itemName, int levelIncrease)
{
User user = await _userRepository.GetUser(steamId);
Card card = await _cardRepository.GetCardByItemCode(itemName);
CardLevel userCardLevel = user.CardLevels.Find(cl => cl.Card.ItemCode == itemName) ?? new CardLevel()
{
Card = card,
Level = 0
};
int totalCost =
(card.Quality == Rarity.NORMAL ? AppSettings.PointsPerLevelNormal : AppSettings.PointsPerLevel) *
levelIncrease;
if (user.Point < totalCost)
{
throw new InsufficientPointsException();
}
if (!user.CardLevels.Contains(userCardLevel))
{
user.CardLevels.Add(userCardLevel);
}
await AddPoints(-totalCost, user);
userCardLevel.Level += levelIncrease;
await _userRepository.UpdateUser(user);
_logger.LogInformation($"User with steamid {user.SteamId} leveled up card {card.CardName} to level {userCardLevel.Level}");
return totalCost;
}
public async Task<int> PurchaseMagicKey(int amt, string steamId)
{
int totalCost = AppSettings.PointsPerKey * amt;
amt = Math.Clamp(amt, 0, AppSettings.MaxKeyPurchaseAmount);
User user = _userRepository.GetUser(steamId);
if (user == null)
{
throw new InvalidUserException();
}
User user = await _userRepository.GetUser(steamId);
if (user.Point < totalCost)
{
throw new InsufficientPointsException();
}
AddPoints(-totalCost, user);
await AddPoints(-totalCost, user);
user.KeyTotal += amt;
_userRepository.UpdateUser(user);
await _userRepository.UpdateUser(user);
_logger.LogInformation($"User with steamid {user.SteamId} purchased {amt} keys for {totalCost} points. New point value is {user.Point}");
return totalCost;
}
public async Task<int> GiveFirstWinBonus(string steamId)
{
User user = _userRepository.GetUser(steamId);
if (user == null)
{
throw new InvalidUserException();
}
User user = await _userRepository.GetUser(steamId);
if (user.LastFirstWin.AddDays(1) > DateTime.Now)
{
throw new NotFirstWinException();
}
AddPoints(AppSettings.FirstWinBonusPoints, user);
await AddPoints(AppSettings.FirstWinBonusPoints, user);
user.LastFirstWin = DateTime.Now;
_userRepository.UpdateUser(user);
await _userRepository.UpdateUser(user);
_logger.LogInformation($"User with steamid {user.SteamId} received first win of the day bonus, earning {AppSettings.FirstWinBonusPoints} points. New value: {user.Point}");
return AppSettings.FirstWinBonusPoints;
}
}
public class InsufficientPointsResponse : AbstractResponse
{
public InsufficientPointsResponse()
{
Code = "0002";
Message = "Insufficient points";
}
}
public class NotFirstWinResponse : AbstractResponse
{
public NotFirstWinResponse()
{
Code = "0003";
Message = "Not first win of the day";
}
}
}