# BudgetDatabaseInitConfigTask.java

    package cloud.wconnect.rateshop.businesstask.budgetdatabase;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.FunctionIdConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.query.OpenFunctionPropertyQuery;
    import cloud.wconnect.commonlib.service.MailInternalService;
    import cloud.wconnect.commonlib.service.PropertyInternalService;
    import cloud.wconnect.rateshop.service.budgetdatabase.IBudgetDatabaseInitConfigService;
    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;

    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.List;
    import java.util.concurrent.TimeUnit;

    /**
     * Budget Database Init Config
     *
     * @author: yyt
     * 2025-11-17
     */
    @Service
    @Slf4j
    public class BudgetDatabaseInitConfigTask {

        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
        @Autowired
        private MailInternalService mailInternalService;
        @Autowired
        private IBudgetDatabaseInitConfigService budgetDatabaseInitConfigService;
        @Autowired
        private PropertyInternalService propertyInternalService;

        private static final String PREFIX_KEY = "wconnect:task:budget-database-init-config:";

        /**
         * 每年1月1号，初始化下一年的预算配置
         *
         * @author yyt
         * @date 2025/11/17
         **/
        @Scheduled(cron = "${schedule.cron.budget-database-init-config}", zone = "${default.timezone}")
        public void initNextYearConfig() {
            log.info("BudgetDatabaseInitConfigTask... start");
            String alertTitle = "[Warning] - Exceptions About Auto Init Next Year Budget Database Config";
            StringBuilder warningEmail = new StringBuilder("<p>There is something wrong when auto init next year budget database config.</p>");

            try {
                // 处理并发
                String currentDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYMMDDHH));
                if (!preventConcurrency(PREFIX_KEY + currentDate)) {
                    log.warn("BudgetDatabaseInitConfigTask... date:{}. other machines have already executed", currentDate);
                    log.info("BudgetDatabaseInitConfigTask... end");
                    return;
                }

                // 查询开通此功能 的资产
                List<PropertyDTO> openModuleProperty = propertyInternalService.getOpenFunctionProperty(OpenFunctionPropertyQuery.builder()
                        .openFunctionIds(Lists.newArrayList(FunctionIdConstant.BUDGET_DATABASE)).build())
                        .stream().filter(PropertyDTO::getStatus).toList();
                if(CollectionUtils.isEmpty(openModuleProperty)) {
                    log.info("BudgetDatabaseInitConfigTask... no open module property");
                    log.info("BudgetDatabaseInitConfigTask... end");
                    return;
                }

                budgetDatabaseInitConfigService.initNextYearConfig(openModuleProperty);

            } catch (Exception e) {
                log.warn("BudgetDatabaseInitConfigTask...  exception occurs:", e);
                warningEmail.append("<p>Exception:").append(e).append("</p>");
                mailInternalService.sendAlertEmail(alertTitle, warningEmail.toString());
            }

            log.info("BudgetDatabaseInitConfigTask... end");
        }

        /**
         * 防止并发
         *
         * @param key 唯一标识
         * @return 锁成功
         * @author yyt
         * @date 2025/11/18
         */
        private Boolean preventConcurrency(String key) {
            return redisTemplate.opsForValue().setIfAbsent(key, 0, 1, TimeUnit.DAYS);
        }

    }