Add api to get best data and set favorite songs
This commit is contained in:
parent
0f71da4353
commit
a0af130d1d
6
SharedProject/Models/Responses/SongBestResponse.cs
Normal file
6
SharedProject/Models/Responses/SongBestResponse.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace SharedProject.Models.Responses;
|
||||
|
||||
public class SongBestResponse
|
||||
{
|
||||
public List<SongBestData> SongBestData { get; set; } = new();
|
||||
}
|
22
SharedProject/Models/SongBestData.cs
Normal file
22
SharedProject/Models/SongBestData.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using SharedProject.Enums;
|
||||
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public class SongBestData
|
||||
{
|
||||
public uint SongId { get; set; }
|
||||
|
||||
public Difficulty Difficulty { get; set; }
|
||||
|
||||
public uint BestScore { get; set; }
|
||||
|
||||
public uint BestRate { get; set; }
|
||||
|
||||
public CrownType BestCrown { get; set; }
|
||||
|
||||
public ScoreRank BestScoreRank { get; set; }
|
||||
|
||||
public DateTime LastPlayTime { get; set; }
|
||||
|
||||
public bool IsFavorite { get; set; }
|
||||
}
|
@ -6,6 +6,11 @@ namespace TaikoLocalServer.Common.Utils;
|
||||
|
||||
public static class GZipBytesUtil
|
||||
{
|
||||
public static MemoryStream GenerateStreamFromString(string value)
|
||||
{
|
||||
return new MemoryStream(Encoding.UTF8.GetBytes(value));
|
||||
}
|
||||
|
||||
public static byte[] GetEmptyJsonGZipBytes()
|
||||
{
|
||||
var outputStream = new MemoryStream(1024);
|
||||
|
@ -9,13 +9,10 @@ namespace TaikoLocalServer.Controllers.Api;
|
||||
[Route("/api/[controller]")]
|
||||
public class DashboardController : BaseController<DashboardController>
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
private readonly ICardService cardService;
|
||||
|
||||
public DashboardController(TaikoDbContext context, ICardService cardService)
|
||||
public DashboardController(ICardService cardService)
|
||||
{
|
||||
this.context = context;
|
||||
this.cardService = cardService;
|
||||
}
|
||||
|
||||
|
29
TaikoLocalServer/Controllers/Api/FavoriteSongsController.cs
Normal file
29
TaikoLocalServer/Controllers/Api/FavoriteSongsController.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class FavoriteSongsController : BaseController<FavoriteSongsController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public FavoriteSongsController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdateFavoriteSong(uint baid, uint songId, bool isFavorite)
|
||||
{
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(baid);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await userDatumService.UpdateFavoriteSong(baid, songId, isFavorite);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
42
TaikoLocalServer/Controllers/Api/PlayDataController.cs
Normal file
42
TaikoLocalServer/Controllers/Api/PlayDataController.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using SharedProject.Models.Responses;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class PlayDataController : BaseController<PlayDataController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
public PlayDataController(IUserDatumService userDatumService, ISongBestDatumService songBestDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
}
|
||||
|
||||
[HttpGet("{baid}")]
|
||||
public async Task<ActionResult<SongBestResponse>> GetSongBestRecords(uint baid)
|
||||
{
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(baid);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var songBestRecords = await songBestDatumService.GetAllSongBestAsModel(baid);
|
||||
var favoriteSongs = await userDatumService.GetFavoriteSongIds(baid);
|
||||
var favoriteSet = favoriteSongs.ToHashSet();
|
||||
foreach (var songBestRecord in songBestRecords.Where(songBestRecord => favoriteSet.Contains(songBestRecord.SongId)))
|
||||
{
|
||||
songBestRecord.IsFavorite = true;
|
||||
}
|
||||
|
||||
return Ok(new SongBestResponse
|
||||
{
|
||||
SongBestData = songBestRecords
|
||||
});
|
||||
}
|
||||
}
|
@ -11,13 +11,10 @@ namespace TaikoLocalServer.Controllers.Api;
|
||||
[Route("/api/[controller]/{baid}")]
|
||||
public class UserSettingsController : BaseController<UserSettingsController>
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public UserSettingsController(TaikoDbContext context, IUserDatumService userDatumService)
|
||||
public UserSettingsController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.context = context;
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
namespace TaikoLocalServer.Services.Interfaces;
|
||||
using SharedProject.Models;
|
||||
|
||||
namespace TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
public interface ISongBestDatumService
|
||||
{
|
||||
public Task<List<SongBestDatum>> GetAllSongBestData(uint baid);
|
||||
|
||||
public Task UpdateOrInsertSongBestDatum(SongBestDatum datum);
|
||||
|
||||
public Task<List<SongBestData>> GetAllSongBestAsModel(uint baid);
|
||||
}
|
@ -13,4 +13,8 @@ public interface IUserDatumService
|
||||
public Task InsertUserDatum(UserDatum userDatum);
|
||||
|
||||
public Task UpdateUserDatum(UserDatum userDatum);
|
||||
|
||||
public Task<List<uint>> GetFavoriteSongIds(uint baid);
|
||||
|
||||
public Task UpdateFavoriteSong(uint baid, uint songId, bool isFavorite);
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using SharedProject.Models;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
@ -32,4 +35,23 @@ public class SongBestDatumService : ISongBestDatumService
|
||||
await context.SongBestData.AddAsync(datum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<SongBestData>> GetAllSongBestAsModel(uint baid)
|
||||
{
|
||||
var songbestDbData = await context.SongBestData.Where(datum => datum.Baid == baid).ToListAsync();
|
||||
|
||||
var result = songbestDbData.Select(datum => datum.CopyPropertiesToNew<SongBestData>()).ToList();
|
||||
|
||||
var playLogs = await context.SongPlayData.Where(datum => datum.Baid == baid).ToListAsync();
|
||||
foreach (var bestData in result)
|
||||
{
|
||||
var lastPlayLog = playLogs.Where(datum => datum.Difficulty == bestData.Difficulty &&
|
||||
datum.SongId == bestData.SongId)
|
||||
.MaxBy(datum => datum.PlayTime);
|
||||
lastPlayLog.ThrowIfNull("Last play log is null! Something is wrong with db!");
|
||||
bestData.LastPlayTime = lastPlayLog.PlayTime;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
@ -6,9 +8,12 @@ public class UserDatumService : IUserDatumService
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
public UserDatumService(TaikoDbContext context)
|
||||
private readonly ILogger<UserDatumService> logger;
|
||||
|
||||
public UserDatumService(TaikoDbContext context, ILogger<UserDatumService> logger)
|
||||
{
|
||||
this.context = context;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<UserDatum?> GetFirstUserDatumOrNull(uint baid)
|
||||
@ -50,4 +55,58 @@ public class UserDatumService : IUserDatumService
|
||||
context.Update(userDatum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<uint>> GetFavoriteSongIds(uint baid)
|
||||
{
|
||||
var userDatum = await context.UserData.FindAsync(baid);
|
||||
userDatum.ThrowIfNull();
|
||||
|
||||
using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
|
||||
List<uint>? result;
|
||||
try
|
||||
{
|
||||
result = await JsonSerializer.DeserializeAsync<List<uint>>(stringStream);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
logger.LogError(e, "Parse favorite song array json failed! Is the user initialized correctly?");
|
||||
result = new List<uint>();
|
||||
}
|
||||
result.ThrowIfNull("Song favorite array should never be null!");
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task UpdateFavoriteSong(uint baid, uint songId, bool isFavorite)
|
||||
{
|
||||
var userDatum = await context.UserData.FindAsync(baid);
|
||||
userDatum.ThrowIfNull();
|
||||
|
||||
using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
|
||||
List<uint>? favoriteSongIds;
|
||||
try
|
||||
{
|
||||
favoriteSongIds = await JsonSerializer.DeserializeAsync<List<uint>>(stringStream);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
logger.LogError(e, "Parse favorite song array json failed! Is the user initialized correctly?");
|
||||
favoriteSongIds = new List<uint>();
|
||||
}
|
||||
favoriteSongIds.ThrowIfNull("Song favorite array should never be null!");
|
||||
var favoriteSet = new HashSet<uint>(favoriteSongIds);
|
||||
if (isFavorite)
|
||||
{
|
||||
favoriteSet.Add(songId);
|
||||
}
|
||||
else
|
||||
{
|
||||
favoriteSet.Remove(songId);
|
||||
}
|
||||
|
||||
await JsonSerializer.SerializeAsync(stringStream, favoriteSet);
|
||||
|
||||
context.Update(userDatum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user