system-config.service.mt.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import { GenericCrudService } from '@d8d/shared-crud';
  2. import { DataSource, In } from 'typeorm';
  3. import { SystemConfigMt } from '../entities/system-config.entity.mt';
  4. import { redisUtil } from '@d8d/shared-utils';
  5. export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
  6. constructor(dataSource: DataSource) {
  7. super(dataSource, SystemConfigMt, {
  8. tenantOptions: {
  9. enabled: true,
  10. tenantIdField: 'tenantId',
  11. autoExtractFromContext: true
  12. },
  13. userTracking: {
  14. createdByField: 'createdBy',
  15. updatedByField: 'updatedBy'
  16. }
  17. });
  18. }
  19. /**
  20. * 根据配置键获取配置值(带缓存)
  21. */
  22. async getConfigByKey(configKey: string, tenantId: number): Promise<string | null> {
  23. // 1. 先尝试从缓存获取
  24. const cachedValue = await redisUtil.getSystemConfig(tenantId, configKey);
  25. // 2. 如果缓存命中且不是空值缓存
  26. if (cachedValue !== null && !redisUtil.isNullValue(cachedValue)) {
  27. return cachedValue;
  28. }
  29. // 3. 如果是空值缓存,直接返回null
  30. if (cachedValue !== null && redisUtil.isNullValue(cachedValue)) {
  31. return null;
  32. }
  33. // 4. 缓存未命中,查询数据库
  34. const config = await this.repository.findOne({
  35. where: { configKey, tenantId }
  36. });
  37. const configValue = config?.configValue || null;
  38. // 5. 将结果写入缓存
  39. if (configValue !== null) {
  40. // 正常值,缓存1小时
  41. await redisUtil.setSystemConfig(tenantId, configKey, configValue, 3600);
  42. } else {
  43. // 空值,缓存5分钟防止缓存穿透
  44. await redisUtil.setNullSystemConfig(tenantId, configKey, 300);
  45. }
  46. return configValue;
  47. }
  48. /**
  49. * 根据配置键获取配置值,如果不存在则返回默认值
  50. */
  51. async getConfigByKeyWithDefault(configKey: string, defaultValue: string, tenantId: number): Promise<string> {
  52. const value = await this.getConfigByKey(configKey, tenantId);
  53. return value || defaultValue;
  54. }
  55. /**
  56. * 批量获取配置(带缓存)
  57. */
  58. async getConfigsByKeys(configKeys: string[], tenantId: number): Promise<Record<string, string>> {
  59. // 1. 先尝试从缓存批量获取
  60. const cachedValues = await redisUtil.getSystemConfigs(tenantId, configKeys);
  61. const result: Record<string, string> = {};
  62. const missingKeys: string[] = [];
  63. // 2. 处理缓存命中的键
  64. configKeys.forEach(key => {
  65. const cachedValue = cachedValues[key];
  66. if (cachedValue !== null && !redisUtil.isNullValue(cachedValue)) {
  67. result[key] = cachedValue;
  68. } else if (cachedValue === null) {
  69. // 缓存未命中,需要查询数据库
  70. missingKeys.push(key);
  71. }
  72. // 如果是空值缓存,不添加到结果中(保持为undefined)
  73. });
  74. // 3. 如果有未命中的键,查询数据库
  75. if (missingKeys.length > 0) {
  76. const configs = await this.repository.find({
  77. where: {
  78. configKey: In(missingKeys),
  79. tenantId
  80. }
  81. });
  82. const dbValues: Record<string, string> = {};
  83. configs.forEach(config => {
  84. if (config.configValue !== null) {
  85. dbValues[config.configKey] = config.configValue;
  86. }
  87. });
  88. // 4. 处理数据库查询结果并更新缓存
  89. const cachePromises: Promise<void>[] = [];
  90. missingKeys.forEach(key => {
  91. const dbValue = dbValues[key];
  92. if (dbValue !== undefined) {
  93. // 数据库中有值,添加到结果并缓存
  94. result[key] = dbValue;
  95. cachePromises.push(redisUtil.setSystemConfig(tenantId, key, dbValue, 3600));
  96. } else {
  97. // 数据库中无值,设置空值缓存
  98. cachePromises.push(redisUtil.setNullSystemConfig(tenantId, key, 300));
  99. }
  100. });
  101. // 等待所有缓存操作完成
  102. if (cachePromises.length > 0) {
  103. await Promise.all(cachePromises);
  104. }
  105. }
  106. return result;
  107. }
  108. /**
  109. * 设置配置值,如果配置键不存在则创建,存在则更新
  110. */
  111. async setConfig(configKey: string, configValue: string, tenantId: number, description?: string): Promise<SystemConfigMt> {
  112. const existingConfig = await this.repository.findOne({
  113. where: { configKey, tenantId }
  114. });
  115. if (existingConfig) {
  116. // 更新现有配置
  117. const updatedConfig = await this.update(existingConfig.id, {
  118. configValue,
  119. description: description || existingConfig.description
  120. });
  121. // 清除相关缓存
  122. await redisUtil.deleteSystemConfig(tenantId, configKey);
  123. return updatedConfig!;
  124. } else {
  125. // 创建新配置
  126. const newConfig = await this.create({
  127. configKey,
  128. configValue,
  129. description,
  130. tenantId
  131. } as SystemConfigMt);
  132. // 清除相关缓存(如果之前有空值缓存)
  133. await redisUtil.deleteSystemConfig(tenantId, configKey);
  134. return newConfig!;
  135. }
  136. }
  137. /**
  138. * 获取租户的所有配置
  139. */
  140. async getAllConfigs(tenantId: number): Promise<SystemConfigMt[]> {
  141. return await this.repository.find({ where: { tenantId } });
  142. }
  143. /**
  144. * 删除配置
  145. */
  146. async deleteConfig(configKey: string, tenantId: number): Promise<boolean> {
  147. const config = await this.repository.findOne({
  148. where: { configKey, tenantId }
  149. });
  150. if (!config) {
  151. return false;
  152. }
  153. await this.delete(config.id);
  154. // 清除相关缓存
  155. await redisUtil.deleteSystemConfig(tenantId, configKey);
  156. return true;
  157. }
  158. /**
  159. * 重写update方法,在更新时清除缓存
  160. */
  161. async update(id: number, data: Partial<SystemConfigMt>): Promise<SystemConfigMt | null> {
  162. const updatedConfig = await super.update(id, data);
  163. if (updatedConfig) {
  164. // 清除相关缓存
  165. const effectiveTenantId = updatedConfig.tenantId ?? 0;
  166. await redisUtil.deleteSystemConfig(effectiveTenantId, updatedConfig.configKey);
  167. }
  168. return updatedConfig;
  169. }
  170. /**
  171. * 重写delete方法,在删除时清除缓存
  172. */
  173. async delete(id: number): Promise<boolean> {
  174. // 先获取配置信息,以便后续清除缓存
  175. const config = await this.getById(id);
  176. const result = await super.delete(id);
  177. if (result && config) {
  178. // 清除相关缓存
  179. const effectiveTenantId = config.tenantId ?? 0;
  180. await redisUtil.deleteSystemConfig(effectiveTenantId, config.configKey);
  181. }
  182. return result;
  183. }
  184. /**
  185. * 缓存预热 - 预加载常用配置到缓存
  186. */
  187. async warmUpCache(tenantId: number, configKeys?: string[]): Promise<void> {
  188. // 如果没有指定配置键,预加载所有配置
  189. const keysToWarm = configKeys || ['app.login.enabled', 'app.payment.enabled', 'app.notification.enabled'];
  190. // 批量获取配置并缓存
  191. await this.getConfigsByKeys(keysToWarm, tenantId);
  192. }
  193. }