f3220ee191
<UserStory> 1927: Implement Service Refactorings </UserStory> UserStory #1927 <Changes> 1. Added DataStoreLockGuard integration across ServiceManagementService methods for thread-safe datastore operations. 2. Updated DataStore::getServices(), getComboPackages(), getServiceBookings(), and getJobCards() to enrich records with linked entities (inventory items, services, bookings, technicians). 3. Modified ServiceManagementService::purchaseService() and purchaseComboPackage() to use tracked records and persist bookings safely. 4. Enhanced restoreInventory() and processBookingCancellation() to handle tracked records, update record states, and restore inventory items correctly. 5. Refactored cancelCustomerServiceBookings() and cancelTechnicianJobs() to use tracked records, restore inventory, and persist changes. 6. Updated createComboPackage(), removeComboPackage(), and createJobCard() to use tracked records and save changes to datastore. 7. Updated createService() to validate inventory items with tracked records and persist new services safely. 8. Added required header dependencies for DataStoreLockGuard and shared memory support. </Changes> <Test> N/A </Test> <Review> Sreeja Reghukumar, please review </Review>
238 lines
9.1 KiB
C++
238 lines
9.1 KiB
C++
/*
|
|
File: DataStore.h
|
|
Description: Declares the DataStore singleton class responsible for managing collections of users, services, combo packages, service bookings, job cards, inventory items, invoices, and payments in the Vehicle Service Management System.
|
|
Author: Trenser
|
|
Date: 19-May-2026
|
|
*/
|
|
|
|
#pragma once
|
|
#include <windows.h>
|
|
#include <string>
|
|
#include "Map.h"
|
|
#include "MappingInfo.h"
|
|
#include "TrackedRecord.h"
|
|
#include "SerializedRecords.h"
|
|
#include "SharedMemory.h"
|
|
class User;
|
|
class Notification;
|
|
class Service;
|
|
class ComboPackage;
|
|
class InventoryItem;
|
|
class ServiceBooking;
|
|
class JobCard;
|
|
class Invoice;
|
|
|
|
class DataStore
|
|
{
|
|
private:
|
|
DataStore();
|
|
~DataStore();
|
|
DataStore(const DataStore&) = delete;
|
|
DataStore& operator=(const DataStore&) = delete;
|
|
DataStore(DataStore&&) = delete;
|
|
DataStore& operator=(DataStore&&) = delete;
|
|
HANDLE m_globalMutex;
|
|
MappingInfo m_users;
|
|
MappingInfo m_notifications;
|
|
MappingInfo m_services;
|
|
MappingInfo m_comboPackages;
|
|
MappingInfo m_inventoryItems;
|
|
MappingInfo m_serviceBookings;
|
|
MappingInfo m_jobCards;
|
|
MappingInfo m_invoices;
|
|
MappingInfo m_payments;
|
|
MappingInfo m_serviceManagementObservers;
|
|
MappingInfo m_paymentManagementObservers;
|
|
MappingInfo m_inventoryManagementObservers;
|
|
util::Map<std::string, TrackedRecord<User>> m_userCache;
|
|
util::Map<std::string, TrackedRecord<Notification>> m_notificationCache;
|
|
util::Map<std::string, TrackedRecord<Service>> m_serviceCache;
|
|
util::Map<std::string, TrackedRecord<ComboPackage>> m_comboPackageCache;
|
|
util::Map<std::string, TrackedRecord<InventoryItem>> m_inventoryItemCache;
|
|
util::Map<std::string, TrackedRecord<ServiceBooking>> m_serviceBookingCache;
|
|
util::Map<std::string, TrackedRecord<JobCard>> m_jobCardCache;
|
|
util::Map<std::string, TrackedRecord<Invoice>> m_invoiceCache;
|
|
public:
|
|
static DataStore& getInstance();
|
|
bool initialize();
|
|
void shutdown();
|
|
util::Map<std::string, TrackedRecord<User>>& getUsers();
|
|
util::Map<std::string, TrackedRecord<Notification>>& getNotifications();
|
|
util::Map<std::string, TrackedRecord<Service>>& getServices();
|
|
util::Map<std::string, TrackedRecord<ComboPackage>>& getComboPackages();
|
|
util::Map<std::string, TrackedRecord<InventoryItem>>& getInventoryItems();
|
|
util::Map<std::string, TrackedRecord<ServiceBooking>>& getServiceBookings();
|
|
util::Map<std::string, TrackedRecord<JobCard>>& getJobCards();
|
|
util::Map<std::string, TrackedRecord<Invoice>>& getInvoices();
|
|
util::Map<std::string, User*> getServiceManagementObservers();
|
|
util::Map<std::string, User*> getPaymentManagementObservers();
|
|
util::Map<std::string, User*> getInventoryManagementObservers();
|
|
void saveUsers();
|
|
void saveNotifications();
|
|
void saveServices();
|
|
void saveComboPackages();
|
|
void saveInventoryItems();
|
|
void saveServiceBookings();
|
|
void saveJobCards();
|
|
void saveInvoices();
|
|
void saveServiceManagementObservers(util::Map<std::string, User*>& observers);
|
|
void savePaymentManagementObservers(util::Map<std::string, User*>& observers);
|
|
void saveInventoryManagementObservers(util::Map<std::string, User*>& observers);
|
|
bool lockDataStore();
|
|
bool unlockDataStore();
|
|
private:
|
|
template<typename TObject, typename TSerialized>
|
|
util::Map<std::string, TrackedRecord<TObject>> loadRecords(MappingInfo& mapping);
|
|
template<typename TObject, typename TSerialized>
|
|
void saveRecords(MappingInfo& mapping, util::Map<std::string, TrackedRecord<TObject>>& records);
|
|
template<typename TObject> void clearCache(util::Map<std::string, TrackedRecord<TObject>>&cache);
|
|
template<typename TObject> void refreshCache(util::Map<std::string, TrackedRecord<TObject>>&cache, util::Map<std::string, TrackedRecord<TObject>>&refreshedCache);
|
|
};
|
|
|
|
/*
|
|
Function: loadRecords
|
|
Description: Loads all serialized records from the specified
|
|
shared-memory mapping, deserializes them into
|
|
application objects, and returns them as a map
|
|
of tracked records. Each loaded record is marked
|
|
as CLEAN and assigned its corresponding slot index
|
|
within the mapping so that future modifications
|
|
can be written back efficiently.
|
|
Parameter:
|
|
- mapping: Reference to the mapping containing the
|
|
serialized records.
|
|
Return type:
|
|
util::Map<std::string, TrackedRecord<TObject>>
|
|
A map containing all loaded records keyed by
|
|
their unique identifier.
|
|
*/
|
|
template<typename TObject, typename TSerialized>
|
|
util::Map<std::string, TrackedRecord<TObject>> DataStore::loadRecords(MappingInfo& mapping)
|
|
{
|
|
util::Map<std::string, TrackedRecord<TObject>> records;
|
|
SharedMemory::ensureLatestMapping(mapping);
|
|
size_t recordCount = SharedMemory::getRecordCount(mapping);
|
|
for (size_t index = 0; index < recordCount; ++index)
|
|
{
|
|
TSerialized* serialized = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping,index));
|
|
TObject* object = TObject::deserialize(*serialized);
|
|
TrackedRecord<TObject> trackedRecord;
|
|
trackedRecord.data = object;
|
|
trackedRecord.state = RecordState::CLEAN;
|
|
trackedRecord.slotIndex = index;
|
|
records.insert(object->getId(), trackedRecord);
|
|
}
|
|
return records;
|
|
}
|
|
|
|
/*
|
|
Function: saveRecords
|
|
Description: Persists all modified and newly added records
|
|
contained in the provided map to the specified
|
|
shared-memory mapping. Modified records overwrite
|
|
their existing slots, while new records are
|
|
appended to the end of the mapping. Records marked
|
|
as CLEAN are ignored.
|
|
Parameter:
|
|
- mapping: Reference to the mapping where records are
|
|
stored.
|
|
- records: Map containing the records to be persisted.
|
|
Return type: void
|
|
*/
|
|
template<typename TObject, typename TSerialized> void DataStore::saveRecords(MappingInfo& mapping, util::Map<std::string, TrackedRecord<TObject>>& records)
|
|
{
|
|
SharedMemory::ensureLatestMapping(mapping);
|
|
for (int index = 0; index < records.getSize(); ++index)
|
|
{
|
|
TrackedRecord<TObject>& record = records.getValueAt(index);
|
|
if (record.state == RecordState::CLEAN)
|
|
{
|
|
continue;
|
|
}
|
|
TSerialized serialized = record.data->serialize();
|
|
if (record.state == RecordState::MODIFIED)
|
|
{
|
|
TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping, record.slotIndex));
|
|
if (destination)
|
|
{
|
|
*destination = serialized;
|
|
}
|
|
}
|
|
else if (record.state == RecordState::NEW_RECORD)
|
|
{
|
|
if (!SharedMemory::ensureCapacityForInsert(mapping))
|
|
{
|
|
continue;
|
|
}
|
|
size_t recordCount = SharedMemory::getRecordCount(mapping);
|
|
TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping,recordCount));
|
|
*destination = serialized;
|
|
SharedMemory::setRecordCount(mapping, recordCount + 1);
|
|
record.slotIndex = recordCount;
|
|
}
|
|
record.state = RecordState::CLEAN;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: clearCache
|
|
Description: Releases all objects owned by the cache and
|
|
clears the cache contents.
|
|
Parameters:
|
|
- cache: Cache to be cleared.
|
|
Returns:
|
|
- None
|
|
*/
|
|
template<typename TObject>
|
|
void DataStore::clearCache(util::Map<std::string, TrackedRecord<TObject>>&cache)
|
|
{
|
|
for (int index = 0; index < cache.getSize(); ++index)
|
|
{
|
|
delete cache.getValueAt(index).data;
|
|
cache.getValueAt(index).data = nullptr;
|
|
}
|
|
cache.clear();
|
|
}
|
|
|
|
/*
|
|
Function: refreshCache
|
|
Description: Refreshes the cache while preserving object addresses
|
|
for records that already exist. Existing objects are
|
|
updated in-place so that pointers held elsewhere remain
|
|
valid after the refresh.
|
|
Parameters:
|
|
- cache: Existing cache to refresh.
|
|
- refreshedCache: Newly loaded cache contents.
|
|
Returns:
|
|
- None
|
|
*/
|
|
template<typename TObject>
|
|
void DataStore::refreshCache(util::Map<std::string, TrackedRecord<TObject>>& cache, util::Map<std::string, TrackedRecord<TObject>>& refreshedCache)
|
|
{
|
|
util::Map<std::string, TrackedRecord<TObject>> oldCache = cache;
|
|
cache.clear();
|
|
for (int index = 0; index < refreshedCache.getSize(); ++index)
|
|
{
|
|
const std::string& id = refreshedCache.getKeyAt(index);
|
|
TrackedRecord<TObject>& refreshedRecord = refreshedCache.getValueAt(index);
|
|
int oldIndex = oldCache.find(id);
|
|
if (oldIndex != -1)
|
|
{
|
|
TrackedRecord<TObject>& oldRecord = oldCache.getValueAt(oldIndex);
|
|
*oldRecord.data = *refreshedRecord.data;
|
|
oldRecord.slotIndex = refreshedRecord.slotIndex;
|
|
oldRecord.state = refreshedRecord.state;
|
|
delete refreshedRecord.data;
|
|
refreshedRecord.data = oldRecord.data;
|
|
}
|
|
cache.insert(id, refreshedRecord);
|
|
}
|
|
for (int index = 0; index < oldCache.getSize(); ++index)
|
|
{
|
|
const std::string& id = oldCache.getKeyAt(index);
|
|
if (cache.find(id) == -1)
|
|
{
|
|
delete oldCache.getValueAt(index).data;
|
|
}
|
|
}
|
|
} |