@jayfong/x-server 2.110.0 → 2.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,24 +4,46 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  exports.__esModule = true;
5
5
  exports.CacheService = void 0;
6
6
  var _cuid = _interopRequireDefault(require("cuid"));
7
+ var _superjson = _interopRequireDefault(require("superjson"));
7
8
  var _vtils = require("vtils");
8
9
  var _x = require("../x");
10
+ const nativeJsonSerializer = {
11
+ stringify: JSON.stringify,
12
+ parse: JSON.parse
13
+ };
14
+ const superJsonSerializer = {
15
+ stringify: _superjson.default.stringify,
16
+ parse: _superjson.default.parse
17
+ };
9
18
  class CacheService {
10
19
  constructor(options) {
11
20
  this.options = options;
12
21
  this.serviceName = 'cache';
13
22
  this.prefix = void 0;
23
+ this.hasMemoryCache = false;
24
+ this.hasMemoryCacheOnly = false;
14
25
  this.memoryCache = void 0;
26
+ this.serializer = void 0;
15
27
  this.prefix = `${_x.x.appId}:`;
16
- this.memoryCache = !options.memory ? undefined : new Map();
28
+ this.hasMemoryCache = !!options.memory;
29
+ this.hasMemoryCacheOnly = options.memory === 'only';
30
+ this.serializer = options.serializer == null || options.serializer === 'nativeJson' ? nativeJsonSerializer : options.serializer === 'superJson' ? superJsonSerializer : options.serializer;
31
+ this.memoryCache = !this.hasMemoryCache ? undefined : new Map();
17
32
  }
18
33
  toRedisKey(key) {
19
34
  return `${this.prefix}${Array.isArray(key) ? key.join('_') : String(key)}`;
20
35
  }
36
+ serialize(value) {
37
+ return this.serializer.stringify(value);
38
+ }
39
+ deserialize(value) {
40
+ return this.serializer.parse(value);
41
+ }
21
42
 
22
43
  /**
23
44
  * 设置缓存内容。对象类内容尽量避免使用,以免造成问题。
24
45
  *
46
+ * @memory 支持
25
47
  * @param key 键
26
48
  * @param value 值
27
49
  * @param ttlOrOptions 缓存时间或选项
@@ -35,11 +57,13 @@ class CacheService {
35
57
  // Redis 的 TTL 不能小于等于 0
36
58
  if (redisTtlMs > 0) {
37
59
  const redisKey = this.toRedisKey(key);
38
- const redisValue = JSON.stringify(value);
39
- await _x.x.redis.set(redisKey, redisValue,
40
- // 毫秒
41
- 'PX', redisTtlMs);
42
- if (this.options.memory) {
60
+ if (!this.hasMemoryCacheOnly) {
61
+ const redisValue = this.serialize(value);
62
+ await _x.x.redis.set(redisKey, redisValue,
63
+ // 毫秒
64
+ 'PX', redisTtlMs);
65
+ }
66
+ if (this.hasMemoryCache) {
43
67
  this.memoryCache.set(redisKey, value);
44
68
  }
45
69
  }
@@ -50,6 +74,7 @@ class CacheService {
50
74
  /**
51
75
  * 存储值然后返回其对应的键,该键 URL 友好。
52
76
  *
77
+ * @memory 支持
53
78
  * @param value 值
54
79
  * @param ttl 存活时间
55
80
  */
@@ -62,20 +87,25 @@ class CacheService {
62
87
  /**
63
88
  * 获取缓存内容。
64
89
  *
90
+ * @memory 支持
65
91
  * @param key 键
66
92
  * @param options 选项
67
93
  * @returns 返回获取到的缓存内容
68
94
  */
69
95
  async get(key) {
70
96
  const redisKey = this.toRedisKey(key);
71
- if (this.options.memory && this.memoryCache.has(redisKey)) {
72
- return this.memoryCache.get(redisKey);
97
+ if (this.hasMemoryCache) {
98
+ if (this.memoryCache.has(redisKey)) {
99
+ return this.memoryCache.get(redisKey);
100
+ } else if (this.hasMemoryCacheOnly) {
101
+ return null;
102
+ }
73
103
  }
74
104
  const gotValue = await _x.x.redis.get(redisKey);
75
105
  if (gotValue) {
76
106
  try {
77
- const parsedValue = JSON.parse(gotValue);
78
- if (this.options.memory) {
107
+ const parsedValue = this.deserialize(gotValue);
108
+ if (this.hasMemoryCache) {
79
109
  this.memoryCache.set(redisKey, parsedValue);
80
110
  }
81
111
  return parsedValue;
@@ -87,17 +117,47 @@ class CacheService {
87
117
  /**
88
118
  * 获取多个缓存内容。
89
119
  *
120
+ * @memory 支持
90
121
  * @param keys 键
91
122
  * @returns 返回获取到的缓存内容
92
123
  */
93
124
  async getMany(...keys) {
94
125
  const redisKeys = keys.map(key => this.toRedisKey(key));
95
126
  if (!redisKeys.length) return [];
127
+ if (this.hasMemoryCacheOnly) {
128
+ return redisKeys.map(redisKey => this.memoryCache.has(redisKey) ? this.memoryCache.get(redisKey) : null);
129
+ }
130
+ if (this.hasMemoryCache) {
131
+ const result = new Array(keys.length).fill(null);
132
+ const missIndexes = [];
133
+ const missRedisKeys = [];
134
+ for (let i = 0; i < redisKeys.length; i++) {
135
+ if (this.memoryCache.has(redisKeys[i])) {
136
+ result[i] = this.memoryCache.get(redisKeys[i]);
137
+ } else {
138
+ missIndexes.push(i);
139
+ missRedisKeys.push(redisKeys[i]);
140
+ }
141
+ }
142
+ if (missRedisKeys.length) {
143
+ const gotValues = await _x.x.redis.mget(...missRedisKeys);
144
+ gotValues.forEach((value, idx) => {
145
+ if (value != null) {
146
+ try {
147
+ const parsed = this.deserialize(value);
148
+ this.memoryCache.set(missRedisKeys[idx], parsed);
149
+ result[missIndexes[idx]] = parsed;
150
+ } catch {}
151
+ }
152
+ });
153
+ }
154
+ return result;
155
+ }
96
156
  const gotValues = await _x.x.redis.mget(...redisKeys);
97
157
  return gotValues.map(value => {
98
158
  if (value == null) return null;
99
159
  try {
100
- return JSON.parse(value);
160
+ return this.deserialize(value);
101
161
  } catch {
102
162
  return null;
103
163
  }
@@ -106,11 +166,16 @@ class CacheService {
106
166
 
107
167
  /**
108
168
  * 获取距离过期时间的毫秒数(未设置过期时间返回-1,键不存在返回-2)。
169
+ * memory=only 模式下内存缓存无 TTL 概念,始终返回-1。
109
170
  *
171
+ * @memory 支持
110
172
  * @param key 键
111
173
  * @returns 返回距离过期时间的毫秒数
112
174
  */
113
175
  async ttl(key) {
176
+ if (this.hasMemoryCacheOnly) {
177
+ return -1;
178
+ }
114
179
  const redisKey = this.toRedisKey(key);
115
180
  return _x.x.redis.pttl(redisKey);
116
181
  }
@@ -118,6 +183,7 @@ class CacheService {
118
183
  /**
119
184
  * 缓存内容。
120
185
  *
186
+ * @memory 支持
121
187
  * @param key 键
122
188
  * @param action 获取缓存内容的操作
123
189
  * @param ttlOrOptions 缓存时间或选项
@@ -153,17 +219,29 @@ class CacheService {
153
219
  /**
154
220
  * 自增。
155
221
  *
222
+ * @memory 支持
156
223
  * @param key 键
157
224
  * @param increment 增量
158
225
  */
159
226
  async increase(key, increment = 1) {
160
227
  const redisKey = this.toRedisKey(key);
161
- return _x.x.redis.incrby(redisKey, increment);
228
+ if (this.hasMemoryCacheOnly) {
229
+ const current = this.memoryCache.get(redisKey) ?? 0;
230
+ const next = current + increment;
231
+ this.memoryCache.set(redisKey, next);
232
+ return next;
233
+ }
234
+ const result = await _x.x.redis.incrby(redisKey, increment);
235
+ if (this.hasMemoryCache) {
236
+ this.memoryCache.delete(redisKey);
237
+ }
238
+ return result;
162
239
  }
163
240
 
164
241
  /**
165
242
  * 自减。
166
243
  *
244
+ * @memory 支持
167
245
  * @param key 键
168
246
  * @param decrement 减量
169
247
  */
@@ -174,37 +252,63 @@ class CacheService {
174
252
  /**
175
253
  * 新增元素。
176
254
  *
255
+ * @memory 支持
177
256
  * @param key 键
178
257
  * @param values 元素列表
179
258
  */
180
259
  async push(key, ...values) {
181
260
  const redisKey = this.toRedisKey(key);
182
- return _x.x.redis.rpush(redisKey, ...values.map(value => JSON.stringify(value)));
261
+ if (this.hasMemoryCacheOnly) {
262
+ const arr = this.memoryCache.get(redisKey) ?? [];
263
+ arr.push(...values);
264
+ this.memoryCache.set(redisKey, arr);
265
+ return arr.length;
266
+ }
267
+ const result = await _x.x.redis.rpush(redisKey, ...values.map(value => this.serialize(value)));
268
+ if (this.hasMemoryCache) {
269
+ this.memoryCache.delete(redisKey);
270
+ }
271
+ return result;
183
272
  }
184
273
 
185
274
  /**
186
275
  * 从头部弹出元素并返回。
187
276
  *
277
+ * @memory 支持
188
278
  * @param key 键
189
279
  * @param count 弹出元素个数
190
280
  */
191
281
  async shift(key, count = 1) {
192
282
  const redisKey = this.toRedisKey(key);
283
+ if (this.hasMemoryCacheOnly) {
284
+ const arr = this.memoryCache.get(redisKey) ?? [];
285
+ const res = arr.splice(0, count);
286
+ if (arr.length) {
287
+ this.memoryCache.set(redisKey, arr);
288
+ } else {
289
+ this.memoryCache.delete(redisKey);
290
+ }
291
+ return res;
292
+ }
193
293
  const res = [];
194
294
  for (let i = 0; i < count; i++) {
195
295
  const item = await _x.x.redis.lpop(redisKey);
196
296
  if (item) {
197
- res.push(JSON.parse(item));
297
+ res.push(this.deserialize(item));
198
298
  } else {
199
299
  break;
200
300
  }
201
301
  }
302
+ if (this.hasMemoryCache && res.length) {
303
+ this.memoryCache.delete(redisKey);
304
+ }
202
305
  return res;
203
306
  }
204
307
 
205
308
  /**
206
309
  * 删除缓存。
207
310
  *
311
+ * @memory 支持
208
312
  * @param keys 键列表
209
313
  */
210
314
  async remove(...keys) {
@@ -212,42 +316,73 @@ class CacheService {
212
316
  return 0;
213
317
  }
214
318
  const redisKeys = keys.map(key => this.toRedisKey(key));
215
- const count = await _x.x.redis.del(...redisKeys);
216
- if (this.options.memory) {
319
+ let count = 0;
320
+ if (!this.hasMemoryCacheOnly) {
321
+ count = await _x.x.redis.del(...redisKeys);
322
+ }
323
+ if (this.hasMemoryCache) {
324
+ let deletedCount = 0;
217
325
  redisKeys.forEach(redisKey => {
218
- this.memoryCache.delete(redisKey);
326
+ const isDeleted = this.memoryCache.delete(redisKey);
327
+ if (isDeleted) {
328
+ deletedCount++;
329
+ }
219
330
  });
331
+ if (this.hasMemoryCacheOnly) {
332
+ count = deletedCount;
333
+ }
220
334
  }
221
335
  return count;
222
336
  }
223
337
 
224
338
  /**
225
339
  * 清空全部缓存。
340
+ *
341
+ * @memory 支持
226
342
  */
227
343
  async clearAll() {
228
- const keys = await _x.x.redis.keys(`${this.prefix}*`);
229
- const count = keys.length ? await _x.x.redis.del(...keys) : 0;
230
- if (this.options.memory) {
344
+ let count = 0;
345
+ if (!this.hasMemoryCacheOnly) {
346
+ const keys = await _x.x.redis.keys(`${this.prefix}*`);
347
+ count = keys.length ? await _x.x.redis.del(...keys) : 0;
348
+ }
349
+ if (this.hasMemoryCache) {
350
+ const memoryCacheSize = this.memoryCache.size;
231
351
  this.memoryCache.clear();
352
+ if (this.hasMemoryCacheOnly) {
353
+ count = memoryCacheSize;
354
+ }
232
355
  }
233
356
  return count;
234
357
  }
235
358
 
236
359
  /**
237
360
  * 清空指定前缀的缓存。
361
+ *
362
+ * @memory 支持
238
363
  */
239
364
  async clear(...prefixes) {
240
365
  if (!prefixes.length) {
241
366
  return 0;
242
367
  }
243
- const keys = (await Promise.all(prefixes.map(prefix => _x.x.redis.keys(`${this.toRedisKey(prefix)}*`)))).flat();
244
- const count = keys.length ? await _x.x.redis.del(...keys) : 0;
368
+ let count = 0;
369
+ if (!this.hasMemoryCacheOnly) {
370
+ const keys = (await Promise.all(prefixes.map(prefix => _x.x.redis.keys(`${this.toRedisKey(prefix)}*`)))).flat();
371
+ count = keys.length ? await _x.x.redis.del(...keys) : 0;
372
+ }
245
373
  if (this.options.memory) {
374
+ let deletedCount = 0;
246
375
  this.memoryCache.forEach((_v, k) => {
247
376
  if (prefixes.some(prefix => k.startsWith(this.toRedisKey(prefix)))) {
248
- this.memoryCache.delete(k);
377
+ const isDeleted = this.memoryCache.delete(k);
378
+ if (isDeleted) {
379
+ deletedCount++;
380
+ }
249
381
  }
250
382
  });
383
+ if (this.hasMemoryCacheOnly) {
384
+ count = deletedCount;
385
+ }
251
386
  }
252
387
  return count;
253
388
  }
@@ -67,9 +67,9 @@ class DbValueService {
67
67
  return !value || !(0, _vtils.isPlainObject)(value) ? undefined : JSON.stringify(value);
68
68
  };
69
69
  this.options = options;
70
- this.hasTransformAdd = !!options.transform?.add;
71
- this.hasTransformGet = !!options.transform?.get;
72
- this.hasTransformUpdate = !!options.transform?.update;
70
+ this.hasTransformAdd = !!options?.transform?.add;
71
+ this.hasTransformGet = !!options?.transform?.get;
72
+ this.hasTransformUpdate = !!options?.transform?.update;
73
73
  }
74
74
  };
75
75
  }
@@ -1,11 +1,28 @@
1
1
  import { type MsValue } from 'vtils';
2
2
  import type { AsyncOrSync, IsEmptyArray } from 'vtils/types';
3
3
  import type { BaseService } from './base';
4
+ export interface CacheSerializer {
5
+ stringify(value: unknown): string;
6
+ parse<T>(value: string): T;
7
+ }
4
8
  export interface CacheServiceOptions {
5
9
  /** 默认过期时间 */
6
10
  ttl: MsValue;
7
- /** 启用内存缓存,仅 get, set, remove, remember, clear, clearAll 支持 */
8
- memory?: boolean;
11
+ /**
12
+ * 启用内存缓存
13
+ *
14
+ * - `true` 表示启用内存缓存
15
+ * - `'only'` 表示仅使用内存缓存,不使用 Redis
16
+ */
17
+ memory?: boolean | 'only';
18
+ /**
19
+ * 序列化工具
20
+ *
21
+ * - `'nativeJson'` 使用原生 JSON 进行序列化和反序列化(默认)
22
+ * - `'superJson'` 使用 SuperJson 进行序列化和反序列化,支持更多数据类型(比如Date、RegExp等)但性能较原生 JSON 略差
23
+ * - 也可直接传入自定义 `{ stringify, parse }`
24
+ */
25
+ serializer?: 'nativeJson' | 'superJson' | CacheSerializer;
9
26
  }
10
27
  type Data = {
11
28
  [K in string]: (...args: any[]) => any;
@@ -29,12 +46,18 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
29
46
  options: CacheServiceOptions;
30
47
  serviceName: string;
31
48
  private prefix;
49
+ private hasMemoryCache;
50
+ private hasMemoryCacheOnly;
32
51
  private memoryCache;
52
+ private serializer;
33
53
  constructor(options: CacheServiceOptions);
34
54
  private toRedisKey;
55
+ private serialize;
56
+ private deserialize;
35
57
  /**
36
58
  * 设置缓存内容。对象类内容尽量避免使用,以免造成问题。
37
59
  *
60
+ * @memory 支持
38
61
  * @param key 键
39
62
  * @param value 值
40
63
  * @param ttlOrOptions 缓存时间或选项
@@ -44,6 +67,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
44
67
  /**
45
68
  * 存储值然后返回其对应的键,该键 URL 友好。
46
69
  *
70
+ * @memory 支持
47
71
  * @param value 值
48
72
  * @param ttl 存活时间
49
73
  */
@@ -51,6 +75,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
51
75
  /**
52
76
  * 获取缓存内容。
53
77
  *
78
+ * @memory 支持
54
79
  * @param key 键
55
80
  * @param options 选项
56
81
  * @returns 返回获取到的缓存内容
@@ -59,13 +84,16 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
59
84
  /**
60
85
  * 获取多个缓存内容。
61
86
  *
87
+ * @memory 支持
62
88
  * @param keys 键
63
89
  * @returns 返回获取到的缓存内容
64
90
  */
65
91
  getMany<K extends TKeyPath, V extends Value<TData, K>>(...keys: K[]): Promise<Array<V | null>>;
66
92
  /**
67
93
  * 获取距离过期时间的毫秒数(未设置过期时间返回-1,键不存在返回-2)。
94
+ * memory=only 模式下内存缓存无 TTL 概念,始终返回-1。
68
95
  *
96
+ * @memory 支持
69
97
  * @param key 键
70
98
  * @returns 返回距离过期时间的毫秒数
71
99
  */
@@ -73,6 +101,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
73
101
  /**
74
102
  * 缓存内容。
75
103
  *
104
+ * @memory 支持
76
105
  * @param key 键
77
106
  * @param action 获取缓存内容的操作
78
107
  * @param ttlOrOptions 缓存时间或选项
@@ -83,6 +112,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
83
112
  /**
84
113
  * 自增。
85
114
  *
115
+ * @memory 支持
86
116
  * @param key 键
87
117
  * @param increment 增量
88
118
  */
@@ -90,6 +120,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
90
120
  /**
91
121
  * 自减。
92
122
  *
123
+ * @memory 支持
93
124
  * @param key 键
94
125
  * @param decrement 减量
95
126
  */
@@ -97,6 +128,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
97
128
  /**
98
129
  * 新增元素。
99
130
  *
131
+ * @memory 支持
100
132
  * @param key 键
101
133
  * @param values 元素列表
102
134
  */
@@ -104,6 +136,7 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
104
136
  /**
105
137
  * 从头部弹出元素并返回。
106
138
  *
139
+ * @memory 支持
107
140
  * @param key 键
108
141
  * @param count 弹出元素个数
109
142
  */
@@ -111,15 +144,20 @@ export declare class CacheService<TData extends Data = Data, TKey extends Key<TD
111
144
  /**
112
145
  * 删除缓存。
113
146
  *
147
+ * @memory 支持
114
148
  * @param keys 键列表
115
149
  */
116
150
  remove<K extends TKeyPath>(...keys: K[]): Promise<number>;
117
151
  /**
118
152
  * 清空全部缓存。
153
+ *
154
+ * @memory 支持
119
155
  */
120
156
  clearAll(): Promise<number>;
121
157
  /**
122
158
  * 清空指定前缀的缓存。
159
+ *
160
+ * @memory 支持
123
161
  */
124
162
  clear(...prefixes: Array<TKey | TKeyPartialPath>): Promise<number>;
125
163
  /**
@@ -1,22 +1,44 @@
1
1
  import cuid from 'cuid';
2
+ import superjson from 'superjson';
2
3
  import { ms } from 'vtils';
3
4
  import { x } from "../x";
5
+ const nativeJsonSerializer = {
6
+ stringify: JSON.stringify,
7
+ parse: JSON.parse
8
+ };
9
+ const superJsonSerializer = {
10
+ stringify: superjson.stringify,
11
+ parse: superjson.parse
12
+ };
4
13
  export class CacheService {
5
14
  constructor(options) {
6
15
  this.options = options;
7
16
  this.serviceName = 'cache';
8
17
  this.prefix = void 0;
18
+ this.hasMemoryCache = false;
19
+ this.hasMemoryCacheOnly = false;
9
20
  this.memoryCache = void 0;
21
+ this.serializer = void 0;
10
22
  this.prefix = `${x.appId}:`;
11
- this.memoryCache = !options.memory ? undefined : new Map();
23
+ this.hasMemoryCache = !!options.memory;
24
+ this.hasMemoryCacheOnly = options.memory === 'only';
25
+ this.serializer = options.serializer == null || options.serializer === 'nativeJson' ? nativeJsonSerializer : options.serializer === 'superJson' ? superJsonSerializer : options.serializer;
26
+ this.memoryCache = !this.hasMemoryCache ? undefined : new Map();
12
27
  }
13
28
  toRedisKey(key) {
14
29
  return `${this.prefix}${Array.isArray(key) ? key.join('_') : String(key)}`;
15
30
  }
31
+ serialize(value) {
32
+ return this.serializer.stringify(value);
33
+ }
34
+ deserialize(value) {
35
+ return this.serializer.parse(value);
36
+ }
16
37
 
17
38
  /**
18
39
  * 设置缓存内容。对象类内容尽量避免使用,以免造成问题。
19
40
  *
41
+ * @memory 支持
20
42
  * @param key 键
21
43
  * @param value 值
22
44
  * @param ttlOrOptions 缓存时间或选项
@@ -30,11 +52,13 @@ export class CacheService {
30
52
  // Redis 的 TTL 不能小于等于 0
31
53
  if (redisTtlMs > 0) {
32
54
  const redisKey = this.toRedisKey(key);
33
- const redisValue = JSON.stringify(value);
34
- await x.redis.set(redisKey, redisValue,
35
- // 毫秒
36
- 'PX', redisTtlMs);
37
- if (this.options.memory) {
55
+ if (!this.hasMemoryCacheOnly) {
56
+ const redisValue = this.serialize(value);
57
+ await x.redis.set(redisKey, redisValue,
58
+ // 毫秒
59
+ 'PX', redisTtlMs);
60
+ }
61
+ if (this.hasMemoryCache) {
38
62
  this.memoryCache.set(redisKey, value);
39
63
  }
40
64
  }
@@ -45,6 +69,7 @@ export class CacheService {
45
69
  /**
46
70
  * 存储值然后返回其对应的键,该键 URL 友好。
47
71
  *
72
+ * @memory 支持
48
73
  * @param value 值
49
74
  * @param ttl 存活时间
50
75
  */
@@ -57,20 +82,25 @@ export class CacheService {
57
82
  /**
58
83
  * 获取缓存内容。
59
84
  *
85
+ * @memory 支持
60
86
  * @param key 键
61
87
  * @param options 选项
62
88
  * @returns 返回获取到的缓存内容
63
89
  */
64
90
  async get(key) {
65
91
  const redisKey = this.toRedisKey(key);
66
- if (this.options.memory && this.memoryCache.has(redisKey)) {
67
- return this.memoryCache.get(redisKey);
92
+ if (this.hasMemoryCache) {
93
+ if (this.memoryCache.has(redisKey)) {
94
+ return this.memoryCache.get(redisKey);
95
+ } else if (this.hasMemoryCacheOnly) {
96
+ return null;
97
+ }
68
98
  }
69
99
  const gotValue = await x.redis.get(redisKey);
70
100
  if (gotValue) {
71
101
  try {
72
- const parsedValue = JSON.parse(gotValue);
73
- if (this.options.memory) {
102
+ const parsedValue = this.deserialize(gotValue);
103
+ if (this.hasMemoryCache) {
74
104
  this.memoryCache.set(redisKey, parsedValue);
75
105
  }
76
106
  return parsedValue;
@@ -82,17 +112,47 @@ export class CacheService {
82
112
  /**
83
113
  * 获取多个缓存内容。
84
114
  *
115
+ * @memory 支持
85
116
  * @param keys 键
86
117
  * @returns 返回获取到的缓存内容
87
118
  */
88
119
  async getMany(...keys) {
89
120
  const redisKeys = keys.map(key => this.toRedisKey(key));
90
121
  if (!redisKeys.length) return [];
122
+ if (this.hasMemoryCacheOnly) {
123
+ return redisKeys.map(redisKey => this.memoryCache.has(redisKey) ? this.memoryCache.get(redisKey) : null);
124
+ }
125
+ if (this.hasMemoryCache) {
126
+ const result = new Array(keys.length).fill(null);
127
+ const missIndexes = [];
128
+ const missRedisKeys = [];
129
+ for (let i = 0; i < redisKeys.length; i++) {
130
+ if (this.memoryCache.has(redisKeys[i])) {
131
+ result[i] = this.memoryCache.get(redisKeys[i]);
132
+ } else {
133
+ missIndexes.push(i);
134
+ missRedisKeys.push(redisKeys[i]);
135
+ }
136
+ }
137
+ if (missRedisKeys.length) {
138
+ const gotValues = await x.redis.mget(...missRedisKeys);
139
+ gotValues.forEach((value, idx) => {
140
+ if (value != null) {
141
+ try {
142
+ const parsed = this.deserialize(value);
143
+ this.memoryCache.set(missRedisKeys[idx], parsed);
144
+ result[missIndexes[idx]] = parsed;
145
+ } catch {}
146
+ }
147
+ });
148
+ }
149
+ return result;
150
+ }
91
151
  const gotValues = await x.redis.mget(...redisKeys);
92
152
  return gotValues.map(value => {
93
153
  if (value == null) return null;
94
154
  try {
95
- return JSON.parse(value);
155
+ return this.deserialize(value);
96
156
  } catch {
97
157
  return null;
98
158
  }
@@ -101,11 +161,16 @@ export class CacheService {
101
161
 
102
162
  /**
103
163
  * 获取距离过期时间的毫秒数(未设置过期时间返回-1,键不存在返回-2)。
164
+ * memory=only 模式下内存缓存无 TTL 概念,始终返回-1。
104
165
  *
166
+ * @memory 支持
105
167
  * @param key 键
106
168
  * @returns 返回距离过期时间的毫秒数
107
169
  */
108
170
  async ttl(key) {
171
+ if (this.hasMemoryCacheOnly) {
172
+ return -1;
173
+ }
109
174
  const redisKey = this.toRedisKey(key);
110
175
  return x.redis.pttl(redisKey);
111
176
  }
@@ -113,6 +178,7 @@ export class CacheService {
113
178
  /**
114
179
  * 缓存内容。
115
180
  *
181
+ * @memory 支持
116
182
  * @param key 键
117
183
  * @param action 获取缓存内容的操作
118
184
  * @param ttlOrOptions 缓存时间或选项
@@ -148,17 +214,29 @@ export class CacheService {
148
214
  /**
149
215
  * 自增。
150
216
  *
217
+ * @memory 支持
151
218
  * @param key 键
152
219
  * @param increment 增量
153
220
  */
154
221
  async increase(key, increment = 1) {
155
222
  const redisKey = this.toRedisKey(key);
156
- return x.redis.incrby(redisKey, increment);
223
+ if (this.hasMemoryCacheOnly) {
224
+ const current = this.memoryCache.get(redisKey) ?? 0;
225
+ const next = current + increment;
226
+ this.memoryCache.set(redisKey, next);
227
+ return next;
228
+ }
229
+ const result = await x.redis.incrby(redisKey, increment);
230
+ if (this.hasMemoryCache) {
231
+ this.memoryCache.delete(redisKey);
232
+ }
233
+ return result;
157
234
  }
158
235
 
159
236
  /**
160
237
  * 自减。
161
238
  *
239
+ * @memory 支持
162
240
  * @param key 键
163
241
  * @param decrement 减量
164
242
  */
@@ -169,37 +247,63 @@ export class CacheService {
169
247
  /**
170
248
  * 新增元素。
171
249
  *
250
+ * @memory 支持
172
251
  * @param key 键
173
252
  * @param values 元素列表
174
253
  */
175
254
  async push(key, ...values) {
176
255
  const redisKey = this.toRedisKey(key);
177
- return x.redis.rpush(redisKey, ...values.map(value => JSON.stringify(value)));
256
+ if (this.hasMemoryCacheOnly) {
257
+ const arr = this.memoryCache.get(redisKey) ?? [];
258
+ arr.push(...values);
259
+ this.memoryCache.set(redisKey, arr);
260
+ return arr.length;
261
+ }
262
+ const result = await x.redis.rpush(redisKey, ...values.map(value => this.serialize(value)));
263
+ if (this.hasMemoryCache) {
264
+ this.memoryCache.delete(redisKey);
265
+ }
266
+ return result;
178
267
  }
179
268
 
180
269
  /**
181
270
  * 从头部弹出元素并返回。
182
271
  *
272
+ * @memory 支持
183
273
  * @param key 键
184
274
  * @param count 弹出元素个数
185
275
  */
186
276
  async shift(key, count = 1) {
187
277
  const redisKey = this.toRedisKey(key);
278
+ if (this.hasMemoryCacheOnly) {
279
+ const arr = this.memoryCache.get(redisKey) ?? [];
280
+ const res = arr.splice(0, count);
281
+ if (arr.length) {
282
+ this.memoryCache.set(redisKey, arr);
283
+ } else {
284
+ this.memoryCache.delete(redisKey);
285
+ }
286
+ return res;
287
+ }
188
288
  const res = [];
189
289
  for (let i = 0; i < count; i++) {
190
290
  const item = await x.redis.lpop(redisKey);
191
291
  if (item) {
192
- res.push(JSON.parse(item));
292
+ res.push(this.deserialize(item));
193
293
  } else {
194
294
  break;
195
295
  }
196
296
  }
297
+ if (this.hasMemoryCache && res.length) {
298
+ this.memoryCache.delete(redisKey);
299
+ }
197
300
  return res;
198
301
  }
199
302
 
200
303
  /**
201
304
  * 删除缓存。
202
305
  *
306
+ * @memory 支持
203
307
  * @param keys 键列表
204
308
  */
205
309
  async remove(...keys) {
@@ -207,42 +311,73 @@ export class CacheService {
207
311
  return 0;
208
312
  }
209
313
  const redisKeys = keys.map(key => this.toRedisKey(key));
210
- const count = await x.redis.del(...redisKeys);
211
- if (this.options.memory) {
314
+ let count = 0;
315
+ if (!this.hasMemoryCacheOnly) {
316
+ count = await x.redis.del(...redisKeys);
317
+ }
318
+ if (this.hasMemoryCache) {
319
+ let deletedCount = 0;
212
320
  redisKeys.forEach(redisKey => {
213
- this.memoryCache.delete(redisKey);
321
+ const isDeleted = this.memoryCache.delete(redisKey);
322
+ if (isDeleted) {
323
+ deletedCount++;
324
+ }
214
325
  });
326
+ if (this.hasMemoryCacheOnly) {
327
+ count = deletedCount;
328
+ }
215
329
  }
216
330
  return count;
217
331
  }
218
332
 
219
333
  /**
220
334
  * 清空全部缓存。
335
+ *
336
+ * @memory 支持
221
337
  */
222
338
  async clearAll() {
223
- const keys = await x.redis.keys(`${this.prefix}*`);
224
- const count = keys.length ? await x.redis.del(...keys) : 0;
225
- if (this.options.memory) {
339
+ let count = 0;
340
+ if (!this.hasMemoryCacheOnly) {
341
+ const keys = await x.redis.keys(`${this.prefix}*`);
342
+ count = keys.length ? await x.redis.del(...keys) : 0;
343
+ }
344
+ if (this.hasMemoryCache) {
345
+ const memoryCacheSize = this.memoryCache.size;
226
346
  this.memoryCache.clear();
347
+ if (this.hasMemoryCacheOnly) {
348
+ count = memoryCacheSize;
349
+ }
227
350
  }
228
351
  return count;
229
352
  }
230
353
 
231
354
  /**
232
355
  * 清空指定前缀的缓存。
356
+ *
357
+ * @memory 支持
233
358
  */
234
359
  async clear(...prefixes) {
235
360
  if (!prefixes.length) {
236
361
  return 0;
237
362
  }
238
- const keys = (await Promise.all(prefixes.map(prefix => x.redis.keys(`${this.toRedisKey(prefix)}*`)))).flat();
239
- const count = keys.length ? await x.redis.del(...keys) : 0;
363
+ let count = 0;
364
+ if (!this.hasMemoryCacheOnly) {
365
+ const keys = (await Promise.all(prefixes.map(prefix => x.redis.keys(`${this.toRedisKey(prefix)}*`)))).flat();
366
+ count = keys.length ? await x.redis.del(...keys) : 0;
367
+ }
240
368
  if (this.options.memory) {
369
+ let deletedCount = 0;
241
370
  this.memoryCache.forEach((_v, k) => {
242
371
  if (prefixes.some(prefix => k.startsWith(this.toRedisKey(prefix)))) {
243
- this.memoryCache.delete(k);
372
+ const isDeleted = this.memoryCache.delete(k);
373
+ if (isDeleted) {
374
+ deletedCount++;
375
+ }
244
376
  }
245
377
  });
378
+ if (this.hasMemoryCacheOnly) {
379
+ count = deletedCount;
380
+ }
246
381
  }
247
382
  return count;
248
383
  }
@@ -42,8 +42,8 @@ export declare class DbValueService implements BaseService {
42
42
  };
43
43
  };
44
44
  JsonObject: {
45
- new <T>(options: DbValueServiceJsonObjectOptions<T>): {
46
- options: DbValueServiceJsonObjectOptions<T>;
45
+ new <T>(options?: DbValueServiceJsonObjectOptions<T>): {
46
+ options?: DbValueServiceJsonObjectOptions<T>;
47
47
  hasTransformAdd: boolean;
48
48
  hasTransformGet: boolean;
49
49
  hasTransformUpdate: boolean;
@@ -63,9 +63,9 @@ export class DbValueService {
63
63
  return !value || !isPlainObject(value) ? undefined : JSON.stringify(value);
64
64
  };
65
65
  this.options = options;
66
- this.hasTransformAdd = !!options.transform?.add;
67
- this.hasTransformGet = !!options.transform?.get;
68
- this.hasTransformUpdate = !!options.transform?.update;
66
+ this.hasTransformAdd = !!options?.transform?.add;
67
+ this.hasTransformGet = !!options?.transform?.get;
68
+ this.hasTransformUpdate = !!options?.transform?.update;
69
69
  }
70
70
  };
71
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayfong/x-server",
3
- "version": "2.110.0",
3
+ "version": "2.111.0",
4
4
  "license": "ISC",
5
5
  "sideEffects": false,
6
6
  "main": "lib/_cjs/index.js",
@@ -70,6 +70,7 @@
70
70
  "prisma": "^7.2.0",
71
71
  "randomstring": "^1.3.1",
72
72
  "select-run": "^1.1.2",
73
+ "superjson": "^2.2.6",
73
74
  "supports-color": "^8",
74
75
  "svg-captcha": "^1.4.0",
75
76
  "tencentcloud-sdk-nodejs-sms": "^4.0.681",