# 子千项目技能文档

## 1. 项目概述

子千项目是一个基于 Gin 框架开发的 Web 应用，提供了一套完整的工具函数库，用于简化开发流程和统一代码风格。本文档主要介绍项目中的核心工具函数，包括响应处理、身份验证、IP 处理等功能。

## 2. 核心工具包

### 2.1 utils/zi.go - 响应处理工具

`Zi` 是一个核心工具类，用于统一 API 响应格式，提供了多种响应方法，包括成功响应、错误响应、调试响应等。

#### 2.1.1 Zi.Echo - 成功响应

**功能**：返回标准的成功响应，状态码为 200。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `data interface{}` - 要返回的数据，可以是任意类型

**返回格式**：
```json
{
  "code": 200,
  "message": "ok",
  "data": {...}, // 传入的数据
  "uuid": "..."  // 请求 UUID
}
```

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func testHandler(c *gin.Context) {
    // 简单成功响应，仅返回状态
    u.Zi.Echo(c, gin.H{})
    
    // 返回复杂数据结构
    userInfo := map[string]interface{}{
        "id": 1,
        "name": "张三",
        "age": 25
    }
    u.Zi.Echo(c, gin.H{"user": userInfo})
    
    // 直接返回数据列表
    users := []string{"user1", "user2", "user3"}
    u.Zi.Echo(c, gin.H{"users": users})
    
    // 返回处理结果和相关数据
    u.Zi.Echo(c, gin.H{"userId": 1001, "action": "create"})
}
```

**代码规范**：
> 注意1：请不要在 `data` 中使用 `message` 字段，因为 `Zi.Echo` 方法会自动在响应根级别添加 `message` 字段（值为 "ok"）。
> 
> 注意2：对于简单的成功响应，后端只需要返回状态即可，不需要在 `data` 中返回成功信息。成功信息应由前端根据返回的 `code` 和 `message` 来处理：
> ```go
> // 推荐：简单成功响应
> u.Zi.Echo(c, gin.H{})
> 
> // 不推荐：后端返回成功信息
> u.Zi.Echo(c, gin.H{"result": "测试成功"}) // 成功信息应交给前端处理
> ```
> 
> 注意3：只有当需要返回具体数据时，才在 `data` 中添加相应字段：
> ```go
> // 推荐：返回具体数据
> u.Zi.Echo(c, gin.H{"users": users})
> u.Zi.Echo(c, gin.H{"user": userInfo})
> ```

#### 2.1.2 Zi.Error - 错误响应

**功能**：返回标准的错误响应，默认状态码为 100001。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `message string` - 错误信息
- `code ...int` - 可选参数，自定义错误码

**返回格式**：
```json
{
  "code": 100001, // 或自定义错误码
  "message": "错误信息",
  "data": {},
  "uuid": "..."
}
```

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func testHandler(c *gin.Context) {
    // 使用默认错误码
    u.Zi.Error(c, "参数错误")
    
    // 使用自定义错误码
    u.Zi.Error(c, "权限不足", 403)
    
    // 在条件判断中使用
    if !hasPermission {
        u.Zi.Error(c, "没有操作权限")
        return
    }
}
```

#### 2.1.3 Zi.Exit - 自定义响应

**功能**：返回自定义的响应，包括状态码、消息和数据。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `code int` - 状态码
- `message string` - 响应消息
- `data interface{}` - 响应数据

**返回格式**：
```json
{
  "code": code,
  "message": message,
  "data": data,
  "uuid": "..."
}
```

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func testHandler(c *gin.Context) {
    // 自定义响应
    u.Zi.Exit(c, 2001, "自定义成功", gin.H{"data": "自定义数据"})
}
```

#### 2.1.4 Zi.Debug - 调试响应

**功能**：返回调试响应，用于开发阶段调试。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `data interface{}` - 调试数据

**返回格式**：
```json
{
  "code": 100000,
  "message": "debug",
  "data": {...}, // 调试数据
  "uuid": "..."
}
```

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func testHandler(c *gin.Context) {
    // 调试响应
    u.Zi.Debug(c, gin.H{"request": c.Request}) 
}
```

#### 2.1.5 Zi.Html - HTML 响应

**功能**：返回 HTML 页面响应。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `name string` - 模板名称
- `data interface{}` - 模板数据

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func testHandler(c *gin.Context) {
    // 返回 HTML 页面
    u.Zi.Html(c, "test.html", gin.H{"title": "测试页面"})
}
```

### 2.2 utils/login.go - 身份验证工具

`Login` 工具类用于处理管理员身份验证和权限检查，提供了简洁的 API 用于保护需要登录的路由。

#### 2.2.1 Login.Admin - 管理员身份验证

**功能**：检查管理员是否登录，并验证权限。

**参数**：
- `c *gin.Context` - Gin 上下文对象
- `mustAuth []string` - 必须具备的权限列表
- `oneAuth []string` - 至少具备一个的权限列表

**返回值**：
- `bool` - 验证通过返回 true，否则返回 false

**使用示例**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

// 不需要权限的路由
func adminStatusHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{}, []string{}) {
        return
    }
    u.Zi.Echo(c, gin.H{"status": "online"})
}

// 需要特定权限的路由
func adminUserHandler(c *gin.Context) {
    // 必须具备 user:read 权限
    if !u.Login.Admin(c, []string{"user:read"}, []string{}) {
        return
    }
    u.Zi.Echo(c, gin.H{"users": []string{"user1", "user2"}})
}

// 至少具备一个权限的路由
func adminActionHandler(c *gin.Context) {
    // 至少具备 create 或 update 权限
    if !u.Login.Admin(c, []string{}, []string{"action:create", "action:update"}) {
        return
    }
    // 简单成功响应，仅返回状态
    u.Zi.Echo(c, gin.H{})
}
```

### 2.3 IP 处理工具

#### 2.3.1 GetClientIP - 获取客户端真实 IP

**功能**：从请求头或上下文获取客户端的真实 IP 地址，支持代理转发。

**参数**：
- `c *gin.Context` - Gin 上下文对象

**返回值**：
- `string` - 客户端 IP 地址

**使用示例**：
```go
package middleware

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func IpMiddleware(c *gin.Context) {
    clientIP := u.GetClientIP(c)
    c.Set("client_ip", clientIP)
    c.Next()
}
```

#### 2.3.2 Zi.Region - IP 地址解析

**功能**：根据 IP 地址解析地理位置信息。

**参数**：
- `ip string` - IP 地址字符串

**返回值**：
- `string` - 地理位置信息，格式如 "省份|城市|运营商"
- `error` - 解析错误

**使用示例**：
```go
package utils

import (
    "log"
)

func testIpRegion() {
    ip := "8.8.8.8"
    region, err := Zi.Region(ip)
    if err != nil {
        log.Printf("IP 解析错误: %v", err)
        return
    }
    log.Printf("IP %s 所属地区: %s", ip, region)
}
```

## 3. 路由注册示例

### 3.1 基本路由注册

**功能**：注册普通路由，无需身份验证。

**示例代码**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func SetupRouter() *gin.Engine {
    r := gin.Default()
    
    // 注册普通路由
    r.GET("/", func(c *gin.Context) {
        u.Zi.Echo(c, gin.H{"welcome": "欢迎访问子千项目"})
    })
    
    return r
}
```

### 3.2 API 路由注册

**功能**：注册 API 路由，按模块分组管理。

**示例代码**：
```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func RegisterAdminRoutes(router *gin.RouterGroup) {
    group := router.Group("/admin")
    {
        group.POST("/login", adminLoginHandler)
        group.POST("/status", adminStatusHandler)
        group.POST("/info", adminInfoHandler)
        group.POST("/menu", adminMenuHandler)
    }
}

func RegisterApiRoutes(r *gin.Engine) {
    apiGroup := r.Group("/api")
    {
        RegisterAdminRoutes(apiGroup)
        // 注册其他模块路由
    }
}
```

### 3.3 子路由注册

**功能**：从子包注册路由，实现模块化管理。

**示例代码**：

```go
// router/admin.go
package router

import (
    "ziqian/router/admin"
    "github.com/gin-gonic/gin"
)

func RegisterAdminRoutes(router *gin.RouterGroup) {
    group := router.Group("/admin")
    {
        // 注册基础路由
        group.POST("/login", adminLoginHandler)
        
        // 从子包注册路由
        admin.RegisterAdminChangeRoutes(group)
    }
}

// router/admin/change.go
package admin

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
)

func RegisterAdminChangeRoutes(router *gin.RouterGroup) {
    group := router.Group("/change")
    {
        group.POST("/info", adminChangeInfoHandler)
        group.POST("/password", adminChangePasswordHandler)
    }
}
```

## 4. 最佳实践

### 4.1 统一响应格式

在所有 API 路由中使用 `Zi` 工具类返回响应，保持 API 响应格式的一致性。

**核心规范**：

1. **禁止在 `data` 中使用 `message` 字段**：`Zi.Echo` 会自动在响应根级别添加 `message` 字段（值为 "ok"），避免嵌套混乱。

2. **简单成功响应只返回状态**：对于简单的成功操作，后端只需要返回状态即可，成功信息由前端根据 `code` 和 `message` 处理：
   ```go
   // 推荐：简单成功响应
   u.Zi.Echo(c, gin.H{})
   
   // 不推荐：后端返回成功信息
   u.Zi.Echo(c, gin.H{"result": "操作成功"}) // 成功信息应交给前端处理
   ```

3. **仅在需要时返回数据**：只有当需要返回具体业务数据时，才在 `data` 中添加相应字段：
   ```go
   // 推荐：返回具体数据
   u.Zi.Echo(c, gin.H{"users": users}) // 返回用户列表
   u.Zi.Echo(c, gin.H{"user": userInfo}) // 返回用户信息
   u.Zi.Echo(c, gin.H{"userId": 1001}) // 返回新创建的用户ID
   ```

4. **推荐格式示例**：
   ```json
   // 简单成功响应
   {
     "code": 200,
     "message": "ok",
     "data": {},
     "uuid": "..."
   }
   
   // 返回具体数据
   {
     "code": 200,
     "message": "ok",
     "data": {
       "users": ["user1", "user2", "user3"]
     },
     "uuid": "..."
   }
   ```

### 4.2 权限验证

对于需要登录的路由，使用 `Login.Admin` 方法进行身份验证和权限检查，确保 API 安全。

### 4.3 错误处理

使用 `Zi.Error` 方法返回错误信息，提供明确的错误码和错误描述，方便前端处理。

### 4.4 模块化路由

按功能模块组织路由，使用子包注册路由，提高代码的可维护性和扩展性。

### 4.5 模型导入别名约定

了解 `models` 文件夹结构，需要使用别名导入不同模块的模型，以保持代码的清晰性和一致性。

**核心约定**：

1. **admin 模块模型**：使用 `amod` 作为别名
   ```go
   import (
       amod "ziqian/models/admin"
   )
   ```

2. **user 模块模型**：使用 `umod` 作为别名
   ```go
   import (
       umod "ziqian/models/user"
   )
   ```

**使用示例**：

```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
    "ziqian/database"
    amod "ziqian/models/admin"
    umod "ziqian/models/user"
)

// 使用 admin 模块模型
func getAdminListHandler(c *gin.Context) {
    var admins []amod.Admin
    // 业务逻辑...
}

// 使用 user 模块模型
func getUserListHandler(c *gin.Context) {
    var users []umod.User
    // 业务逻辑...
}
```

**注意事项**：

- 严格按照模块使用对应的别名，避免混用
- 在所有导入模型的文件中保持一致的别名约定
- 当需要同时使用多个模块的模型时，同时导入多个别名

```go
// 同时使用 admin 和 user 模块模型
import (
    amod "ziqian/models/admin"
    umod "ziqian/models/user"
)
```

## 5. 完整示例

### 5.1 实现一个完整的 API 路由

```go
package router

import (
    u "ziqian/utils"
    "github.com/gin-gonic/gin"
    "ziqian/database"
    amod "ziqian/models/admin"
    umod "ziqian/models/user"
)

func RegisterUserRoutes(router *gin.RouterGroup) {
    group := router.Group("/user")
    {
        group.GET("/list", getUserListHandler)
        group.GET("/info", getUserInfoHandler)
        group.POST("/create", createUserHandler)
        group.POST("/update", updateUserHandler)
        group.POST("/delete", deleteUserHandler)
    }
}

func getUserListHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{"user:read"}, []string{}) {
        return
    }
    
    db := database.GetDB("main")
    if db == nil {
        u.Zi.Error(c, "数据库连接失败")
        return
    }
    
    var users []umod.User
    if err := db.Find(&users).Error; err != nil {
        u.Zi.Error(c, "获取用户列表失败")
        return
    }
    
    u.Zi.Echo(c, gin.H{"users": users})
}

func getUserInfoHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{"user:read"}, []string{}) {
        return
    }
    
    // 实现获取用户信息逻辑
    userInfo := map[string]interface{}{
        "id": 1,
        "name": "张三",
        "age": 25
    }
    u.Zi.Echo(c, gin.H{"user": userInfo})
}

func createUserHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{"user:create"}, []string{}) {
        return
    }
    
    // 实现创建用户逻辑
    // 如果需要返回新创建的用户ID，可以这样做
    u.Zi.Echo(c, gin.H{"userId": 1001})
    
    // 如果不需要返回数据，直接返回空即可
    // u.Zi.Echo(c, gin.H{})
}

func updateUserHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{"user:update"}, []string{}) {
        return
    }
    
    // 实现更新用户逻辑
    // 简单更新操作，只返回状态
    u.Zi.Echo(c, gin.H{})
}

func deleteUserHandler(c *gin.Context) {
    if !u.Login.Admin(c, []string{"user:delete"}, []string{}) {
        return
    }
    
    // 实现删除用户逻辑
    // 简单删除操作，只返回状态
    u.Zi.Echo(c, gin.H{})
}
```

## 6. 总结

本文档介绍了子千项目中的核心工具函数，包括响应处理、身份验证、IP 处理等功能。通过使用这些工具函数，可以简化开发流程，统一代码风格，提高代码的可维护性和扩展性。

在实际开发中，建议按照本文档中的最佳实践使用这些工具函数，确保 API 设计的一致性和安全性。

## 7. 后续更新

本文档将随着项目的发展不断更新，新增的功能和工具函数将及时添加到文档中。

## 8. 注意事项

### 8.1 时间格式化规范

**功能**：统一 API 响应中的时间格式，确保前端展示一致性。

**规范**：
- 所有时间字段（如 `created_at`、`updated_at`）应使用 `Format("2006-01-02 15:04:05")` 进行格式化
- 格式化后的时间格式为：`2026-01-31 15:28:07`
- 避免使用默认的 RFC3339 格式（如 `2026-01-31T15:28:07+08:00`），因为这种格式在前端展示时不够直观

**使用示例**：
```go
// 错误：使用默认时间格式
resultList = append(resultList, map[string]interface{}{
    "created_at": item.CreatedAt,  // 会返回 RFC3339 格式
    "updated_at": item.UpdatedAt
})

// 正确：使用标准时间格式
resultList = append(resultList, map[string]interface{}{
    "created_at": item.CreatedAt.Format("2006-01-02 15:04:05"),
    "updated_at": item.UpdatedAt.Format("2006-01-02 15:04:05")
})
```

**适用范围**：
- 所有 API 响应中的时间字段
- 特别是列表接口（如 `/api/admin/admin/list`）中的时间展示

---

## 9. 项目构建与运行

### 9.1 构建项目

使用以下命令构建 Linux 64 位版本的可执行文件：

```bash
GOOS=linux GOARCH=amd64 go build -o build/ziqian-linux-amd64-$(date +%Y%m%d%H%M) main.go

GOOS=windows GOARCH=amd64 go build -o build/ziqian-windows-amd64-$(date +%Y%m%d%H%M).exe main.go
```

### 9.2 运行项目

使用以下命令直接运行项目：

```bash
go run main.go
```

---

## 10. Nginx 配置

以下是项目的 Nginx 配置示例，用于部署到生产环境：

```nginx
# mana 管理目录（无缓存，内容频繁变动）
location /mana/ {
    root /data/web/ziqian.online/golang/hoho;
    autoindex off;  # 关闭目录列表（安全）
    expires -1;     # 完全禁止缓存，确保获取最新内容
    add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate";
    add_header Pragma "no-cache";  # 兼容旧浏览器
    add_header Expires "0";        # 立即过期
    try_files $uri $uri/ =404;
}

# static 静态目录（长期缓存，资源稳定）
location /static/ {
    root /data/web/ziqian.online/golang/hoho;
    autoindex off;
    expires 30d;
    add_header Cache-Control "public, max-age=2592000";
    try_files $uri $uri/ =404;
}

# upload 上传目录（无缓存+请求限制）
location /upload/ {
    root /data/web/ziqian.online/golang/hoho;
    autoindex off;
    expires -1;
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    try_files $uri $uri/ =404;
    # 仅允许GET/HEAD请求，防止恶意上传
    limit_except GET HEAD {
        deny all;
    }
}
```

---

**文档版本**：1.0.0
**更新时间**：2026-02-08
**作者**：子千项目组