Files
billai/server/repository/repository.go
cheliangzhao e2e1beb6f7 feat: implement cross-batch Alipay refund reconciliation
When a refund row in an uploaded Alipay bill has no matching expense
row in the same batch (because the original purchase was uploaded in a
prior batch), the refund is now reconciled against the stored record in
bills_cleaned rather than being silently discarded.

Changes:
- analyzer/cleaners/base.py: add unresolved_refunds list to BaseCleaner
- analyzer/cleaners/alipay.py: _aggregate_refunds stores full refund
  metadata (dict); _process_expenses tracks matched keys and populates
  self.unresolved_refunds for unmatched refunds
- analyzer/server.py: thread unresolved_refunds through do_clean,
  CleanResponse, and both /clean endpoints
- server/adapter/adapter.go: add UnresolvedRefund type and field to CleanResult
- server/adapter/http/cleaner.go: deserialize unresolved_refunds from
  Python response and populate CleanResult
- server/repository/repository.go: add ReconcileRefund to BillRepository interface
- server/repository/mongo/repository.go: implement ReconcileRefund —
  full refund soft-deletes the bill, partial refund reduces amount and
  appends remark with original amount and refund order number
- server/handler/upload.go: capture clean result and call ReconcileRefund
  for each unresolved refund after saving cleaned bills
- server/model/response.go: add ReconciledRefundCount to UploadData

Also: add CLAUDE.md (@AGENTS.md), update AGENTS.md, fix DailyTrendChart
missing-date gap by filling zero-expense dates in daily map.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:29:47 +08:00

66 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package repository 定义数据存储层接口
// 负责所有数据持久化操作的抽象
package repository
import "billai-server/model"
// BillRepository 账单存储接口
type BillRepository interface {
// Connect 建立连接
Connect() error
// Disconnect 断开连接
Disconnect() error
// SaveRawBills 保存原始账单数据
SaveRawBills(bills []model.RawBill) (int, error)
// SaveCleanedBills 保存清洗后的账单数据
// 返回: 保存数量、重复数量、错误
SaveCleanedBills(bills []model.CleanedBill) (saved int, duplicates int, err error)
// CheckRawDuplicate 检查原始数据是否重复
CheckRawDuplicate(fieldName, value string) (bool, error)
// CheckCleanedDuplicate 检查清洗后数据是否重复
CheckCleanedDuplicate(bill *model.CleanedBill) (bool, error)
// GetCleanedBills 获取清洗后的账单列表
GetCleanedBills(filter map[string]interface{}) ([]model.CleanedBill, error)
// GetCleanedBillsPaged 获取清洗后的账单列表(带分页)
// 返回: 账单列表、总数、错误
GetCleanedBillsPaged(filter map[string]interface{}, page, pageSize int) ([]model.CleanedBill, int64, error)
// GetBillsAggregate 获取账单聚合统计(总收入、总支出)
// 返回: 总支出、总收入、错误
GetBillsAggregate(filter map[string]interface{}) (totalExpense float64, totalIncome float64, err error)
// GetMonthlyStats 获取月度统计(全部数据,不受筛选条件影响)
// 返回: 月度统计列表、错误
GetMonthlyStats() ([]model.MonthlyStat, error)
// GetBillsNeedReview 获取需要复核的账单
GetBillsNeedReview() ([]model.CleanedBill, error)
// UpdateCleanedBillByID 按 ID 更新清洗后的账单,并返回更新后的记录
UpdateCleanedBillByID(id string, updates map[string]interface{}) (*model.CleanedBill, error)
// DeleteCleanedBillByID 按 ID 删除清洗后的账单
DeleteCleanedBillByID(id string) error
// CountRawByField 按字段统计原始数据数量
CountRawByField(fieldName, value string) (int64, error)
// SoftDeleteJDRelatedBills 软删除描述中包含"京东-订单编号"的非京东账单
// 用于避免京东账单与其他来源(微信、支付宝)账单重复计算
// 返回: 删除数量、错误
SoftDeleteJDRelatedBills() (int64, error)
// ReconcileRefund 将跨批次退款核销到已存储的清洗后账单
// 按 bill_type + (transaction_id == orderNo 或 merchant_order_no == merchantOrderNo) 查找未删除记录
// 全额退款(剩余金额 <= 0.005)则软删除;部分退款则扣减 amount 并追加备注
// 返回: 是否找到并核销了匹配记录、错误(未找到匹配记录不算错误,返回 matched=false
ReconcileRefund(billType, orderNo, merchantOrderNo string, refundAmount float64, refundTime, merchant, description, refundOrderNo string) (matched bool, err error)
}