95 lines
4.3 KiB
C#
95 lines
4.3 KiB
C#
using LiteDB;
|
|
|
|
namespace Campus_live_rewrite.api.dbs
|
|
{
|
|
/// <summary>
|
|
/// Item wishlists (POST /api/itemWishlists/v1/wishlist/add|remove). The client's add 405'd
|
|
/// (only the reads existed); the row is recorded here so the add answers and the panel can
|
|
/// read it back. The wishlist DTO the client renders is not pinned yet, so the reads stay
|
|
/// empty until it is (see the wishlist routes) - nothing is lost, the rows are here.
|
|
/// </summary>
|
|
public static class WishlistDB
|
|
{
|
|
private static readonly Lazy<LiteDatabase> _db = new(() => new LiteDatabase(Path.Combine("Data", "DBs", "Wishlists.db")));
|
|
|
|
public class Row
|
|
{
|
|
[BsonId] public string Key { get; set; } = ""; // "{account}|{storefront}|{item}"
|
|
public ulong AccountId { get; set; }
|
|
public int StorefrontType { get; set; }
|
|
public int PurchasableItemId { get; set; }
|
|
// A UGC (custom avatar item) wish: PurchasableItemId is -1 and this is the item's GUID.
|
|
public string? CustomAvatarItemId { get; set; }
|
|
public string RawBody { get; set; } = "";
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
private static ILiteCollection<Row> Col()
|
|
{
|
|
var c = _db.Value.GetCollection<Row>("items");
|
|
c.EnsureIndex(x => x.AccountId);
|
|
return c;
|
|
}
|
|
|
|
private static string KeyFor(ulong account, int item, string? custom) =>
|
|
string.IsNullOrWhiteSpace(custom) ? $"{account}|{item}" : $"{account}|ugc|{custom.ToLowerInvariant()}";
|
|
|
|
/// <summary>Record a wish; returns the row (existing or new) so the add can answer it.</summary>
|
|
public static Row Add(ulong account, int storefront, int item, string? custom, string raw)
|
|
{
|
|
var key = KeyFor(account, item, custom);
|
|
try
|
|
{
|
|
var existing = Col().FindById(key);
|
|
if (existing != null) return existing;
|
|
var row = new Row { Key = key, AccountId = account, StorefrontType = storefront, PurchasableItemId = item, CustomAvatarItemId = string.IsNullOrWhiteSpace(custom) ? null : custom, RawBody = raw };
|
|
Col().Upsert(row);
|
|
return row;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[wishlist] add failed: {ex.Message}");
|
|
return new Row { Key = key, AccountId = account, StorefrontType = storefront, PurchasableItemId = item, CustomAvatarItemId = custom };
|
|
}
|
|
}
|
|
|
|
public static void Remove(ulong account, int item, string? custom = null)
|
|
{
|
|
try
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(custom))
|
|
{
|
|
string c = custom.ToLowerInvariant();
|
|
Col().DeleteMany(x => x.AccountId == account && x.CustomAvatarItemId != null && x.CustomAvatarItemId.ToLower() == c);
|
|
}
|
|
else Col().DeleteMany(x => x.AccountId == account && x.PurchasableItemId == item && x.CustomAvatarItemId == null);
|
|
}
|
|
catch (Exception ex) { Console.WriteLine($"[wishlist] remove failed: {ex.Message}"); }
|
|
}
|
|
|
|
/// <summary>The wire entry (real RecNet: WishlistItemId guid, AccountId, PurchasableItemId, CreatedAt;
|
|
/// the 2025 client adds CustomAvatarItemId for UGC wishes). The guid is derived from the key so it is stable.</summary>
|
|
public static Guid WireId(Row r)
|
|
{
|
|
using var md5 = System.Security.Cryptography.MD5.Create();
|
|
return new Guid(md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes("wish|" + r.Key)));
|
|
}
|
|
|
|
public static object ToWire(Row r)
|
|
{
|
|
Guid id = WireId(r);
|
|
return new
|
|
{
|
|
WishlistItemId = id,
|
|
AccountId = r.AccountId,
|
|
PurchasableItemId = r.PurchasableItemId,
|
|
CustomAvatarItemId = r.CustomAvatarItemId,
|
|
CreatedAt = r.CreatedAt
|
|
};
|
|
}
|
|
public static List<object> WireFor(ulong account) => For(account).Select(ToWire).ToList();
|
|
|
|
public static List<Row> For(ulong account) => Col().Find(x => x.AccountId == account).OrderBy(x => x.CreatedAt).ToList();
|
|
}
|
|
}
|