fix_wx_add_frequency_utf8.mjs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { writeFileSync, readFileSync } from 'fs';
  2. function u(s) {
  3. return s.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
  4. }
  5. const support = u(`package com.fs.company.service.easycall;
  6. import cn.hutool.core.util.RandomUtil;
  7. import cn.hutool.json.JSONUtil;
  8. import com.fs.company.domain.CompanyWxAccount;
  9. import com.fs.company.mapper.CompanyWxAccountMapper;
  10. import com.fs.company.param.ExecutionContext;
  11. import com.fs.course.config.WxConfig;
  12. import com.fs.system.service.ISysConfigService;
  13. import lombok.Data;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.stereotype.Component;
  17. import java.time.LocalDate;
  18. import java.time.LocalDateTime;
  19. import java.time.ZoneId;
  20. import java.time.temporal.ChronoUnit;
  21. /**
  22. * \u4e2a\u5fae\u52a0\u597d\u53cb\u9891\u7387\u6821\u9a8c\uff0c\u652f\u6301\u9ad8\u5e76\u53d1\u4e0b\u8d26\u53f7\u7ea7\u539f\u5b50\u62a2\u5360\u3002
  23. */
  24. @Slf4j
  25. @Component
  26. public class WxAddWxFrequencySupport {
  27. @Autowired
  28. private ISysConfigService sysConfigService;
  29. @Autowired
  30. private CompanyWxAccountMapper companyWxAccountMapper;
  31. @Autowired
  32. private WxAddWxFrequencyRedisHelper redisHelper;
  33. @Data
  34. public static class FrequencyCheckResult {
  35. private boolean canAddNow;
  36. private long waitMinutes;
  37. private long waitMillis;
  38. private String reason;
  39. public static FrequencyCheckResult ready() {
  40. FrequencyCheckResult result = new FrequencyCheckResult();
  41. result.canAddNow = true;
  42. result.waitMinutes = 0;
  43. result.waitMillis = 0;
  44. return result;
  45. }
  46. public static FrequencyCheckResult wait(long waitMinutes, String reason) {
  47. return waitMillis(Math.max(1, waitMinutes) * 60_000L, reason);
  48. }
  49. public static FrequencyCheckResult waitMillis(long waitMillis, String reason) {
  50. FrequencyCheckResult result = new FrequencyCheckResult();
  51. result.canAddNow = false;
  52. result.waitMillis = Math.max(1000L, waitMillis);
  53. result.waitMinutes = (result.waitMillis + 59_999L) / 60_000L;
  54. result.reason = reason;
  55. return result;
  56. }
  57. }
  58. public WxConfig loadWxConfig() {
  59. String json = sysConfigService.selectConfigByKey("wx.config");
  60. if (json == null || json.isEmpty()) {
  61. throw new IllegalStateException("\u672a\u914d\u7f6e wx.config");
  62. }
  63. return JSONUtil.toBean(json, WxConfig.class);
  64. }
  65. /**
  66. * \u539f\u5b50\u62a2\u5360\u52a0\u5fae\u69fd\u4f4d\uff08\u540c\u8d26\u53f7\u4e92\u65a5 + \u9891\u7387 + \u65e5\u9650\uff09\u3002
  67. */
  68. public FrequencyCheckResult tryAcquireAddWxSlot(CompanyWxAccount account, String workflowInstanceId) {
  69. if (account == null) {
  70. return FrequencyCheckResult.waitMillis(5000L, "\u8d26\u53f7\u4e0d\u5b58\u5728");
  71. }
  72. seedRedisStateFromDbIfNeeded(account);
  73. WxConfig config = loadWxConfig();
  74. int intervalMinutes = resolveRequiredIntervalMinutes(account, config);
  75. long intervalMs = intervalMinutes * 60_000L;
  76. int dailyLimit = account.getAddNum() == null ? Integer.MAX_VALUE : account.getAddNum();
  77. long midnightWaitMs = ChronoUnit.MILLIS.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay());
  78. if (midnightWaitMs <= 0) {
  79. midnightWaitMs = 3600_000L;
  80. }
  81. WxAddWxFrequencyRedisHelper.SlotAcquireResult acquireResult = redisHelper.tryAcquireSlot(
  82. account.getId(), workflowInstanceId, intervalMs, dailyLimit,
  83. redisHelperSecondsUntilMidnight(), midnightWaitMs);
  84. if (acquireResult.granted()) {
  85. return FrequencyCheckResult.ready();
  86. }
  87. switch (acquireResult.getCode()) {
  88. case -1:
  89. log.info("\u8d26\u53f7 {} \u52a0\u5fae\u9891\u7387\u672a\u5230\uff0c\u7b49\u5f85 {} ms",
  90. account.getWxNickName(), acquireResult.getWaitMs());
  91. return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u52a0\u5fae\u9891\u7387\u95f4\u9694\u672a\u5230");
  92. case -2:
  93. log.info("\u8d26\u53f7 {} \u540c\u8d26\u53f7\u52a0\u5fae\u8fdb\u884c\u4e2d\uff0c\u6392\u961f\u7b49\u5f85 {} ms",
  94. account.getWxNickName(), acquireResult.getWaitMs());
  95. return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u540c\u8d26\u53f7\u52a0\u5fae\u8fdb\u884c\u4e2d");
  96. case -3:
  97. log.info("\u8d26\u53f7 {} \u4eca\u65e5\u52a0\u5fae\u5df2\u8fbe\u4e0a\u9650", account.getWxNickName());
  98. return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u4eca\u65e5\u52a0\u5fae\u6570\u91cf\u5df2\u8fbe\u4e0a\u9650");
  99. default:
  100. return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u52a0\u5fae\u6392\u961f\u7b49\u5f85");
  101. }
  102. }
  103. /**
  104. * \u53d1\u8d77\u52a0\u5fae\u8bf7\u6c42\u540e\u66f4\u65b0\u6570\u636e\u5e93\u5e76\u91ca\u653e\u8fdb\u884c\u4e2d\u9501\u3002
  105. */
  106. public void markAddWxSent(Long accountId) {
  107. if (accountId == null) {
  108. return;
  109. }
  110. CompanyWxAccount account = companyWxAccountMapper.selectCompanyWxAccountById(accountId);
  111. if (account != null) {
  112. account.setLastAddWxTime(LocalDateTime.now());
  113. int current = account.getIsAddNum() == null ? 0 : account.getIsAddNum();
  114. account.setIsAddNum(current + 1);
  115. companyWxAccountMapper.updateCompanyWxAccount(account);
  116. }
  117. redisHelper.releaseInflight(accountId);
  118. }
  119. public void releaseInflight(Long accountId) {
  120. redisHelper.releaseInflight(accountId);
  121. }
  122. public void scheduleFrequencyRetry(Integer cidGroupNo, String workflowInstanceId, ExecutionContext context, long retryAtMs) {
  123. redisHelper.scheduleFrequencyRetry(cidGroupNo, workflowInstanceId, context, retryAtMs);
  124. }
  125. public java.util.List<String> popDueFrequencyRetries(Integer cidGroupNo, long nowMs, int batchSize) {
  126. return redisHelper.popDueFrequencyRetries(cidGroupNo, nowMs, batchSize);
  127. }
  128. public ExecutionContext loadFrequencyRetryContext(String workflowInstanceId) {
  129. return redisHelper.loadFrequencyRetryContext(workflowInstanceId);
  130. }
  131. public void clearFrequencyRetry(String workflowInstanceId) {
  132. redisHelper.clearFrequencyRetry(workflowInstanceId);
  133. }
  134. public boolean tryMarkFrequencyProcessing(String workflowInstanceId) {
  135. return redisHelper.tryMarkFrequencyProcessing(workflowInstanceId);
  136. }
  137. public void clearFrequencyProcessing(String workflowInstanceId) {
  138. redisHelper.clearFrequencyProcessing(workflowInstanceId);
  139. }
  140. private void seedRedisStateFromDbIfNeeded(CompanyWxAccount account) {
  141. if (account.getIsAddNum() != null && account.getIsAddNum() > 0) {
  142. redisHelper.seedDailyCount(account.getId(), account.getIsAddNum());
  143. }
  144. if (account.getLastAddWxTime() == null) {
  145. return;
  146. }
  147. WxConfig config = loadWxConfig();
  148. int minute = resolveRequiredIntervalMinutes(account, config);
  149. long elapsed = account.getLastAddWxTime().until(LocalDateTime.now(), ChronoUnit.MINUTES);
  150. if (elapsed <= minute) {
  151. long nextAllowed = account.getLastAddWxTime().plusMinutes(minute + 1L)
  152. .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
  153. redisHelper.seedNextSlot(account.getId(), nextAllowed);
  154. }
  155. }
  156. private int redisHelperSecondsUntilMidnight() {
  157. long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay());
  158. return (int) Math.max(seconds + 60, 3600);
  159. }
  160. private int resolveRequiredIntervalMinutes(CompanyWxAccount account, WxConfig config) {
  161. if (Integer.valueOf(0).equals(account.getIsNew())) {
  162. return RandomUtil.randomInt(config.getNewAccountAddWxMin(), config.getNewAccountAddWxMax());
  163. }
  164. return RandomUtil.randomInt(config.getAccountAddWxMin(), config.getAccountAddWxMax());
  165. }
  166. }
  167. `);
  168. writeFileSync('src/main/java/com/fs/company/service/easycall/WxAddWxFrequencySupport.java', support, 'utf8');
  169. const redisHelperPath = 'src/main/java/com/fs/company/service/easycall/WxAddWxFrequencyRedisHelper.java';
  170. let redisHelper = readFileSync(redisHelperPath, 'utf8');
  171. redisHelper = redisHelper.replace(
  172. /\/\*\*[\s\S]*?\*\/\s*@Component\s*public class WxAddWxFrequencyRedisHelper/,
  173. u(`/**
  174. * \u4e2a\u5fae\u52a0\u5fae\u9891\u7387 Redis \u539f\u5b50\u64cd\u4f5c\uff08\u8d26\u53f7\u7ea7\u4e92\u65a5\u3001\u9891\u7387\u69fd\u4f4d\u3001\u5ef6\u65f6\u961f\u5217\uff09\u3002
  175. */
  176. @Component
  177. public class WxAddWxFrequencyRedisHelper`)
  178. );
  179. writeFileSync(redisHelperPath, redisHelper, 'utf8');
  180. console.log('fixed WxAddWxFrequencySupport.java and WxAddWxFrequencyRedisHelper.java javadoc');