# 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) ```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** ```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` 中添加路由: ```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 链式查询 ```go // 基本查询 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操作 ```go // 插入 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. 权限验证 ```go // 完整验证(带用户信息) 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. 文件上传 **本地上传:** ```go 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上传(生成签名):** ```go 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. 日志记录 ```go // 记录操作日志 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 ```go 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) ```go 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 客户端 ```go // 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 处理 ```go // JSON 编码 jsonStr, err := c.JsonEncode(map[string]any{ "name": "张三", "age": 25, }) // JSON 解码 var data map[string]any err := c.JsonDecode(jsonStr, &data) ``` ### 9. 配置文件读取 ```go // 读取配置值 value := c.CiyVars.Ini.GetKey("db", "host", "") ``` ### 10. 内存 KV 存储 ```go // 设置值 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 的删除权限 ``` ### 权限验证 **后端验证:** ```go if Nopower(c.CiyDB, userid, "p123v") { return c.ErrJSON(w, "您未被授权操作") } ``` --- ## 部署指南 ### 编译 ```bash # 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 配置 ```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** ```ini [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 ``` **启动服务:** ```bash 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` **注释规范:** ```go // 函数功能说明 // 参数说明 // 返回值说明 func Demo_init(w http.ResponseWriter, r *http.Request) bool { // ... } ``` ### 错误处理 ```go // 统一错误返回 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. 做好日志记录和错误处理 **技术支持:** - 官网:https://ciy.cn/code - 文档:本文档 - 示例:web/admin/ 目录下的示例代码 --- **文档版本:** v2.0 **更新日期:** 2026-03-18 **作者:** AI Assistant