Assist_Design/apps/bff/src/common/cache/cache.service.ts
T. Narantuya f305ee6e1a Implement Salesforce Platform Events for Order Provisioning
- Added support for Salesforce Platform Events, specifically subscribing to `OrderProvisionRequested__e` to trigger provisioning jobs.
- Introduced new environment variables for Salesforce event configuration, including SF_EVENTS_ENABLED, SF_PROVISION_EVENT_CHANNEL, and SF_PUBSUB_ENDPOINT.
- Refactored order fulfillment process to utilize event-driven architecture, enhancing reliability and scalability.
- Updated documentation to reflect changes in the provisioning workflow and environment variable requirements.
- Removed deprecated webhook handling code to streamline the integration.
2025-09-06 10:01:44 +09:00

80 lines
2.1 KiB
TypeScript

import { Inject, Injectable } from "@nestjs/common";
import Redis from "ioredis";
import { Logger } from "nestjs-pino";
@Injectable()
export class CacheService {
constructor(
@Inject("REDIS_CLIENT") private readonly redis: Redis,
@Inject(Logger) private readonly logger: Logger
) {}
async get<T>(key: string): Promise<T | null> {
const value = await this.redis.get(key);
return value ? (JSON.parse(value) as T) : null;
}
async set(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
const serialized = JSON.stringify(value);
if (ttlSeconds) {
await this.redis.setex(key, ttlSeconds, serialized);
} else {
await this.redis.set(key, serialized);
}
}
async del(key: string): Promise<void> {
await this.redis.del(key);
}
async delPattern(pattern: string): Promise<void> {
// Use SCAN to avoid blocking Redis for large keyspaces
let cursor = "0";
const batch: string[] = [];
const flush = async () => {
if (batch.length > 0) {
// Use pipeline to delete in bulk
const pipeline = this.redis.pipeline();
for (const k of batch.splice(0, batch.length)) pipeline.del(k);
await pipeline.exec();
}
};
do {
const [next, keys] = (await this.redis.scan(
cursor,
"MATCH",
pattern,
"COUNT",
1000
)) as unknown as [string, string[]];
cursor = next;
if (keys && keys.length) {
batch.push(...keys);
if (batch.length >= 1000) {
await flush();
}
}
} while (cursor !== "0");
await flush();
}
async exists(key: string): Promise<boolean> {
return (await this.redis.exists(key)) === 1;
}
buildKey(prefix: string, userId: string, ...parts: string[]): string {
return [prefix, userId, ...parts].join(":");
}
async getOrSet<T>(key: string, fetcher: () => Promise<T>, ttlSeconds: number = 300): Promise<T> {
const cached = await this.get<T>(key);
if (cached !== null) {
return cached;
}
const fresh = await fetcher();
await this.set(key, fresh, ttlSeconds);
return fresh;
}
}