# 默认超级管理员自动初始化功能

## 功能概述

系统在 API 服务启动时，自动检查数据库中是否存在超级管理员账号。如果不存在，则自动创建一个默认的超级管理员账号，确保系统首次部署后可以立即登录使用。

## 业务背景

**问题**：
- 首次部署新环境（开发、测试、生产）时，数据库中没有任何管理员账号
- 无法登录管理后台，需要手动执行 SQL 或脚本创建管理员
- 人为操作容易出错，不同环境的初始账号可能不一致

**解决方案**：
- 系统启动时自动检测并创建默认超级管理员
- 支持配置文件自定义账号信息
- 幂等操作，多次启动不会重复创建

## 技术实现

### 核心逻辑

#### 1. 初始化时机

```go
// internal/bootstrap/bootstrap.go
func Bootstrap(deps *Dependencies) (*BootstrapResult, error) {
    // ... 其他初始化 ...
    
    // 4. 初始化默认超级管理员（降级处理：失败不中断启动）
    if err := initDefaultAdmin(deps, services); err != nil {
        deps.Logger.Error("初始化默认超级管理员失败", zap.Error(err))
    }
    
    // ... 继续初始化 ...
}
```

**初始化顺序**：
1. Store 层初始化
2. GORM Callbacks 注册
3. Service 层初始化
4. **默认管理员初始化** ← 在此执行
5. Middleware 层初始化
6. Handler 层初始化

#### 2. 检查逻辑

```go
// 检查是否已存在超级管理员（user_type = 1）
var count int64
err := db.Model(&Account{}).Where("user_type = ?", constants.UserTypeSuperAdmin).Count(&count).Error

if count > 0 {
    // 已存在，跳过创建
    logger.Info("超级管理员账号已存在，跳过初始化", zap.Int64("count", count))
    return nil
}

// count == 0，创建默认管理员
```

**确保唯一性**：
- ✅ 检查条件：`user_type = 1`（精确匹配超级管理员类型）
- ✅ 创建条件：只有 `count == 0` 时才创建
- ✅ 幂等性：多次启动不会重复创建
- ✅ 并发安全：启动时单进程执行，无并发问题

#### 3. 账号信息来源

优先级：**配置文件 > 代码默认值**

```go
// 代码默认值（pkg/constants/constants.go）
const (
    DefaultAdminUsername = "admin"
    DefaultAdminPassword = "Admin@123456"
    DefaultAdminPhone    = "13800000000"
)

// 读取配置文件（可选）
username := constants.DefaultAdminUsername
password := constants.DefaultAdminPassword
phone := constants.DefaultAdminPhone

if cfg.DefaultAdmin.Username != "" {
    username = cfg.DefaultAdmin.Username  // 使用配置文件值
}
if cfg.DefaultAdmin.Password != "" {
    password = cfg.DefaultAdmin.Password
}
if cfg.DefaultAdmin.Phone != "" {
    phone = cfg.DefaultAdmin.Phone
}
```

#### 4. 创建账号

```go
account := &model.Account{
    Username: username,
    Phone:    phone,
    Password: password,  // 原始密码，CreateSystemAccount 内部会进行 bcrypt 哈希
    UserType: constants.UserTypeSuperAdmin,  // 1=超级管理员
    Status:   constants.StatusEnabled,       // 1=启用
}

// 调用 Service 层的系统创建方法（绕过当前用户检查）
err := services.Account.CreateSystemAccount(ctx, account)
```

**CreateSystemAccount 方法特点**：
- 绕过 `currentUserID` 检查（因为是系统初始化，没有当前用户）
- 仍然保留用户名和手机号唯一性检查
- 密码自动进行 bcrypt 哈希
- 跳过数据权限过滤（使用 `SkipDataPermission(ctx)`）

### 降级处理

```go
if err := initDefaultAdmin(deps, services); err != nil {
    deps.Logger.Error("初始化默认超级管理员失败", zap.Error(err))
    // 不返回错误，继续启动服务
}
```

**设计理由**：
- 默认管理员创建失败不应导致整个服务无法启动
- 可能的失败原因：数据库连接问题、用户名/手机号冲突等
- 管理员可以通过日志查看失败原因，手动处理

## 配置说明

### 使用代码默认值（无需配置）

直接启动服务，系统使用内置默认值：
- 用户名：`admin`
- 密码：`Admin@123456`
- 手机号：`13800000000`

### 自定义配置

通过环境变量自定义：

```bash
export JUNHONG_DEFAULT_ADMIN_USERNAME="自定义用户名"
export JUNHONG_DEFAULT_ADMIN_PASSWORD="自定义密码"
export JUNHONG_DEFAULT_ADMIN_PHONE="自定义手机号"
```

**注意**：
- 配置项为可选，不参与 `ValidateRequired()` 验证
- 任何字段留空则使用代码默认值
- 密码必须足够复杂（建议包含大小写字母、数字、特殊字符）

## 使用示例

### 场景1：首次部署（空数据库）

```bash
# 启动服务
go run cmd/api/main.go
```

**日志输出**：
```
{"level":"info","msg":"默认超级管理员创建成功","username":"admin","phone":"13800000000"}
```

**结果**：
- 数据库 `tb_account` 表新增一条记录
- `user_type = 1`（超级管理员）
- `username = "admin"`
- `phone = "13800000000"`
- `password` 为 bcrypt 哈希值

### 场景2：已有管理员（再次启动）

```bash
# 再次启动服务
go run cmd/api/main.go
```

**日志输出**：
```
{"level":"info","msg":"超级管理员账号已存在，跳过初始化","count":1}
```

**结果**：
- 跳过创建，不修改现有数据

### 场景3：使用自定义配置

**设置环境变量**：
```bash
export JUNHONG_DEFAULT_ADMIN_USERNAME="myadmin"
export JUNHONG_DEFAULT_ADMIN_PASSWORD="MySecurePass@2024"
export JUNHONG_DEFAULT_ADMIN_PHONE="13900000000"
```

**启动服务**：
```bash
go run cmd/api/main.go
```

**日志输出**：
```
{"level":"info","msg":"默认超级管理员创建成功","username":"myadmin","phone":"13900000000"}
```

## 安全注意事项

### 1. 密码安全

- ✅ 密码使用 bcrypt 哈希存储，不可逆
- ⚠️ 默认密码相对简单，建议首次登录后立即修改
- ⚠️ 生产环境建议通过配置文件使用更复杂的密码

### 2. 权限控制

- ✅ 超级管理员拥有所有权限，跳过数据权限过滤
- ⚠️ 不要将超级管理员账号用于日常操作
- ⚠️ 建议为日常管理员创建独立的平台用户账号

### 3. 审计日志

- ✅ 创建成功/跳过都记录在 `logs/app.log`
- ✅ 包含时间戳、用户名、手机号
- ⚠️ 日志中不会记录明文密码

### 4. 配置安全

- ✅ 配置通过环境变量设置，不存储在代码仓库中
- ⚠️ 确保环境变量安全（使用密钥管理服务或加密存储）
- ⚠️ 生产环境务必修改默认密码

## 手动创建管理员（备用方案）

如果自动初始化失败，可以手动执行以下 SQL：

```sql
-- 生成 bcrypt 哈希密码（使用 Go 代码或在线工具）
-- bcrypt.GenerateFromPassword([]byte("Admin@123456"), bcrypt.DefaultCost)
-- 示例哈希值（实际使用时需重新生成）：
-- $2a$10$abcdefghijklmnopqrstuvwxyz...

INSERT INTO tb_account (
    username, 
    phone, 
    password, 
    user_type, 
    status,
    created_at,
    updated_at
) VALUES (
    'admin',
    '13800000000',
    '$2a$10$...your-bcrypt-hash...',  -- 替换为实际的 bcrypt 哈希
    1,  -- 超级管理员
    1,  -- 启用
    NOW(),
    NOW()
);
```

**生成 bcrypt 哈希工具**：
```go
package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    password := "Admin@123456"
    hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    fmt.Println(string(hash))
}
```

## 相关文件

- `pkg/constants/constants.go` - 默认值常量定义
- `pkg/config/config.go` - 配置结构定义
- `pkg/config/defaults/config.yaml` - 嵌入式默认配置
- `docs/environment-variables.md` - 环境变量配置文档
- `internal/service/account/service.go` - CreateSystemAccount 方法
- `internal/bootstrap/admin.go` - initDefaultAdmin 函数
- `internal/bootstrap/bootstrap.go` - Bootstrap 主流程

## 常见问题

### Q1: 为什么要用系统创建方法而不是直接插入数据库？

**A**: 保持业务逻辑统一性和数据一致性：
- 复用用户名/手机号唯一性检查逻辑
- 自动进行密码哈希处理
- 遵循分层架构原则（通过 Service 层操作）
- 未来扩展更容易（如添加审计日志、事件通知等）

### Q2: 如果数据库有多个超级管理员怎么办？

**A**: 系统检查 `user_type = 1` 的账号数量：
- `count == 0`：创建默认管理员
- `count > 0`：跳过创建（不管有几个）
- 系统不会删除或修改现有的超级管理员

### Q3: 可以通过配置禁用这个功能吗？

**A**: 目前不支持配置禁用，因为这是核心初始化逻辑。如果需要禁用：
1. 手动创建一个超级管理员账号（系统会自动跳过）
2. 或者修改代码注释掉 `initDefaultAdmin()` 调用

### Q4: 创建失败会影响服务启动吗？

**A**: 不会。采用降级处理策略：
- 创建失败只记录错误日志
- 服务继续正常启动
- 管理员可通过日志排查原因并手动创建

### Q5: 如何验证管理员创建成功？

**A**: 三种方式：
1. 查看 `logs/app.log`，搜索 "默认超级管理员创建成功"
2. 查询数据库：`SELECT * FROM tb_account WHERE user_type = 1;`
3. 尝试使用默认账号登录管理后台
