# Change: Handler 层参数校验安全加固

**功能 ID**: `handler-validation-security-001`

## Why

防止参数校验错误泄露内部实现细节（validator 规则、字段名、类型信息），提升 API 安全性。

**当前问题**：
- Handler 层在参数解析/验证失败时，直接返回 `err.Error()` 给客户端
- 暴露了 validator 内部信息（如 `Field validation for 'Username' failed on the 'required' tag`）
- 泄露了 DTO 字段名、验证规则等内部实现细节
- 客户端可以根据错误信息进行反向工程和攻击探测

**安全风险示例**：

```go
// ❌ 当前实现
if err := c.BodyParser(&req); err != nil {
    return response.Error(c, 400, errors.CodeInvalidParam, "参数解析失败: "+err.Error())
    // 可能返回：参数解析失败: Unmarshal type error: expected=uint got=string field=shop_id offset=123
}

if err := validate.Struct(&req); err != nil {
    return response.Error(c, 400, errors.CodeInvalidParam, "参数验证失败: "+err.Error())
    // 可能返回：参数验证失败: Field validation for 'Username' failed on the 'required' tag
}
```

**影响范围**（基于实际扫描结果）：
- `internal/handler/admin/**` - **29 个文件**，发现 **8 处**错误泄露
- `internal/handler/h5/**` - **3 个文件**，发现 **3 处**错误泄露
- **总计**: 32 个文件，11 处需要修复

## What Changes

### 修复模式

#### 1. 参数解析错误

```go
// ❌ 当前（泄露细节）
if err := c.BodyParser(&req); err != nil {
    return response.Error(c, 400, errors.CodeInvalidParam, "参数解析失败: "+err.Error())
}

// ✅ 修复后（安全）
if err := c.BodyParser(&req); err != nil {
    logger.GetAppLogger().Warn("参数解析失败", 
        zap.String("path", c.Path()),
        zap.String("method", c.Method()),
        zap.Error(err),
    )
    return response.Error(c, 400, errors.CodeInvalidParam, "参数解析失败")
}
```

#### 2. 参数验证错误

```go
// ❌ 当前（泄露细节）
if err := validate.Struct(&req); err != nil {
    return response.Error(c, 400, errors.CodeInvalidParam, "参数验证失败: "+err.Error())
}

// ✅ 修复后（安全）
if err := validate.Struct(&req); err != nil {
    logger.GetAppLogger().Warn("参数验证失败",
        zap.String("path", c.Path()),
        zap.String("method", c.Method()),
        zap.Error(err),
    )
    return errors.New(errors.CodeInvalidParam)  // 使用默认 msg："参数验证失败"
}
```

#### 3. 查询参数解析错误

```go
// ❌ 当前（泄露细节）
page, err := strconv.Atoi(c.Query("page", "1"))
if err != nil {
    return response.Error(c, 400, errors.CodeInvalidParam, "页码格式错误: "+err.Error())
}

// ✅ 修复后（安全）
page, err := strconv.Atoi(c.Query("page", "1"))
if err != nil {
    logger.GetAppLogger().Warn("页码参数格式错误",
        zap.String("path", c.Path()),
        zap.String("page", c.Query("page")),
        zap.Error(err),
    )
    return response.Error(c, 400, errors.CodeInvalidParam, "页码格式错误")
}
```

### 修改清单

#### Admin Handler (29 个文件)

**包含错误泄露的文件（优先修复）**：
- [ ] `auth.go` - 后台认证（3 处错误）
- [ ] `role.go` - 角色管理（4 处错误）
- [ ] `storage.go` - 对象存储（1 处错误）

**其他需检查的文件**：
- [ ] `account.go` - 账号管理
- [ ] `asset_allocation_record.go` - 资产分配记录
- [ ] `authorization.go` - 权限授权
- [ ] `carrier.go` - 运营商管理
- [ ] `commission_withdrawal.go` - 分佣提现
- [ ] `commission_withdrawal_setting.go` - 提现设置
- [ ] `customer_account.go` - 客户账号
- [ ] `device.go` - 设备管理
- [ ] `device_import.go` - 设备导入
- [ ] `enterprise.go` - 企业管理
- [ ] `enterprise_card.go` - 企业卡管理
- [ ] `enterprise_device.go` - 企业设备管理
- [ ] `iot_card.go` - IoT 卡管理
- [ ] `iot_card_import.go` - IoT 卡导入
- [ ] `my_commission.go` - 我的分佣
- [ ] `order.go` - 订单管理
- [ ] `package.go` - 套餐管理
- [ ] `package_series.go` - 套餐系列
- [ ] `permission.go` - 权限管理
- [ ] `shop.go` - 店铺管理
- [ ] `shop_account.go` - 店铺账号
- [ ] `shop_commission.go` - 店铺分佣
- [ ] `shop_package_allocation.go` - 店铺套餐分配
- [ ] `shop_package_batch_allocation.go` - 批量套餐分配
- [ ] `shop_package_batch_pricing.go` - 批量套餐定价
- [ ] `shop_series_allocation.go` - 店铺系列分配

#### H5 Handler (3 个文件)

**包含错误泄露的文件（优先修复）**：
- [ ] `auth.go` - H5 认证（3 处错误）

**其他需检查的文件**：
- [ ] `enterprise_device.go` - H5 企业设备
- [ ] `order.go` - H5 订单

## Decisions

### 错误消息策略

| 场景 | 对外返回 | 日志记录 |
|-----|---------|---------|
| 参数解析失败 | "参数解析失败" | 完整 err.Error() + 请求路径 |
| 参数验证失败 | "参数验证失败" | 完整 validator 错误 + 请求路径 |
| 参数格式错误 | "XX 格式错误" | 完整错误 + 参数值 |
| 业务校验失败 | 业务错误消息 | 不记录（Service 层已记录） |

### 日志级别

- 参数错误：`WARN` 级别（客户端错误）
- 包含必要上下文：path、method、query/body（脱敏后）

### 执行策略

1. **按目录分批**：admin → h5 → personal
2. **搜索模式**：grep 查找所有包含 `err.Error()` 的 handler 文件
3. **验证方式**：为关键 Handler 补充参数校验测试

## Impact

### Security Improvements

- ✅ 隐藏 DTO 字段名和验证规则
- ✅ 防止反向工程和探测攻击
- ✅ 统一错误返回格式
- ✅ 保留完整日志用于问题排查

### Breaking Changes

- 客户端收到的错误消息更通用（不再包含具体字段名）
- 需要前端调整错误提示逻辑（如根据 `code` 显示友好提示）

### Testing Requirements

为关键 Handler 补充参数校验测试：

```go
func TestHandler_InvalidParam(t *testing.T) {
    env := testutils.NewIntegrationTestEnv(t)
    
    t.Run("参数缺失 - 不泄露字段名", func(t *testing.T) {
        resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/users", `{}`)
        require.NoError(t, err)
        assert.Equal(t, 400, resp.StatusCode)
        
        var result map[string]interface{}
        json.Unmarshal(resp.Body, &result)
        
        // 验证不包含 validator 内部细节
        msg := result["msg"].(string)
        assert.NotContains(t, msg, "Field validation")
        assert.NotContains(t, msg, "required")
        assert.NotContains(t, msg, "Username")
        assert.Equal(t, "参数验证失败", msg)
    })
    
    t.Run("参数类型错误 - 不泄露类型信息", func(t *testing.T) {
        resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/users", `{"shop_id":"invalid"}`)
        require.NoError(t, err)
        assert.Equal(t, 400, resp.StatusCode)
        
        var result map[string]interface{}
        json.Unmarshal(resp.Body, &result)
        
        // 验证不包含类型转换细节
        msg := result["msg"].(string)
        assert.NotContains(t, msg, "Unmarshal")
        assert.NotContains(t, msg, "expected=")
        assert.NotContains(t, msg, "got=")
    })
}
```

## Affected Specs

- **UPDATE**: `openspec/specs/error-handling/spec.md`
  - 补充 Handler 层参数校验规范
  - 添加安全加固说明

## Verification Checklist

### 编译检查
```bash
go build -o /tmp/test_api ./cmd/api
```

### 搜索残留泄露点
```bash
# 查找所有可能泄露 err.Error() 的地方
grep -r "err.Error()" internal/handler/ | grep -v "_test.go"

# 查找可能拼接错误的地方
grep -r '"+err' internal/handler/ | grep -v "_test.go"
grep -r '"+.*Error()' internal/handler/ | grep -v "_test.go"
```

### 集成测试
```bash
source .env.local && go test -v ./tests/integration/...
```

### 手动验证

发送错误参数到关键接口，确认返回：

- ✅ 参数缺失：返回 "参数验证失败"（不包含字段名）
- ✅ 参数类型错误：返回 "参数解析失败"（不包含类型信息）
- ✅ 参数格式错误：返回通用格式错误（不包含具体值）
- ✅ 日志中包含完整错误信息（用于排查）

### 日志检查

检查 `logs/app.log` 确认：
- 参数错误记录为 `WARN` 级别
- 包含完整的 validator 错误（仅日志）
- 包含请求路径和方法

## Estimated Effort

| 任务 | 预估时间 |
|-----|---------|
| Admin Handler（29 个文件，8 处错误） | 2h |
| H5 Handler（3 个文件，3 处错误） | 0.5h |
| 测试验证 | 1h |
| 文档更新 | 0.5h |

**总计**：约 4 小时
