You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
17 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 子千项目技能文档
## 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
**作者**:子千项目组