Refactor CustomerPlanService to use FacilityRepository, remove PlaceService, and introduce RfidTagService
- Updated CustomerPlanService to replace references to PlaceRepo with FacilityRepository for managing customer plans and associated facilities. - Removed PlaceService and its related methods, consolidating functionality into FacilityService. - Added RfidTagService for managing RFID tags, including methods for creating, updating, and setting statuses of tags. - Updated TenantDbContext to include new DbSet for RfidTags and CustomerPlanFacilities, and adjusted model configurations accordingly. - Enhanced FacilityService to support new booking modes and validation for facility creation and updates.
This commit is contained in:
+38
-18
@@ -64,16 +64,19 @@ namespace Fitness_core_BE
|
||||
public DbSet<CustomerWorkoutExerciseCompletion> CustomerWorkoutExerciseCompletions => Set<CustomerWorkoutExerciseCompletion>();
|
||||
|
||||
// Multi-facility session booking + RFID time-bank cards (pool/court/class booking, one
|
||||
// per club since single-tenant -- see Models/Facility.cs and friends).
|
||||
// per club since single-tenant), and access-zone/device-scoping -- Facility merges what
|
||||
// used to be two separate concepts (bookable capacity + access zone), see Models/Facility.cs.
|
||||
public DbSet<Facility> Facilities => Set<Facility>();
|
||||
public DbSet<FacilitySession> FacilitySessions => Set<FacilitySession>();
|
||||
public DbSet<Booking> Bookings => Set<Booking>();
|
||||
public DbSet<TimeBankCard> TimeBankCards => Set<TimeBankCard>();
|
||||
public DbSet<TimeBankCardUsageLog> TimeBankCardUsageLogs => Set<TimeBankCardUsageLog>();
|
||||
public DbSet<CustomerPlanFacility> CustomerPlanFacilities => Set<CustomerPlanFacility>();
|
||||
|
||||
// Physical access zones (Pool Area, Indoor Gym, ...) -- see Models/Place.cs.
|
||||
public DbSet<Place> Places => Set<Place>();
|
||||
public DbSet<CustomerPlanPlace> CustomerPlanPlaces => Set<CustomerPlanPlace>();
|
||||
// Booking-scoped RFID tag inventory (assigned to a Booking at check-in, released at
|
||||
// Complete/no-show sweep) -- see Models/RfidTag.cs. Not to be confused with
|
||||
// TimeBankCard's own, unrelated RfidTagId string field.
|
||||
public DbSet<RfidTag> RfidTags => Set<RfidTag>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -183,12 +186,14 @@ namespace Fitness_core_BE
|
||||
modelBuilder.Entity<Booking>().Ignore(b => b.Client);
|
||||
modelBuilder.Entity<TimeBankCard>().Ignore(t => t.Client);
|
||||
modelBuilder.Entity<TimeBankCardUsageLog>().Ignore(l => l.Client);
|
||||
modelBuilder.Entity<Place>().Ignore(p => p.Client);
|
||||
|
||||
// A session's capacity is enforced by counting non-cancelled bookings against it at
|
||||
// booking time, not by a unique index -- multiple guests/members can share one slot
|
||||
// up to Facility.DefaultCapacity. What must stay unique is a member double-booking
|
||||
// the same session twice.
|
||||
// the same session twice. FKFacilitySessionId is null for every AdHoc booking, and
|
||||
// MySQL/InnoDB never treats two NULLs as duplicates in a unique index, so this index
|
||||
// is naturally a no-op for AdHoc bookings -- their own overlap-vs-NumberOfUnits check
|
||||
// lives in BookingRepository.GetOverlappingAdhocBookingCountAsync instead.
|
||||
modelBuilder.Entity<Booking>()
|
||||
.HasIndex(b => new { b.FKFacilitySessionId, b.FKCustomerId })
|
||||
.IsUnique()
|
||||
@@ -241,22 +246,37 @@ namespace Fitness_core_BE
|
||||
// same-context FK anymore (same pattern as every other central-pointing scalar).
|
||||
modelBuilder.Entity<AccessDevice>().Ignore(d => d.Agent);
|
||||
|
||||
// One device per place for now -- see AccessDevice.FKPlaceId comment. Lifting
|
||||
// this to many-devices-per-place later is just dropping this index.
|
||||
// One device per facility for now -- see AccessDevice.FKFacilityId comment. Lifting
|
||||
// this to many-devices-per-facility later is just dropping this index.
|
||||
modelBuilder.Entity<AccessDevice>()
|
||||
.HasIndex(d => d.FKPlaceId)
|
||||
.HasIndex(d => d.FKFacilityId)
|
||||
.IsUnique()
|
||||
.HasFilter("`FKPlaceId` IS NOT NULL");
|
||||
.HasFilter("`FKFacilityId` IS NOT NULL");
|
||||
|
||||
modelBuilder.Entity<CustomerPlanPlace>()
|
||||
.HasIndex(pp => new { pp.FKCustomerPlanId, pp.FKPlaceId })
|
||||
modelBuilder.Entity<CustomerPlanFacility>()
|
||||
.HasIndex(pf => new { pf.FKCustomerPlanId, pf.FKFacilityId })
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<CustomerPlanPlace>()
|
||||
.HasOne(pp => pp.CustomerPlan).WithMany()
|
||||
.HasForeignKey(pp => pp.FKCustomerPlanId).OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<CustomerPlanPlace>()
|
||||
.HasOne(pp => pp.Place).WithMany()
|
||||
.HasForeignKey(pp => pp.FKPlaceId).OnDelete(DeleteBehavior.Restrict);
|
||||
modelBuilder.Entity<CustomerPlanFacility>()
|
||||
.HasOne(pf => pf.CustomerPlan).WithMany()
|
||||
.HasForeignKey(pf => pf.FKCustomerPlanId).OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<CustomerPlanFacility>()
|
||||
.HasOne(pf => pf.Facility).WithMany()
|
||||
.HasForeignKey(pf => pf.FKFacilityId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// RFID tag inventory is per-client; a tag code only needs to be unique within one
|
||||
// club's pool.
|
||||
modelBuilder.Entity<RfidTag>()
|
||||
.HasIndex(t => new { t.FKClientId, t.TagCode })
|
||||
.IsUnique();
|
||||
|
||||
// AdHoc bookings share a facility's NumberOfUnits, checked by counting overlapping
|
||||
// non-cancelled/non-completed bookings at creation/reschedule time (see
|
||||
// BookingRepository.GetOverlappingAdhocBookingCountAsync) rather than a DB
|
||||
// constraint -- an exclusion constraint over a time range isn't expressible as a
|
||||
// simple unique index, and MySQL/Pomelo has no range-overlap index type. This index
|
||||
// just makes that overlap query efficient.
|
||||
modelBuilder.Entity<Booking>()
|
||||
.HasIndex(b => new { b.FKFacilityId, b.StartTime, b.EndTime });
|
||||
|
||||
modelBuilder.Entity<UnlinkedDeviceEnrollment>()
|
||||
.HasIndex(e => new { e.FKDeviceId, e.DeviceEmployeeNo, e.LinkedAt });
|
||||
|
||||
Reference in New Issue
Block a user