feat: 优化通知处理逻辑,忽略未命中规则的通知并更新规则同步说明

This commit is contained in:
2025-11-28 17:57:53 +08:00
parent 6b916a4a4b
commit a81b106ac8
2 changed files with 53 additions and 23 deletions

View File

@@ -98,10 +98,17 @@ export const useTransactionEntry = () => {
const queue = await fetchNotificationQueue()
const manualQueue = []
for (const item of queue) {
const { transaction, rule, requiresConfirmation } = transformNotificationToTransaction(
item,
{ ruleSet: ruleSet.value, date: item.createdAt },
)
const { transaction, rule, requiresConfirmation } = transformNotificationToTransaction(item, {
ruleSet: ruleSet.value,
date: item.createdAt,
})
// 如果没有任何规则命中rule 为空),默认忽略该通知,避免系统通知等噪音进入待确认列表
if (!rule) {
await acknowledgeNotification(item.id)
continue
}
if (!requiresConfirmation) {
await transactionStore.addTransaction(transaction)
await acknowledgeNotification(item.id)

View File

@@ -3,8 +3,8 @@ const fallbackRules = [
{
id: 'alipay-expense',
label: '支付宝消费',
channels: ['支付宝', '支付', 'Alipay'],
keywords: ['支付', '扣款', '支出', '消费', '成功支付'],
channels: ['支付宝', 'Alipay'],
keywords: ['支付', '扣款', '支出', '消费', '成功支付'],
direction: 'expense',
defaultCategory: 'Food',
autoCapture: false,
@@ -14,7 +14,7 @@ const fallbackRules = [
{
id: 'alipay-income',
label: '支付宝收款',
channels: ['支付宝', '支付', 'Alipay'],
channels: ['支付宝', 'Alipay'],
keywords: ['到账', '收入', '收款', '入账'],
direction: 'income',
defaultCategory: 'Income',
@@ -25,7 +25,7 @@ const fallbackRules = [
{
id: 'wechat-expense',
label: '微信消费',
channels: ['微信', '微信支付', 'WeChat'],
channels: ['微信支付', '微信', 'WeChat'],
keywords: ['支付', '支出', '扣款', '消费成功'],
direction: 'expense',
defaultCategory: 'Groceries',
@@ -36,7 +36,7 @@ const fallbackRules = [
{
id: 'wechat-income',
label: '微信收款',
channels: ['微信', '微信支付', 'WeChat'],
channels: ['微信支付', '微信', 'WeChat'],
keywords: ['收款', '到账', '入账', '转入'],
direction: 'income',
defaultCategory: 'Income',
@@ -98,28 +98,30 @@ const fallbackRules = [
id: 'transit-card-expense',
label: '公交出行',
channels: [
'公交',
'地铁',
'乘车码',
'交通联合',
'一卡通',
'杭州通互联互通卡',
'杭州通',
'北京一卡通',
'上海公共交通卡',
'深圳通',
'苏州公交卡',
'杭州通互联互通卡',
'交通联合',
'乘车码',
'地铁',
'公交',
'Mi Pay',
'Huawei Pay',
'华为钱包',
'小米智能卡',
'小米钱包',
'OPPO 钱包',
],
keywords: ['扣款', '支出', '乘车', '刷卡', '过闸','行程中','地铁','公交','进站','出站'],
// 对公交卡类通知,只要来源匹配即可视为出行支出,不强制正文关键字
keywords: [],
direction: 'expense',
defaultCategory: 'Transport',
autoCapture: true,
// 公交卡类通知里商户往往不重要,给一个统一名称
// 优先从正文中提取「文三路->祥园路」之类的起点-终点信息作为“商户”
merchantPattern: /([\u4e00-\u9fa5]{2,}\s*(?:-|—|>|→|至|到)\s*[\u4e00-\u9fa5]{2,})/,
// 若无法提取线路,则使用统一名称
defaultMerchant: '公交/地铁出行',
},
]
@@ -128,7 +130,19 @@ let cachedRules = [...fallbackRules]
let remoteRuleFetcher = async () => []
/**
* 允许后续接入服务端规则同步,只需在应用启动时调用并注入实际的 fetch 函数
* 允许后续接入服务端规则同步,只需在应用启动时调用并注入实际的 fetch 函数
* 服务端可以返回一组规则对象,形如:
* {
* id: string;
* label?: string;
* enabled?: boolean; // false 时在本地完全忽略此规则
* priority?: number; // 预留优先级字段(当前仅在服务端使用)
* channels?: string[]; // 匹配通知来源(标题 / 包名)
* keywords?: string[]; // 匹配通知正文的关键字
* direction?: 'income' | 'expense';
* defaultCategory?: string;
* autoCapture?: boolean;
* }
* @param {(currentRules: Array) => Promise<Array>} fetcher
*/
export const setRemoteRuleFetcher = (fetcher) => {
@@ -178,18 +192,28 @@ const extractMerchant = (text, rule) => {
const m = text.match(rule.merchantPattern)
if (m?.[1]) return m[1].trim()
}
const generic = text.match(/向\s*([\u4e00-\u9fa5A-Za-z0-9\-\s&]+)/)
if (generic?.[1]) return generic[1].trim()
const genericPatterns = [
/向\s*([\u4e00-\u9fa5A-Za-z0-9\-\s&]+)/,
/商户[:]?\s*([\u4e00-\u9fa5A-Za-z0-9\-\s&]+)/,
]
for (const pattern of genericPatterns) {
const m = text.match(pattern)
if (m?.[1]) return m[1].trim()
}
const parts = text.split(/|:/)
if (parts.length > 1) {
const candidate = parts[1].split(/[,\s]/)[0]
if (candidate) return candidate.trim()
}
return 'Unknown'
}
const matchRule = (notification, rule) => {
if (!rule) return false
if (!rule || rule.enabled === false) return false
const channel = notification?.channel || ''
const text = notification?.text || ''
const channelHit = !rule.channels?.length || rule.channels.some((item) => channel.includes(item))
@@ -234,4 +258,3 @@ export const transformNotificationToTransaction = (notification, options = {}) =
return { transaction, rule, requiresConfirmation }
}