## 解析器接口设计

- **DDS-Section**: 6.1 解析器接口设计 + 6.2 解析器注册中心
- **DDS-Lines**: L438-L498

### Extract

#### BillParser 接口

```go
// BillParser 是所有平台解析器必须实现的接口
type BillParser interface {
    // Platform 返回平台标识符，如 "alipay", "wechat", "ccb"
    Platform() string

    // Detect 根据文件元信息和表头判断是否为本平台文件
    Detect(fileMeta FileMeta, header []string) bool

    // Parse 解析指定文件，返回原始记录列表
    Parse(ctx context.Context, reader io.Reader) ([]RawBillRecord, error)
}
```

#### FileMeta 文件元信息

```go
type FileMeta struct {
    FileName string
    FileType string // csv, xlsx, txt
    FileHash string
    FileSize int64
}
```

#### RawBillRecord 原始账单记录

```go
type RawBillRecord struct {
    SourcePlatform  string
    SourceRecordID  string
    RawFields       map[string]string // 原始 K-V 字段
    RowNo           int
    RowFingerprint  string
}
```

#### Registry 解析器注册中心

```go
type Registry struct {
    parsers []BillParser
}

func NewRegistry() *Registry {
    r := &Registry{}
    // 注册所有解析器
    r.Register(&alipay.Parser{})
    r.Register(&wechat.Parser{})
    r.Register(&ccb.Parser{})
    r.Register(&icbc.Parser{})
    return r
}

// Detect 自动检测文件对应的解析器
func (r *Registry) Detect(meta FileMeta, header []string) (BillParser, error) {
    for _, p := range r.parsers {
        if p.Detect(meta, header) {
            return p, nil
        }
    }
    return nil, ErrUnknownPlatform
}
```

#### 新增解析器步骤

1. 在 `parser/<platform>/` 目录下创建 `<platform>_parser.go`
2. 实现 `BillParser` 接口的 3 个方法
3. 在 `parser/registry.go` 的 `NewRegistry()` 中调用 `r.Register()`
4. `Detect()` 方法基于文件名特征或 CSV 表头关键词判定
5. `Parse()` 逐行读取文件，填充 `RawBillRecord.RawFields`

#### V1.0 支持的平台

| 平台 | 标识符 | 文件格式 | 优先级 |
|------|--------|----------|--------|
| 支付宝 | `alipay` | CSV | P0（优先） |
| 微信支付 | `wechat` | CSV | P0（优先） |
| 建设银行 | `ccb` | CSV/Excel | P1（次优先） |
| 工商银行 | `icbc` | CSV/Excel | P1（次优先） |
