chore: 初始化文档站仓库

This commit is contained in:
2026-09-21 13:38:20 +08:00
commit a4ae7ac979
58 changed files with 4622 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
// 向测试环境补充标签与自定义规则演示数据。
import axios from 'axios'
import { login } from './browser.js'
import { API_URL } from './config.js'
/** 需要补充的标签:名称与颜色 */
const TAGS: Array<{ name: string; color: string }> = [
{ name: '咖啡', color: '#8B5A2B' },
{ name: '外卖', color: '#F97316' },
]
/**
* 创建标签并返回其主键。
*
* @param token 访问令牌
* @param name 标签名称
* @param color 标签颜色
* @returns 标签主键
*/
async function createTag(token: string, name: string, color: string): Promise<number> {
const headers = { Authorization: `Bearer ${token}` }
const res = await axios.post(`${API_URL}/tag`, { name, color }, { headers })
console.log(`标签 ${name} -> ${res.data.code}`)
// 创建接口不返回主键,统一从列表中按名称查找
const list = await axios.get(`${API_URL}/tag/list`, { headers })
const found = (list.data.data as Array<{ id: number; name: string }>).find((t) => t.name === name)
if (!found) throw new Error(`未找到标签: ${name}`)
return found.id
}
/**
* 创建自定义归类规则。
*
* @param token 访问令牌
* @param payload 规则请求体
*/
async function createRule(token: string, payload: Record<string, unknown>): Promise<void> {
const res = await axios.post(`${API_URL}/rule`, payload, { headers: { Authorization: `Bearer ${token}` } })
console.log(`规则 ${payload.name} -> ${res.data.code}`)
}
/**
* 按名称查询分类主键。
*
* @param token 访问令牌
* @param name 分类名称
* @returns 分类主键
*/
async function getCategoryId(token: string, name: string): Promise<number> {
const headers = { Authorization: `Bearer ${token}` }
const res = await axios.get(`${API_URL}/category/list`, { headers })
const found = (res.data.data as Array<{ id: number; name: string }>).find((c) => c.name === name)
if (!found) throw new Error(`未找到分类: ${name}`)
return found.id
}
/**
* 按名称查询标签主键,不存在时创建。
*
* @param token 访问令牌
* @param name 标签名称
* @param color 标签颜色
* @returns 标签主键
*/
async function getTagId(token: string, name: string, color: string): Promise<number> {
const headers = { Authorization: `Bearer ${token}` }
const res = await axios.get(`${API_URL}/tag/list`, { headers })
const found = (res.data.data as Array<{ id: number; name: string }>).find((t) => t.name === name)
if (found) return found.id
return createTag(token, name, color)
}
/** 交易记录(用于给若干记录打标签,供「AI 生成规则」截图使用) */
interface TransactionRow {
id: number
categoryId: number | null
type: string
amount: number
channel: string | null
remark: string | null
transactionTime: string
includeInStats: boolean
}
/**
* 给若干记录打上标签,便于「AI 生成规则」按已打标签记录归纳。
*
* @param token 访问令牌
* @param coffeeId 咖啡标签主键
* @param takeoutId 外卖标签主键
* @param bigAmountId 大额消费标签主键
*/
async function tagSomeTransactions(
token: string,
coffeeId: number,
takeoutId: number,
bigAmountId: number,
): Promise<void> {
const headers = { Authorization: `Bearer ${token}` }
const res = await axios.get(`${API_URL}/transaction/page`, { headers, params: { page: 1, size: 50 } })
const rows = (res.data.data?.records ?? []) as TransactionRow[]
let tagged = 0
for (const row of rows) {
if (tagged >= 6) break
const remark = row.remark ?? ''
let tagIds: number[] = []
if (remark.includes('星巴克') || remark.includes('瑞幸') || remark.includes('咖啡')) {
tagIds = [coffeeId]
} else if (remark.includes('美团') || remark.includes('外卖') || remark.includes('饿了么')) {
tagIds = [takeoutId]
} else if (Number(row.amount) >= 500) {
tagIds = [bigAmountId]
}
if (tagIds.length === 0) continue
await axios.put(`${API_URL}/transaction/${row.id}`, {
categoryId: row.categoryId,
type: row.type,
amount: row.amount,
channel: row.channel,
remark: row.remark,
transactionTime: row.transactionTime,
includeInStats: row.includeInStats,
tagIds,
}, { headers })
tagged++
}
console.log(`已为 ${tagged} 条记录打标签`)
}
/** 清空现有规则,避免重复创建 */
async function clearRules(token: string): Promise<void> {
const headers = { Authorization: `Bearer ${token}` }
const res = await axios.get(`${API_URL}/rule/list`, { headers })
for (const rule of res.data.data as Array<{ id: number }>) {
await axios.delete(`${API_URL}/rule/${rule.id}`, { headers })
}
}
async function main(): Promise<void> {
const token = await login()
await clearRules(token)
const coffeeId = await getTagId(token, '咖啡', '#8B5A2B')
const takeoutId = await getTagId(token, '外卖', '#F97316')
const bigAmountId = await getTagId(token, '大额消费', '#F97316')
const diningId = await getCategoryId(token, '餐饮')
const transportId = await getCategoryId(token, '交通')
console.log('标签:', { 咖啡: coffeeId, 外卖: takeoutId, 大额消费: bigAmountId })
const rules: Array<Record<string, unknown>> = [
{ name: '星巴克归入餐饮', enabled: true, type: 'EXPENSE', keyword: '星巴克', categoryId: diningId, tagIds: [coffeeId] },
{ name: '滴滴出行归入交通', enabled: true, type: 'EXPENSE', keyword: '滴滴', categoryId: transportId },
{ name: '大额支出标记', enabled: true, type: 'EXPENSE', minAmount: 500, tagIds: [bigAmountId] },
]
for (const rule of rules) {
await createRule(token, rule)
}
// 给若干记录打标签,供「AI 生成规则」截图
await tagSomeTransactions(token, coffeeId, takeoutId, bigAmountId)
}
main().catch((err) => {
console.error('FAIL', err)
process.exit(1)
})