MyI18nMessageProvider.java 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package jnpf.i18n.provider;
  2. import cn.hutool.cache.CacheUtil;
  3. import cn.hutool.cache.impl.TimedCache;
  4. import jnpf.base.UserInfo;
  5. import jnpf.config.ConfigValueUtil;
  6. import jnpf.constant.GlobalConst;
  7. import jnpf.constant.model.MCode;
  8. import jnpf.util.TenantHolder;
  9. import jnpf.util.UserProvider;
  10. import lombok.extern.slf4j.Slf4j;
  11. import java.util.Locale;
  12. import java.util.Properties;
  13. import java.util.concurrent.ConcurrentHashMap;
  14. import java.util.concurrent.locks.ReentrantLock;
  15. /**
  16. * 国际化语言翻译
  17. */
  18. @Slf4j
  19. public class MyI18nMessageProvider implements I18nMessageProvider {
  20. private ConfigValueUtil configValueUtil;
  21. private final long CACHE_TIME = 48 * 60 * 60000L;
  22. // 未使用的情况下, 默认48小时失效, 使用后重新计算缓存时效
  23. private TimedCache<String, TimedCache<String, Properties>> tenantMessageProperties = CacheUtil.newTimedCache(CACHE_TIME);
  24. // 翻译内容提供者
  25. private DynamicMessageProvider dynamicMessageProvider;
  26. // 租户语言加载锁
  27. private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
  28. private Properties emptyProperties = new Properties();
  29. public MyI18nMessageProvider(ConfigValueUtil configValueUtil, DynamicMessageProvider dynamicMessageProvider) {
  30. this.configValueUtil = configValueUtil;
  31. this.dynamicMessageProvider = dynamicMessageProvider;
  32. // 执行定时清理
  33. tenantMessageProperties.schedulePrune(CACHE_TIME);
  34. }
  35. public void removeTenantCache(String tenantId) {
  36. tenantMessageProperties.remove(tenantId);
  37. }
  38. public void loadTenantMessage(String tenantId, Locale locale) {
  39. Properties i18nListProperties = dynamicMessageProvider.getI18nListProperties(locale);
  40. TimedCache<String, Properties> tenantProperties = tenantMessageProperties.get(tenantId);
  41. if (tenantProperties == null) {
  42. tenantProperties = CacheUtil.newTimedCache(CACHE_TIME);
  43. tenantMessageProperties.put(tenantId, tenantProperties);
  44. }
  45. if (i18nListProperties == null) {
  46. // 语言加载失败, 一分钟后重新加载, 避免高频请求
  47. tenantProperties.put(locale.toLanguageTag(), emptyProperties, 60000L);
  48. } else {
  49. // 无论是否有内容返回都是加载成功, 如果properties为空没有翻译数据, 服务端新增翻译时会清空缓存重新加载
  50. tenantProperties.put(locale.toLanguageTag(), i18nListProperties);
  51. }
  52. }
  53. @Override
  54. public MCode getI18nMessage(String code, Locale locale) {
  55. UserInfo userInfo = UserProvider.getUser();
  56. // 未登录不获取
  57. if (userInfo.getToken() == null) {
  58. return null;
  59. }
  60. // 默认租户或者当前租户
  61. String tenantId = GlobalConst.DEFAULT_TENANT_VALUE;
  62. if (configValueUtil.isMultiTenancy()) {
  63. tenantId = TenantHolder.getDatasourceId();
  64. }
  65. // 开启租户未获取到租户 不进行翻译获取
  66. if (tenantId != null) {
  67. // 租户配置中的语言配置
  68. String languageTag = locale.toLanguageTag();
  69. if (!tenantMessageProperties.containsKey(tenantId) || !tenantMessageProperties.get(tenantId).containsKey(languageTag)) {
  70. // 租户其他线程正在加载多语言则直接返回
  71. ReentrantLock lock = lockMap.computeIfAbsent(tenantId, k -> new ReentrantLock());
  72. boolean isLock = false;
  73. try {
  74. isLock = lock.tryLock();
  75. if (isLock) {
  76. loadTenantMessage(tenantId, locale);
  77. } else {
  78. log.debug("[{}]语言加载中, 直接返回", tenantId);
  79. return null;
  80. }
  81. } finally {
  82. if (isLock) {
  83. lock.unlock();
  84. }
  85. }
  86. }
  87. Properties languageProperty = tenantMessageProperties.get(tenantId).get(languageTag, false);
  88. if (languageProperty != null) {
  89. Object message = languageProperty.get(code);
  90. if (message != null) {
  91. return new MCode("", code, message.toString());
  92. }
  93. }
  94. }
  95. return null;
  96. }
  97. }