912 lines
21 KiB
Markdown
912 lines
21 KiB
Markdown
# 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 |