fix:修改bug
This commit is contained in:
parent
365f783ea0
commit
18aff3b682
@ -51,24 +51,33 @@ var allowedTypes = map[string]struct{}{
|
||||
|
||||
// NotificationService 通知服务业务层。
|
||||
type NotificationService struct {
|
||||
db *gorm.DB
|
||||
notifRepo *repository.NotificationRepository
|
||||
statsRepo *repository.NotificationStatsRepository
|
||||
device *UserDeviceService // 用于推送时拉取用户活跃 cid;若 nil 则跳过推送
|
||||
pusher push.Pusher // 推送客户端;若 nil 则跳过推送
|
||||
db *gorm.DB
|
||||
notifRepo *repository.NotificationRepository
|
||||
statsRepo *repository.NotificationStatsRepository
|
||||
device *UserDeviceService // 用于推送时拉取用户活跃 cid;若 nil 则跳过推送
|
||||
pusher push.Pusher // 推送客户端;若 nil 则跳过推送
|
||||
rateLimiter *push.RateLimiter // P1-3:推送节流器,按 (user, star, type) 60s 滑窗,>1 改 summary 标题
|
||||
}
|
||||
|
||||
// NewNotificationService 创建 NotificationService。
|
||||
//
|
||||
// 参数 device 与 pusher 用于在 CreateNotification 成功后触发手机通知栏推送;
|
||||
// 若任一为 nil,则不会触发推送(便于测试 / 关闭推送功能)。
|
||||
//
|
||||
// P1-3 修复:rateLimiter 在 NewNotificationService 中初始化,pusher 为 nil 时不创建
|
||||
// (避免占用内存)。triggerPush 内部仍需 nil 检查,避免 panic。
|
||||
func NewNotificationService(db *gorm.DB, device *UserDeviceService, pusher push.Pusher) *NotificationService {
|
||||
var rl *push.RateLimiter
|
||||
if pusher != nil {
|
||||
rl = push.NewRateLimiter(60000)
|
||||
}
|
||||
return &NotificationService{
|
||||
db: db,
|
||||
notifRepo: repository.NewNotificationRepository(db),
|
||||
statsRepo: repository.NewNotificationStatsRepository(db),
|
||||
device: device,
|
||||
pusher: pusher,
|
||||
db: db,
|
||||
notifRepo: repository.NewNotificationRepository(db),
|
||||
statsRepo: repository.NewNotificationStatsRepository(db),
|
||||
device: device,
|
||||
pusher: pusher,
|
||||
rateLimiter: rl,
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,9 +269,22 @@ func (s *NotificationService) triggerPush(n *model.Notification) {
|
||||
go func() {
|
||||
cctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// P1-3 fix:节流后再发送——60s 滑窗内同一 (user, star, type) 多条推送,
|
||||
// 首条走原 title,后续合并为 "您有 N 条新<type中文>" summary。
|
||||
// 注意只改 Title,不覆盖 Content(Content 是具体业务文案,客户端要展示)。
|
||||
title := n.Title
|
||||
if s.rateLimiter != nil {
|
||||
key := fmt.Sprintf("%d:%d:%s", n.UserID, n.StarID, n.Type)
|
||||
mode, count := s.rateLimiter.Allow(key)
|
||||
if mode == "summary" && count > 1 {
|
||||
title = fmt.Sprintf("您有 %d 条新%s", count, push.TypeChinese(n.Type))
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.pusher.Send(cctx, push.Payload{
|
||||
CIDs: cids,
|
||||
Title: n.Title,
|
||||
Title: title,
|
||||
Content: n.Content,
|
||||
Data: data,
|
||||
}); err != nil {
|
||||
|
||||
@ -13,7 +13,9 @@ import { clearAllSandboxTmpFiles } from '@/utils/ioPath.js'
|
||||
// 记录上次隐藏时间的 storage key
|
||||
const HIDE_TIME_KEY = "app_last_hide_time";
|
||||
|
||||
// 推送事件去抖:同 type 在 PUSH_DEBOUNCE_MS 毫秒内只处理一次(避免系统重投/重复抵达)
|
||||
// 推送事件去抖:同 (type, notification_id) 在 PUSH_DEBOUNCE_MS 毫秒内只处理一次
|
||||
// (避免系统重投/重复抵达)—— P1-4 修复:之前 keyed-by-type 导致
|
||||
// "不同 nid 的同 type 推送"被丢弃,现在按 (type, nid) 区分。
|
||||
const PUSH_DEBOUNCE_MS = 10000;
|
||||
const recentByType = {};
|
||||
|
||||
@ -320,13 +322,16 @@ export default {
|
||||
const envelope = payloadStr ? JSON.parse(payloadStr) : {};
|
||||
const payload = envelope?.data || data || {};
|
||||
const t = payload.type;
|
||||
const nid = payload.notification_id || "";
|
||||
const debounceKey = `${t}:${nid}`;
|
||||
const now = Date.now();
|
||||
// V1.2.7 10s 同 type 去抖,避免系统重投导致重复 dispatch
|
||||
if (t && recentByType[t] && now - recentByType[t] < PUSH_DEBOUNCE_MS) {
|
||||
console.log("[push] debounce skip", t);
|
||||
// 10s 同 (type, notification_id) 去抖,避免系统重投导致重复 dispatch
|
||||
// (P1-4:原 key=type 会丢不同的 nid,改为组合 key)
|
||||
if (debounceKey && recentByType[debounceKey] && now - recentByType[debounceKey] < PUSH_DEBOUNCE_MS) {
|
||||
console.log("[push] debounce skip", debounceKey);
|
||||
return;
|
||||
}
|
||||
if (t) recentByType[t] = now;
|
||||
if (debounceKey) recentByType[debounceKey] = now;
|
||||
// 派发到 mailbox store:更新未读计数 + (若在站内信页)PREPEND_ITEM
|
||||
this.$store.dispatch("mailbox/applyPushPayload", {
|
||||
data: payload,
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"packagename" : "online.topfans.app",
|
||||
"packagename" : "online.topfans.app",
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -44,13 +44,22 @@ const typeMap = {
|
||||
const typeLabel = computed(() => typeMap[props.notification.type] || props.notification.type || '通知')
|
||||
|
||||
const businessMeta = computed(() => {
|
||||
// data 是 JSON 字符串,反序列化展示业务字段
|
||||
// data 可能是 string(JSON) 或 object(proto structpb.AsMap() 的产物)。
|
||||
// P0-1 fix:网关 convertNotification 走 AsMap() 直接发对象,旧写法 JSON.parse(obj) 会 SyntaxError → {}。
|
||||
let data = {}
|
||||
try { data = JSON.parse(props.notification.data || '{}') } catch (e) { data = {} }
|
||||
const raw = props.notification.data
|
||||
if (raw) {
|
||||
if (typeof raw === 'string') {
|
||||
try { data = JSON.parse(raw) } catch (e) { data = {} }
|
||||
} else if (typeof raw === 'object') {
|
||||
data = raw
|
||||
}
|
||||
}
|
||||
const list = []
|
||||
if (data.feedback_id) list.push({ k: '反馈 ID', v: data.feedback_id })
|
||||
if (data.report_id) list.push({ k: '举报 ID', v: data.report_id })
|
||||
if (data.resolved_action) list.push({ k: '处理动作', v: actionLabel(data.resolved_action) })
|
||||
// P1-1 fix:admin backend moderation_admin.py 用 action(规范名),不是 resolved_action
|
||||
if (data.action) list.push({ k: '处理动作', v: actionLabel(data.action) })
|
||||
if (data.category_code) list.push({ k: '分类', v: data.category_code })
|
||||
if (data.original_title) list.push({ k: '原标题', v: data.original_title })
|
||||
if (data.activity_id) list.push({ k: '活动 ID', v: data.activity_id })
|
||||
|
||||
@ -70,8 +70,10 @@ const notification = computed(() => {
|
||||
|
||||
async function loadOne() {
|
||||
try {
|
||||
// P0-2 fix:utils/api.js 统一返回 { code, message, data: { items, ... } },
|
||||
// 旧写法读 resp.items 永远是 undefined → find 永远 null → detailCache 不写。
|
||||
const resp = await getNotificationsApi({ type: type.value, page: 1, page_size: 50 })
|
||||
const item = (resp.items || []).find(n => n.id === nid.value)
|
||||
const item = (resp.data?.items || []).find(n => n.id === nid.value)
|
||||
if (item) store.commit('mailbox/SET_DETAIL_CACHE', item)
|
||||
} catch (e) {
|
||||
console.warn('[mailbox/detail] loadOne failed:', e)
|
||||
|
||||
@ -23,7 +23,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||
import { useStore } from 'vuex'
|
||||
// import Header from '../components/Header.vue'
|
||||
import MailboxGroup from './components/MailboxGroup.vue'
|
||||
import { useMailboxCenter } from './composables/useMailboxCenter.js'
|
||||
@ -35,8 +36,11 @@ const {
|
||||
onMarkAllRead, onClearAll,
|
||||
onSelectItem, onLongPress,
|
||||
isCollapsed, toggleCollapse,
|
||||
onMarkRead,
|
||||
} = useMailboxCenter()
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const goBack = () => {
|
||||
// 获取页面栈
|
||||
const pages = getCurrentPages();
|
||||
@ -51,7 +55,34 @@ const goBack = () => {
|
||||
}
|
||||
};
|
||||
|
||||
onShow(() => { loadAll() })
|
||||
// P1-5 fix:App.vue 推送 click 带 ?focus=mailbox&nid=N&type=T 跳进来,
|
||||
// 这里读 query → 标记已读 → 跳到详情。让"点推送"直达具体消息,不再只是"进收件箱"。
|
||||
// 用 composable 已有的 onMarkRead(nid, type),不走 store 的同名 action(不存在)。
|
||||
let focusHandled = false
|
||||
onLoad((options) => {
|
||||
if (!options || focusHandled) return
|
||||
const nid = Number(options.nid)
|
||||
const type = options.type || ''
|
||||
if (nid && type) {
|
||||
focusHandled = true
|
||||
// fire-and-forget:失败仅 warn,不影响用户进站
|
||||
onMarkRead(nid, type).catch((e) => {
|
||||
console.warn('[mailbox/index] markAsRead from push failed:', e)
|
||||
})
|
||||
uni.navigateTo({ url: `/pages/mailbox/detail?nid=${nid}&type=${encodeURIComponent(type)}` })
|
||||
}
|
||||
})
|
||||
|
||||
// P1-2 fix:用户停留在收件箱页时,推送来要 PREPEND_ITEM(否则只剩角标更新、列表无新条目)。
|
||||
// onShow 置 SET_IS_IN_PAGE=true,onHide=false。App.vue 走 mailbox/applyPushPayload 时,
|
||||
// mailbox store 的 applyPushPayload 会读 rootState.mailbox.isInMailboxPage 决定是否 PREPEND_ITEM。
|
||||
onShow(() => {
|
||||
store.commit('mailbox/SET_IS_IN_PAGE', true)
|
||||
loadAll()
|
||||
})
|
||||
onHide(() => {
|
||||
store.commit('mailbox/SET_IS_IN_PAGE', false)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user