From 0912a4d3faa21f148c99a6d9b8daca40f2ca3e8d Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 30 Jun 2026 04:08:58 +0800 Subject: [PATCH] feat: surface notifications in taro admin and profile --- README.md | 8 +- apps/taro/src/pages/student/profile/index.tsx | 125 +++++++++++++++++- apps/taro/src/pages/student/student.css | 24 ++++ apps/taro/src/pages/tenant-admin/admin.css | 5 + .../pages/tenant-admin/marketing/index.tsx | 119 +++++++++++++++++ docs/refactor/backend-handoff-roadmap.md | 4 +- docs/refactor/frontend-handoff-index.md | 10 +- docs/refactor/next-development-todo.md | 6 +- docs/refactor/taro-frontend-integration.md | 7 +- 9 files changed, 290 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 86acd3ed..b65d8aa9 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,11 @@ - Supabase Auth/JWT、租户角色模板、班级/教师/学生范围权限已可联调;生产前还要做真实云端 Auth/JWKS 回归和 RLS 深测。 - 阿里云/腾讯云短信、微信小程序登录、微信网页登录、QQ 登录、手机号绑定/换绑、微信支付、支付宝主链路、微信/支付宝发起退款/查询确认/退款通知、支付/退款补偿 worker 已完成本地适配;资金对账已支持手工/API 账单导入、微信/支付宝官方账单下载任务、provider-bills worker 自动导入比对、差错工单处理、异常订单运营台和人工调整凭证复核报表;真实生产账号、真实回调域名和真实生产账单抽样验收还没接完。 - OSS/COS/Supabase Storage 上传下载签名 provider 已接入;上传后校验、PDF/图片预览、资源访问事件、动态水印上下文、锁定资源 CDN 边界、资源复检 worker、内置 `metadata_rules` 安全扫描和外部 HTTP 杀毒/内容安全 scanner 接入层已完成;Taro 学生资料页已按短期签名和 `watermark.traceId` 渲染可见水印确认/预览第一版。生产还要配置真实扫描服务 endpoint/token,并继续补转码/CDN 级水印、CDN 刷新和对象生命周期策略。 -- Excel/CSV 导入解析已完成并复用 `content_import_jobs/items/issues` 管线;大批量异步导入 worker 基础已接入,支持 queued job 消费、重试和审计;导入后复检、模板下载和字段映射 API 已完成,前端 UI 待接。 +- Excel/CSV 导入解析已完成并复用 `content_import_jobs/items/issues` 管线;大批量异步导入 worker 基础已接入,支持 queued job 消费、重试和审计;导入后复检、模板下载、字段映射 API 和 Taro 租户内容页第一版导入操作台已完成。 - 题库导出已完成服务端结构化 payload、PDF/Word 二进制 worker、每日一练基础导出和每日一练 ZIP 图片素材包;后续还要补更精细试卷模板、多模板排版和导出操作台体验。 - 优惠券复杂规则和核销报表已可联调,包含状态启停、活动分组、最低订单金额、优惠封顶、单用户限次、首单限制、适用套餐/地区、核销明细和活动报表;Taro 租户营销中心已接优惠券规则表单、筛选、核销明细和报表第一版。 -- 勋章管理、手动发放、签到连续天数、积分阈值、反馈解决和积分活动任务自动发放已可联调;积分活动任务、积分兑换商品、兑换订单、优惠券兑换履约、租户后台配置和用户站内通知第一版已完成。后续还要补连续签到奖励规则、练习次数/单词掌握/模考成绩系统触发勋章、外部微信订阅消息/短信推送、积分风控报表、分佣真实打款 provider、发票、批量凭证上传、CRM 富卡片模板、失败告警、死信运营台、销售转化看板、公共题库版本通知和冲突处理操作台。 -- `apps/taro` 已建立 Taro 4 React 跨端前端地基,包含 H5 学生端、租户后台、平台后台三套构建入口、租户解析、统一 API client 和 Supabase Auth client 初始化;学生端第一批页面已接入登录、首页、题库、练习、背单词、知识手册、分数线、AI 择校推荐、资料和个人中心,已新增 `RichContent` 安全渲染组件用于题干、选项、解析、知识手册和逐题复盘,H5 端已用 KaTeX 渲染 `$...$`、`$$...$$`、`\(...\)`、`\[...\]` 公式,私有题图可用 `asset:`/`content_asset:` 资源引用走短期预览签名,已升级背单词为今日计划/单元学习/收藏练习、卡片翻转、发音、美/英音切换和本地位置恢复第一版,资料页已补齐预览/下载的短签名、水印 traceId 和强制水印容器第一版,个人中心已接学习报告、14 天趋势、题型表现、最近练习和 7 日答题榜当前排名第一版;租户后台第一批页面已接入工作台、数据看板、学生/班级、题库内容、营销中心、财务运营和租户设置,营销中心已接 CRM、分佣结算、优惠券规则/核销报表,财务运营已接退款状态机、官方账单任务、对账异常、差错工单和调整凭证第一版,设置页已接主题模板、草稿预览/发布、角色模板和成员绑定第一版;平台后台已接入工作台、租户管理、账务中心、公共题库授权,以及创建租户、状态变更、订阅、账单、收款、用量和题库授权第一版写操作。 +- 勋章管理、手动发放、签到连续天数、积分阈值、反馈解决和积分活动任务自动发放已可联调;积分活动任务、积分兑换商品、兑换订单、优惠券兑换履约、租户后台配置和用户站内通知第一版已完成,Taro 学生个人中心已接消息中心第一版,租户营销中心已接用户通知查看第一版。后续还要补连续签到奖励规则、练习次数/单词掌握/模考成绩系统触发勋章、外部微信订阅消息/短信推送、积分风控报表、分佣真实打款 provider、发票、批量凭证上传、CRM 富卡片模板、失败告警、死信运营台、销售转化看板、公共题库版本通知和冲突处理操作台。 +- `apps/taro` 已建立 Taro 4 React 跨端前端地基,包含 H5 学生端、租户后台、平台后台三套构建入口、租户解析、统一 API client 和 Supabase Auth client 初始化;学生端第一批页面已接入登录、首页、题库、练习、背单词、知识手册、分数线、AI 择校推荐、资料和个人中心,已新增 `RichContent` 安全渲染组件用于题干、选项、解析、知识手册和逐题复盘,H5 端已用 KaTeX 渲染 `$...$`、`$$...$$`、`\(...\)`、`\[...\]` 公式,私有题图可用 `asset:`/`content_asset:` 资源引用走短期预览签名,已升级背单词为今日计划/单元学习/收藏练习、卡片翻转、发音、美/英音切换和本地位置恢复第一版,资料页已补齐预览/下载的短签名、水印 traceId 和强制水印容器第一版,个人中心已接学习报告、14 天趋势、题型表现、最近练习、7 日答题榜当前排名和消息中心第一版;租户后台第一批页面已接入工作台、数据看板、学生/班级、题库内容、营销中心、财务运营和租户设置,营销中心已接 CRM、分佣结算、优惠券规则/核销报表和用户通知查看第一版,财务运营已接退款状态机、官方账单任务、对账异常、差错工单和调整凭证第一版,设置页已接主题模板、草稿预览/发布、角色模板和成员绑定第一版;平台后台已接入工作台、租户管理、账务中心、公共题库授权,以及创建租户、状态变更、订阅、账单、收款、用量和题库授权第一版写操作。 - 根目录已清理为新 Supabase SaaS monorepo 编排层;旧 PocketBase/React 项目和旧构建产物仅保留在 `参考/` 目录作为迁移参考,不进入 Git 提交。 更完整的进度看这些文档: @@ -430,7 +430,7 @@ git diff --check 优先继续补: 1. 真实云端 Auth/JWKS 回归、RLS 深测和生产环境配置验收。 -2. 继续补 Taro 前端:学生端小程序公式真机验收、题图资源后台字段化、个人中心积分任务/兑换 UI、用户消息中心 UI、背单词更细统计、小程序支付与分享,租户后台积分任务/兑换操作台、用户通知查看页、更细导入体验/数据范围 UI/主题素材库/财务复核细节,平台后台租户详情/审计/自动计费增强,小程序兼容验证。 +2. 继续补 Taro 前端:学生端小程序公式真机验收、题图资源后台字段化、个人中心积分任务/兑换 UI、独立消息中心增强、背单词更细统计、小程序支付与分享,租户后台积分任务/兑换操作台、更细导入体验/数据范围 UI/主题素材库/财务复核细节,平台后台租户详情/审计/自动计费增强,小程序兼容验证。 3. 对象存储真实 AV/内容安全扫描服务联调、CDN 防盗链、转码/CDN 级水印和生命周期策略。 4. 题库导出模板精排、导出操作台、真实数据 dry-run、导入字段映射 UI 和复检结果操作台。 5. 真实 OAuth/短信/支付生产账号联调、真实生产账单抽样验收、真实打款 provider、发票、公共题库版本通知/冲突处理操作台、积分活动风控和连续签到奖励深化,以及排行榜防刷/预聚合。 diff --git a/apps/taro/src/pages/student/profile/index.tsx b/apps/taro/src/pages/student/profile/index.tsx index d4650b34..dbf15b65 100644 --- a/apps/taro/src/pages/student/profile/index.tsx +++ b/apps/taro/src/pages/student/profile/index.tsx @@ -13,7 +13,16 @@ import { type LearningTrendItem, type PracticeHistoryItem, } from '@/services/learning'; -import { checkIn, loadBadges, loadExamCountdowns, loadProfile, type StudentProfile } from '@/services/profile'; +import { + checkIn, + loadBadges, + loadExamCountdowns, + loadNotifications, + loadProfile, + updateNotificationStatus, + type StudentProfile, + type UserNotificationItem, +} from '@/services/profile'; import '../student.css'; function percent(value?: number | null) { @@ -72,6 +81,31 @@ function sessionStatusClass(status?: string | null) { return ''; } +type ProfileNotificationStatus = NonNullable; + +function notificationTypeLabel(type?: string | null) { + if (type === 'feedback_status_updated') return '反馈处理'; + if (type === 'feedback_reward_granted') return '反馈奖励'; + if (type === 'badge_granted') return '勋章发放'; + if (type === 'point_exchange_completed') return '兑换完成'; + if (type === 'point_exchange_pending_fulfillment') return '待履约兑换'; + return '系统通知'; +} + +function notificationStatusLabel(status?: string | null) { + if (status === 'unread') return '未读'; + if (status === 'read') return '已读'; + if (status === 'dismissed') return '已忽略'; + if (status === 'archived') return '已归档'; + return '通知'; +} + +function notificationStatusClass(status?: string | null, severity?: string | null) { + if (severity === 'error') return 'danger'; + if (status === 'unread' || severity === 'warning') return 'warning'; + return ''; +} + export default function StudentProfilePage() { const [profile, setProfile] = useState(null); const [plans, setPlans] = useState([]); @@ -83,11 +117,15 @@ export default function StudentProfilePage() { const [learningStats, setLearningStats] = useState(null); const [learningTrend, setLearningTrend] = useState([]); const [practiceHistory, setPracticeHistory] = useState([]); + const [notifications, setNotifications] = useState([]); + const [notificationSummary, setNotificationSummary] = useState>({}); + const [notificationFilter, setNotificationFilter] = useState<'' | ProfileNotificationStatus>('unread'); + const [notificationBusy, setNotificationBusy] = useState(''); const [activationCode, setActivationCode] = useState(''); const [message, setMessage] = useState(''); const [error, setError] = useState(''); - function reload() { + function reload(nextNotificationFilter = notificationFilter) { Promise.all([ loadProfile().catch(() => ({ item: null })), loadSvipPlans().catch(() => ({ items: [] })), @@ -98,6 +136,7 @@ export default function StudentProfilePage() { loadLearningStats(30).catch(() => ({ item: undefined })), loadLearningTrend(14).catch(() => ({ items: [] })), loadPracticeHistory({ limit: 6 }).catch(() => ({ items: [] })), + loadNotifications({ status: nextNotificationFilter || undefined, limit: 8 }).catch(() => ({ items: [], summary: {} })), loadEntitlements().catch(() => ({ summary: {} })), ]).then(([ profilePayload, @@ -109,6 +148,7 @@ export default function StudentProfilePage() { statsPayload, trendPayload, historyPayload, + notificationPayload, ]) => { setProfile(profilePayload.item || null); setPlans(planPayload.items || []); @@ -120,6 +160,8 @@ export default function StudentProfilePage() { setLearningStats(statsPayload.item || null); setLearningTrend(trendPayload.items || []); setPracticeHistory(historyPayload.items || []); + setNotifications(notificationPayload.items || []); + setNotificationSummary(notificationPayload.summary || {}); }).catch(nextError => setError(nextError instanceof Error ? nextError.message : '个人中心加载失败')); } @@ -154,6 +196,37 @@ export default function StudentProfilePage() { Taro.redirectTo({ url: '/pages/student/login/index' }); } + async function reloadNotifications(nextFilter = notificationFilter) { + setNotificationBusy('reload'); + try { + const payload = await loadNotifications({ status: nextFilter || undefined, limit: 12 }); + setNotifications(payload.items || []); + setNotificationSummary(payload.summary || {}); + } catch (nextError) { + setError(nextError instanceof Error ? nextError.message : '消息加载失败'); + } finally { + setNotificationBusy(''); + } + } + + async function changeNotificationFilter(nextFilter: '' | ProfileNotificationStatus) { + setNotificationFilter(nextFilter); + await reloadNotifications(nextFilter); + } + + async function markNotification(item: UserNotificationItem, status: 'read' | 'dismissed' | 'archived') { + setNotificationBusy(`${item.id}:${status}`); + try { + await updateNotificationStatus({ notificationIds: [item.id], status }); + setMessage(status === 'read' ? '消息已标记为已读' : status === 'archived' ? '消息已归档' : '消息已忽略'); + await reloadNotifications(notificationFilter); + } catch (nextError) { + setError(nextError instanceof Error ? nextError.message : '消息状态更新失败'); + } finally { + setNotificationBusy(''); + } + } + const maxAnswers = maxTrendAnswers(learningTrend); const answerStats = learningStats?.answers; const reportStats = learningStats?.reports; @@ -294,6 +367,54 @@ export default function StudentProfilePage() { + + 消息中心 + + {String(notificationSummary.unread || 0)}未读消息 + {String((notificationSummary.read || 0) + (notificationSummary.archived || 0))}已处理消息 + + + {([ + ['', '全部'], + ['unread', '未读'], + ['read', '已读'], + ['archived', '归档'], + ] as Array<['' | ProfileNotificationStatus, string]>).map(([value, label]) => ( + + ))} + + {notifications.length ? ( + + {notifications.map(item => ( + + + {item.title || notificationTypeLabel(item.notificationType)} + {notificationStatusLabel(item.status)} + + {notificationTypeLabel(item.notificationType)} · {dateText(item.createdAt)} + {item.message || '暂无消息内容'} + + {item.status !== 'read' ? ( + + ) : null} + {item.status !== 'archived' ? ( + + ) : null} + {item.actionLabel ? {item.actionLabel} : null} + + + ))} + + ) : 暂无消息。反馈处理、勋章发放和积分兑换结果会出现在这里。} + + 会员套餐 diff --git a/apps/taro/src/pages/student/student.css b/apps/taro/src/pages/student/student.css index a5150c0a..18c03a8b 100644 --- a/apps/taro/src/pages/student/student.css +++ b/apps/taro/src/pages/student/student.css @@ -77,6 +77,12 @@ color: #1e3a8a; } +.secondary-button.active { + border-color: #2563eb; + background: #eff6ff; + color: #1d4ed8; +} + .danger-button { border: 1px solid #fecaca; background: #fff1f2; @@ -143,6 +149,10 @@ background: #eff6ff; } +.notification-row.active { + border-color: #93c5fd; +} + .split-row { display: flex; align-items: flex-start; @@ -222,6 +232,16 @@ line-height: 1.45; } +.notification-message { + color: #334155; +} + +.notification-action-text { + align-self: center; + color: #1d4ed8; + font-weight: 760; +} + .grid-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -337,6 +357,10 @@ padding-bottom: 0; } +.notification-toolbar { + margin-top: 16px; +} + .compact-toolbar .primary-button, .compact-toolbar .secondary-button { min-width: 104px; diff --git a/apps/taro/src/pages/tenant-admin/admin.css b/apps/taro/src/pages/tenant-admin/admin.css index e208795b..cf2d53c3 100644 --- a/apps/taro/src/pages/tenant-admin/admin.css +++ b/apps/taro/src/pages/tenant-admin/admin.css @@ -144,6 +144,11 @@ background: #fff; } +.admin-row.active { + border-color: #93c5fd; + background: #eff6ff; +} + .admin-row-main { display: block; color: #111827; diff --git a/apps/taro/src/pages/tenant-admin/marketing/index.tsx b/apps/taro/src/pages/tenant-admin/marketing/index.tsx index a8da4423..20c505e1 100644 --- a/apps/taro/src/pages/tenant-admin/marketing/index.tsx +++ b/apps/taro/src/pages/tenant-admin/marketing/index.tsx @@ -18,6 +18,7 @@ import { loadCrmConfig, loadCrmQueue, loadTenantMembers, + loadUserNotifications, updateCommissionSettings, updateCommissionSettlementProofStatus, updateCommissionSettlementStatus, @@ -36,6 +37,7 @@ import { type CrmConfigItem, type CrmQueueItem, type TenantMemberItem, + type UserNotificationAdminItem, } from '@/services/tenantAdmin'; import '../admin.css'; @@ -79,6 +81,28 @@ function rateToPercent(value: unknown) { return String((Number(value || 0) * 100).toFixed(2)).replace(/\.00$/, ''); } +function notificationTypeLabel(type?: string | null) { + if (type === 'feedback_status_updated') return '反馈处理'; + if (type === 'feedback_reward_granted') return '反馈奖励'; + if (type === 'badge_granted') return '勋章发放'; + if (type === 'point_exchange_completed') return '兑换完成'; + if (type === 'point_exchange_pending_fulfillment') return '待履约兑换'; + return '系统通知'; +} + +function notificationStatusLabel(status?: string | null) { + if (status === 'unread') return '未读'; + if (status === 'read') return '已读'; + if (status === 'dismissed') return '已忽略'; + if (status === 'archived') return '已归档'; + return '通知'; +} + +function shortDate(value?: string | null) { + if (!value) return '-'; + return value.replace('T', ' ').slice(0, 16); +} + function downloadBase64File(filename: string, contentBase64: string, mimeType: string) { if (typeof document === 'undefined') return false; const link = document.createElement('a'); @@ -146,6 +170,8 @@ export default function TenantMarketingPage() { const [settlements, setSettlements] = useState([]); const [settlementProofs, setSettlementProofs] = useState>({}); const [members, setMembers] = useState([]); + const [userNotifications, setUserNotifications] = useState([]); + const [userNotificationSummary, setUserNotificationSummary] = useState>({}); const [crmForm, setCrmForm] = useState({ enabled: false, url: '', @@ -171,6 +197,11 @@ export default function TenantMarketingPage() { const [memberRate, setMemberRate] = useState({ userId: '', ratePercent: '' }); const [settlementPay, setSettlementPay] = useState({ paymentMethod: 'offline_bank', paymentAccount: '' }); const [proofForm, setProofForm] = useState({ externalUrl: '', title: '', reviewNote: '' }); + const [userNotificationFilter, setUserNotificationFilter] = useState({ + status: 'unread' as '' | NonNullable, + notificationType: '', + userId: '', + }); const [crmStatus, setCrmStatus] = useState(''); const [busy, setBusy] = useState(''); const [error, setError] = useState(''); @@ -196,6 +227,7 @@ export default function TenantMarketingPage() { orderPayload, settlementPayload, memberPayload, + notificationPayload, ] = await Promise.all([ loadCoupons({ status: couponFilter.status || undefined, campaignName: couponFilter.campaignName || undefined }).catch(() => ({ items: [] })), loadCouponReport({ @@ -218,6 +250,7 @@ export default function TenantMarketingPage() { loadCommissionOrders({ ...period, limit: 20 }).catch(() => ({ items: [] })), loadCommissionSettlements({ limit: 20 }).catch(() => ({ items: [] })), loadTenantMembers({ limit: 100 }).catch(() => ({ items: [] })), + loadUserNotifications({ status: userNotificationFilter.status || undefined, limit: 30 }).catch(() => ({ items: [], summary: {} })), ]); const nextCrm = crmConfigPayload.item || null; const nextSettings = commissionSettingsPayload.item || null; @@ -233,6 +266,8 @@ export default function TenantMarketingPage() { setCommissionOrders(orderPayload.items || []); setSettlements(settlementPayload.items || []); setMembers(memberPayload.items || []); + setUserNotifications(notificationPayload.items || []); + setUserNotificationSummary(notificationPayload.summary || {}); if (nextCrm) { setCrmForm({ enabled: nextCrm.enabled === true, @@ -399,6 +434,27 @@ export default function TenantMarketingPage() { } } + async function refreshUserNotifications(override: Partial = {}) { + const nextFilter = { ...userNotificationFilter, ...override }; + setUserNotificationFilter(nextFilter); + setBusy('user-notifications'); + setError(''); + try { + const payload = await loadUserNotifications({ + status: nextFilter.status || undefined, + notificationType: nextFilter.notificationType || undefined, + userId: nextFilter.userId.trim() || undefined, + limit: 50, + }); + setUserNotifications(payload.items || []); + setUserNotificationSummary(payload.summary || {}); + } catch (nextError) { + setError(nextError instanceof Error ? nextError.message : '用户通知加载失败'); + } finally { + setBusy(''); + } + } + async function saveCommissionSettings() { setBusy('commission-settings'); setError(''); @@ -604,6 +660,69 @@ export default function TenantMarketingPage() { 预计佣金{money(commission?.commissionAmountCents)} + + 用户通知 + + 未读{String(userNotificationSummary.unread || 0)} + 已归档{String(userNotificationSummary.archived || 0)} + + + setUserNotificationFilter(prev => ({ ...prev, userId: String(event.detail.value || '') }))} + /> + + + {([ + ['', '全部状态'], + ['unread', '未读'], + ['read', '已读'], + ['dismissed', '已忽略'], + ['archived', '已归档'], + ] as Array<[typeof userNotificationFilter.status, string]>).map(([status, label]) => ( + + ))} + + + {([ + ['', '全部类型'], + ['feedback_status_updated', '反馈处理'], + ['feedback_reward_granted', '反馈奖励'], + ['badge_granted', '勋章发放'], + ['point_exchange_completed', '兑换完成'], + ['point_exchange_pending_fulfillment', '待履约兑换'], + ] as Array<[string, string]>).map(([type, label]) => ( + + ))} + + + + {userNotifications.map(item => ( + + {item.title || notificationTypeLabel(item.notificationType)} · {notificationStatusLabel(item.status)} + {item.userName || item.userPhone || item.userId || '学生'} · {notificationTypeLabel(item.notificationType)} · {shortDate(item.createdAt)} + {item.message || '暂无消息内容'} + 来源 {item.sourceType || '-'} · {item.sourceId || '-'}{item.createdByName ? ` · 操作人 ${item.createdByName}` : ''} + + ))} + + {!userNotifications.length ? 暂无用户通知,或当前角色没有 `notifications:read` 权限。 : null} + + 优惠券规则 diff --git a/docs/refactor/backend-handoff-roadmap.md b/docs/refactor/backend-handoff-roadmap.md index 59c8ef66..f0a5bb21 100644 --- a/docs/refactor/backend-handoff-roadmap.md +++ b/docs/refactor/backend-handoff-roadmap.md @@ -22,7 +22,7 @@ | --- | --- | --- | --- | | 多租户底座 | 可联调 | 租户、域名、品牌、设置、RLS 基础、审计、Supabase JWT/API 身份映射;`npm run test:rls` 已提供本地动态租户隔离验收;`npm run smoke:auth:remote` 已提供真实云端 Supabase access token 回归脚本 | 真实云端 Auth/JWKS 回归需要在预生产/生产环境执行并留档,生产 RLS 深测继续执行 | | 平台后台 | 基础完成 | 租户、套餐、订阅、账单、服务费、用量、公共题库授权、公共题库自动同步 worker、公共题库冲突单条/批量处理 API、公共题库同步通知第一版 | 自动计费、平台审计、更完整运营消息 | -| 租户后台 | 可联调 | 品牌、域名、支付账户、登录配置、密钥掩码、活动、兑换码、优惠券、勋章管理/手动发放/签到/积分/反馈/活动自动发放、积分任务、积分兑换、用户站内通知查看、成员权限、角色模板、菜单/模块/字段权限配置 API、班级/教师/学生范围权限;Taro 工作台已接权限驱动模块入口,学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、备注和跟进任务第一版,租户设置页已接角色模板和成员绑定操作台第一版,营销中心已接 CRM 配置/队列、分佣结算和积分任务/兑换/用户通知 service 第一版 | 更细的数据范围组合、成员批量运营、积分任务/兑换操作台页面、用户通知查看页、真实打款/导出/凭证和完整权限菜单 | +| 租户后台 | 可联调 | 品牌、域名、支付账户、登录配置、密钥掩码、活动、兑换码、优惠券、勋章管理/手动发放/签到/积分/反馈/活动自动发放、积分任务、积分兑换、用户站内通知查看、成员权限、角色模板、菜单/模块/字段权限配置 API、班级/教师/学生范围权限;Taro 工作台已接权限驱动模块入口,学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、备注和跟进任务第一版,租户设置页已接角色模板和成员绑定操作台第一版,营销中心已接 CRM 配置/队列、分佣结算、优惠券规则/核销报表和用户通知查看第一版 | 更细的数据范围组合、成员批量运营、积分任务/兑换操作台页面、真实打款/导出/凭证和完整权限菜单 | | 题库与练习 | 可联调 | 内容入口、任意深度分类、题目集合、顺序/随机/全真模拟蓝图、组卷快照、客观题后端判分、主观题 `selfJudgedCorrect` 自评、阅读理解/案例分析 `subAnswers` 多小题判分、答题、错题、收藏、模考报告、排行榜、公共题库采纳快照、手动同步、自动同步 worker、冲突查询/单条和批量处理 API、公共题库同步通知、JSON/试卷 payload 导出、PDF/Word 异步导出 worker、水印和资料发布路径、每日一练九宫格 metadata、PDF/Word 运营版式和 ZIP 图片素材包 | 长题干/公式图片混排体验、导出模板精排、导出操作台、排行榜防刷/预聚合 | | 背单词 | 可联调 | 单元、单词、进度、收藏、统计、每日计划、JSON/CSV/Excel 导入、排行榜 | 更细复习参数 | | 知识手册 | 可联调 | 科目、章节、条目、Markdown 内容、嵌套 JSON/CSV/Excel 导入 | 富文本资源、版本管理、附件/PDF 关联 | @@ -79,7 +79,7 @@ - 对象存储:上传/下载签名已接入阿里云 OSS、腾讯云 COS、Supabase Storage;上传确认、PDF/图片预览签名、动态水印上下文、assets worker 复检、内置安全扫描、外部 HTTP scanner 接入层和题库导出 PDF/Word/每日一练 ZIP worker 已完成,继续补视频播放防盗链、真实 AV/内容安全服务联调和转码/CDN 级水印。 - 真实数据 dry-run:导出 PocketBase 用户、题库、单词、知识手册、分数线、订单、权益,先跑 `npm run pb:import:dry-run -- --profile=production --json --fail-on-warnings`,确认 `migrationReadiness` 的必需集合和关键字段覆盖率通过,再跑迁移和校验报告。 - 生产环境配置:`.env.example` 和 `npm run readiness:production` / `npm run readiness:production:db` 已补;继续补数据库迁移流程、备份恢复、日志、告警和 API 容器部署说明。 -- Taro scaffold:`apps/taro` 地基已建立;学生端、租户后台、平台后台第一批 H5 页面已接真实 API,学生端已接地区选择、错题/收藏复习、阅读理解/案例分析多小题作答、题目反馈、视频解析、练习/模考报告、收银台、订单详情和售后入口第一版,积分任务/兑换/站内通知 service 已接;平台后台关键写操作第一版已接入,租户工作台已接权限驱动模块入口,租户学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、备注和跟进任务第一版,租户内容页已接公共题库采纳/同步、冲突查看、单条/批量采纳平台或保留本地、导入问题、字段模板预览/下载、上传/粘贴预览、字段别名覆盖、同步/异步导入、异步轮询和复检详情第一版,租户设置页已接角色模板创建/编辑/停用、成员绑定模板和权限可见性配置第一版,租户营销中心已接 CRM 配置/队列、分佣结算和积分任务/兑换/用户通知 service 第一版;下一步补积分任务/兑换页面 UI、消息中心 UI、公式图片混排、更细数据范围 UI、平台后台审计增强和小程序兼容验证。 +- Taro scaffold:`apps/taro` 地基已建立;学生端、租户后台、平台后台第一批 H5 页面已接真实 API,学生端已接地区选择、错题/收藏复习、阅读理解/案例分析多小题作答、题目反馈、视频解析、练习/模考报告、收银台、订单详情、售后入口和消息中心第一版,积分任务/兑换 service 已接;平台后台关键写操作第一版已接入,租户工作台已接权限驱动模块入口,租户学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、备注和跟进任务第一版,租户内容页已接公共题库采纳/同步、冲突查看、单条/批量采纳平台或保留本地、导入问题、字段模板预览/下载、上传/粘贴预览、字段别名覆盖、同步/异步导入、异步轮询和复检详情第一版,租户设置页已接角色模板创建/编辑/停用、成员绑定模板和权限可见性配置第一版,租户营销中心已接 CRM 配置/队列、分佣结算、优惠券规则/核销报表和用户通知查看第一版;下一步补积分任务/兑换页面 UI、独立消息中心增强、公式图片混排、更细数据范围 UI、平台后台审计增强和小程序兼容验证。 ### P1:商用收费和运营能力 diff --git a/docs/refactor/frontend-handoff-index.md b/docs/refactor/frontend-handoff-index.md index eacf01eb..c213e36d 100644 --- a/docs/refactor/frontend-handoff-index.md +++ b/docs/refactor/frontend-handoff-index.md @@ -30,7 +30,7 @@ ## 当前可进入的前端工作 - `apps/taro` 已经建立,且学生端第一批 H5 页面已经可构建:登录、首页、地区选择、题库、练习、错题/收藏、练习报告、视频解析、会员收银台、订单详情、背单词、知识手册、分数线、资料、个人中心。 -- 租户后台第一批 H5 页面已经可构建:工作台、数据看板、学生/班级、题库内容、营销中心、财务运营、租户设置;工作台已接 `/api/tenant-admin/permissions` 做权限驱动模块入口;学生运营页已具备学生创建/更新、状态禁用/恢复、批量导入、批量分班、学生备注和跟进任务第一版;题库内容页已具备公共题库采纳/同步、同步通知、冲突查看、单条/批量采纳平台版本或保留本地版本、导入任务详情、异步轮询、导入问题查看、模板预览/下载、导入后复检详情、JSON/CSV/Excel 选择文件或粘贴内容、后端预览、字段别名覆盖和同步/异步执行导入的第一版操作能力;营销中心已具备 CRM 配置、CRM 队列查看、分佣规则、成员分佣比例、分佣订单、结算单生成/审核/标记打款第一版;财务运营页已具备退款申请/审核/供应商提交与查询、官方账单下载任务、对账批次/异常明细、差错工单处理、人工调整凭证提交/复核和异常订单运营台第一版;租户设置页已具备主题模板、草稿预览/发布、角色模板新建、编辑、停用、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖第一版。 +- 租户后台第一批 H5 页面已经可构建:工作台、数据看板、学生/班级、题库内容、营销中心、财务运营、租户设置;工作台已接 `/api/tenant-admin/permissions` 做权限驱动模块入口;学生运营页已具备学生创建/更新、状态禁用/恢复、批量导入、批量分班、学生备注和跟进任务第一版;题库内容页已具备公共题库采纳/同步、同步通知、冲突查看、单条/批量采纳平台版本或保留本地版本、导入任务详情、异步轮询、导入问题查看、模板预览/下载、导入后复检详情、JSON/CSV/Excel 选择文件或粘贴内容、后端预览、字段别名覆盖和同步/异步执行导入的第一版操作能力;营销中心已具备 CRM 配置、CRM 队列查看、分佣规则、成员分佣比例、分佣订单、结算单生成/审核/标记打款、优惠券规则/核销报表和用户通知查看第一版;财务运营页已具备退款申请/审核/供应商提交与查询、官方账单下载任务、对账批次/异常明细、差错工单处理、人工调整凭证提交/复核和异常订单运营台第一版;租户设置页已具备主题模板、草稿预览/发布、角色模板新建、编辑、停用、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖第一版。 - 平台后台第一批 H5 页面已经可构建:工作台、租户管理、账务中心、公共题库授权。 - 可以继续复刻旧题库学生端主要视觉和交互:勋章展示、小程序端分享/支付体验、背单词更细统计和更完整复盘体验。地区选择、刷题答题卡、后端权威断点续练、本地进度恢复、模拟倒计时、主观题后端自评、阅读理解/案例分析多小题、题干/选项/解析 RichContent 安全渲染、视频解析、题目反馈、模考/练习报告逐题复盘、错题复习、收藏复习、背单词卡片学习/发音/收藏练习、商城收银台、订单详情和售后入口已经有第一版页面。 - 可以按新后端主模型接入内容导航: @@ -84,9 +84,9 @@ | 知识手册 | `apps/taro/src/pages/student/handbook/index.tsx` | `handbook-subjects`、`handbook-chapters`、`handbook-entries`;已接 RichContent 阅读渲染第一版 | | 分数线 | `apps/taro/src/pages/student/scoreline/index.tsx` | `scoreline/records` | | 资料 | `apps/taro/src/pages/student/assets/index.tsx` | `assets`、`assets/preview`、`assets/download`;已接短期签名、过期信息、可见水印覆盖、traceId 展示和强制水印资源外部预览限制 | -| 个人中心 | `apps/taro/src/pages/student/profile/index.tsx` | `profile/me`、`check-in`、`badges`、`exam-countdowns`、`svip-plans`、`orders`、`entitlements`、`activation-codes`、`leaderboard`、`learning/stats`、`learning/trend`、`practice-sessions/history`、`profile/notifications`、`profile/notifications/status`;`src/services/profile.ts` 已补积分任务/兑换/站内通知 service,页面 UI 待接 | +| 个人中心 | `apps/taro/src/pages/student/profile/index.tsx` | `profile/me`、`check-in`、`badges`、`exam-countdowns`、`svip-plans`、`orders`、`entitlements`、`activation-codes`、`leaderboard`、`learning/stats`、`learning/trend`、`practice-sessions/history`、`profile/notifications`、`profile/notifications/status`;已接学习报告、练习趋势、最近练习、会员订单、激活码和消息中心第一版;`src/services/profile.ts` 已补积分任务/兑换 service,积分任务/兑换页面 UI 待接 | -当前页面主要用于打通接口和路由。学生端第一版学习闭环已经覆盖“选地区 -> 进题库 -> 创建 session -> 答题卡/答题/主观题自评/复合题多小题/收藏/反馈/视频 -> 交卷报告逐题复盘 -> 错题/收藏复习”,背单词已经覆盖“单元 -> 今日计划/全单元/收藏练习 -> 卡片翻转 -> 发音 -> 认识/再记上报 -> 本地恢复”,资料页已经覆盖“列表 -> 申请预览/下载短签名 -> 展示水印 traceId -> H5 水印容器预览或确认下载”,个人中心已经覆盖“基础资料 -> 学习报告 -> 14 天趋势 -> 题型表现 -> 最近练习 -> 排名/勋章/签到 -> 会员/订单/激活码”,积分活动/兑换/站内通知 service 已具备但页面 UI 待接,会员闭环已经覆盖“选套餐 -> 领优惠券 -> 下单 -> 创建支付参数 -> 状态轮询 -> 订单详情/售后入口”。`apps/taro/src/components/RichContent.tsx` 是学生端题干、选项、解析和知识手册的统一安全渲染组件:它只支持受控 Markdown 图片、基础表格、粗体、代码和被 parser 识别出的公式 token;H5 端用 KaTeX 渲染 `$...$`、`$$...$$`、`\(...\)`、`\[...\]`,私有题图可用 `asset:`、`content_asset:` 或 `/asset/` 资源引用向后端申请短期预览签名。组件不执行导入内容中的任意 HTML/JS,也会拒绝 `javascript:`、`data:` 等危险图片地址。后续 UI 需要继续按旧题库视觉和 Taro H5/小程序限制优化,并重点补积分任务/兑换页面、消息中心页面、小程序公式真机验收、题图资源字段化、小程序分享/支付容器体验。 +当前页面主要用于打通接口和路由。学生端第一版学习闭环已经覆盖“选地区 -> 进题库 -> 创建 session -> 答题卡/答题/主观题自评/复合题多小题/收藏/反馈/视频 -> 交卷报告逐题复盘 -> 错题/收藏复习”,背单词已经覆盖“单元 -> 今日计划/全单元/收藏练习 -> 卡片翻转 -> 发音 -> 认识/再记上报 -> 本地恢复”,资料页已经覆盖“列表 -> 申请预览/下载短签名 -> 展示水印 traceId -> H5 水印容器预览或确认下载”,个人中心已经覆盖“基础资料 -> 学习报告 -> 14 天趋势 -> 题型表现 -> 最近练习 -> 排名/勋章/签到 -> 会员/订单/激活码 -> 消息筛选/已读/归档”,积分活动/兑换 service 已具备但页面 UI 待接,会员闭环已经覆盖“选套餐 -> 领优惠券 -> 下单 -> 创建支付参数 -> 状态轮询 -> 订单详情/售后入口”。`apps/taro/src/components/RichContent.tsx` 是学生端题干、选项、解析和知识手册的统一安全渲染组件:它只支持受控 Markdown 图片、基础表格、粗体、代码和被 parser 识别出的公式 token;H5 端用 KaTeX 渲染 `$...$`、`$$...$$`、`\(...\)`、`\[...\]`,私有题图可用 `asset:`、`content_asset:` 或 `/asset/` 资源引用向后端申请短期预览签名。组件不执行导入内容中的任意 HTML/JS,也会拒绝 `javascript:`、`data:` 等危险图片地址。后续 UI 需要继续按旧题库视觉和 Taro H5/小程序限制优化,并重点补积分任务/兑换页面、独立消息中心增强、小程序公式真机验收、题图资源字段化、小程序分享/支付容器体验。 ## 已落地的 Taro 租户后台页面 @@ -96,11 +96,11 @@ | 数据看板 | `apps/taro/src/pages/tenant-admin/dashboard/index.tsx` | `tenant-admin/dashboard` | | 学生运营 | `apps/taro/src/pages/tenant-admin/students/index.tsx` | `tenant-admin/classes`、`tenant-admin/teachers`、`tenant-admin/students`、`students/bulk-upsert`、`students/status`、`classes/members/bulk-assign`、`students/notes`、`students/followups` | | 题库内容 | `apps/taro/src/pages/tenant-admin/content/index.tsx` | `tenant-content/content-entries`、`tenant-content/imports`、`imports/detail`、`imports/issues`、`imports/field-mapping`、`imports/templates`、`imports/post-check`、`tenant-content/exports/questions`、`tenant-content/exports/jobs`、`tenant-content/assets/sign-download`、`tenant-content/assets/sign-preview`、`tenant-content/assets/security-scan-events`、`tenant-content/public-question-banks`、`public-question-banks/adopt`、`public-question-banks/sync`、`public-question-banks/conflicts`、`public-question-banks/conflicts/resolve`、`public-question-banks/conflicts/resolve-batch`、`tenant-content/notifications`、`tenant-content/notifications/status` | -| 营销中心 | `apps/taro/src/pages/tenant-admin/marketing/index.tsx` | `tenant-admin/coupons`、`code-batches`、`activation-codes`、`crm/config`、`crm/queue`、`commission/settings`、`member-rate`、`summary`、`orders`、`settlements`、`settlements/generate`、`settlements/status`、`tenant-admin/user-notifications`;`src/services/tenantAdmin.ts` 已补积分任务/兑换配置和记录、用户通知查看 service,页面 UI 待接 | +| 营销中心 | `apps/taro/src/pages/tenant-admin/marketing/index.tsx` | `tenant-admin/coupons`、`code-batches`、`activation-codes`、`crm/config`、`crm/queue`、`commission/settings`、`member-rate`、`summary`、`orders`、`settlements`、`settlements/generate`、`settlements/status`、`tenant-admin/user-notifications`;已接 CRM、分佣、优惠券规则/核销报表和用户通知查看第一版;`src/services/tenantAdmin.ts` 已补积分任务/兑换配置和记录 service,积分任务/兑换操作台 UI 待接 | | 财务运营 | `apps/taro/src/pages/tenant-admin/finance/index.tsx` | `commerce/refunds`、`commerce/refunds/status`、`commerce/operations/anomalies`、`commerce/reconciliation/batches`、`commerce/reconciliation/items`、`commerce/reconciliation/issues/create`、`commerce/reconciliation/issues`、`commerce/reconciliation/issues/status`、`commerce/reconciliation/provider-bills/request`、`commerce/reconciliation/provider-bills/jobs`、`commerce/adjustment-vouchers`、`commerce/adjustment-vouchers/status`、`commerce/adjustment-vouchers/report` | | 租户设置 | `apps/taro/src/pages/tenant-admin/settings/index.tsx` | `tenant-admin/overview`、`domains`、`payment-accounts`、`auth-providers`、`theme-templates`、`theme`、`theme/preview`、`theme/publish`、`permissions`、`GET/PUT role-templates`、`POST role-templates/disable`、`GET/PUT members`、`POST members/disable` | -当前租户后台已有第一批运营操作:工作台按权限矩阵隐藏不可见模块;学生运营页支持学生创建/更新、状态禁用/恢复、批量导入、批量分班、学生备注、跟进任务和完成跟进;题库内容页支持公共题库采纳/同步、同步通知查看与已读/忽略、同步冲突查看、单条/批量采纳平台版本或保留本地版本、导入任务详情、异步 job 轮询、导入问题查看、字段映射/模板预览/下载、JSON/CSV/Excel 导入预览和执行、字段别名覆盖和导入后复检详情,也可接 JSON/试卷 payload 同步导出与 PDF/Word 异步导出 job 轮询,完成后用 `assetId` 走后台资源签名下载/预览;营销中心支持 CRM 配置保存、队列按状态查看、分佣规则、成员分佣比例、分佣订单明细、结算单生成、审核通过/驳回和标记线下打款,积分任务/兑换和用户通知查看 service 已具备但页面 UI 待接;财务运营页支持退款状态流、官方账单任务、对账异常、差错工单和调整凭证,且只通过后端命令层写审计与状态;租户设置页支持主题模板选择、草稿预览、发布、角色模板创建、编辑、停用、权限点、菜单、模块、字段、基础数据范围配置、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖。下一批需要继续补积分任务/兑换操作台、用户通知查看页、更精细的学生导入模板体验、更细数据范围 UI、主题素材库、真实打款 provider、发票、真实生产账单抽样验收和小程序端兼容。 +当前租户后台已有第一批运营操作:工作台按权限矩阵隐藏不可见模块;学生运营页支持学生创建/更新、状态禁用/恢复、批量导入、批量分班、学生备注、跟进任务和完成跟进;题库内容页支持公共题库采纳/同步、同步通知查看与已读/忽略、同步冲突查看、单条/批量采纳平台版本或保留本地版本、导入任务详情、异步 job 轮询、导入问题查看、字段映射/模板预览/下载、JSON/CSV/Excel 导入预览和执行、字段别名覆盖和导入后复检详情,也可接 JSON/试卷 payload 同步导出与 PDF/Word 异步导出 job 轮询,完成后用 `assetId` 走后台资源签名下载/预览;营销中心支持 CRM 配置保存、队列按状态查看、分佣规则、成员分佣比例、分佣订单明细、结算单生成、审核通过/驳回、标记线下打款、优惠券规则/核销报表和用户通知查看第一版;财务运营页支持退款状态流、官方账单任务、对账异常、差错工单和调整凭证,且只通过后端命令层写审计与状态;租户设置页支持主题模板选择、草稿预览、发布、角色模板创建、编辑、停用、权限点、菜单、模块、字段、基础数据范围配置、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖。下一批需要继续补积分任务/兑换操作台、更精细的学生导入模板体验、更细数据范围 UI、主题素材库、真实打款 provider、发票、真实生产账单抽样验收和小程序端兼容。 ## 已落地的 Taro 平台后台页面 diff --git a/docs/refactor/next-development-todo.md b/docs/refactor/next-development-todo.md index 35b4b7e9..db223873 100644 --- a/docs/refactor/next-development-todo.md +++ b/docs/refactor/next-development-todo.md @@ -24,7 +24,7 @@ - 旧题库运营缺口已补一批:考试日期/倒计时、题目反馈/纠错处理、每日签到积分和积分流水、学习排行榜已完成接口和集成测试。 - 勋章管理已完成租户后台维护、手动发放、重复发放幂等、学生个人中心展示、权限点和集成测试;签到连续天数、积分阈值、反馈解决和积分活动任务自动发放第一阶段已完成并纳入 API 集成测试;自动/手动发放会写入用户站内通知。后续补练习次数、单词掌握、模考成绩触发和前端运营配置体验。 - 积分活动和兑换第一阶段已完成:租户后台可配置积分任务/兑换商品并查看领取/兑换记录,学生端可查询任务、领取奖励、查看兑换商品、兑换并生成 `redeem_cost` 积分流水;优惠券兑换会生成 `coupon_redemptions`,手工/自定义商品进入待履约订单;兑换完成或待履约会写入用户站内通知。后续补连续签到奖励配置、积分风控报表和前端操作台。 -- 用户站内通知第一版已完成:反馈处理、反馈奖励、勋章发放、积分兑换会创建 `user_notifications`;学生端可查询/标记状态,租户后台具备 `notifications:read` 权限的成员可查看租户内通知。后续补 Taro 消息中心 UI、租户后台通知页、外部微信订阅消息/短信和批量统计。 +- 用户站内通知第一版已完成:反馈处理、反馈奖励、勋章发放、积分兑换会创建 `user_notifications`;学生端可查询/标记状态,租户后台具备 `notifications:read` 权限的成员可查看租户内通知;Taro 学生个人中心已接消息筛选、已读和归档第一版,租户营销中心已接用户通知查看和筛选第一版。后续补独立消息中心增强、外部微信订阅消息/短信和批量统计。 - 旧商城体验已补齐主链路:订单详情、订单状态轮询、激活码预检查、自用激活码拒绝、优惠券前台领取、下单抵扣、零元订单自动支付开通权益,且手工支付确认已限制为租户后台 `tenant:payment:write` 权限。 - 优惠券商用规则已补齐后端和 Taro 租户后台第一版:租户后台可配置启停/归档、活动分组、最低金额、优惠封顶、单用户限次、首单限制、适用套餐/地区和 metadata;学生领取/下单会由后端复核规则,后台可查核销明细和按活动/日期/券聚合的核销报表。 - 公共题库商业化基础闭环已完成:平台公共题库可由平台管理员按 SaaS 套餐/指定租户/全部活跃租户授权;租户内容管理员只能看到自己被授权的公共题库,并可采纳为本租户题库、内容入口、题目集合和题目快照,采纳后可直接进入练习 session;平台题库后续新增/更新题目可通过手动同步 API 或 `public-banks` worker 进入租户副本,并生成租户内容通知;租户自改题目会返回冲突并保留原内容,后台可查询最近一次冲突明细,并可单条或批量选择采纳平台版本/保留本地版本,冲突处理完成后通知自动 resolved。 @@ -185,7 +185,7 @@ - H5 和小程序共用同一套业务 API client。 - 租户通过域名、小程序配置或启动参数解析。 - 页面主题、品牌、功能开关都从后端租户配置读取;学生端和后台只消费 `/api/tenant/resolve` 的已发布 `branding.theme/publicAssets`,租户后台草稿只通过 `/api/tenant-admin/theme` 查看。 -- 当前已完成 H5 学生端、租户后台、平台后台三套构建入口和统一 API client;学生端、租户后台、平台后台都有第一批真实 API 页面;学生端已补地区选择、刷题答题卡、后端权威断点续练、本地进度恢复、模拟倒计时、主观题后端自评、阅读理解/案例分析多小题、错题/收藏复习、题目反馈、视频解析、练习/模考报告、个人中心学习报告可视化、会员收银台、订单详情、售后入口、题干/选项/解析/知识手册 RichContent 安全渲染、H5 KaTeX 公式渲染、私有资源 ID 题图短签名渲染、逐题复盘、背单词卡片学习/发音/收藏练习第一版,积分任务/兑换/站内通知 service 已补;平台后台已接入创建租户、状态变更、订阅、账单、收款、用量和公共题库授权第一版写操作;租户后台已接权限驱动工作台、学生运营操作台、主题模板预览/发布、角色模板、成员绑定和 CRM/分佣操作台第一版,积分任务/兑换/用户通知查看 service 已补;下一步补小程序公式真机验收、题图资源字段化、个人中心积分任务/兑换 UI、消息中心 UI、状态管理、更细数据范围 UI、学生批量运营增强和小程序兼容验证。 +- 当前已完成 H5 学生端、租户后台、平台后台三套构建入口和统一 API client;学生端、租户后台、平台后台都有第一批真实 API 页面;学生端已补地区选择、刷题答题卡、后端权威断点续练、本地进度恢复、模拟倒计时、主观题后端自评、阅读理解/案例分析多小题、错题/收藏复习、题目反馈、视频解析、练习/模考报告、个人中心学习报告可视化、会员收银台、订单详情、售后入口、站内消息中心第一版、题干/选项/解析/知识手册 RichContent 安全渲染、H5 KaTeX 公式渲染、私有资源 ID 题图短签名渲染、逐题复盘、背单词卡片学习/发音/收藏练习第一版,积分任务/兑换 service 已补;平台后台已接入创建租户、状态变更、订阅、账单、收款、用量和公共题库授权第一版写操作;租户后台已接权限驱动工作台、学生运营操作台、主题模板预览/发布、角色模板、成员绑定、CRM/分佣操作台和用户通知查看第一版,积分任务/兑换 service 已补;下一步补小程序公式真机验收、题图资源字段化、个人中心积分任务/兑换 UI、独立消息中心增强、状态管理、更细数据范围 UI、学生批量运营增强和小程序兼容验证。 ### 第一批页面 @@ -237,7 +237,7 @@ ## 推荐下一步顺序 1. 补租户后台写操作台:公共题库采纳/同步、冲突查看、单条/批量冲突采纳平台或保留本地、导入问题、模板预览/下载、上传/粘贴 preview/import、字段映射编辑、异步导入轮询、导入后复检详情、权限驱动工作台、学生创建/更新/批量导入/批量分班/备注/跟进、角色模板配置、成员绑定模板、CRM 配置/队列/跟进分配策略、分佣规则/成员比例/结算生成审核打款/导出/凭证复核已接第一版;继续补成员批量运营、更细数据范围 UI、真实打款 provider 和发票。 -2. 继续补 Taro 学生端旧体验:地区选择、刷题答题卡、后端权威断点续练、本地进度恢复、模拟倒计时、主观题后端自评、阅读理解/案例分析多小题、视频播放、反馈、模考报告、逐题复盘、错题/收藏专题、个人中心学习报告、收银台、订单详情、售后入口、题干/解析/知识手册 RichContent 安全渲染、H5 KaTeX 公式渲染、私有资源 ID 题图短签名、背单词卡片学习/发音/收藏练习、资料短签名水印预览/下载确认已接第一版,积分任务/兑换/站内通知 service 已补;继续补小程序公式真机验收、题图资源字段化、个人中心积分任务/兑换 UI、消息中心 UI、背单词更细统计、小程序支付容器、分享场景和状态管理。 +2. 继续补 Taro 学生端旧体验:地区选择、刷题答题卡、后端权威断点续练、本地进度恢复、模拟倒计时、主观题后端自评、阅读理解/案例分析多小题、视频播放、反馈、模考报告、逐题复盘、错题/收藏专题、个人中心学习报告、收银台、订单详情、售后入口、站内消息筛选/已读/归档、题干/解析/知识手册 RichContent 安全渲染、H5 KaTeX 公式渲染、私有资源 ID 题图短签名、背单词卡片学习/发音/收藏练习、资料短签名水印预览/下载确认已接第一版,积分任务/兑换 service 已补;继续补小程序公式真机验收、题图资源字段化、个人中心积分任务/兑换 UI、独立消息中心增强、背单词更细统计、小程序支付容器、分享场景和状态管理。 3. 补平台后台增强:租户详情/编辑、平台审计报表、自动计费、账单批量操作和更细平台权限点。 4. 云服务器部署 Supabase/PostgreSQL 和 API,配置对象存储生产环境变量,跑 `check:refactor` 的远程等价测试。 5. 导出现有 PocketBase 数据,按 `docs/refactor/pocketbase-real-data-migration-runbook.md` 做 production dry-run、导入演练、校验和抽样验收。 diff --git a/docs/refactor/taro-frontend-integration.md b/docs/refactor/taro-frontend-integration.md index a2b20c0d..3ce7e2c9 100644 --- a/docs/refactor/taro-frontend-integration.md +++ b/docs/refactor/taro-frontend-integration.md @@ -1219,6 +1219,8 @@ GET /api/profile/notifications?notificationType=badge_granted POST /api/profile/notifications/status ``` +当前 Taro 第一版已经把学生消息中心嵌入 `apps/taro/src/pages/student/profile/index.tsx`:展示未读/已处理统计,支持全部、未读、已读、归档筛选,并允许学生把自己的通知标记为已读或归档。后续如果拆成独立消息中心页面,仍复用同一组接口,不要在前端根据通知 metadata 推导权益、积分或订单状态。 + 状态更新请求: ```json @@ -1269,6 +1271,7 @@ GET /api/tenant-admin/user-notifications?userId=¬ificationType=po - 后台只能查看租户内用户通知,不代学生改已读状态。 - 前端可以按 `actionPath` 做站内跳转,但跳转后的页面仍要重新请求对应业务接口,不能信任通知 metadata 作为最终业务数据。 - 当前支持的通知类型包括 `feedback_status_updated`、`feedback_reward_granted`、`badge_granted`、`point_exchange_completed`、`point_exchange_pending_fulfillment`。 +- 当前 Taro 第一版已经把租户用户通知查看嵌入 `apps/taro/src/pages/tenant-admin/marketing/index.tsx`,支持按状态、类型和学生用户 ID 筛选。该页面只读展示,不提供代学生标记已读、归档或删除的操作。 ## 题库新模型接入方式 @@ -2517,8 +2520,8 @@ npm run build:taro:h5:platform 下一批前端开发重点: -- 学生端:地区选择、题目视频播放、题目反馈、错题/收藏专题页、模考交卷报告、收银台、订单详情、售后入口、题干/解析/知识手册 RichContent 安全渲染、逐题复盘、背单词卡片学习/发音/收藏练习、资料短签名水印预览/下载确认和积分任务/兑换 service 已接第一版;下一批继续补积分任务/兑换页面 UI、真正 KaTeX/小程序公式方案、私有题图签名资源映射、背单词更细统计、小程序支付容器和分享场景。 -- 租户后台:工作台已接权限驱动模块入口;学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、学生备注、跟进任务和完成跟进第一版;题库内容页已接公共题库采纳/同步、冲突查看、单条/批量采纳平台或保留本地、导入问题、模板预览/下载、异步任务轮询和导入后复检第一版;营销中心已接 CRM 配置保存、CRM 队列按状态查看、分佣默认规则、成员分佣比例、分佣订单明细、结算单生成、审核通过/驳回、标记线下打款、优惠券规则表单、筛选、核销明细、核销报表、积分任务和积分兑换 service 第一版;财务运营页已接退款申请/审核/供应商提交与查询、官方账单任务、对账批次/异常明细、差错工单处理、人工调整凭证提交/复核和异常订单运营台第一版;租户设置页已接主题模板、草稿预览、发布、角色模板创建、编辑、停用、权限点、菜单、模块、字段、基础数据范围、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖第一版;下一批继续补积分任务/兑换操作台页面、更精细的学生导入模板体验、真实生产账单抽样验收、真实打款 provider、发票、更细数据范围 UI 和主题素材库。 +- 学生端:地区选择、题目视频播放、题目反馈、错题/收藏专题页、模考交卷报告、收银台、订单详情、售后入口、题干/解析/知识手册 RichContent 安全渲染、逐题复盘、背单词卡片学习/发音/收藏练习、资料短签名水印预览/下载确认和个人中心消息中心第一版已接,积分任务/兑换 service 已接;下一批继续补积分任务/兑换页面 UI、独立消息中心增强、真正 KaTeX/小程序公式方案、私有题图签名资源映射、背单词更细统计、小程序支付容器和分享场景。 +- 租户后台:工作台已接权限驱动模块入口;学生运营页已接学生创建/更新、禁用/恢复、批量导入、批量分班、学生备注、跟进任务和完成跟进第一版;题库内容页已接公共题库采纳/同步、冲突查看、单条/批量采纳平台或保留本地、导入问题、模板预览/下载、异步任务轮询和导入后复检第一版;营销中心已接 CRM 配置保存、CRM 队列按状态查看、分佣默认规则、成员分佣比例、分佣订单明细、结算单生成、审核通过/驳回、标记线下打款、优惠券规则表单、筛选、核销明细、核销报表和用户通知查看第一版,积分任务和积分兑换 service 已接;财务运营页已接退款申请/审核/供应商提交与查询、官方账单任务、对账批次/异常明细、差错工单处理、人工调整凭证提交/复核和异常订单运营台第一版;租户设置页已接主题模板、草稿预览、发布、角色模板创建、编辑、停用、权限点、菜单、模块、字段、基础数据范围、成员搜索/新建、成员绑定模板、成员状态和额外权限覆盖第一版;下一批继续补积分任务/兑换操作台页面、更精细的学生导入模板体验、真实生产账单抽样验收、真实打款 provider、发票、更细数据范围 UI 和主题素材库。 - 平台后台:租户创建、状态变更、订阅开通、账单生成、人工收款确认、用量录入、公共题库授权编辑已接第一版;继续补租户详情/编辑、平台审计、自动计费和批量账单操作。 - 小程序:验证 `Taro.login`、微信支付、分享 scene/referral、Supabase client 兼容性;如不稳定,保留 `apps/api/auth/*` 作为小程序登录适配层。