rwadurian/frontend/mobile-app/lib/core/services
hailin 1d1c60e2a2 feat(notification): 新增强制阅读弹窗功能(管理员可配置 requiresForceRead)
## 功能概述

在不影响任何现有业务的前提下,新增"强制阅读弹窗"功能:
- 管理员创建通知时可勾选「需要强制弹窗阅读」
- App 冷启动进入主页 或 从后台切回前台时自动触发检查
- 存在未读且标记 requiresForceRead=true 的通知时,依次逐条弹窗
- 用户无法通过点击背景或返回键关闭弹窗(强制阅读)
- 最后一条通知弹窗底部显示 checkbox「我已经阅读并知晓」
  - 未勾选时"确定"按钮置灰禁用
  - 勾选后"确定"变为金色可点击,点击后所有弹窗消失
- 全部看完后仅对已展示的强制阅读通知按 ID 逐一标记已读
  (不影响普通未读通知的 badge 计数)

## 涉及改动

### 后端 admin-service

- `prisma/schema.prisma`
  - Notification 模型新增字段 `requiresForceRead Boolean @default(false)`

- `prisma/migrations/20260227100000_add_requires_force_read_to_notifications/migration.sql`
  - 手动创建 SQL migration(本地无 DATABASE_URL 环境)
  - 部署时需在服务器执行 `npx prisma migrate deploy`

- `src/domain/entities/notification.entity.ts`
  - 实体类构造器新增 `requiresForceRead`
  - create() / update() 方法均支持该字段,默认值 false

- `src/infrastructure/persistence/mappers/notification.mapper.ts`
  - toDomain() 从 Prisma 记录读取 requiresForceRead
  - toPersistence() 写入 requiresForceRead

- `src/api/dto/request/notification.dto.ts`
  - CreateNotificationDto / UpdateNotificationDto 各新增可选字段 requiresForceRead

- `src/api/dto/response/notification.dto.ts`
  - NotificationResponseDto(管理端)新增 requiresForceRead
  - UserNotificationResponseDto(移动端)新增 requiresForceRead

- `src/api/controllers/notification.controller.ts`
  - create() / update() 透传 requiresForceRead 到 entity

### 前端 admin-web

- `src/services/notificationService.ts`
  - NotificationItem / CreateNotificationRequest / UpdateNotificationRequest 新增 requiresForceRead

- `src/app/(dashboard)/notifications/page.tsx`
  - 通知列表:requiresForceRead=true 时显示红色「强制阅读」标签
  - 创建/编辑表单:新增 checkbox「需要强制弹窗阅读」及说明文字
  - form state / submit payload / edit 初始化均包含 requiresForceRead

### 移动端 mobile-app

- `lib/core/services/notification_service.dart`
  - NotificationItem 新增字段 requiresForceRead(默认 false,fromJson 安全读取)

- `lib/features/notification/presentation/pages/notification_inbox_page.dart`
  - markAsRead / markAllAsRead 重建 NotificationItem 时保留 requiresForceRead

- `lib/features/notification/presentation/widgets/force_read_notification_dialog.dart`(新建)
  - 单条强制阅读弹窗组件
  - 顶部显示通知类型图标 + 进度「1/3」
  - 可滚动内容区展示完整通知
  - 非最后条:「下一条 ▶」按钮(始终可点)
  - 最后一条:checkbox + 「确定」(勾选后才可点)
  - barrierDismissible: false + PopScope(canPop: false),无法逃出

- `lib/features/home/presentation/pages/home_shell_page.dart`
  - 新增状态:_isShowingForceReadDialog(实例,防重入)
                _lastForceReadDialogShownAt(静态,60秒冷却)
  - 新增方法 _checkAndShowForceReadDialog():
      Guard 1: 防重入锁
      Guard 2: 60秒冷却(防回前台闪弹)
      Guard 3: 检查用户已登录
      Guard 4: 检查无其他弹窗在显示
    弹窗期间同时设置 _isShowingDialog=true,阻止后台合同/KYC检查并发
    全部看完后仅标记 forceReadList 中的通知为已读,再 refresh() 刷新 badge
  - initState addPostFrameCallback 中新增调用
  - didChangeAppLifecycleState resumed 分支中新增调用
  - resetContractCheckState() 中重置 _lastForceReadDialogShownAt(账号切换隔离)

## 安全与兼容性

- API 调用失败时静默返回,不阻断用户进入 App
- 仅对 requiresForceRead=true 的通知弹窗,普通通知完全不受影响
- 与现有合同弹窗、KYC弹窗、维护弹窗、更新弹窗无冲突
- 静态冷却变量在账号切换时重置,避免新账号被旧账号冷却影响
- badge 准确:仅标记已展示的强制通知,不动其他未读通知计数

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 19:33:51 -08:00
..
account_service.dart fix(mobile-app): 修复头像更新未同步到账号列表的问题 2025-12-27 10:25:19 -08:00
app_asset_service.dart fix(app-assets): 修复公开API路径双重前缀 + Kong网关路由缺失 2026-02-05 04:36:53 -08:00
auth_event_service.dart feat(auth): 实现修改密码API和Token过期自动跳转登录 2025-12-23 20:25:56 -08:00
authorization_service.dart fix(mobile): 改进用户详情页风格和火柴人进度计算 2025-12-24 01:41:41 -08:00
contract_check_service.dart chore: temporarily disable KYC and contract check logs 2026-01-02 20:51:13 -08:00
contract_signing_service.dart feat(mobile-app): 增强合同PDF下载可靠性和用户体验 2026-01-09 07:12:21 -08:00
customer_service_contact_service.dart feat(customer-service): 客服联系方式从硬编码改为后台可配置 2026-02-05 05:00:25 -08:00
deposit_service.dart fix(mobile-app): always fetch deposit address from server in deposit_service 2026-01-04 10:17:41 -08:00
leaderboard_service.dart fix(mobile-app): correct leaderboard status API path 2026-01-04 03:45:55 -08:00
maintenance_service.dart feat(mobile-app,admin): 添加系统维护功能和通知徽章功能 2025-12-27 23:26:01 -08:00
multi_account_service.dart fix(mobile-app): 增加切换账号全局防护,彻底解决切换期间自动退出登录 2026-02-26 12:41:45 -08:00
notification_service.dart feat(notification): 新增强制阅读弹窗功能(管理员可配置 requiresForceRead) 2026-02-27 19:33:51 -08:00
pending_action_check_service.dart feat(pending-actions): add user pending actions system 2026-01-02 18:22:51 -08:00
pending_action_polling_service.dart feat(mobile-app): 添加待办操作轮询机制 2026-01-05 05:36:18 -08:00
pending_action_service.dart feat(pending-actions): add special deduction feature for admin-created user actions 2026-01-03 07:04:46 -08:00
planting_service.dart fix(mobile-app): 修复认种订单解析和状态检查问题 2026-01-09 02:49:31 -08:00
pre_planting_service.dart fix(pre-planting): 修复前后端 API 路径不匹配导致预种页面打不开 2026-02-26 23:07:47 -08:00
referral_service.dart feat(profile): 添加我的伞下功能 - 展示下级用户树形结构 2025-12-14 10:34:56 -08:00
reward_service.dart feat(ledger): add detailed ledger entry views with source tracking 2026-01-03 20:09:17 -08:00
system_config_service.dart fix(app-assets): 修复公开API路径双重前缀 + Kong网关路由缺失 2026-02-05 04:36:53 -08:00
transfer_service.dart feat(transfer): 树转让功能全量实现(纯新增,零侵入) 2026-02-19 03:44:02 -08:00
tree_pricing_service.dart feat(pricing): 预种每份价格从 3171 调整为 3566 绿积分 2026-02-26 08:02:17 -08:00
wallet_service.dart feat(wallet-service): add offline settlement deduction feature 2026-01-04 06:56:39 -08:00