// // FirstRechargeManager.m // YuMi // // Created by P on 2025/6/25. // #import "FirstRechargeManager.h" #import "FirstRechargeModel.h" #import "Api+FirstRecharge.h" #import "AccountInfoStorage.h" #import "NSDate+DateUtils.h" // 存储键 static NSString * const kFirstRechargeModelKey = @"FirstRechargeModel"; static NSString * const kLastCheckDateKey = @"FirstRechargeLastCheckDate"; static NSString * const kTodayShownKey = @"FirstRechargeTodayShown"; static NSString * const kCurrentUserIdKey = @"FirstRechargeCurrentUserId"; @interface FirstRechargeManager () @property (nonatomic, strong) FirstRechargeModel *currentFirstRechargeData; @property (nonatomic, strong) NSTimer *dailyTimer; @property (nonatomic, assign) BOOL isMonitoring; @property (nonatomic, copy) NSString *currentUserId; @end @implementation FirstRechargeManager + (instancetype)sharedManager { static FirstRechargeManager *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[FirstRechargeManager alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { _isMonitoring = NO; [self loadCachedModel]; [self updateCurrentUserId]; } return self; } - (void)dealloc { [self stopMonitoring]; } #pragma mark - Public Methods - (FirstRechargeModel *)loadCurrentModel { return self.currentFirstRechargeData; } - (void)startMonitoring { if (self.isMonitoring) { return; } self.isMonitoring = YES; // 检查用户是否切换 [self checkUserSwitch]; // 立即检查一次 [self checkFirstRechargeStatus]; // 设置每日定时检查 [self setupDailyTimer]; // 监听应用进入前台事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; } - (void)stopMonitoring { if (!self.isMonitoring) { return; } self.isMonitoring = NO; // 停止定时器 if (self.dailyTimer) { [self.dailyTimer invalidate]; self.dailyTimer = nil; } // 移除通知监听 [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)manualCheckFirstRecharge { [self checkFirstRechargeStatus]; } - (void)markTodayShown { NSString *today = [self getTodayString]; [self setTodayShownForCurrentUser:today]; } - (void)updateChargeStatusToCompleted { if (self.currentFirstRechargeData && !self.currentFirstRechargeData.chargeStatus) { // 更新首充状态为已完成 self.currentFirstRechargeData.chargeStatus = YES; // 保存更新后的模型 [self saveCachedModel:self.currentFirstRechargeData]; // 通知代理(如果需要) [self notifyDelegatesWithModel:self.currentFirstRechargeData shouldShow:NO]; } } #pragma mark - Private Methods - (void)setupDailyTimer { // 计算到明天凌晨的时间间隔 NSTimeInterval secondsUntilMidnight = [self secondsUntilNextMidnight]; // 设置定时器在明天凌晨触发,然后每24小时重复 self.dailyTimer = [NSTimer scheduledTimerWithTimeInterval:secondsUntilMidnight target:self selector:@selector(dailyTimerFired) userInfo:nil repeats:NO]; } - (void)dailyTimerFired { // 检查首充状态 [self checkFirstRechargeStatus]; // 重新设置明天的定时器 [self setupDailyTimer]; } - (NSTimeInterval)secondsUntilNextMidnight { NSCalendar *calendar = [NSCalendar currentCalendar]; NSDate *now = [NSDate date]; // 获取明天 NSDate *tomorrow = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:now options:0]; // 获取明天的开始时间(凌晨) NSDate *tomorrowStart = [calendar startOfDayForDate:tomorrow]; // 计算时间差 return [tomorrowStart timeIntervalSinceDate:now]; } - (void)applicationDidBecomeActive { // 检查用户是否切换 [self checkUserSwitch]; // 应用进入前台时检查是否需要更新 if ([self shouldCheckToday]) { [self checkFirstRechargeStatus]; } } - (BOOL)shouldCheckToday { NSString *userSpecificKey = [self getUserSpecificKey:kLastCheckDateKey]; NSString *lastCheckDate = [[NSUserDefaults standardUserDefaults] objectForKey:userSpecificKey]; NSString *today = [self getTodayString]; return ![today isEqualToString:lastCheckDate]; } - (void)checkFirstRechargeStatus { // 检查是否登录 if ([AccountInfoStorage instance].getTicket.length == 0 || [AccountInfoStorage instance].getUid.length == 0) { return; } // 更新当前用户ID [self updateCurrentUserId]; // 调用API获取首充信息 @kWeakify(self); [Api firstchargeInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { @kStrongify(self); if (code == 200 && data.data) { FirstRechargeModel *model = [FirstRechargeModel modelWithJSON:data.data]; [self handleFirstRechargeResult:model]; } }]; } - (void)handleFirstRechargeResult:(FirstRechargeModel *)model { if (!model) { return; } // 保存模型 self.currentFirstRechargeData = model; [self saveCachedModel:model]; // 更新检查日期(针对当前用户) NSString *today = [self getTodayString]; NSString *userSpecificKey = [self getUserSpecificKey:kLastCheckDateKey]; [[NSUserDefaults standardUserDefaults] setObject:today forKey:userSpecificKey]; [[NSUserDefaults standardUserDefaults] synchronize]; // 判断是否需要展示 BOOL shouldShow = [self shouldShowFirstRecharge:model]; // 通知代理 [self notifyDelegatesWithModel:model shouldShow:shouldShow]; } - (BOOL)shouldShowFirstRecharge:(FirstRechargeModel *)model { // 如果已经首充过,不展示 if (model.chargeStatus) { return NO; } // 检查今天是否已经展示过(针对当前用户) NSString *shownDate = [self getTodayShownForCurrentUser]; NSString *today = [self getTodayString]; return ![today isEqualToString:shownDate]; } - (void)notifyDelegatesWithModel:(FirstRechargeModel *)model shouldShow:(BOOL)shouldShow { @kWeakify(self); dispatch_async(dispatch_get_main_queue(), ^{ @kStrongify(self); if (self.delegate && [self.delegate respondsToSelector:@selector(firstRechargeManager:didCheckFirstRecharge:shouldShow:)]) { [self.delegate firstRechargeManager:self didCheckFirstRecharge:model shouldShow:shouldShow]; } }); } - (NSString *)getTodayString { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-MM-dd"]; return [formatter stringFromDate:[NSDate date]]; } #pragma mark - User Switch Detection - (void)updateCurrentUserId { NSString *newUserId = [AccountInfoStorage instance].getUid; if (newUserId.length > 0) { self.currentUserId = newUserId; // 保存当前用户ID [[NSUserDefaults standardUserDefaults] setObject:newUserId forKey:kCurrentUserIdKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } } - (void)checkUserSwitch { NSString *savedUserId = [[NSUserDefaults standardUserDefaults] objectForKey:kCurrentUserIdKey]; NSString *currentUserId = [AccountInfoStorage instance].getUid; // 如果用户ID发生变化,说明用户切换了 if (currentUserId.length > 0 && savedUserId.length > 0 && ![currentUserId isEqualToString:savedUserId]) { // 更新当前用户ID [self updateCurrentUserId]; // 清除当前缓存的模型数据(因为是新用户) self.currentFirstRechargeData = nil; // 立即检查新用户的首充状态 [self checkFirstRechargeStatus]; } else if (currentUserId.length > 0 && savedUserId.length == 0) { // 首次登录的情况 [self updateCurrentUserId]; } } #pragma mark - User-specific Storage - (NSString *)getUserSpecificKey:(NSString *)baseKey { if (self.currentUserId.length == 0) { return baseKey; } return [NSString stringWithFormat:@"%@_%@", baseKey, self.currentUserId]; } - (NSString *)getTodayShownForCurrentUser { NSString *userSpecificKey = [self getUserSpecificKey:kTodayShownKey]; return [[NSUserDefaults standardUserDefaults] objectForKey:userSpecificKey]; } - (void)setTodayShownForCurrentUser:(NSString *)date { NSString *userSpecificKey = [self getUserSpecificKey:kTodayShownKey]; [[NSUserDefaults standardUserDefaults] setObject:date forKey:userSpecificKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void)saveCachedModel:(FirstRechargeModel *)model { if (model) { NSString *userSpecificKey = [self getUserSpecificKey:kFirstRechargeModelKey]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model.toJSONObject requiringSecureCoding:NO error:nil]; [[NSUserDefaults standardUserDefaults] setObject:data forKey:userSpecificKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } } - (void)loadCachedModel { NSString *userSpecificKey = [self getUserSpecificKey:kFirstRechargeModelKey]; NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:userSpecificKey]; if (data) { NSDictionary *dict = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSDictionary class] fromData:data error:nil]; if (dict) { self.currentFirstRechargeData = [FirstRechargeModel modelWithJSON:dict]; } } } @end