feat: refine city selection process for weather art

- Implement daily generation limit for weather arts
- Improve city selection by prioritizing active cities
- Introduce logging for city generation start time and list

This update enhances the efficiency of the `BatchGenerateWeatherArtsWorker`. It ensures that the system respects a daily limit on the number of generated images and improves how cities are selected for processing by prioritizing active cities while also conditionally selecting others if slots remain. Additionally, the added logging provides better insights into the generation process.
This commit is contained in:
songtianlun 2025-02-13 16:18:40 +08:00
parent 5ae0367525
commit 15c1fc654d

View File

@ -1,38 +1,85 @@
class BatchGenerateWeatherArtsWorker class BatchGenerateWeatherArtsWorker
include Sidekiq::Worker include Sidekiq::Worker
GENERATION_INTERVAL = 36.hours GENERATION_INTERVAL = 36.hours
MAX_DURATION = 50.minutes MAX_DURATION = 50.minutes
SLEEP_DURATION = 120.seconds SLEEP_DURATION = 120.seconds
BATCH_SIZE = 20 DAILY_GENERATION_LIMIT = 20 # 每日生成图片上限
module ProcessType
DO_NOTHING = 0
GET_WEATHER_WITH_GENERATE_AI_ART = 1
GET_WEATHER_ONLY = 2
end
def perform(*args) def perform(*args)
start_time = Time.current start_time = Time.current
cities_to_process = get_eligible_cities.shuffle.take(BATCH_SIZE) remaining_slots = calculate_remaining_slots
cities_to_process.each do |city| return if remaining_slots <= 0
# 获取符合条件的城市并处理
cities_to_process = select_cities(remaining_slots)&.shuffle
print_cities_list(cities_to_process, start_time)
process_cities(cities_to_process, start_time)
end
private
def calculate_remaining_slots
today_generations = WeatherArt
.where("DATE(created_at) = ?", Date.today)
.where.not(image_attachment: nil)
.count
[ DAILY_GENERATION_LIMIT - today_generations, 0 ].max
end
def print_cities_list(cities, start_time)
Rails.logger.info "Generate city task start at: #{start_time}"
Rails.logger.info "Generate city list: "
Rails.logger.info "======================================"
Rails.logger.info "\tID\tName\tState\tCountry\tRegion"
cities.each do |city|
Rails.logger.info "\t#{city.id}\t#{city&.name}\t#{city&.state&.name}\t#{city&.country&.name}\t#{city&.country&.region&.name}"
end
end
def select_cities(limit)
cutoff_time = Time.current - GENERATION_INTERVAL
# 基础查询:排除最近生成过的城市
base_query = City
.joins("LEFT JOIN (
SELECT city_id, MAX(created_at) as last_generation_time
FROM weather_arts
GROUP BY city_id
) latest_arts ON cities.id = latest_arts.city_id")
.where("latest_arts.last_generation_time IS NULL OR latest_arts.last_generation_time < ?", cutoff_time)
# 优先选择活跃城市
active_cities = base_query
.where(active: true)
.order(:priority)
.limit(limit)
.to_a
remaining_slots = limit - active_cities.size
if remaining_slots > 0
# 如果还有剩余名额,从其他城市中随机选择
other_cities = base_query
.where.not(id: active_cities.map(&:id))
.order("RANDOM()")
.limit(remaining_slots)
.to_a
active_cities + other_cities
else
active_cities
end
end
def process_cities(cities, start_time)
cities.each do |city|
break if Time.current - start_time > MAX_DURATION break if Time.current - start_time > MAX_DURATION
Rails.logger.info "Generating weather art for #{city.name}" Rails.logger.info "Generating weather art for #{city.name}"
GenerateWeatherArtWorker.perform_async(city.id) GenerateWeatherArtWorker.perform_async(city.id)
sleep SLEEP_DURATION sleep SLEEP_DURATION
end end
end end
private
def get_eligible_cities
cutoff_time = Time.current - GENERATION_INTERVAL
City.active
.joins("LEFT JOIN (
SELECT city_id, MAX(created_at) as last_generation_time
FROM weather_arts
GROUP BY city_id
) latest_arts ON cities.id = latest_arts.city_id")
.where("latest_arts.last_generation_time IS NULL OR latest_arts.last_generation_time < ?", cutoff_time)
.order(:priority)
end
end end