Files
api/services/nginx/app/classes/redis.php
T
Jeppe Bundgaard 0399cb4bb4 Implement economic config round-trip test and enhance department handling
- Added a test to ensure correct round-tripping of default distribution department config value through economic config updates.
- Improved department handling by adding fallback logic to use the default economic distribution department id when a customer's department id is missing.
- Enhanced weather API routes to fetch, cache, and return detailed employee contributions per department for a given time slot.
2026-05-13 17:37:49 +02:00

698 lines
18 KiB
PHP

<?php
namespace classes;
use interfaces\redis_i;
class redis implements redis_i
{
use \traits\redis_t;
public function __construct()
{
global $REDIS_CONFIG;
// Make sure the Redis configuration is set
if (!isset($REDIS_CONFIG)) {
throw new \Exception('Redis configuration is not set');
}
// Apply the Redis configuration
$this->redis_host = $REDIS_CONFIG['host'];
$this->redis_user = $REDIS_CONFIG['user'] ?? '';
$this->redis_database = $REDIS_CONFIG['database'];
$this->redis_password = $REDIS_CONFIG['password'];
if (isset($REDIS_CONFIG['port']) && is_numeric($REDIS_CONFIG['port'])) {
$this->redis_port = (int)$REDIS_CONFIG['port'];
}
}
/**
* @inheritDoc
*/
public function cache_department_booking_count(int $department_id, int $count): redis_i
{
// Cache the department booking count
$this->set('department_booking_count_' . $department_id, $count);
return $this;
}
/**
* @inheritDoc
*/
public function get_department_booking_count(int $department_id): int|null
{
// Get the department booking count
return $this->get('department_booking_count_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department_booking_count(int $department_id): redis_i
{
// Clear the department booking count
$this->delete('department_booking_count_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department_webhook(int $department_id, string $webhook): self
{
// Cache the department webhook
$this->set('department_webhook_' . $department_id, $webhook);
$this->expire('department_webhook_' . $department_id, 300); // Expire in 5 minutes
return $this;
}
/**
* @inheritDoc
*/
public function get_department_webhook(int $department_id): string|null
{
// Get the department webhook
return $this->get('department_webhook_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department_webhook(int $department_id): self
{
// Clear the department webhook
$this->delete('department_webhook_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department_name(int $department_id, string $department_name): self
{
// Cache the department name (If it is not empty or null)
if (empty($department_name)) {
$department_name = 'IS_EMPTY_OR_NULL';
return $this;
}
// Set the department's name in the cache
if ($this->get_department_name($department_id) === null) {
$this->set('department_' . $department_id, json_encode(['name' => $department_name]
));
};
return $this;
}
/**
* @inheritDoc
*/
public function get_department_name(int $department_id): string|null
{
// Get the department name
$tmp_department = $this->get('department_' . $department_id) ?? null;
if ($tmp_department === 'IS_EMPTY_OR_NULL' || $tmp_department === null) {
return null;
}
$tmp_department = json_decode($tmp_department);
// Return the department name
return $tmp_department->name ?: $tmp_department['name'] ?? null;
}
/**
* @inheritDoc
*/
public function clear_department_name(int $department_id): self
{
// Clear the department name
$this->delete('department_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_economic_customer_discount_percentage(int $user_id, int $discount_percentage): self
{
// Cache the economic customer discount percentage
$this->set('users_' . $user_id . '_economic_customer_discount_percentage', $discount_percentage);
return $this;
}
/**
* @inheritDoc
*/
public function get_economic_customer_discount_percentage(int $user_id): int|null
{
// Get the economic customer discount percentage
return $this->get('users_' . $user_id . '_economic_customer_discount_percentage');
}
/**
* @inheritDoc
*/
public function clear_economic_customer_discount_percentage(int $user_id): self
{
// Clear the economic customer discount percentage
$this->delete('users_' . $user_id . '_economic_customer_discount_percentage');
return $this;
}
/**
* @inheritDoc
*/
public function cache_customer_notes(int $user_id, array $customer_notes): self
{
// Cache the customer notes
$this->set_array('users_' . $user_id . '_customer_notes', $customer_notes);
return $this;
}
/**
* @inheritDoc
*/
public function get_customer_notes(int $user_id): array|null
{
// Get the customer notes
return $this->get_array('users_' . $user_id . '_customer_notes');
}
/**
* @inheritDoc
*/
public function clear_customer_notes(int $user_id): self
{
// Clear the customer notes
$this->delete('users_' . $user_id . '_customer_notes');
return $this;
}
/**
* @inheritDoc
*/
public function add_log(string $module, string $department, int $type, int $user_id, string $action, string $message): redis_i
{
// Get the current timestamp
$timestamp = strtotime('now');
// Get unique id for the log
$log_id = uniqid();
// Add the log to the cache
$this->set_array('log_' . $log_id, [
'id' => $log_id,
'module' => $module,
'department' => $department,
'type' => $type,
'user_id' => $user_id,
'action' => $action,
'message' => $message,
'timestamp' => $timestamp
]);
return $this;
}
/**
* @inheritDoc
*/
public function get_logs(): array|null
{
// Get all logs from the cache
return $this->get_arrays('log_*');
}
/**
* @inheritDoc
*/
public function clear_logs(): self
{
// Clear all logs from the cache
$keys = $this->get_keys('log_*');
foreach ( $keys as $key ) {
$this->delete($key);
}
return $this;
}
/**
* @inheritDoc
*/
public function get_department(int $department_id): array|null
{
// Get the department
return $this->get_array('department_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department(int $department_id): self
{
// Clear the departments
$this->clear_departments();
return $this;
}
/**
* @inheritDoc
*/
public function clear_departments(): self
{
// Clear all departments from the cache
$this->delete('departments');
$keys = $this->get_keys('department_*');
foreach ( $keys as $key ) {
$this->delete($key);
}
return $this;
}
/**
* @inheritDoc
*/
public function get_departments(): array|null
{
// Get all departments from the cache
return $this->get_array('departments');
}
/**
* @inheritDoc
*/
public function cache_departments(array $departments): self
{
// Cache the departments
foreach ( $departments as $department ) {
$this->cache_department($department['id'], $department);
}
redis->set_array('departments', $departments);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department(int $department_id, array $department): self
{
// Cache the department
$this->set_array('department_' . $department_id, $department);
return $this;
}
/**
* @inheritDoc
*/
public function get_log_count(): int
{
// Get the log count
return $this->count_keys('log_*');
}
/**
* @inheritDoc
*/
public function cache_token(string $token, array $row): self
{
// Cache the token
$this->set_array('token_' . $token, $row);
return $this;
}
/**
* @inheritDoc
*/
public function get_token(string $token): array|null
{
// Get the token
return $this->get_array('token_' . $token);
}
/**
* @inheritDoc
*/
public function clear_token(string $token): self
{
// Clear the token
$this->delete('token_' . $token);
return $this;
}
/**
* Cache auth session payload for a token with TTL
*/
public function cache_auth_session(string $token, array $data, int $ttl = 60): self
{
$key = 'auth_session_' . $token;
$this->set_array($key, $data);
$this->expire($key, $ttl);
return $this;
}
/**
* Get cached auth session payload by token
*/
public function get_auth_session(string $token): array|null
{
return $this->get_array('auth_session_' . $token);
}
/**
* Clear cached auth session payload by token
*/
public function clear_auth_session(string $token): self
{
$this->delete('auth_session_' . $token);
return $this;
}
/**
* @inheritDoc
*/
public function cache_invoice_period_manual_flags(array $flags): self
{
$this->set_array('invoice_period_manual_flags', $flags);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_manual_flags(): array|null
{
return $this->get_array('invoice_period_manual_flags');
}
/**
* @inheritDoc
*/
public function clear_invoice_period_manual_flags(): self
{
$this->delete('invoice_period_manual_flags');
return $this;
}
private function invoicePeriodCacheKey(string $prefix, string $dateFrom, string $dateTo): string
{
return $prefix . ':' . $dateFrom . ':' . $dateTo;
}
private function workfeedEmployeeNameCacheKey(string $employeeId): string
{
return 'workfeed_employee_name:' . rawurlencode($employeeId);
}
/**
* @inheritDoc
*/
public function cache_invoice_period_automatic_flags(string $dateFrom, string $dateTo, array $flags): self
{
$this->set_array($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo), $flags);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_automatic_flags(string $dateFrom, string $dateTo): array|null
{
return $this->get_array($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo));
}
/**
* @inheritDoc
*/
public function clear_invoice_period_automatic_flags(string $dateFrom, string $dateTo): self
{
$this->delete($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo));
return $this;
}
/**
* @inheritDoc
*/
public function cache_invoice_period_order_item_rows(string $dateFrom, string $dateTo, array $rows): self
{
$this->set_array($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo), $rows);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_order_item_rows(string $dateFrom, string $dateTo): array|null
{
return $this->get_array($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo));
}
/**
* @inheritDoc
*/
public function clear_invoice_period_order_item_rows(string $dateFrom, string $dateTo): self
{
$this->delete($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo));
return $this;
}
/**
* @inheritDoc
*/
public function cache_workfeed_employee_name(string $employeeId, string $employeeName, int $ttl = 86400): self
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return $this;
}
$normalizedEmployeeName = trim($employeeName);
if ($normalizedEmployeeName === '') {
return $this;
}
$key = $this->workfeedEmployeeNameCacheKey($normalizedEmployeeId);
$this->set($key, $normalizedEmployeeName);
$this->expire($key, $ttl);
return $this;
}
/**
* @inheritDoc
*/
public function get_workfeed_employee_name(string $employeeId): string|null
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return null;
}
$value = $this->get($this->workfeedEmployeeNameCacheKey($normalizedEmployeeId));
if ($value === null) {
return null;
}
$normalized = trim((string)$value);
return $normalized !== '' ? $normalized : null;
}
/**
* @inheritDoc
*/
public function clear_workfeed_employee_name(string $employeeId): self
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return $this;
}
$this->delete($this->workfeedEmployeeNameCacheKey($normalizedEmployeeId));
return $this;
}
/**
* @inheritDoc
*/
public function enqueue_invoice_period_warming(string $dateFrom, string $dateTo): self
{
$this->get_client()->sadd('invoice_period_warming_queue', [$dateFrom . '|' . $dateTo]);
return $this;
}
/**
* @inheritDoc
*/
public function consume_invoice_period_warming_queue(): array
{
$client = $this->get_client();
$members = $client->smembers('invoice_period_warming_queue');
if (!empty($members)) {
$client->del('invoice_period_warming_queue');
}
$periods = [];
foreach ($members as $member) {
$parts = explode('|', (string)$member, 2);
if (count($parts) === 2 && $parts[0] !== '' && $parts[1] !== '') {
$periods[] = ['dateFrom' => $parts[0], 'dateTo' => $parts[1]];
}
}
return $periods;
}
/**
* @inheritDoc
*/
public function get_last_crond_run(string $task): int|null
{
// Get the last cron task
return $this->get('last_cron_task_' . $task) ?? null;
}
/**
* @inheritDoc
*/
public function set_last_crond_run(string $task, int $time): self
{
// Set the last cron task
$this->set('last_cron_task_' . $task, $time);
return $this;
}
/**
* @inheritDoc
*/
public function cache_user_id_from_customer_number(int $customer_number, int $user_id): self
{
// Cache the user id from customer number
$this->set('user_id_from_customer_number_' . $customer_number, $user_id);
return $this;
}
/**
* @inheritDoc
*/
public function get_user_id_from_customer_number(int $customer_number): int|null
{
// Get the user id from customer number
return $this->get('user_id_from_customer_number_' . $customer_number);
}
/**
* @inheritDoc
*/
public function clear_user_id_from_customer_number(int $customer_number): self
{
// Clear the user id from customer number
$this->delete('user_id_from_customer_number_' . $customer_number);
return $this;
}
/**
* @inheritDoc
*/
public function cache_customer_number_from_user_id(int $user_id, int $customer_number): self
{
// Cache the customer number from user id
$this->set('customer_number_from_user_id_' . $user_id, $customer_number);
return $this;
}
/**
* @inheritDoc
*/
public function get_customer_number_from_user_id(int $user_id): int|null
{
// Get the customer number from user id
return $this->get('customer_number_from_user_id_' . $user_id);
}
/**
* @inheritDoc
*/
public function clear_customer_number_from_user_id(int $user_id): self
{
// Clear the customer number from user id
$this->delete('customer_number_from_user_id_' . $user_id);
return $this;
}
public function expire(string $key, int $seconds): self
{
// Set the expiration time for a key
$this->redis->expire($key, $seconds);
return $this;
}
public function generateTemporaryCacheKey(): string
{
// Generate a temporary cache key
return 'temporary_cache_' . uniqid();
}
/**
* Atomically set a key with TTL only when it does not already exist.
*/
public function set_if_absent_with_expiration(string $key, string $value, int $seconds): bool
{
if (!self::is_connected()) {
self::connect();
}
$seconds = max(1, $seconds);
$result = $this->redis->set($key, $value, 'EX', $seconds, 'NX');
return $result === true || strtoupper((string)$result) === 'OK';
}
public function mget(array $array_map): array
{
if (empty($array_map)) {
return [];
}
// Get multiple keys from Redis
return $this->redis->mget($array_map);
}
/**
* @inheritDoc
*/
public function cache_permission(string $cache_key, bool $allowed, int $ttl = 300): self
{
$this->setEx('perm:' . $cache_key, $allowed ? '1' : '0', $ttl);
return $this;
}
/**
* @inheritDoc
*/
public function get_permission(string $cache_key): bool|null
{
$val = $this->get('perm:' . $cache_key);
if ($val === null) {
return null;
}
return $val === '1';
}
/**
* @inheritDoc
*/
public function clear_permission(string $cache_key): self
{
$this->delete('perm:' . $cache_key);
return $this;
}
/**
* @throws \Exception
*/
public function ping(): bool
{
if (!self::is_connected()) {
self::connect();
}
if (!self::is_connected()) {
return false;
}
return true;
}
}