ciyon_ai/aiskill/golang/ciyon-后端API.md
2026-04-15 17:28:46 +08:00

21 KiB
Raw Blame History

Ciyon 后台管理系统开发框架 - 后端开发指南

框架概述

Ciyon 是一个基于 Golang 开发的轻量级、高性能的后台管理系统开发框架,提供完整的用户权限管理、数据操作、文件上传、日志记录等功能。

核心特点:

  • 轻量级:核心库代码精简,依赖少
  • 高性能:原生 Golang 编写,充分利用 Go 协程优势
  • 易扩展:模块化设计,组件可独立使用
  • RESTful API 设计:支持多种前端框架对接
  • 完整权限体系基于角色的权限控制RBAC
  • 多租户支持:支持 SaaS 模式部署

技术栈:

  • 后端Go 1.22+、MySQL
  • 存储:本地存储 + S3 云存储腾讯云、阿里云、Cloudflare R2

项目结构

golang/
├── main.go              # 程序入口
├── route_adm.go         # 路由配置
├── go.mod               # Go 模块配置
├── go.sum               # 依赖版本锁定
├── gobuild.bat          # Windows 编译脚本
├── web.ini              # 配置文件
├── zciyon/              # 核心库
│   ├── web.go           # Web 服务器
│   ├── mysql.go         # MySQL 数据库
│   ├── sql.go           # SQL 构建器
│   ├── db.go            # 数据库接口
│   ├── upload.go        # 文件上传
│   ├── redis.go         # Redis 接口
│   ├── log.go           # 日志系统
│   ├── excel.go         # Excel 导出
│   ├── ws.go            # WebSocket
│   ├── sse.go           # Server-Sent Events
│   ├── i18n.go          # 国际化
│   ├── c.go             # 通用工具函数
│   ├── http.go          # HTTP 客户端
│   ├── json.go          # JSON 处理
│   ├── ini.go           # 配置文件解析
│   ├── memkv.go         # 内存 KV 存储
│   ├── mqtt.go          # MQTT 客户端
│   ├── post.go          # POST 数据处理
│   ├── tcpclient.go     # TCP 客户端
│   ├── tcpserver.go     # TCP 服务器
│   ├── udpclient.go     # UDP 客户端
│   ├── udpserver.go     # UDP 服务器
│   ├── grpc.go          # gRPC 服务
│   ├── sys_linux.go     # Linux 系统调用
│   └── sys_win.go       # Windows 系统调用
├── web/                 # Web 资源(静态文件)
│   ├── admin/           # 管理后台页面
│   └── jscss/           # 前端库
└── test/                # 测试文件

核心架构

1. 请求处理流程

客户端请求
    ↓
Nginx (可选,反向代理)
    ↓
Ciyon Web Server
    ↓
路由解析 (/admin/xxx.action)
    ↓
业务处理函数 (execFunc)
    ↓ (失败)
Mock 数据 (execMock)
    ↓ (失败)
转发到 PHP 接口 (execPHP)
    ↓ (失败)
返回错误 JSON

详细说明:

  1. 路由解析:根据 URL 路径解析出对应的业务函数
  2. 业务处理函数:优先执行 Golang 业务函数
    • 成功:返回 JSON 响应
    • 失败:继续下一步
  3. Mock 数据:如果业务函数不存在,尝试从 Mock 数据返回
    • 成功:返回 Mock 数据
    • 失败:继续下一步
  4. 转发到 PHP 接口:如果 Mock 也不存在,转发请求到 PHP 接口
    • 成功:返回 PHP 接口的响应
    • 失败(未找到函数):返回 "未找到业务函数" 错误
  5. 返回错误:所有尝试都失败,返回错误信息

2. 模块化设计

zciyon 核心库模块

模块 文件 功能描述
Web 服务器 web.go HTTP 服务器、路由、静态文件服务
数据库 mysql.go MySQL 连接池、增删改查操作
SQL 构建器 sql.go 链式 SQL 构建器
数据库接口 db.go 数据库操作抽象接口
文件上传 upload.go 本地文件上传处理
Redis redis.go Redis 客户端接口
日志 log.go 文件日志、TCP 日志
Excel excel.go Excel 导出功能
WebSocket ws.go WebSocket 服务器
SSE sse.go Server-Sent Events
国际化 i18n.go 多语言支持
HTTP 客户端 http.go HTTP 请求客户端
JSON 处理 json.go JSON 编解码
配置文件 ini.go INI 配置文件解析
内存 KV memkv.go 内存键值存储
MQTT mqtt.go MQTT 协议支持
gRPC grpc.go gRPC 服务支持
TCP/UDP tcp*.go, udp*.go TCP/UDP 网络通信

快速开始

1. 环境准备

配置文件 (web.ini)

[db]
host=127.0.0.1
port=3306
user=root
pass=your_password
name=your_database
maxopenconn=0
maxidelconn=0
maxlifesec=0

[main]
runmode=dev           # dev 或 prod
tasksec=5            # 自动任务执行间隔(秒)

[log]
logfile=log/ciyon.log
logtcp=
logfilelevel=1        # 1-5数字越小级别越高
logtcplevel=3

[web]
webmode=http
webipsk=127.0.0.1:4003
webhttp2=             # HTTP2配置端口,证书.crt,证书.key

[php]
host=                 # PHP代理地址可选

[mock]
target=               # Mock数据库表名
docpass=

[s3A]                 # Cloudflare R2
access=
secret=
endpoint=1d486c90526e9c65512a35642f26d0c3.r2.cloudflarestorage.com
region=us-east-1
bucket=ciy5
acl=public-read
url=https://dao.ciy.cn/ud/

[s3B]                 # 腾讯云COS
access=
secret=
endpoint=tob-1322789299.cos.ap-nanjing.myqcloud.com
region=ap-nanjing
bucket=tob-1322789299
acl=public-read
url=https://tob-1322789299.cos.ap-nanjing.myqcloud.com/ud/

[s3C]                 # 阿里云OSS
access=
secret=
endpoint=expn.oss-cn-hangzhou.aliyuncs.com
region=oss-cn-hangzhou
bucket=expn
acl=public-read
url=https://expn.oss-cn-hangzhou.aliyuncs.com/ud/

[cdn]
weburl=https://ciyon.ciy.cn
files=.html;.js;.css
provider=cloud.tencent.com
secretId=
secretKey=

2. 创建业务模块

创建后端处理函数

文件web/admin/demo.go

package admin

import (
    "net/http"
    c "ciyon/zciyon"
)

// 初始化数据
func Demo_init(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    _, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false
    }

    // 查询数据
    csql := c.NewCiySQL("zc_demo")
    csql.Where("status", 1).Order("id desc").Limit(1, 10)
    list, total, err := c.CiyDB.Get(csql)
    if err != nil {
        return c.ErrJSON(w, "查询失败", err)
    }

    // 返回数据
    return c.SuccJSON(w, r, map[string]any{
        "list": list,
        "total": total,
        "status": c.Getcatas(c.CiyDB, "status"),
    })
}

// 更新数据
func Demo_update(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    _, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false
    }

    id := post.Getint("id")
    name := post.Get("name")

    // 更新数据
    csql := c.NewCiySQL("zc_demo")
    csql.Where("id", id)
    _, err := c.CiyDB.Update(csql, map[string]any{
        "name": name,
        "updatetime": c.Tostamp(),
    })
    if err != nil {
        return c.ErrJSON(w, "更新失败", err)
    }

    // 记录日志
    SaveLogDB(c.CiyDB, "DEMO", nil, map[string]any{"id": id, "name": name})

    return c.SuccJSON(w, r)
}

// 删除数据
func Demo_del(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    _, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false
    }

    ids := post.Get("ids")

    // 删除数据
    csql := c.NewCiySQL("zc_demo")
    csql.Where("id in", ids)
    _, err := c.CiyDB.Delete(csql, c.CIYDB_DELETE_BACKUP_TABLE)
    if err != nil {
        return c.ErrJSON(w, "删除失败", err)
    }

    // 记录日志
    SaveLog(c.CiyDB, "DEMO", "删除记录 IDs: "+ids)

    return c.SuccJSON(w, r)
}

// 导出数据
func Demo_exportxls(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    _, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false
    }

    // 查询数据
    csql := c.NewCiySQL("zc_demo")
    csql.Where("status", 1)
    list, _, err := c.CiyDB.Get(csql)
    if err != nil {
        return c.ErrJSON(w, "查询失败", err)
    }

    // 生成Excel
    fields := []map[string]string{
        {"name": "ID", "width": "50"},
        {"name": "名称", "width": "100"},
        {"name": "创建时间", "type": "dt", "width": "120"},
    }

    datas := [][]string{}
    for _, row := range list {
        datas = append(datas, []string{
            c.Tostr(row["id"]),
            c.Tostr(row["name"]),
            c.Todate(c.Toint(row["createtime"]), "Y-m-d H:i"),
        })
    }

    xml := c.General_excel_xml(fields, datas, map[string]any{
        "toptitle": "数据列表",
        "sheetname": "Sheet1",
    })

    w.Header().Set("Content-Type", "application/vnd.ms-excel")
    w.Header().Set("Content-Disposition", "attachment; filename=demo.xls")
    w.Write([]byte(xml))

    return true
}

注册路由

route_adm.go 中添加路由:

func setWebRoute_adm(web *c.CiyWebServer) {
    web.RouterFunc("/admin/demo", map[string]map[string]func(http.ResponseWriter, *http.Request) bool{
        "normal": {
            "init":    demo.Normal_init,
            "update":  demo.Normal_update,
            "del":     demo.Normal_del,
            "getdata": demo.Normal_getdata,
            "exportxls": demo.Normal_exportxls,
        },
    })
}

后端开发

1. 数据库操作

CiySQL 链式查询

// 基本查询
csql := c.NewCiySQL("user")
csql.Where("name like", "张").Where("age>", 18)
csql.Order("id desc").Limit(1, 10)
list, total, err := c.CiyDB.Get(csql)

// 日期区间查询
csql.Where_daterange("createtime", "2024-01-01~2024-12-31")

// 相对日期查询
csql.Where_dayrange("createtime", "-30~0")  // 最近30天
csql.Where_dayrange("createtime", "0")     // 今天
csql.Where_dayrange("createtime", "-1")    // 昨天

// 月份区间查询
csql.Where_monthrange("createtime", "2024-1")     // 本月
csql.Where_monthrange("createtime", "2024-1~2024-6")  // 月份区间

// 数值区间查询
csql.Where_numrange("price", 100, 1000, 100)  // 10000~100000

// 原始SQL
csql.RawSQL("select * from user where id=?", 123)

// 字段排除
csql.Column("!id,password")  // 排除id和password字段

// JOIN查询
csql.Join("role", "user.roleid=role.id", "left")

// 分组查询
csql.Group("department").Having("count(*)>10")

数据库CRUD操作

// 插入
csql := c.NewCiySQL("user")
lastid, err := c.CiyDB.Insert(csql, map[string]any{
    "name": "张三",
    "age": 25,
    "createtime": c.Tostamp(),
})

// 更新
csql := c.NewCiySQL("user")
csql.Where("id", 123)
_, err := c.CiyDB.Update(csql, map[string]any{
    "name": "李四",
    "age": []string{"age+1"},  // SQL表达式age+1
    "updatetime": c.Tostamp(),
})

// 删除(三种模式)
csql := c.NewCiySQL("user")
csql.Where("id", 123)

// 直接删除
_, err := c.CiyDB.Delete(csql, c.CIYDB_DELETE_BACKUP_NONE)

// 备份到_bak表
_, err := c.CiyDB.Delete(csql, c.CIYDB_DELETE_BACKUP_TABLE)

// 标记删除设置deltimes字段
_, err := c.CiyDB.Delete(csql, c.CIYDB_DELETE_BACKUP_FIELD)

// 事务处理
err := c.CiyDB.Tran(func() error {
    // 扣款
    csql1 := c.NewCiySQL("account")
    csql1.Where("id", 123)
    c.CiyDB.Update(csql1, map[string]any{"balance": []string{"balance-100"}})

    // 记录日志
    csql2 := c.NewCiySQL("log")
    c.CiyDB.Insert(csql2, map[string]any{"msg": "扣款100"})

    return nil
})

2. 权限验证

// 完整验证(带用户信息)
func Verifyuser(r *http.Request, db *c.CiyMysql, post *c.CiyPost) (map[string]any, error)

// 快速验证仅返回用户ID
func Verifyfast(r *http.Request, db *c.CiyMysql, post *c.CiyPost) (map[string]any, int)

// 权限检查
func Nopower(db *c.CiyMysql, userid int, chkpower string) bool

// 使用示例
func Demo_init(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    rsuser, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false  // 自动返回登录错误
    }

    // 检查权限p123v表示菜单ID 123的查看权限
    if Nopower(c.CiyDB, userid, "p123v") {
        return c.ErrJSON(w, "您未被授权操作")
    }

    // 业务逻辑
    return c.SuccJSON(w, r)
}

3. 文件上传

本地上传:

func Upload_upload(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    _, userid := Verifyfast(r, c.CiyDB, post)
    if userid == 0 {
        return false
    }

    path := post.Get("pathfile")
    file := post.Getfile()

    // 保存文件
    json, err := c.SaveUploadFile(path, file)
    if err != nil {
        return c.ErrJSON(w, err.Error())
    }

    return c.SuccJSON(w, r, json)
}

S3上传生成签名

func Upload_s3(w http.ResponseWriter, r *http.Request) bool {
    post := c.NewCiyPost(w, r)
    storselect := post.Get("storselect")  // A/B/C
    path := post.Get("pathfile")

    // 获取S3配置
    accessKey := c.CiyVars.Ini.GetKey("s3"+storselect, "access", "")
    secretKey := c.CiyVars.Ini.GetKey("s3"+storselect, "secret", "")
    endpoint := c.CiyVars.Ini.GetKey("s3"+storselect, "endpoint", "")
    region := c.CiyVars.Ini.GetKey("s3"+storselect, "region", "")
    bucket := c.CiyVars.Ini.GetKey("s3"+storselect, "bucket", "")

    // 生成S3签名示例代码
    // 返回签名信息前端直接上传到S3

    return c.SuccJSON(w, r, ret)
}

4. 日志记录

// 记录操作日志
SaveLog(db *c.CiyMysql, types string, msg string)

// 记录数据变更日志
SaveLogDB(db *c.CiyMysql, types string, oldrow map[string]any, newrow map[string]any)

// 使用示例
func Demo_update(w http.ResponseWriter, r *http.Request) bool {
    // 获取旧数据
    csql := c.NewCiySQL("zc_demo")
    csql.Where("id", id)
    oldrow, _ := c.CiyDB.Getone(csql)

    // 执行更新
    // ...

    // 记录操作日志
    SaveLog(c.CiyDB, "DEMO", "更新记录 ID: "+c.Tostr(id))

    // 记录数据变更
    SaveLogDB(c.CiyDB, "DEMO", oldrow, newrow)

    return c.SuccJSON(w, r)
}

5. WebSocket

func Wsdemo(w http.ResponseWriter, r *http.Request) bool {
    ws, err := c.NewCiyWebsocket(w, r)
    if err != nil {
        return false
    }

    // 接收消息
    ws.OnMessageJSON(func(code int, id int, json map[string]any) {
        // 处理消息
        ws.SendSucc(id, map[string]any{
            "msg": "收到消息",
            "data": json,
        })
    })

    // 错误处理
    ws.OnError(func(err error) {
        c.Log.Error("WS", err.Error())
    })

    // 关闭处理
    ws.OnClose(func() {
        c.Log.Info("WS", "连接关闭")
    })

    return true
}

6. SSE (Server-Sent Events)

func SSE_get(w http.ResponseWriter, r *http.Request) bool {
    if !c.SSEInit(w) {
        return false
    }

    for i := 0; i < 10; i++ {
        c.SSESend_data(w, "消息 "+c.Tostr(i))
        c.Sleep(1)
    }

    return true
}

7. HTTP 客户端

// GET 请求
resp, err := c.HttpGet("https://api.example.com/data")

// POST 请求
resp, err := c.HttpPost("https://api.example.com/api", map[string]any{
    "name": "张三",
    "age": 25,
})

// 带Header的请求
headers := map[string]string{
    "Authorization": "Bearer token",
}
resp, err := c.HttpGet("https://api.example.com/data", headers)

8. JSON 处理

// JSON 编码
jsonStr, err := c.JsonEncode(map[string]any{
    "name": "张三",
    "age": 25,
})

// JSON 解码
var data map[string]any
err := c.JsonDecode(jsonStr, &data)

9. 配置文件读取

// 读取配置值
value := c.CiyVars.Ini.GetKey("db", "host", "")

10. 内存 KV 存储

// 设置值
c.CiyVars.MemKV.Set("key", "value", 3600)  // 3600秒过期

// 获取值
value, exists := c.CiyVars.MemKV.Get("key")

// 删除值
c.CiyVars.MemKV.Delete("key")

权限体系

权限编码规范

权限编码格式:

p{菜单ID}{操作类型}

操作类型:

  • v - 查看
  • e - 编辑
  • d - 删除
  • a - 添加
  • s - 审批
  • x - 自定义

示例:

p123v  - 菜单ID 123 的查看权限
p123e  - 菜单ID 123 的编辑权限
p123d  - 菜单ID 123 的删除权限

权限验证

后端验证:

if Nopower(c.CiyDB, userid, "p123v") {
    return c.ErrJSON(w, "您未被授权操作")
}

部署指南

编译

# Windows
gobuild.bat

# Linux
GOOS=linux GOARCH=amd64 go build -o zgo main.go

# macOS
GOOS=darwin GOARCH=amd64 go build -o zgo main.go

Nginx 配置

server {
    listen 80;
    server_name yourdomain.com;

    # 静态文件
    location / {
        root /path/to/web;
        index index.html;
    }

    # API 代理
    location / {
        proxy_pass http://127.0.0.1:4003;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # WebSocket
    location /ws/ {
        proxy_pass http://127.0.0.1:4003;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Systemd 服务

创建服务文件:/etc/systemd/system/ciyon.service

[Unit]
Description=Ciyon Web Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/path/to/golang
ExecStart=/path/to/golang/zgo
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

启动服务:

systemctl daemon-reload
systemctl start ciyon
systemctl enable ciyon
systemctl status ciyon

常见问题

1. 数据库连接失败

问题: 数据库连接失败 解决:

  1. 检查 web.ini 中数据库配置是否正确
  2. 检查 MySQL 服务是否启动
  3. 检查数据库用户权限

2. 权限验证失败

问题: 提示"请重新登录" 解决:

  1. 检查 zc_online 表中会话是否存在
  2. 检查会话是否过期
  3. 检查 Gtokenswapsec 配置
  4. 检查 Token 加密密钥是否一致

3. 文件上传失败

问题: 文件上传失败 解决:

  1. 检查 web/ud/ 目录是否存在
  2. 检查目录写入权限
  3. 检查文件大小限制
  4. 检查文件类型是否在允许列表中

4. 日志不输出

问题: 日志不输出到文件 解决:

  1. 检查 log/ciyon.log 文件是否存在
  2. 检查文件写入权限
  3. 检查 logfilelevel 配置

最佳实践

代码规范

命名规范:

  • 包名:小写单词,如 admin
  • 文件名:小写单词+下划线,如 demo.go
  • 函数名:大写开头(导出)或小写开头(私有)
  • 变量名:尽量小写,如 userid
  • 常量名:大写+下划线,如 MAX_COUNT

注释规范:

// 函数功能说明
// 参数说明
// 返回值说明
func Demo_init(w http.ResponseWriter, r *http.Request) bool {
    // ...
}

错误处理

// 统一错误返回
if err != nil {
    return c.ErrJSON(w, "操作失败", err)
}

// 记录错误日志
if err != nil {
    c.Log.Error("DEMO", "操作失败: "+err.Error())
    return c.ErrJSON(w, "操作失败", err)
}

安全建议

  1. SQL 注入防护: 使用参数化查询
  2. XSS 防护: 对输出进行转义
  3. CSRF 防护: 使用 Token 验证
  4. 密码加密: 使用 SHA256 加密
  5. 权限控制: 严格的权限验证
  6. 日志记录: 记录所有操作日志

附录

A. 配置参数说明

参数 说明 默认值
runmode 运行模式 dev
logfile 日志文件路径 log/ciyon.log
logfilelevel 日志级别 3
webmode Web 模式 http
webipsk 监听地址 127.0.0.1:4003
dbhost 数据库地址 127.0.0.1
dbport 数据库端口 3306
dbuser 数据库用户 root
dbpass 数据库密码 -
dbname 数据库名称 -

B. 工具函数速查

函数 说明 示例
Tostamp() 获取当前时间戳 c.Tostamp()
Todate() 时间格式化 c.Todate(c.Tostamp(), "Y-m-d H:i:s")
Tostr() 转字符串 c.Tostr(123)
Toint() 转整数 c.Toint("123")
Tofloat() 转浮点数 c.Tofloat("123.45")
MD5() MD5 加密 c.MD5("password")
Sha256() SHA256 加密 c.Sha256("password")
Encrypt() 字符串加密 c.Encrypt("text", "E", "key")
Uniqid() 生成唯一ID c.Uniqid(10)
Randstr() 生成随机字符串 c.Randstr(10)

C. 错误码说明

错误码 说明
1 成功
2 未登录
3 权限不足
4 参数错误
9 操作失败

总结

Ciyon 框架是一个轻量级、高性能的后台管理系统开发框架,提供了完整的用户权限管理、数据操作、文件上传、日志记录等功能。开发者可以根据本文档快速上手开发,并根据实际需求进行扩展。

开发建议:

  1. 先熟悉核心库的使用方法
  2. 参考示例代码进行开发
  3. 遵循代码规范和最佳实践
  4. 注重安全性和性能优化
  5. 做好日志记录和错误处理

技术支持:


文档版本: v2.0
更新日期: 2026-03-18
作者: AI Assistant