import { writeFileSync, readFileSync } from 'fs'; function u(s) { return s.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))); } const support = u(`package com.fs.company.service.easycall; import cn.hutool.core.util.RandomUtil; import cn.hutool.json.JSONUtil; import com.fs.company.domain.CompanyWxAccount; import com.fs.company.mapper.CompanyWxAccountMapper; import com.fs.company.param.ExecutionContext; import com.fs.course.config.WxConfig; import com.fs.system.service.ISysConfigService; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; /** * \u4e2a\u5fae\u52a0\u597d\u53cb\u9891\u7387\u6821\u9a8c\uff0c\u652f\u6301\u9ad8\u5e76\u53d1\u4e0b\u8d26\u53f7\u7ea7\u539f\u5b50\u62a2\u5360\u3002 */ @Slf4j @Component public class WxAddWxFrequencySupport { @Autowired private ISysConfigService sysConfigService; @Autowired private CompanyWxAccountMapper companyWxAccountMapper; @Autowired private WxAddWxFrequencyRedisHelper redisHelper; @Data public static class FrequencyCheckResult { private boolean canAddNow; private long waitMinutes; private long waitMillis; private String reason; public static FrequencyCheckResult ready() { FrequencyCheckResult result = new FrequencyCheckResult(); result.canAddNow = true; result.waitMinutes = 0; result.waitMillis = 0; return result; } public static FrequencyCheckResult wait(long waitMinutes, String reason) { return waitMillis(Math.max(1, waitMinutes) * 60_000L, reason); } public static FrequencyCheckResult waitMillis(long waitMillis, String reason) { FrequencyCheckResult result = new FrequencyCheckResult(); result.canAddNow = false; result.waitMillis = Math.max(1000L, waitMillis); result.waitMinutes = (result.waitMillis + 59_999L) / 60_000L; result.reason = reason; return result; } } public WxConfig loadWxConfig() { String json = sysConfigService.selectConfigByKey("wx.config"); if (json == null || json.isEmpty()) { throw new IllegalStateException("\u672a\u914d\u7f6e wx.config"); } return JSONUtil.toBean(json, WxConfig.class); } /** * \u539f\u5b50\u62a2\u5360\u52a0\u5fae\u69fd\u4f4d\uff08\u540c\u8d26\u53f7\u4e92\u65a5 + \u9891\u7387 + \u65e5\u9650\uff09\u3002 */ public FrequencyCheckResult tryAcquireAddWxSlot(CompanyWxAccount account, String workflowInstanceId) { if (account == null) { return FrequencyCheckResult.waitMillis(5000L, "\u8d26\u53f7\u4e0d\u5b58\u5728"); } seedRedisStateFromDbIfNeeded(account); WxConfig config = loadWxConfig(); int intervalMinutes = resolveRequiredIntervalMinutes(account, config); long intervalMs = intervalMinutes * 60_000L; int dailyLimit = account.getAddNum() == null ? Integer.MAX_VALUE : account.getAddNum(); long midnightWaitMs = ChronoUnit.MILLIS.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay()); if (midnightWaitMs <= 0) { midnightWaitMs = 3600_000L; } WxAddWxFrequencyRedisHelper.SlotAcquireResult acquireResult = redisHelper.tryAcquireSlot( account.getId(), workflowInstanceId, intervalMs, dailyLimit, redisHelperSecondsUntilMidnight(), midnightWaitMs); if (acquireResult.granted()) { return FrequencyCheckResult.ready(); } switch (acquireResult.getCode()) { case -1: log.info("\u8d26\u53f7 {} \u52a0\u5fae\u9891\u7387\u672a\u5230\uff0c\u7b49\u5f85 {} ms", account.getWxNickName(), acquireResult.getWaitMs()); return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u52a0\u5fae\u9891\u7387\u95f4\u9694\u672a\u5230"); case -2: log.info("\u8d26\u53f7 {} \u540c\u8d26\u53f7\u52a0\u5fae\u8fdb\u884c\u4e2d\uff0c\u6392\u961f\u7b49\u5f85 {} ms", account.getWxNickName(), acquireResult.getWaitMs()); return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u540c\u8d26\u53f7\u52a0\u5fae\u8fdb\u884c\u4e2d"); case -3: log.info("\u8d26\u53f7 {} \u4eca\u65e5\u52a0\u5fae\u5df2\u8fbe\u4e0a\u9650", account.getWxNickName()); return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u4eca\u65e5\u52a0\u5fae\u6570\u91cf\u5df2\u8fbe\u4e0a\u9650"); default: return FrequencyCheckResult.waitMillis(acquireResult.getWaitMs(), "\u52a0\u5fae\u6392\u961f\u7b49\u5f85"); } } /** * \u53d1\u8d77\u52a0\u5fae\u8bf7\u6c42\u540e\u66f4\u65b0\u6570\u636e\u5e93\u5e76\u91ca\u653e\u8fdb\u884c\u4e2d\u9501\u3002 */ public void markAddWxSent(Long accountId) { if (accountId == null) { return; } CompanyWxAccount account = companyWxAccountMapper.selectCompanyWxAccountById(accountId); if (account != null) { account.setLastAddWxTime(LocalDateTime.now()); int current = account.getIsAddNum() == null ? 0 : account.getIsAddNum(); account.setIsAddNum(current + 1); companyWxAccountMapper.updateCompanyWxAccount(account); } redisHelper.releaseInflight(accountId); } public void releaseInflight(Long accountId) { redisHelper.releaseInflight(accountId); } public void scheduleFrequencyRetry(Integer cidGroupNo, String workflowInstanceId, ExecutionContext context, long retryAtMs) { redisHelper.scheduleFrequencyRetry(cidGroupNo, workflowInstanceId, context, retryAtMs); } public java.util.List popDueFrequencyRetries(Integer cidGroupNo, long nowMs, int batchSize) { return redisHelper.popDueFrequencyRetries(cidGroupNo, nowMs, batchSize); } public ExecutionContext loadFrequencyRetryContext(String workflowInstanceId) { return redisHelper.loadFrequencyRetryContext(workflowInstanceId); } public void clearFrequencyRetry(String workflowInstanceId) { redisHelper.clearFrequencyRetry(workflowInstanceId); } public boolean tryMarkFrequencyProcessing(String workflowInstanceId) { return redisHelper.tryMarkFrequencyProcessing(workflowInstanceId); } public void clearFrequencyProcessing(String workflowInstanceId) { redisHelper.clearFrequencyProcessing(workflowInstanceId); } private void seedRedisStateFromDbIfNeeded(CompanyWxAccount account) { if (account.getIsAddNum() != null && account.getIsAddNum() > 0) { redisHelper.seedDailyCount(account.getId(), account.getIsAddNum()); } if (account.getLastAddWxTime() == null) { return; } WxConfig config = loadWxConfig(); int minute = resolveRequiredIntervalMinutes(account, config); long elapsed = account.getLastAddWxTime().until(LocalDateTime.now(), ChronoUnit.MINUTES); if (elapsed <= minute) { long nextAllowed = account.getLastAddWxTime().plusMinutes(minute + 1L) .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); redisHelper.seedNextSlot(account.getId(), nextAllowed); } } private int redisHelperSecondsUntilMidnight() { long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay()); return (int) Math.max(seconds + 60, 3600); } private int resolveRequiredIntervalMinutes(CompanyWxAccount account, WxConfig config) { if (Integer.valueOf(0).equals(account.getIsNew())) { return RandomUtil.randomInt(config.getNewAccountAddWxMin(), config.getNewAccountAddWxMax()); } return RandomUtil.randomInt(config.getAccountAddWxMin(), config.getAccountAddWxMax()); } } `); writeFileSync('src/main/java/com/fs/company/service/easycall/WxAddWxFrequencySupport.java', support, 'utf8'); const redisHelperPath = 'src/main/java/com/fs/company/service/easycall/WxAddWxFrequencyRedisHelper.java'; let redisHelper = readFileSync(redisHelperPath, 'utf8'); redisHelper = redisHelper.replace( /\/\*\*[\s\S]*?\*\/\s*@Component\s*public class WxAddWxFrequencyRedisHelper/, u(`/** * \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 */ @Component public class WxAddWxFrequencyRedisHelper`) ); writeFileSync(redisHelperPath, redisHelper, 'utf8'); console.log('fixed WxAddWxFrequencySupport.java and WxAddWxFrequencyRedisHelper.java javadoc');