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 class GZipBytesUtil
|
||||||
{
|
{
|
||||||
|
public static MemoryStream GenerateStreamFromString(string value)
|
||||||
|
{
|
||||||
|
return new MemoryStream(Encoding.UTF8.GetBytes(value));
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] GetEmptyJsonGZipBytes()
|
public static byte[] GetEmptyJsonGZipBytes()
|
||||||
{
|
{
|
||||||
var outputStream = new MemoryStream(1024);
|
var outputStream = new MemoryStream(1024);
|
||||||
|
@ -9,13 +9,10 @@ namespace TaikoLocalServer.Controllers.Api;
|
|||||||
[Route("/api/[controller]")]
|
[Route("/api/[controller]")]
|
||||||
public class DashboardController : BaseController<DashboardController>
|
public class DashboardController : BaseController<DashboardController>
|
||||||
{
|
{
|
||||||
private readonly TaikoDbContext context;
|
|
||||||
|
|
||||||
private readonly ICardService cardService;
|
private readonly ICardService cardService;
|
||||||
|
|
||||||
public DashboardController(TaikoDbContext context, ICardService cardService)
|
public DashboardController(ICardService cardService)
|
||||||
{
|
{
|
||||||
this.context = context;
|
|
||||||
this.cardService = cardService;
|
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}")]
|
[Route("/api/[controller]/{baid}")]
|
||||||
public class UserSettingsController : BaseController<UserSettingsController>
|
public class UserSettingsController : BaseController<UserSettingsController>
|
||||||
{
|
{
|
||||||
private readonly TaikoDbContext context;
|
|
||||||
|
|
||||||
private readonly IUserDatumService userDatumService;
|
private readonly IUserDatumService userDatumService;
|
||||||
|
|
||||||
public UserSettingsController(TaikoDbContext context, IUserDatumService userDatumService)
|
public UserSettingsController(IUserDatumService userDatumService)
|
||||||
{
|
{
|
||||||
this.context = context;
|
|
||||||
this.userDatumService = userDatumService;
|
this.userDatumService = userDatumService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
namespace TaikoLocalServer.Services.Interfaces;
|
using SharedProject.Models;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Services.Interfaces;
|
||||||
|
|
||||||
public interface ISongBestDatumService
|
public interface ISongBestDatumService
|
||||||
{
|
{
|
||||||
public Task<List<SongBestDatum>> GetAllSongBestData(uint baid);
|
public Task<List<SongBestDatum>> GetAllSongBestData(uint baid);
|
||||||
|
|
||||||
public Task UpdateOrInsertSongBestDatum(SongBestDatum datum);
|
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 InsertUserDatum(UserDatum userDatum);
|
||||||
|
|
||||||
public Task UpdateUserDatum(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;
|
namespace TaikoLocalServer.Services;
|
||||||
|
|
||||||
@ -32,4 +35,23 @@ public class SongBestDatumService : ISongBestDatumService
|
|||||||
await context.SongBestData.AddAsync(datum);
|
await context.SongBestData.AddAsync(datum);
|
||||||
await context.SaveChangesAsync();
|
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;
|
namespace TaikoLocalServer.Services;
|
||||||
|
|
||||||
@ -6,9 +8,12 @@ public class UserDatumService : IUserDatumService
|
|||||||
{
|
{
|
||||||
private readonly TaikoDbContext context;
|
private readonly TaikoDbContext context;
|
||||||
|
|
||||||
public UserDatumService(TaikoDbContext context)
|
private readonly ILogger<UserDatumService> logger;
|
||||||
|
|
||||||
|
public UserDatumService(TaikoDbContext context, ILogger<UserDatumService> logger)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserDatum?> GetFirstUserDatumOrNull(uint baid)
|
public async Task<UserDatum?> GetFirstUserDatumOrNull(uint baid)
|
||||||
@ -50,4 +55,58 @@ public class UserDatumService : IUserDatumService
|
|||||||
context.Update(userDatum);
|
context.Update(userDatum);
|
||||||
await context.SaveChangesAsync();
|
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