# 环境变量配置文档

## 概述

君鸿卡管系统使用嵌入式配置机制，默认配置编译在二进制文件中，通过环境变量进行覆盖。

**环境变量前缀**: `JUNHONG_`  
**格式规则**: 配置路径中的 `.` 替换为 `_`，全部大写

## 必填配置

以下配置没有合理的默认值，必须通过环境变量设置：

### 数据库配置

| 环境变量 | 说明 | 示例 |
|---------|------|------|
| `JUNHONG_DATABASE_HOST` | 数据库主机地址 | `localhost` |
| `JUNHONG_DATABASE_PORT` | 数据库端口 | `5432` |
| `JUNHONG_DATABASE_USER` | 数据库用户名 | `postgres` |
| `JUNHONG_DATABASE_PASSWORD` | 数据库密码 | `your_password` |
| `JUNHONG_DATABASE_DBNAME` | 数据库名称 | `junhong_cmp` |

### Redis 配置

| 环境变量 | 说明 | 示例 |
|---------|------|------|
| `JUNHONG_REDIS_ADDRESS` | Redis 主机地址 | `localhost` |

### JWT 配置

| 环境变量 | 说明 | 示例 |
|---------|------|------|
| `JUNHONG_JWT_SECRET_KEY` | JWT 签名密钥（生产环境必须修改） | `your-secret-key` |

### 微信配置

#### 微信公众号

| 环境变量 | 说明 | 示例 |
|---------|------|------|
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_ID` | 公众号 AppID（必填） | `wxabcdef1234567890` |
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_SECRET` | 公众号 AppSecret（必填） | `abcdef1234567890` |
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_TOKEN` | 服务器配置Token（可选） | `your_token` |
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_AES_KEY` | 消息加解密Key（可选） | `` |
| `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_OAUTH_REDIRECT_URL` | OAuth回调URL（可选） | `https://your-domain.com/callback` |

#### 微信支付

| 环境变量 | 说明 | 示例 |
|---------|------|------|
| `JUNHONG_WECHAT_PAYMENT_APP_ID` | 支付 AppID（必填，通常与公众号相同） | `wxabcdef1234567890` |
| `JUNHONG_WECHAT_PAYMENT_MCH_ID` | 商户号（必填） | `1234567890` |
| `JUNHONG_WECHAT_PAYMENT_API_V3_KEY` | APIv3 密钥（必填，32位字符串） | `your_apiv3_key_32_chars_here` |
| `JUNHONG_WECHAT_PAYMENT_API_V2_KEY` | APIv2 密钥（可选，部分接口需要） | `` |
| `JUNHONG_WECHAT_PAYMENT_CERT_PATH` | 商户证书路径（必填） | `/app/certs/apiclient_cert.pem` |
| `JUNHONG_WECHAT_PAYMENT_KEY_PATH` | 商户私钥路径（必填） | `/app/certs/apiclient_key.pem` |
| `JUNHONG_WECHAT_PAYMENT_SERIAL_NO` | 证书序列号（必填） | `1234567890ABCDEF` |
| `JUNHONG_WECHAT_PAYMENT_NOTIFY_URL` | 支付回调URL（必填） | `https://api.your-domain.com/api/callback/wechat-pay` |
| `JUNHONG_WECHAT_PAYMENT_HTTP_DEBUG` | HTTP调试日志（可选） | `false` |
| `JUNHONG_WECHAT_PAYMENT_TIMEOUT` | HTTP请求超时（可选） | `30s` |

**配置说明**：
- 微信公众号和支付配置缺失时，服务启动会失败（FATAL 错误）
- 证书文件必须可读（权限 600 或 644）
- APIv3 密钥必须是 32 位字符串
- 证书序列号可通过 `openssl x509 -in apiclient_cert.pem -noout -serial` 获取
- 详细配置指南参见 [微信集成使用指南](wechat-integration/使用指南.md)

## 可选配置

以下配置有合理的默认值，可按需覆盖：

### 服务器配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_SERVER_ADDRESS` | `:3000` | 服务监听地址 |
| `JUNHONG_SERVER_READ_TIMEOUT` | `30s` | 读取超时时间 |
| `JUNHONG_SERVER_WRITE_TIMEOUT` | `30s` | 写入超时时间 |
| `JUNHONG_SERVER_SHUTDOWN_TIMEOUT` | `30s` | 优雅关闭超时 |
| `JUNHONG_SERVER_PREFORK` | `false` | 是否启用预分叉模式 |

### 数据库连接池

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_DATABASE_SSLMODE` | `disable` | SSL 模式 |
| `JUNHONG_DATABASE_MAX_OPEN_CONNS` | `25` | 最大打开连接数 |
| `JUNHONG_DATABASE_MAX_IDLE_CONNS` | `10` | 最大空闲连接数 |
| `JUNHONG_DATABASE_CONN_MAX_LIFETIME` | `1h` | 连接最大生命周期 |

### Redis 配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_REDIS_PORT` | `6379` | Redis 端口 |
| `JUNHONG_REDIS_PASSWORD` | `""` | Redis 密码 |
| `JUNHONG_REDIS_DB` | `0` | Redis 数据库编号 |
| `JUNHONG_REDIS_POOL_SIZE` | `100` | 连接池大小 |
| `JUNHONG_REDIS_MIN_IDLE_CONNS` | `10` | 最小空闲连接数 |
| `JUNHONG_REDIS_DIAL_TIMEOUT` | `5s` | 连接超时 |
| `JUNHONG_REDIS_READ_TIMEOUT` | `3s` | 读取超时 |
| `JUNHONG_REDIS_WRITE_TIMEOUT` | `3s` | 写入超时 |

### 日志配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_LOGGING_LEVEL` | `info` | 日志级别 (debug/info/warn/error) |
| `JUNHONG_LOGGING_DEVELOPMENT` | `false` | 开发模式（启用彩色输出） |
| `JUNHONG_LOGGING_APP_LOG_FILENAME` | `logs/app.log` | 应用日志文件路径 |
| `JUNHONG_LOGGING_APP_LOG_MAX_SIZE` | `100` | 日志文件最大大小 (MB) |
| `JUNHONG_LOGGING_APP_LOG_MAX_BACKUPS` | `7` | 最大备份文件数 |
| `JUNHONG_LOGGING_APP_LOG_MAX_AGE` | `30` | 日志保留天数 |
| `JUNHONG_LOGGING_APP_LOG_COMPRESS` | `true` | 是否压缩旧日志 |
| `JUNHONG_LOGGING_ACCESS_LOG_FILENAME` | `logs/access.log` | 访问日志文件路径 |

### JWT 配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_JWT_TOKEN_DURATION` | `24h` | Token 有效期 |
| `JUNHONG_JWT_ACCESS_TOKEN_TTL` | `24h` | Access Token TTL |
| `JUNHONG_JWT_REFRESH_TOKEN_TTL` | `168h` | Refresh Token TTL (7天) |

### 客户端配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_CLIENT_REQUIRE_PHONE_BINDING` | `true` | 是否强制 C 端用户绑定手机号（`true` 强制，`false` 不强制） |

### 队列配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_QUEUE_CONCURRENCY` | `10` | 并发 Worker 数量 |
| `JUNHONG_QUEUE_RETRY_MAX` | `3` | 最大重试次数 |
| `JUNHONG_QUEUE_TIMEOUT` | `30m` | 任务超时时间 |

### Worker 运行配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_WORKER_ROLE` | `all` | Worker 运行角色。`all` 为单实例兼容模式，`leader` 负责主动调度和初始化，`consumer` 只消费队列任务 |
| `JUNHONG_WORKER_INSTANCE_NAME` | `""` | Worker 实例名称，用于多实例日志区分，建议在多实例部署时配置唯一值 |

**使用说明**：
- 单实例部署可不设置 `JUNHONG_WORKER_ROLE`，默认使用 `all`，行为与历史版本一致
- 多实例部署推荐固定 `1 leader + N consumer`
- `leader` / `all` 只能保留一个实例承担主动调度职责，横向扩容时请新增 `consumer`

**示例**：

```bash
# 单实例兼容模式（默认可省略）
JUNHONG_WORKER_ROLE=all

# 多实例 leader
JUNHONG_WORKER_ROLE=leader
JUNHONG_WORKER_INSTANCE_NAME=worker-leader-1

# 多实例 consumer
JUNHONG_WORKER_ROLE=consumer
JUNHONG_WORKER_INSTANCE_NAME=worker-consumer-1
```

### Gateway 服务

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_GATEWAY_BASE_URL` | 默认配置文件值 | Gateway API 基础地址，配置后必须为 HTTP/HTTPS URL |
| `JUNHONG_GATEWAY_APP_ID` | 默认配置文件值 | Gateway 应用 ID |
| `JUNHONG_GATEWAY_APP_SECRET` | 默认配置文件值 | Gateway 应用密钥 |
| `JUNHONG_GATEWAY_TIMEOUT` | `60` | Gateway 请求超时秒数，允许 5～300 |

生产环境必须通过环境变量覆盖应用身份和密钥。IoT 卡固定档位限速只向 Gateway 发送卡 ICCID 和档位编码，不支持设备限速。

### 企业微信 Adapter

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_WECOM_BASE_URL` | `https://qyapi.weixin.qq.com` | 企业微信 API 基础地址 |
| `JUNHONG_WECOM_TIMEOUT` | `10s` | 企业微信外部请求超时时间 |
| `JUNHONG_WECOM_CREDENTIAL_ENCRYPTION_KEY` | `""` | 启用企业微信功能前必填，32 字节随机密钥的 Base64 文本 |

可使用 `openssl rand -base64 32` 生成加密密钥。密钥缺失或格式错误不会阻止不使用企微的环境启动，但企业微信配置保存、查询和连接测试会按失败关闭处理。更换密钥前必须先制定已有凭据的重新加密方案，否则历史密文将无法读取。

### 审批旧入口切换

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_APPROVAL_LEGACY_REFUND_MANUAL_ENABLED` | `true` | 是否保留退款人工通过/拒绝入口；仅在企微真实闭环和存量清单完成后改为 `false` |
| `JUNHONG_APPROVAL_LEGACY_OFFLINE_RECHARGE_PAY_ENABLED` | `true` | 是否保留线下充值人工确认入口；仅在企微真实闭环和存量清单完成后改为 `false` |

两个开关默认开启，避免部署新版本时中断存量旧 provider。关闭后接口仍保留路由，但返回“入口已停用”，前端只展示本地批量投影的审批渠道和状态。配置变更需要重启 API 实例，Worker 不依赖这两个旧入口开关。

### 限流中间件

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_MIDDLEWARE_ENABLE_RATE_LIMITER` | `false` | 启用限流 |
| `JUNHONG_MIDDLEWARE_RATE_LIMITER_MAX` | `100` | 最大请求数 |
| `JUNHONG_MIDDLEWARE_RATE_LIMITER_EXPIRATION` | `1m` | 时间窗口 |
| `JUNHONG_MIDDLEWARE_RATE_LIMITER_STORAGE` | `memory` | 存储后端 (memory/redis) |

### 对象存储配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_STORAGE_PROVIDER` | `""` | 存储提供商 (s3) |
| `JUNHONG_STORAGE_TEMP_DIR` | `/tmp/junhong` | 临时文件目录 |
| `JUNHONG_STORAGE_S3_ENDPOINT` | `""` | S3 端点 |
| `JUNHONG_STORAGE_S3_REGION` | `""` | S3 区域 |
| `JUNHONG_STORAGE_S3_BUCKET` | `""` | S3 存储桶 |
| `JUNHONG_STORAGE_S3_ACCESS_KEY_ID` | `""` | S3 访问密钥 ID |
| `JUNHONG_STORAGE_S3_SECRET_ACCESS_KEY` | `""` | S3 访问密钥 |
| `JUNHONG_STORAGE_S3_USE_SSL` | `true` | 是否使用 SSL |
| `JUNHONG_STORAGE_S3_PATH_STYLE` | `true` | 是否使用路径风格 |
| `JUNHONG_STORAGE_PRESIGN_UPLOAD_EXPIRES` | `1h` | 预签名上传 URL 有效期 |
| `JUNHONG_STORAGE_PRESIGN_DOWNLOAD_EXPIRES` | `1h` | 预签名下载 URL 有效期 |

### 短信配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_SMS_GATEWAY_URL` | `""` | 短信网关 URL |
| `JUNHONG_SMS_USERNAME` | `""` | 短信账号 |
| `JUNHONG_SMS_PASSWORD` | `""` | 短信密码 |
| `JUNHONG_SMS_SIGNATURE` | `""` | 短信签名 |
| `JUNHONG_SMS_TIMEOUT` | `10s` | 请求超时 |

### 默认管理员

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_DEFAULT_ADMIN_USERNAME` | `admin` | 默认管理员用户名 |
| `JUNHONG_DEFAULT_ADMIN_PASSWORD` | `Admin@123456` | 默认管理员密码 |
| `JUNHONG_DEFAULT_ADMIN_PHONE` | `13800000000` | 默认管理员手机号 |

### 轮询自动触发配置

| 环境变量 | 默认值 | 说明 |
|---------|--------|------|
| `JUNHONG_POLLING_AUTO_TRIGGER_ENABLE_AUTO_TRIGGER` | `true` | 是否启用C端实名自动触发，可临时关闭 |
| `JUNHONG_POLLING_AUTO_TRIGGER_AUTO_TRIGGER_SYSTEM_USER_ID` | `1` | 自动触发使用的系统用户ID（暂用 SuperAdmin ID=1，**生产环境建议创建专用平台账号并更新此值**） |

## Docker Compose 示例

```yaml
version: '3.8'

services:
  api:
    image: registry.boss160.cn/junhong/cmp-fiber-api:latest
    environment:
      - JUNHONG_DATABASE_HOST=postgres
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=junhong
      - JUNHONG_DATABASE_PASSWORD=secret123
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=your-production-secret-key
      - JUNHONG_LOGGING_LEVEL=info
    volumes:
      - ./logs:/app/logs
    ports:
      - "3000:3000"

  worker:
    image: registry.boss160.cn/junhong/cmp-fiber-worker:latest
    environment:
      - JUNHONG_DATABASE_HOST=postgres
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=junhong
      - JUNHONG_DATABASE_PASSWORD=secret123
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=your-production-secret-key
      - JUNHONG_WORKER_ROLE=all
      - JUNHONG_WORKER_INSTANCE_NAME=worker-all-1
    volumes:
      - ./logs:/app/logs

  postgres:
    image: postgres:14
    environment:
      - POSTGRES_USER=junhong
      - POSTGRES_PASSWORD=secret123
      - POSTGRES_DB=junhong_cmp

  redis:
    image: redis:6
```

- 单实例部署默认就是 `all`，上例显式写出只是为了便于排查日志；不设置 `JUNHONG_WORKER_ROLE` 时效果相同
- 多实例部署推荐 `1 leader + N consumer`，不要直接复制多个完整 `all` Worker

```yaml
services:
  worker-leader:
    image: registry.boss160.cn/junhong/cmp-fiber-worker:latest
    environment:
      - JUNHONG_DATABASE_HOST=postgres
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=junhong
      - JUNHONG_DATABASE_PASSWORD=secret123
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=your-production-secret-key
      - JUNHONG_WORKER_ROLE=leader
      - JUNHONG_WORKER_INSTANCE_NAME=worker-leader-1

  worker-consumer-1:
    image: registry.boss160.cn/junhong/cmp-fiber-worker:latest
    environment:
      - JUNHONG_DATABASE_HOST=postgres
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=junhong
      - JUNHONG_DATABASE_PASSWORD=secret123
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=your-production-secret-key
      - JUNHONG_WORKER_ROLE=consumer
      - JUNHONG_WORKER_INSTANCE_NAME=worker-consumer-1

  worker-consumer-2:
    image: registry.boss160.cn/junhong/cmp-fiber-worker:latest
    environment:
      - JUNHONG_DATABASE_HOST=postgres
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=junhong
      - JUNHONG_DATABASE_PASSWORD=secret123
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=your-production-secret-key
      - JUNHONG_WORKER_ROLE=consumer
      - JUNHONG_WORKER_INSTANCE_NAME=worker-consumer-2
```

## 本地开发

本地开发可以创建 `.env` 文件（不要提交到 Git）：

```bash
# .env
JUNHONG_DATABASE_HOST=localhost
JUNHONG_DATABASE_PORT=5432
JUNHONG_DATABASE_USER=postgres
JUNHONG_DATABASE_PASSWORD=postgres
JUNHONG_DATABASE_DBNAME=junhong_cmp_dev
JUNHONG_REDIS_ADDRESS=localhost
JUNHONG_JWT_SECRET_KEY=dev-secret-key
JUNHONG_LOGGING_LEVEL=debug
JUNHONG_LOGGING_DEVELOPMENT=true
```

然后使用 `source .env` 加载环境变量后运行：

```bash
source .env
go run cmd/api/main.go
```
