- Add queryTimed() wrapper and logPerf() for per-endpoint timing metrics - Add api_perf_log table flush mechanism with background thread batching - Add application-scope cache (appCacheGet/Put/Invalidate) with TTL - Cache businesses/get (5m), addresses/states (24h), menu/items (2m) - Fix N+1 queries in orders/history, orders/listForKDS (batch fetch) - Fix correlated subquery in orders/getDetail (LEFT JOIN) - Combine 4 queries into 1 in portal/stats (subselects) - Optimize getForBuilder tree building with pre-indexed parent lookup - Add cache invalidation in update, saveBrandColor, updateHours, saveFromBuilder - New admin/perf.cfm dashboard (localhost-protected) - Instrument top 10 endpoints with queryTimed + logPerf Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
78 lines
2.2 KiB
Text
78 lines
2.2 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
|
|
<cfscript>
|
|
/**
|
|
* Update Business Hours
|
|
*
|
|
* POST JSON:
|
|
* {
|
|
* "BusinessID": 37,
|
|
* "Hours": [
|
|
* { "dayId": 1, "open": "09:00", "close": "17:00" },
|
|
* { "dayId": 2, "open": "09:00", "close": "17:00" },
|
|
* ...
|
|
* ]
|
|
* }
|
|
*
|
|
* Days not in the Hours array are considered closed.
|
|
*/
|
|
|
|
response = { "OK": false };
|
|
|
|
try {
|
|
requestBody = toString(getHttpRequestData().content);
|
|
if (!len(requestBody)) {
|
|
throw(message="No request body provided");
|
|
}
|
|
|
|
data = deserializeJSON(requestBody);
|
|
|
|
businessId = structKeyExists(data, "BusinessID") ? val(data.BusinessID) : 0;
|
|
if (businessId == 0) {
|
|
throw(message="BusinessID is required");
|
|
}
|
|
|
|
hours = structKeyExists(data, "Hours") && isArray(data.Hours) ? data.Hours : [];
|
|
|
|
// Delete all existing hours for this business
|
|
queryExecute("
|
|
DELETE FROM Hours WHERE HoursBusinessID = :bizID
|
|
", { bizID: businessId }, { datasource: "payfrit" });
|
|
|
|
// Insert new hours
|
|
for (h in hours) {
|
|
if (!isStruct(h)) continue;
|
|
|
|
dayId = structKeyExists(h, "dayId") ? val(h.dayId) : 0;
|
|
openTime = structKeyExists(h, "open") && isSimpleValue(h.open) ? h.open : "09:00";
|
|
closeTime = structKeyExists(h, "close") && isSimpleValue(h.close) ? h.close : "17:00";
|
|
|
|
if (dayId >= 1 && dayId <= 7) {
|
|
// Convert HH:MM to HH:MM:SS if needed
|
|
if (len(openTime) == 5) openTime = openTime & ":00";
|
|
if (len(closeTime) == 5) closeTime = closeTime & ":00";
|
|
|
|
queryExecute("
|
|
INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime)
|
|
VALUES (:bizID, :dayID, :openTime, :closeTime)
|
|
", {
|
|
bizID: businessId,
|
|
dayID: dayId,
|
|
openTime: openTime,
|
|
closeTime: closeTime
|
|
}, { datasource: "payfrit" });
|
|
}
|
|
}
|
|
|
|
response.OK = true;
|
|
response.hoursUpdated = arrayLen(hours);
|
|
appCacheInvalidate("biz_" & businessId);
|
|
|
|
} catch (any e) {
|
|
response.ERROR = e.message;
|
|
}
|
|
|
|
writeOutput(serializeJSON(response));
|
|
</cfscript>
|