refactor: 完成项目架构重构,统一模块导入路径

- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层
- 修复所有相对路径导入错误,统一调整为扁平化模块引用
- 更新多平台 pubspec 版本号与依赖库版本
- 补充后端功能问题管理后台与脚本工具
- 调整部分页面的快捷方式文案适配新功能
- 更新部分翻译覆盖率与API文档
This commit is contained in:
Developer
2026-06-12 08:48:33 +08:00
parent 33e347dea7
commit f91be94e9c
560 changed files with 28489 additions and 14780 deletions

View File

@@ -0,0 +1,146 @@
/// ============================================================
/// 闲言APP — 收藏过期提醒服务
/// 创建时间: 2026-06-12
/// 更新时间: 2026-06-12
/// 作用: 定期检查长期未访问的收藏,发送提醒通知
/// 上次更新: 新建
/// ============================================================
import '../../storage/database/app_database.dart';
import '../../storage/kv_storage.dart';
import '../../utils/logger.dart';
import 'notification_service.dart';
/// 收藏过期提醒服务
class FavoriteReminderService {
FavoriteReminderService._();
// ============================================================
// 存储键
// ============================================================
static const _lastCheckKey = 'favorite.reminder_last_check';
static const _reminderDaysKey = 'favorite.reminder_days';
static const _enabledKey = 'favorite.reminder_enabled';
static const _dismissedKey = 'favorite.reminder_dismissed';
/// 默认过期天数
static const defaultReminderDays = 30;
// ============================================================
// 检查逻辑
// ============================================================
/// 检查是否需要提醒(每天最多检查一次)
static Future<bool> shouldCheck() async {
final enabled = KvStorage.getBool(_enabledKey) ?? true;
if (!enabled) return false;
final lastCheck = KvStorage.getInt(_lastCheckKey) ?? 0;
final now = DateTime.now().millisecondsSinceEpoch;
final diff = now - lastCheck;
// 24小时检查一次
return diff >= 24 * 60 * 60 * 1000;
}
/// 执行过期检查,返回过期的收藏数量
static Future<int> checkExpiredFavorites() async {
try {
if (!await shouldCheck()) return 0;
final db = AppDatabase.instance;
final days = KvStorage.getInt(_reminderDaysKey) ?? defaultReminderDays;
final cutoff = DateTime.now().subtract(Duration(days: days));
// 查询收藏时间早于cutoff的收藏
final expiredCount = await _countOldFavorites(db, cutoff);
// 更新最后检查时间
KvStorage.setInt(
_lastCheckKey,
DateTime.now().millisecondsSinceEpoch,
);
// 如果有过期收藏,发送通知
if (expiredCount > 0) {
await _sendReminderNotification(expiredCount, days);
}
return expiredCount;
} catch (e) {
Log.e('FavoriteReminderService.checkExpiredFavorites', e);
return 0;
}
}
/// 统计过期收藏数量
static Future<int> _countOldFavorites(
AppDatabase db,
DateTime cutoff,
) async {
try {
final sentences = await db.getFavoriteSentences();
final cutoffMs = cutoff.millisecondsSinceEpoch;
return sentences.where((s) {
final createdAt = s.createdAt;
return createdAt.millisecondsSinceEpoch < cutoffMs;
}).length;
} catch (e) {
Log.e('FavoriteReminderService._countOldFavorites', e);
return 0;
}
}
/// 发送提醒通知
static Future<void> _sendReminderNotification(int count, int days) async {
try {
await NotificationService.showImmediate(
'💝 收藏回忆提醒',
'你有 $count 个收藏超过 $days 天未访问,来看看吧',
);
Log.i('FavoriteReminderService: 已推送收藏过期提醒 ($count个过期)');
} catch (e) {
Log.e('FavoriteReminderService._sendReminderNotification', e);
}
}
// ============================================================
// Banner 显示控制
// ============================================================
/// 是否已关闭当前提醒Banner
static bool get isDismissed =>
KvStorage.getBool(_dismissedKey) ?? false;
/// 关闭提醒Banner
static void dismissBanner() => KvStorage.setBool(_dismissedKey, true);
/// 重置Banner关闭状态新检查周期开始时调用
static void resetDismissed() => KvStorage.setBool(_dismissedKey, false);
// ============================================================
// 设置读写
// ============================================================
/// 是否启用提醒
static bool get isEnabled => KvStorage.getBool(_enabledKey) ?? true;
/// 设置是否启用
static void setEnabled(bool value) => KvStorage.setBool(_enabledKey, value);
/// 获取提醒天数
static int get reminderDays =>
KvStorage.getInt(_reminderDaysKey) ?? defaultReminderDays;
/// 设置提醒天数
static void setReminderDays(int days) =>
KvStorage.setInt(_reminderDaysKey, days);
/// 立即检查(用于手动触发)
static Future<int> checkNow() async {
// 重置检查时间,强制执行
KvStorage.setInt(_lastCheckKey, 0);
resetDismissed();
return checkExpiredFavorites();
}
}

View File

@@ -163,6 +163,9 @@ class LocalNotificationService {
Log.i('通知点击: payload=$payload');
// 递增点击计数
_trackClickCount(payload);
final context = rootNavigatorKey.currentContext;
if (context == null || !context.mounted) return;
@@ -191,6 +194,28 @@ class LocalNotificationService {
}
}
// ============================================================
// 点击计数映射
// ============================================================
/// payload → NotificationCenter 计数器类型映射
static const _payloadToClickType = <String, String>{
'daily_sentence': 'daily_recommend',
'signin_reminder': 'signin',
'study_progress': 'study_progress',
'readlater': 'readlater',
'daily_fortune': 'fortune',
'marketing': 'marketing',
};
/// 根据 payload 递增对应类型的点击计数
static void _trackClickCount(String payload) {
final type = _payloadToClickType[payload];
if (type != null) {
NotificationCenter.incrementClickCount(type);
}
}
// ============================================================
// 构建渠道通知详情
// ============================================================

View File

@@ -1,9 +1,9 @@
/// ============================================================
/// 闲言APP — 统一通知中心
/// 创建时间: 2026-05-22
/// 更新时间: 2026-05-22
/// 更新时间: 2026-06-12
/// 作用: 合并 NotificationScheduler + DailyNotifyService统一管理所有本地通知调度
/// 上次更新: 修复Readlater开关未触发configureAll的问题
/// 上次更新: 新增推送计数器pushCount/clickCount每次调度/点击时+1
/// ============================================================
import 'local_notification_service.dart';
@@ -36,6 +36,22 @@ class NotificationCenter {
static const _keyFortuneMinute = 'fortune_minute';
static const _keyStudyProgressEnabled = 'study_progress_enabled';
static const _keyReadlaterEnabled = 'notif_charging_readlater';
static const _keyMarketingPushEnabled = 'marketing_push_enabled';
// ── 推送计数器存储键 ──
static const _keyPushCountDailyRecommend = 'push_count_daily_recommend';
static const _keyPushCountSignin = 'push_count_signin';
static const _keyPushCountStudyProgress = 'push_count_study_progress';
static const _keyPushCountReadlater = 'push_count_readlater';
static const _keyPushCountFortune = 'push_count_fortune';
static const _keyPushCountMarketing = 'push_count_marketing';
static const _keyClickCountDailyRecommend = 'click_count_daily_recommend';
static const _keyClickCountSignin = 'click_count_signin';
static const _keyClickCountStudyProgress = 'click_count_study_progress';
static const _keyClickCountReadlater = 'click_count_readlater';
static const _keyClickCountFortune = 'click_count_fortune';
static const _keyClickCountMarketing = 'click_count_marketing';
// ── 全局通知开关 ──
@@ -141,6 +157,80 @@ class NotificationCenter {
await configureAll();
}
// ── 营销推送 ──
static bool get isMarketingPushEnabled =>
KvStorage.getBool(_keyMarketingPushEnabled) ?? false;
static Future<void> setMarketingPushEnabled(bool v) async {
await KvStorage.setBool(_keyMarketingPushEnabled, v);
}
// ── 推送计数器 ──
/// 各类型推送次数
static int get pushCountDailyRecommend =>
KvStorage.getInt(_keyPushCountDailyRecommend) ?? 0;
static int get pushCountSignin =>
KvStorage.getInt(_keyPushCountSignin) ?? 0;
static int get pushCountStudyProgress =>
KvStorage.getInt(_keyPushCountStudyProgress) ?? 0;
static int get pushCountReadlater =>
KvStorage.getInt(_keyPushCountReadlater) ?? 0;
static int get pushCountFortune =>
KvStorage.getInt(_keyPushCountFortune) ?? 0;
static int get pushCountMarketing =>
KvStorage.getInt(_keyPushCountMarketing) ?? 0;
/// 各类型点击次数
static int get clickCountDailyRecommend =>
KvStorage.getInt(_keyClickCountDailyRecommend) ?? 0;
static int get clickCountSignin =>
KvStorage.getInt(_keyClickCountSignin) ?? 0;
static int get clickCountStudyProgress =>
KvStorage.getInt(_keyClickCountStudyProgress) ?? 0;
static int get clickCountReadlater =>
KvStorage.getInt(_keyClickCountReadlater) ?? 0;
static int get clickCountFortune =>
KvStorage.getInt(_keyClickCountFortune) ?? 0;
static int get clickCountMarketing =>
KvStorage.getInt(_keyClickCountMarketing) ?? 0;
/// 递增推送计数
static Future<void> incrementPushCount(String type) async {
final key = 'push_count_$type';
final current = KvStorage.getInt(key) ?? 0;
await KvStorage.setInt(key, current + 1);
}
/// 递增点击计数
static Future<void> incrementClickCount(String type) async {
final key = 'click_count_$type';
final current = KvStorage.getInt(key) ?? 0;
await KvStorage.setInt(key, current + 1);
}
/// 重置所有计数器
static Future<void> resetAllCounts() async {
final keys = [
_keyPushCountDailyRecommend,
_keyPushCountSignin,
_keyPushCountStudyProgress,
_keyPushCountReadlater,
_keyPushCountFortune,
_keyPushCountMarketing,
_keyClickCountDailyRecommend,
_keyClickCountSignin,
_keyClickCountStudyProgress,
_keyClickCountReadlater,
_keyClickCountFortune,
_keyClickCountMarketing,
];
for (final key in keys) {
await KvStorage.setInt(key, 0);
}
}
// ── 核心调度 ──
static Future<void> configureAll() async {
@@ -187,6 +277,7 @@ class NotificationCenter {
minute: minute,
payload: 'daily_sentence',
);
await incrementPushCount('daily_recommend');
}
static Future<void> _configureSigninReminder() async {
@@ -204,6 +295,7 @@ class NotificationCenter {
minute: minute,
payload: 'signin_reminder',
);
await incrementPushCount('signin');
}
static Future<void> _configureSolarTerm() async {
@@ -244,6 +336,7 @@ class NotificationCenter {
minute: minute,
payload: 'daily_fortune',
);
await incrementPushCount('fortune');
}
static Future<void> _configureStudyProgress() async {
@@ -257,6 +350,7 @@ class NotificationCenter {
hour: 20,
payload: 'study_progress',
);
await incrementPushCount('study_progress');
}
// ── 节气数据 ──

View File

@@ -13,7 +13,7 @@ import 'package:battery_plus/battery_plus.dart';
import '../../storage/kv_storage.dart';
import '../../utils/logger.dart';
import 'notification_service.dart';
import '../../../features/mine/user_center/services/user_center_service.dart';
import '../../../features/user_center/services/user_center_service.dart';
class ReadlaterReminderService {
ReadlaterReminderService._();