feat: 优化统一规则逻辑
This commit is contained in:
63
scripts/rule-smoke.mjs
Normal file
63
scripts/rule-smoke.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { transformNotificationToTransaction } from '../src/services/notificationRuleService.js'
|
||||
|
||||
const cases = [
|
||||
{
|
||||
name: 'ignore transit operation notice',
|
||||
notification: {
|
||||
id: 'n1',
|
||||
channel: '绍兴公交',
|
||||
text: '黄酒小镇专线绍兴北站出站,该线路已于2025年10月17日起暂停运营',
|
||||
createdAt: '2026-03-11T00:00:00.000Z',
|
||||
},
|
||||
verify(result) {
|
||||
assert.equal(result.rule, undefined)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'capture transit expense with payment context',
|
||||
notification: {
|
||||
id: 'n2',
|
||||
channel: '绍兴公交',
|
||||
text: '乘车码扣费2元,绍兴北站->黄酒小镇',
|
||||
createdAt: '2026-03-11T00:00:00.000Z',
|
||||
},
|
||||
verify(result) {
|
||||
assert.equal(result.rule?.id, 'transit-card-expense')
|
||||
assert.equal(result.transaction.amount, -2)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'capture bank expense',
|
||||
notification: {
|
||||
id: 'n3',
|
||||
channel: '招商银行',
|
||||
text: '您尾号1234信用卡消费支出3.23元,商户支付宝-上海拉扎斯信息科技有限公司',
|
||||
createdAt: '2026-03-11T00:00:00.000Z',
|
||||
},
|
||||
verify(result) {
|
||||
assert.equal(result.rule?.id, 'bank-expense')
|
||||
assert.equal(result.transaction.amount, -3.23)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'capture wechat income',
|
||||
notification: {
|
||||
id: 'n4',
|
||||
channel: '微信支付',
|
||||
text: '收款到账12.50元,来自张三',
|
||||
createdAt: '2026-03-11T00:00:00.000Z',
|
||||
},
|
||||
verify(result) {
|
||||
assert.equal(result.rule?.id, 'wechat-income')
|
||||
assert.equal(result.transaction.amount, 12.5)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
for (const testCase of cases) {
|
||||
const result = transformNotificationToTransaction(testCase.notification)
|
||||
testCase.verify(result)
|
||||
}
|
||||
|
||||
console.log(`rule smoke passed: ${cases.length} cases`)
|
||||
57
scripts/sync-notification-rules.mjs
Normal file
57
scripts/sync-notification-rules.mjs
Normal file
@@ -0,0 +1,57 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import notificationRules from '../src/config/notificationRules.js'
|
||||
|
||||
const rootDir = path.resolve(import.meta.dirname, '..')
|
||||
const targetFile = path.join(
|
||||
rootDir,
|
||||
'android',
|
||||
'app',
|
||||
'src',
|
||||
'main',
|
||||
'java',
|
||||
'com',
|
||||
'echo',
|
||||
'app',
|
||||
'notification',
|
||||
'NotificationRuleData.kt',
|
||||
)
|
||||
|
||||
const toKotlinString = (value) => JSON.stringify(value).replace(/\$/g, '\\$')
|
||||
|
||||
const toKotlinStringList = (values = []) =>
|
||||
values.length ? `listOf(${values.map((value) => toKotlinString(value)).join(', ')})` : 'emptyList()'
|
||||
|
||||
const toKotlinRegex = (spec) => {
|
||||
if (!spec?.pattern) return 'null'
|
||||
const flags = spec.flags?.includes('i') ? ', setOf(RegexOption.IGNORE_CASE)' : ''
|
||||
return `Regex(${toKotlinString(spec.pattern)}${flags})`
|
||||
}
|
||||
|
||||
const toRuleBlock = (rule) => ` NotificationRuleDefinition(
|
||||
id = ${toKotlinString(rule.id)},
|
||||
label = ${toKotlinString(rule.label)},
|
||||
channels = ${toKotlinStringList(rule.channels)},
|
||||
keywords = ${toKotlinStringList(rule.keywords)},
|
||||
requiredTextPatterns = ${toKotlinStringList(rule.requiredTextPatterns)},
|
||||
direction = NotificationRuleDirection.${rule.direction === 'income' ? 'INCOME' : 'EXPENSE'},
|
||||
defaultCategory = ${toKotlinString(rule.defaultCategory)},
|
||||
autoCapture = ${rule.autoCapture ? 'true' : 'false'},
|
||||
merchantPattern = ${toKotlinRegex(rule.merchantPattern)},
|
||||
defaultMerchant = ${
|
||||
rule.defaultMerchant ? toKotlinString(rule.defaultMerchant) : 'null'
|
||||
},
|
||||
)`
|
||||
|
||||
const content = `package com.echo.app.notification
|
||||
|
||||
// Generated from src/config/notificationRules.js by scripts/sync-notification-rules.mjs.
|
||||
internal object NotificationRuleData {
|
||||
val fallbackRules: List<NotificationRuleDefinition> = listOf(
|
||||
${notificationRules.map(toRuleBlock).join(',\n')}
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
fs.writeFileSync(targetFile, content)
|
||||
console.log(`[rules:sync] wrote ${path.relative(rootDir, targetFile)}`)
|
||||
Reference in New Issue
Block a user