mirror of
https://gitea.tendokyu.moe/Kayori/Medusa.net.git
synced 2026-09-22 22:38:00 +03:00
Added Handler for Paseli consumption. nd add missing fields from SDVX into Facility. And fix plugin dispose error
This commit is contained in:
+4
-1
@@ -425,4 +425,7 @@ FodyWeavers.xsd
|
||||
# Additional files built by Visual Studio
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudiocode,visualstudio
|
||||
/Core/Server/Medusa.db
|
||||
*.db
|
||||
.idea/
|
||||
|
||||
Core/Server/database/
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Abstractions.SerializationTypes;
|
||||
|
||||
public struct XrpcUByte
|
||||
{
|
||||
[XmlAttribute("__type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public byte Value { get; set; }
|
||||
|
||||
public XrpcUByte(byte value)
|
||||
{
|
||||
Type = "u8";
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator byte(XrpcUByte value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator XrpcUByte(byte value)
|
||||
{
|
||||
return new XrpcUByte(value);
|
||||
}
|
||||
|
||||
public bool Equals(XrpcUByte other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is XrpcUByte other)
|
||||
{
|
||||
return Equals(other);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator ==(XrpcUByte left, XrpcUByte right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(XrpcUByte left, XrpcUByte right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static XrpcUByte FromBool(bool v)
|
||||
{
|
||||
return new XrpcUByte(v ? (byte)1 : (byte)0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Abstractions.SerializationTypes;
|
||||
|
||||
public struct XrpcUShort
|
||||
{
|
||||
[XmlAttribute("__type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public ushort Value { get; set; }
|
||||
|
||||
public XrpcUShort(ushort value)
|
||||
{
|
||||
Type = "u16";
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator ushort(XrpcUShort value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator XrpcUShort(ushort value)
|
||||
{
|
||||
return new XrpcUShort(value);
|
||||
}
|
||||
|
||||
public bool Equals(XrpcUShort other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is XrpcUShort other)
|
||||
{
|
||||
return Equals(other);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator ==(XrpcUShort left, XrpcUShort right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(XrpcUShort left, XrpcUShort right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Abstractions.Handlers;
|
||||
using Abstractions.SerializationTypes;
|
||||
using Abstractions.Services;
|
||||
using Server.Models.Request;
|
||||
using Server.Models.Response;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Handlers.Common.Coin;
|
||||
|
||||
public class EaCoinCheckinHandler(ICardService cardService, IEaCoinSessionService sessionService) : Handler<EaCoinCheckInRequest, EaCoinCheckInResponse>
|
||||
{
|
||||
public override void Configure()
|
||||
{
|
||||
Module("eacoin");
|
||||
Method("checkin");
|
||||
}
|
||||
|
||||
public override async Task<EaCoinCheckInResponse> HandleAsync(EaCoinCheckInRequest req, string model)
|
||||
{
|
||||
var card = await cardService.FindByCardId(req.CardId);
|
||||
|
||||
if (card is null)
|
||||
{
|
||||
return new EaCoinCheckInResponse
|
||||
{
|
||||
Expire = 1200,
|
||||
Status = 1,
|
||||
AcStatus = new XrpcUByte(0)
|
||||
};
|
||||
}
|
||||
|
||||
var pinValid = await cardService.ValidatePinAsync(card.KonamiId, req.PassWd);
|
||||
|
||||
if (!pinValid)
|
||||
{
|
||||
return new EaCoinCheckInResponse
|
||||
{
|
||||
Expire = 1200,
|
||||
Status = 1,
|
||||
AcStatus = new XrpcUByte(0)
|
||||
};
|
||||
}
|
||||
|
||||
var session = sessionService.AddSession(card.User.Id, card.KonamiId);
|
||||
|
||||
return new EaCoinCheckInResponse
|
||||
{
|
||||
Expire = 1200,
|
||||
Status = 0,
|
||||
Sequence = 0,
|
||||
AcStatus = 0,
|
||||
AcId = card.User.Id + "-" + card.KonamiId,
|
||||
AcName = card.User.UserName ?? card.KonamiId,
|
||||
Balance = card.User.PaseliAmount,
|
||||
SessionId = session.SessionId,
|
||||
InShopCharge = 0,
|
||||
Point = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Abstractions.Handlers;
|
||||
using Server.Models.Request;
|
||||
using Server.Models.Response;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Handlers.Common.Coin;
|
||||
|
||||
public class EaCoinCheckoutHandler(IEaCoinSessionService sessionService) : Handler<EaCoinCheckoutRequest, EaCoinCheckoutResponse>
|
||||
{
|
||||
public override void Configure()
|
||||
{
|
||||
Module("eacoin");
|
||||
Method("checkout");
|
||||
}
|
||||
|
||||
public override EaCoinCheckoutResponse Handle(EaCoinCheckoutRequest req, string model)
|
||||
{
|
||||
sessionService.RemoveSession(req.sessionId);
|
||||
|
||||
return new EaCoinCheckoutResponse()
|
||||
{
|
||||
Status = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Abstractions.Handlers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Server.Models.Request;
|
||||
using Server.Models.Response;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Handlers.Common.Coin;
|
||||
|
||||
public class EaCoinConsumeHandler(IEaCoinSessionService sessionService, AppDbContext dbContext) : Handler<EaCoinConsumeRequest, EaCoinConsumeResponse>
|
||||
{
|
||||
public override void Configure()
|
||||
{
|
||||
Module("eacoin");
|
||||
Method("consume");
|
||||
}
|
||||
|
||||
public override async Task<EaCoinConsumeResponse> HandleAsync(EaCoinConsumeRequest req, string model)
|
||||
{
|
||||
var session = sessionService.GetSession(req.sessionId);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return new EaCoinConsumeResponse()
|
||||
{
|
||||
Status = -4, // EA3_EACOIN_ERROR.NOTCHECKIN
|
||||
AcStatus = 0,
|
||||
AutoCharge = 0,
|
||||
Balance = 0
|
||||
};
|
||||
}
|
||||
|
||||
if (session.IsCharging)
|
||||
{
|
||||
return new EaCoinConsumeResponse()
|
||||
{
|
||||
Status = -2, // EA3_EACOIN_ERROR.SLOTINUSE
|
||||
AcStatus = 0,
|
||||
AutoCharge = 0,
|
||||
Balance = 0
|
||||
};
|
||||
}
|
||||
|
||||
session.IsCharging = true;
|
||||
|
||||
var user = await dbContext.Users.FindAsync(session.UserId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
session.IsCharging = false;
|
||||
return new EaCoinConsumeResponse()
|
||||
{
|
||||
Status = -7, // EA3_EACOIN_ERROR.NOACCOUNT
|
||||
AcStatus = 0,
|
||||
AutoCharge = 0,
|
||||
Balance = 0
|
||||
};
|
||||
}
|
||||
|
||||
if (user.PaseliAmount < req.Payment)
|
||||
{
|
||||
session.IsCharging = false;
|
||||
return new EaCoinConsumeResponse()
|
||||
{
|
||||
Status = -8, // EA3_EACOIN_ERROR.SHORTBALANCE
|
||||
AcStatus = 0,
|
||||
AutoCharge = 0,
|
||||
Balance = user.PaseliAmount
|
||||
};
|
||||
}
|
||||
|
||||
user.PaseliAmount -= req.Payment;
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
session.IsCharging = false;
|
||||
return new EaCoinConsumeResponse()
|
||||
{
|
||||
Status = 0,
|
||||
AcStatus = 0,
|
||||
AutoCharge = 0,
|
||||
Balance = user.PaseliAmount
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -23,41 +23,39 @@ public class GetFacilityHandler: HandlerWithoutRequest<GetFacilityResponse>
|
||||
Country = new XrpcString("JP"),
|
||||
Region = new XrpcString("13"),
|
||||
Name = new XrpcString("Medusa"),
|
||||
Type = new XrpcULong(0),
|
||||
Type = new XrpcUByte(0),
|
||||
CompanyCode = new XrpcString("00"),
|
||||
CustomerCode = new XrpcString("0000"),
|
||||
CountryName = new XrpcString("Japan"),
|
||||
CountryJpName = new XrpcString("日本"),
|
||||
RegionName = new XrpcString("Tokyo"),
|
||||
RegionJpName = new XrpcString("東京都"),
|
||||
Accuracy = new XrpcULong(0),
|
||||
Accuracy = new XrpcUByte(0),
|
||||
Latitude = new XrpcInt(0),
|
||||
Longitude = new XrpcInt(0)
|
||||
},
|
||||
Line = new FacilityLine()
|
||||
{
|
||||
Id = new XrpcString("1"),
|
||||
Class = new XrpcULong(0)
|
||||
Class = new XrpcUByte(0),
|
||||
UpClass = new XrpcUByte(0),
|
||||
Rtt = new XrpcUShort(0)
|
||||
},
|
||||
PortForward = new FacilityPortForward()
|
||||
{
|
||||
GlobalIp = new XrpcIp4("127.0.0.1"),
|
||||
GlobalPort = new XrpcULong(5246),
|
||||
PrivatePort = new XrpcULong(5246)
|
||||
GlobalPort = new XrpcUShort(5246),
|
||||
PrivatePort = new XrpcUShort(5246)
|
||||
},
|
||||
Public = new FacilityPublic()
|
||||
{
|
||||
Flag = new XrpcULong(1),
|
||||
Name = new XrpcString("Medusa")
|
||||
Flag = new XrpcUByte(1),
|
||||
Name = new XrpcString("Medusa"),
|
||||
Latitude = new XrpcString("0"),
|
||||
Longitude = new XrpcString("0")
|
||||
},
|
||||
Share = new FacilityShare()
|
||||
{
|
||||
EaCoin = new FacilityEaCoin()
|
||||
{
|
||||
NotchAmount = new XrpcLong((long)3000),
|
||||
NotchCount = new XrpcLong((long)3),
|
||||
SupplyLimit = new XrpcLong(1000000)
|
||||
},
|
||||
Url = new FacilityUrl()
|
||||
{
|
||||
EaPass = new XrpcString("http://eagate.573.jp"),
|
||||
@@ -65,10 +63,32 @@ public class GetFacilityHandler: HandlerWithoutRequest<GetFacilityResponse>
|
||||
KonamiNetDx = new XrpcString("http://eagate.573.jp"),
|
||||
KonamiId = new XrpcString("http://eagate.573.jp"),
|
||||
EaGate = new XrpcString("http://eagate.573.jp")
|
||||
},
|
||||
EaPass = new FacilityEaPass()
|
||||
{
|
||||
Valid = new XrpcUShort(365)
|
||||
},
|
||||
EaCoin = new FacilityEaCoin()
|
||||
{
|
||||
NotchAmount = new XrpcInt(0),
|
||||
NotchCount = new XrpcInt(0),
|
||||
SupplyLimit = new XrpcInt(1000000)
|
||||
}
|
||||
},
|
||||
Calendar = new FacilityCalendar()
|
||||
{
|
||||
Year = new XrpcShort((short)DateTime.UtcNow.Year),
|
||||
// TODO: no real holiday calendar wired up yet - empty list is a
|
||||
// safe default (client just sees "no holidays"), not a guess at
|
||||
// actual dates.
|
||||
Holiday = new FacilityHolidayList()
|
||||
{
|
||||
Count = 0,
|
||||
Value = ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return facility;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Server.Models.Request;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
public class EaCoinCheckInRequest
|
||||
{
|
||||
[XmlElement("slotid")]
|
||||
public required string SlotId { get; set; }
|
||||
[XmlElement("cardtype")]
|
||||
public required string CardType { get; set; }
|
||||
[XmlElement("cardid")]
|
||||
public required string CardId { get; set; }
|
||||
[XmlElement("passwd")]
|
||||
public required string PassWd { get; set; }
|
||||
[XmlElement("ectype")]
|
||||
public required string EcType { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Server.Models.Request;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
public class EaCoinCheckoutRequest
|
||||
{
|
||||
[XmlElement("sessid")]
|
||||
public string sessionId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Server.Models.Request;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
public class EaCoinConsumeRequest
|
||||
{
|
||||
[XmlAttribute("esid")]
|
||||
public string EsId { get; set; }
|
||||
|
||||
[XmlAttribute("esdate")]
|
||||
public string EsDate { get; set; }
|
||||
|
||||
[XmlElement("sessid")]
|
||||
public string sessionId { get; set; }
|
||||
|
||||
[XmlElement("sequence")]
|
||||
public short Sequence { get; set; }
|
||||
|
||||
[XmlElement("payment")]
|
||||
public int Payment { get; set; }
|
||||
|
||||
[XmlElement("service")]
|
||||
public short service { get; set; }
|
||||
|
||||
[XmlElement("itemtype")]
|
||||
public string ItemType { get; set; }
|
||||
|
||||
[XmlElement("detail")]
|
||||
public string Detail { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Abstractions.SerializationTypes;
|
||||
|
||||
namespace Server.Models.Response;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
[Serializable]
|
||||
public class EaCoinCheckInResponse
|
||||
{
|
||||
[XmlAttribute("status")]
|
||||
public required int Status { get; set; }
|
||||
|
||||
[XmlAttribute("expire")]
|
||||
public required int Expire { get; set; }
|
||||
|
||||
[XmlElement("sequence")]
|
||||
public XrpcShort Sequence { get; set; }
|
||||
|
||||
[XmlElement("acstatus")]
|
||||
public XrpcUByte AcStatus { get; set; }
|
||||
|
||||
[XmlElement("acid")]
|
||||
public XrpcString AcId { get; set; }
|
||||
|
||||
[XmlElement("acname")]
|
||||
public XrpcString AcName { get; set; }
|
||||
|
||||
[XmlElement("balance")]
|
||||
public XrpcInt Balance { get; set; }
|
||||
|
||||
[XmlElement("sessid")]
|
||||
public XrpcString SessionId { get; set; }
|
||||
|
||||
[XmlElement("inshopcharge")]
|
||||
public XrpcUByte InShopCharge { get; set; }
|
||||
|
||||
[XmlElement("point")]
|
||||
public XrpcInt Point { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Server.Models.Response;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
public class EaCoinCheckoutResponse
|
||||
{
|
||||
[XmlAttribute("status")]
|
||||
public int Status { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Xml.Serialization;
|
||||
using Abstractions.SerializationTypes;
|
||||
|
||||
namespace Server.Models.Response;
|
||||
|
||||
[XmlRoot("eacoin")]
|
||||
[Serializable]
|
||||
public class EaCoinConsumeResponse
|
||||
{
|
||||
[XmlAttribute("status")]
|
||||
public int Status { get; set; }
|
||||
|
||||
[XmlElement("acstatus")]
|
||||
public XrpcUByte AcStatus { get; set; }
|
||||
|
||||
[XmlElement("autocharge")]
|
||||
public XrpcUByte AutoCharge { get; set; }
|
||||
|
||||
[XmlElement("balance")]
|
||||
public XrpcInt Balance { get; set; }
|
||||
}
|
||||
@@ -12,18 +12,21 @@ public class GetFacilityResponse
|
||||
|
||||
[XmlElement("location")]
|
||||
public required FacilityLocation Location { get; set; }
|
||||
|
||||
|
||||
[XmlElement("line")]
|
||||
public required FacilityLine Line { get; set; }
|
||||
|
||||
[XmlElement("portforward")]
|
||||
|
||||
[XmlElement("portfw")]
|
||||
public required FacilityPortForward PortForward { get; set; }
|
||||
|
||||
|
||||
[XmlElement("public")]
|
||||
public required FacilityPublic Public { get; set; }
|
||||
|
||||
|
||||
[XmlElement("share")]
|
||||
public required FacilityShare Share { get; set; }
|
||||
|
||||
[XmlElement("calendar")]
|
||||
public required FacilityCalendar Calendar { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityLocation
|
||||
@@ -41,7 +44,7 @@ public record FacilityLocation
|
||||
public required XrpcString Name { get; set; }
|
||||
|
||||
[XmlElement("type")]
|
||||
public required XrpcULong Type { get; set; }
|
||||
public required XrpcUByte Type { get; set; }
|
||||
|
||||
[XmlElement("companycode")]
|
||||
public required XrpcString CompanyCode { get; set; }
|
||||
@@ -62,7 +65,7 @@ public record FacilityLocation
|
||||
public required XrpcString RegionJpName { get; set; }
|
||||
|
||||
[XmlElement("accuracy")]
|
||||
public required XrpcULong Accuracy { get; set; }
|
||||
public required XrpcUByte Accuracy { get; set; }
|
||||
|
||||
[XmlElement("latitude")]
|
||||
public required XrpcInt Latitude { get; set; }
|
||||
@@ -75,68 +78,111 @@ public record FacilityLine
|
||||
{
|
||||
[XmlElement("id")]
|
||||
public required XrpcString Id { get; set; }
|
||||
|
||||
|
||||
[XmlElement("class")]
|
||||
public required XrpcULong Class { get; set; }
|
||||
public required XrpcUByte Class { get; set; }
|
||||
|
||||
[XmlElement("upclass")]
|
||||
public required XrpcUByte UpClass { get; set; }
|
||||
|
||||
[XmlElement("rtt")]
|
||||
public required XrpcUShort Rtt { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityPortForward
|
||||
{
|
||||
[XmlElement("globalip")]
|
||||
public required XrpcIp4 GlobalIp { get; set; }
|
||||
|
||||
|
||||
[XmlElement("globalport")]
|
||||
public required XrpcULong GlobalPort { get; set; }
|
||||
|
||||
public required XrpcUShort GlobalPort { get; set; }
|
||||
|
||||
[XmlElement("privateport")]
|
||||
public required XrpcULong PrivatePort { get; set; }
|
||||
public required XrpcUShort PrivatePort { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityPublic
|
||||
{
|
||||
[XmlElement("flag")]
|
||||
public required XrpcULong Flag { get; set; }
|
||||
public required XrpcUByte Flag { get; set; }
|
||||
|
||||
[XmlElement("name")]
|
||||
public required XrpcString Name { get; set; }
|
||||
|
||||
[XmlElement("latitude")]
|
||||
public required XrpcString Latitude { get; set; }
|
||||
|
||||
[XmlElement("longitude")]
|
||||
public required XrpcString Longitude { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityShare
|
||||
{
|
||||
[XmlElement("eacon")]
|
||||
public required FacilityEaCoin EaCoin { get; set; }
|
||||
|
||||
[XmlElement("url")]
|
||||
public required FacilityUrl Url { get; set; }
|
||||
|
||||
[XmlElement("eapass")]
|
||||
public required FacilityEaPass EaPass { get; set; }
|
||||
|
||||
[XmlElement("eacoin")]
|
||||
public required FacilityEaCoin EaCoin { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityEaCoin
|
||||
{
|
||||
[XmlElement("notchamount")]
|
||||
public required XrpcLong NotchAmount { get; set; }
|
||||
|
||||
public required XrpcInt NotchAmount { get; set; }
|
||||
|
||||
[XmlElement("notchcount")]
|
||||
public required XrpcLong NotchCount { get; set; }
|
||||
|
||||
public required XrpcInt NotchCount { get; set; }
|
||||
|
||||
[XmlElement("supplylimit")]
|
||||
public required XrpcLong SupplyLimit { get; set; }
|
||||
|
||||
public required XrpcInt SupplyLimit { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityEaPass
|
||||
{
|
||||
[XmlElement("valid")]
|
||||
public required XrpcUShort Valid { get; set; }
|
||||
}
|
||||
|
||||
public record FacilityUrl
|
||||
{
|
||||
[XmlElement("eapass")]
|
||||
public required XrpcString EaPass { get; set; }
|
||||
|
||||
|
||||
[XmlElement("arcadefan")]
|
||||
public required XrpcString ArcadeFan { get; set;}
|
||||
|
||||
|
||||
[XmlElement("konaminetdx")]
|
||||
public required XrpcString KonamiNetDx { get; set; }
|
||||
|
||||
|
||||
[XmlElement("konamiid")]
|
||||
public required XrpcString KonamiId { get; set; }
|
||||
|
||||
|
||||
[XmlElement("eagate")]
|
||||
public required XrpcString EaGate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public record FacilityCalendar
|
||||
{
|
||||
[XmlElement("year")]
|
||||
public required XrpcShort Year { get; set; }
|
||||
|
||||
// Real wire shape: <holiday __type="s16" __count="35">0 11 41 ...</holiday>
|
||||
// - a space-separated list of day-of-year offsets, not a repeated element.
|
||||
[XmlElement("holiday")]
|
||||
public required FacilityHolidayList Holiday { get; set; }
|
||||
}
|
||||
|
||||
public class FacilityHolidayList
|
||||
{
|
||||
[XmlAttribute("__type")]
|
||||
public string Type { get; set; } = "s16";
|
||||
|
||||
[XmlAttribute("__count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public string Value { get; set; } = "";
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ builder.Services.AddOpenApi("v1");
|
||||
var pluginRegistry = new PluginRegistry();
|
||||
var pluginService = new PluginService(logger, pluginRegistry);
|
||||
builder.Services.AddSingleton(pluginRegistry);
|
||||
builder.Services.AddSingleton<IEaCoinSessionService, EaCoinSessionService>();
|
||||
builder.Services.AddSingleton<IPluginService>(pluginService);
|
||||
builder.Services.AddHostedService<PluginWatcher>();
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Server.Services;
|
||||
|
||||
public class EaCoinSessionService : IEaCoinSessionService
|
||||
{
|
||||
private readonly List<EaCoinSession> sessions = [];
|
||||
public void AddSession(int userId, string sessionId, string refId)
|
||||
{
|
||||
sessions.Add(new EaCoinSession(sessionId, userId, refId));
|
||||
}
|
||||
|
||||
public EaCoinSession AddSession(int userId, string cardid)
|
||||
{
|
||||
var sessionId = GenerateSessionId();
|
||||
var session = new EaCoinSession(sessionId, userId, cardid);
|
||||
sessions.Add(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
public EaCoinSession? GetSession(string sessionId)
|
||||
{
|
||||
return sessions.Find(s => s.SessionId == sessionId);
|
||||
}
|
||||
|
||||
public void RemoveSession(string sessionId)
|
||||
{
|
||||
sessions.Remove(sessions.Find(s => s.SessionId == sessionId));
|
||||
}
|
||||
|
||||
private string GenerateSessionId()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Replace("-", "").ToUpper();
|
||||
}
|
||||
}
|
||||
|
||||
public record EaCoinSession(string SessionId, int UserId, string RefId)
|
||||
{
|
||||
public bool IsCharging { get; set; } = false;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Server.Services;
|
||||
|
||||
public interface IEaCoinSessionService
|
||||
{
|
||||
public void AddSession(int userId, string sessionId, string refId);
|
||||
public EaCoinSession AddSession(int userId, string cardId);
|
||||
public EaCoinSession? GetSession(string sessionId);
|
||||
public void RemoveSession(string sessionId);
|
||||
}
|
||||
@@ -163,30 +163,33 @@ public class PluginService(ILogger logger, PluginRegistry pluginRegistry) : IPlu
|
||||
var method = plugin.DoesProfileExist;
|
||||
var parameters = method.Method.GetParameters();
|
||||
|
||||
Task<bool> InvokeAsync(IServiceProvider composite)
|
||||
{
|
||||
var args = parameters.Select(p =>
|
||||
{
|
||||
if (p.Name == "cardId") return (object)cardId;
|
||||
var isFromServices = p.GetCustomAttribute<FromServicesAttribute>() != null;
|
||||
return isFromServices
|
||||
? composite.GetRequiredService(p.ParameterType)
|
||||
: throw new InvalidOperationException(
|
||||
$"Don't know how to resolve parameter '{p.Name}' on plugin delegate DoesProfileExist");
|
||||
}).ToArray();
|
||||
|
||||
return (Task<bool>)method.DynamicInvoke(args)!;
|
||||
}
|
||||
|
||||
await using var hostScope = _serviceProvider.CreateAsyncScope();
|
||||
|
||||
IServiceProvider composite;
|
||||
if (slot is not null)
|
||||
{
|
||||
// pluginScope must stay alive until the delegate has actually run, since
|
||||
// composite (and anything resolved from it) is only valid while it's open.
|
||||
await using var pluginScope = slot.PluginServices.CreateAsyncScope();
|
||||
composite = new PluginServiceProvider(pluginScope.ServiceProvider, hostScope.ServiceProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
composite = hostScope.ServiceProvider;
|
||||
var composite = new PluginServiceProvider(pluginScope.ServiceProvider, hostScope.ServiceProvider);
|
||||
return await InvokeAsync(composite);
|
||||
}
|
||||
|
||||
var args = parameters.Select(p =>
|
||||
{
|
||||
if (p.Name == "cardId") return (object)cardId;
|
||||
var isFromServices = p.GetCustomAttribute<FromServicesAttribute>() != null;
|
||||
return isFromServices
|
||||
? composite.GetRequiredService(p.ParameterType)
|
||||
: throw new InvalidOperationException(
|
||||
$"Don't know how to resolve parameter '{p.Name}' on plugin delegate DoesProfileExist");
|
||||
}).ToArray();
|
||||
|
||||
return await (Task<bool>)method.DynamicInvoke(args)!;
|
||||
return await InvokeAsync(hostScope.ServiceProvider);
|
||||
}
|
||||
|
||||
// ── Internal ─────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user