优化数据库连接配置,增强性能和稳定性
This commit is contained in:
parent
678686fd48
commit
5bb9ed77b8
431
db/HoTimeDB_API参考.md
Normal file
431
db/HoTimeDB_API参考.md
Normal file
@ -0,0 +1,431 @@
|
||||
# HoTimeDB API 快速参考
|
||||
|
||||
## ⚠️ 重要语法说明
|
||||
|
||||
**条件查询语法规则:**
|
||||
- 单个条件:可以直接写在Map中
|
||||
- 多个条件:必须使用`AND`或`OR`包装
|
||||
- 特殊条件:`ORDER`、`GROUP`、`LIMIT`与条件同级
|
||||
|
||||
```go
|
||||
// ✅ 正确:单个条件
|
||||
Map{"status": 1}
|
||||
|
||||
// ✅ 正确:多个条件用AND包装
|
||||
Map{
|
||||
"AND": Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
}
|
||||
|
||||
// ✅ 正确:条件 + 特殊参数
|
||||
Map{
|
||||
"AND": Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
"ORDER": "id DESC",
|
||||
"LIMIT": 10,
|
||||
}
|
||||
|
||||
// ❌ 错误:多个条件不用AND包装
|
||||
Map{
|
||||
"status": 1,
|
||||
"age[>]": 18, // 这样写不支持!
|
||||
}
|
||||
```
|
||||
|
||||
## 基本方法
|
||||
|
||||
### 数据库连接
|
||||
```go
|
||||
db.SetConnect(func() (master, slave *sql.DB) { ... })
|
||||
db.InitDb()
|
||||
```
|
||||
|
||||
### 链式查询构建器
|
||||
```go
|
||||
// 创建查询构建器
|
||||
builder := db.Table("tablename")
|
||||
|
||||
// 设置条件
|
||||
builder.Where(key, value)
|
||||
builder.And(key, value) 或 builder.And(map)
|
||||
builder.Or(key, value) 或 builder.Or(map)
|
||||
|
||||
// JOIN操作
|
||||
builder.LeftJoin(table, condition)
|
||||
builder.RightJoin(table, condition)
|
||||
builder.InnerJoin(table, condition)
|
||||
builder.FullJoin(table, condition)
|
||||
builder.Join(map) // 通用JOIN
|
||||
|
||||
// 排序和分组
|
||||
builder.Order(fields...)
|
||||
builder.Group(fields...)
|
||||
builder.Limit(args...)
|
||||
|
||||
// 分页
|
||||
builder.Page(page, pageSize)
|
||||
|
||||
// 执行查询
|
||||
builder.Select(fields...) // 返回 []Map
|
||||
builder.Get(fields...) // 返回 Map
|
||||
builder.Count() // 返回 int
|
||||
builder.Update(data) // 返回 int64
|
||||
builder.Delete() // 返回 int64
|
||||
```
|
||||
|
||||
## CRUD 操作
|
||||
|
||||
### 查询 (Select)
|
||||
```go
|
||||
// 基本查询
|
||||
data := db.Select("table")
|
||||
data := db.Select("table", "field1,field2")
|
||||
data := db.Select("table", []string{"field1", "field2"})
|
||||
data := db.Select("table", "*", whereMap)
|
||||
|
||||
// 带JOIN查询
|
||||
data := db.Select("table", joinSlice, "fields", whereMap)
|
||||
```
|
||||
|
||||
### 获取单条 (Get)
|
||||
```go
|
||||
// 自动添加 LIMIT 1
|
||||
row := db.Get("table", "fields", whereMap)
|
||||
```
|
||||
|
||||
### 插入 (Insert)
|
||||
```go
|
||||
id := db.Insert("table", dataMap)
|
||||
// 返回新插入记录的ID
|
||||
```
|
||||
|
||||
### 更新 (Update)
|
||||
```go
|
||||
affected := db.Update("table", dataMap, whereMap)
|
||||
// 返回受影响的行数
|
||||
```
|
||||
|
||||
### 删除 (Delete)
|
||||
```go
|
||||
affected := db.Delete("table", whereMap)
|
||||
// 返回删除的行数
|
||||
```
|
||||
|
||||
## 聚合函数
|
||||
|
||||
### 计数
|
||||
```go
|
||||
count := db.Count("table")
|
||||
count := db.Count("table", whereMap)
|
||||
count := db.Count("table", joinSlice, whereMap)
|
||||
```
|
||||
|
||||
### 求和
|
||||
```go
|
||||
sum := db.Sum("table", "column")
|
||||
sum := db.Sum("table", "column", whereMap)
|
||||
sum := db.Sum("table", "column", joinSlice, whereMap)
|
||||
```
|
||||
|
||||
## 分页查询
|
||||
```go
|
||||
// 设置分页
|
||||
db.Page(page, pageSize)
|
||||
|
||||
// 分页查询
|
||||
data := db.Page(page, pageSize).PageSelect("table", "fields", whereMap)
|
||||
```
|
||||
|
||||
## 条件语法参考
|
||||
|
||||
### 比较操作符
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field": value` | `field = ?` | 等于 |
|
||||
| `"field[!]": value` | `field != ?` | 不等于 |
|
||||
| `"field[>]": value` | `field > ?` | 大于 |
|
||||
| `"field[>=]": value` | `field >= ?` | 大于等于 |
|
||||
| `"field[<]": value` | `field < ?` | 小于 |
|
||||
| `"field[<=]": value` | `field <= ?` | 小于等于 |
|
||||
|
||||
### 模糊查询
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field[~]": "keyword"` | `field LIKE '%keyword%'` | 包含 |
|
||||
| `"field[~!]": "keyword"` | `field LIKE 'keyword%'` | 以...开头 |
|
||||
| `"field[!~]": "keyword"` | `field LIKE '%keyword'` | 以...结尾 |
|
||||
| `"field[~~]": "%keyword%"` | `field LIKE '%keyword%'` | 手动LIKE |
|
||||
|
||||
### 范围查询
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field[<>]": [min, max]` | `field BETWEEN ? AND ?` | 区间内 |
|
||||
| `"field[><]": [min, max]` | `field NOT BETWEEN ? AND ?` | 区间外 |
|
||||
|
||||
### 集合查询
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field": [v1, v2, v3]` | `field IN (?, ?, ?)` | 在集合中 |
|
||||
| `"field[!]": [v1, v2, v3]` | `field NOT IN (?, ?, ?)` | 不在集合中 |
|
||||
|
||||
### NULL查询
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field": nil` | `field IS NULL` | 为空 |
|
||||
| `"field[!]": nil` | `field IS NOT NULL` | 不为空 |
|
||||
|
||||
### 直接SQL
|
||||
| 写法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field[#]": "NOW()"` | `field = NOW()` | 直接SQL函数 |
|
||||
| `"[##]": "a > b"` | `a > b` | 直接SQL片段 |
|
||||
| `"field[#!]": "1"` | `field != 1` | 不等于(不参数化) |
|
||||
|
||||
## 逻辑连接符
|
||||
|
||||
### AND 条件
|
||||
```go
|
||||
whereMap := Map{
|
||||
"AND": Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### OR 条件
|
||||
```go
|
||||
whereMap := Map{
|
||||
"OR": Map{
|
||||
"status": 1,
|
||||
"type": 2,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 嵌套条件
|
||||
```go
|
||||
whereMap := Map{
|
||||
"AND": Map{
|
||||
"status": 1,
|
||||
"OR": Map{
|
||||
"age[<]": 30,
|
||||
"level[>]": 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## JOIN 语法
|
||||
|
||||
### 传统语法
|
||||
```go
|
||||
joinSlice := Slice{
|
||||
Map{"[>]profile": "user.id = profile.user_id"}, // LEFT JOIN
|
||||
Map{"[<]department": "user.dept_id = department.id"}, // RIGHT JOIN
|
||||
Map{"[><]role": "user.role_id = role.id"}, // INNER JOIN
|
||||
Map{"[<>]group": "user.group_id = group.id"}, // FULL JOIN
|
||||
}
|
||||
```
|
||||
|
||||
### 链式语法
|
||||
```go
|
||||
builder.LeftJoin("profile", "user.id = profile.user_id")
|
||||
builder.RightJoin("department", "user.dept_id = department.id")
|
||||
builder.InnerJoin("role", "user.role_id = role.id")
|
||||
builder.FullJoin("group", "user.group_id = group.id")
|
||||
```
|
||||
|
||||
## 特殊字段语法
|
||||
|
||||
### ORDER BY
|
||||
```go
|
||||
Map{
|
||||
"ORDER": []string{"created_time DESC", "id ASC"},
|
||||
}
|
||||
// 或
|
||||
Map{
|
||||
"ORDER": "created_time DESC",
|
||||
}
|
||||
```
|
||||
|
||||
### GROUP BY
|
||||
```go
|
||||
Map{
|
||||
"GROUP": []string{"department", "level"},
|
||||
}
|
||||
// 或
|
||||
Map{
|
||||
"GROUP": "department",
|
||||
}
|
||||
```
|
||||
|
||||
### LIMIT
|
||||
```go
|
||||
Map{
|
||||
"LIMIT": []int{10, 20}, // offset 10, limit 20
|
||||
}
|
||||
// 或
|
||||
Map{
|
||||
"LIMIT": 20, // limit 20
|
||||
}
|
||||
```
|
||||
|
||||
## 事务处理
|
||||
```go
|
||||
success := db.Action(func(tx HoTimeDB) bool {
|
||||
// 在这里执行数据库操作
|
||||
// 返回 true 提交事务
|
||||
// 返回 false 回滚事务
|
||||
|
||||
id := tx.Insert("table", data)
|
||||
if id == 0 {
|
||||
return false // 回滚
|
||||
}
|
||||
|
||||
affected := tx.Update("table2", data2, where2)
|
||||
if affected == 0 {
|
||||
return false // 回滚
|
||||
}
|
||||
|
||||
return true // 提交
|
||||
})
|
||||
```
|
||||
|
||||
## 原生SQL执行
|
||||
|
||||
### 查询
|
||||
```go
|
||||
results := db.Query("SELECT * FROM user WHERE age > ?", 18)
|
||||
```
|
||||
|
||||
### 执行
|
||||
```go
|
||||
result, err := db.Exec("UPDATE user SET status = ? WHERE id = ?", 1, 100)
|
||||
affected, _ := result.RowsAffected()
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
```go
|
||||
// 检查最后的错误
|
||||
if db.LastErr.GetError() != nil {
|
||||
fmt.Println("错误:", db.LastErr.GetError())
|
||||
}
|
||||
|
||||
// 查看最后执行的SQL
|
||||
fmt.Println("SQL:", db.LastQuery)
|
||||
fmt.Println("参数:", db.LastData)
|
||||
```
|
||||
|
||||
## 工具方法
|
||||
|
||||
### 数据库信息
|
||||
```go
|
||||
prefix := db.GetPrefix() // 获取表前缀
|
||||
dbType := db.GetType() // 获取数据库类型
|
||||
```
|
||||
|
||||
### 设置模式
|
||||
```go
|
||||
db.Mode = 0 // 生产模式
|
||||
db.Mode = 1 // 测试模式
|
||||
db.Mode = 2 // 开发模式(输出SQL日志)
|
||||
```
|
||||
|
||||
## 常用查询模式
|
||||
|
||||
### 分页列表查询
|
||||
```go
|
||||
// 获取总数
|
||||
total := db.Count("user", Map{"status": 1})
|
||||
|
||||
// 分页数据
|
||||
users := db.Table("user").
|
||||
Where("status", 1).
|
||||
Order("created_time DESC").
|
||||
Page(page, pageSize).
|
||||
Select("id,name,email,created_time")
|
||||
|
||||
// 计算分页信息
|
||||
totalPages := (total + pageSize - 1) / pageSize
|
||||
```
|
||||
|
||||
### 关联查询
|
||||
```go
|
||||
orders := db.Table("order").
|
||||
LeftJoin("user", "order.user_id = user.id").
|
||||
LeftJoin("product", "order.product_id = product.id").
|
||||
Where("order.status", "paid").
|
||||
Select(`
|
||||
order.*,
|
||||
user.name as user_name,
|
||||
product.title as product_title
|
||||
`)
|
||||
```
|
||||
|
||||
### 统计查询
|
||||
```go
|
||||
stats := db.Select("order",
|
||||
"user_id, COUNT(*) as order_count, SUM(amount) as total_amount",
|
||||
Map{
|
||||
"AND": Map{
|
||||
"status": "paid",
|
||||
"created_time[>]": "2023-01-01",
|
||||
},
|
||||
"GROUP": "user_id",
|
||||
"ORDER": "total_amount DESC",
|
||||
})
|
||||
```
|
||||
|
||||
### 条件组合查询
|
||||
```go
|
||||
products := db.Table("product").
|
||||
Where("status", 1).
|
||||
And(Map{
|
||||
"OR": Map{
|
||||
"category_id": []int{1, 2, 3},
|
||||
"tags[~]": "热销",
|
||||
},
|
||||
}).
|
||||
And(Map{
|
||||
"price[<>]": []float64{10.0, 1000.0},
|
||||
}).
|
||||
Order("sort DESC", "created_time DESC").
|
||||
Limit(0, 20).
|
||||
Select()
|
||||
```
|
||||
|
||||
## 链式调用完整示例
|
||||
|
||||
```go
|
||||
// 复杂查询链式调用
|
||||
result := db.Table("order").
|
||||
LeftJoin("user", "order.user_id = user.id").
|
||||
LeftJoin("product", "order.product_id = product.id").
|
||||
Where("order.status", "paid").
|
||||
And("order.created_time[>]", "2023-01-01").
|
||||
And(Map{
|
||||
"OR": Map{
|
||||
"user.level": "vip",
|
||||
"order.amount[>]": 1000,
|
||||
},
|
||||
}).
|
||||
Group("user.id").
|
||||
Order("total_amount DESC").
|
||||
Page(1, 20).
|
||||
Select(`
|
||||
user.id,
|
||||
user.name,
|
||||
user.email,
|
||||
COUNT(order.id) as order_count,
|
||||
SUM(order.amount) as total_amount
|
||||
`)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*快速参考版本: 1.0*
|
890
db/HoTimeDB_使用说明.md
Normal file
890
db/HoTimeDB_使用说明.md
Normal file
@ -0,0 +1,890 @@
|
||||
# HoTimeDB ORM 使用说明书
|
||||
|
||||
## 概述
|
||||
|
||||
HoTimeDB是一个基于Golang实现的轻量级ORM框架,参考PHP Medoo设计,提供简洁的数据库操作接口。支持MySQL、SQLite等数据库,并集成了缓存、事务、链式查询等功能。
|
||||
|
||||
## 目录
|
||||
|
||||
- [快速开始](#快速开始)
|
||||
- [数据库配置](#数据库配置)
|
||||
- [基本操作](#基本操作)
|
||||
- [查询(Select)](#查询select)
|
||||
- [获取单条记录(Get)](#获取单条记录get)
|
||||
- [插入(Insert)](#插入insert)
|
||||
- [更新(Update)](#更新update)
|
||||
- [删除(Delete)](#删除delete)
|
||||
- [链式查询构建器](#链式查询构建器)
|
||||
- [条件查询语法](#条件查询语法)
|
||||
- [JOIN操作](#join操作)
|
||||
- [分页查询](#分页查询)
|
||||
- [聚合函数](#聚合函数)
|
||||
- [事务处理](#事务处理)
|
||||
- [缓存机制](#缓存机制)
|
||||
- [高级特性](#高级特性)
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 初始化数据库连接
|
||||
|
||||
```go
|
||||
import (
|
||||
"code.hoteas.com/golang/hotime/db"
|
||||
"code.hoteas.com/golang/hotime/common"
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// 创建连接函数
|
||||
func createConnection() (master, slave *sql.DB) {
|
||||
master, _ = sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
|
||||
// slave是可选的,用于读写分离
|
||||
slave = master // 或者连接到从数据库
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化HoTimeDB
|
||||
db := &db.HoTimeDB{}
|
||||
db.SetConnect(createConnection)
|
||||
```
|
||||
|
||||
## 数据库配置
|
||||
|
||||
### 基本配置
|
||||
|
||||
```go
|
||||
type HoTimeDB struct {
|
||||
*sql.DB
|
||||
ContextBase
|
||||
DBName string
|
||||
*cache.HoTimeCache
|
||||
Log *logrus.Logger
|
||||
Type string // 数据库类型
|
||||
Prefix string // 表前缀
|
||||
LastQuery string // 最后执行的SQL
|
||||
LastData []interface{} // 最后的参数
|
||||
ConnectFunc func(err ...*Error) (*sql.DB, *sql.DB)
|
||||
LastErr *Error
|
||||
limit Slice
|
||||
*sql.Tx // 事务对象
|
||||
SlaveDB *sql.DB // 从数据库
|
||||
Mode int // 0生产模式,1测试模式,2开发模式
|
||||
}
|
||||
```
|
||||
|
||||
### 设置表前缀
|
||||
|
||||
```go
|
||||
db.Prefix = "app_"
|
||||
```
|
||||
|
||||
### 设置运行模式
|
||||
|
||||
```go
|
||||
db.Mode = 2 // 开发模式,会输出SQL日志
|
||||
```
|
||||
|
||||
## 基本操作
|
||||
|
||||
### 查询(Select)
|
||||
|
||||
#### 基本查询
|
||||
|
||||
```go
|
||||
// 查询所有字段
|
||||
users := db.Select("user")
|
||||
|
||||
// 查询指定字段
|
||||
users := db.Select("user", "id,name,email")
|
||||
|
||||
// 查询指定字段(数组形式)
|
||||
users := db.Select("user", []string{"id", "name", "email"})
|
||||
|
||||
// 单条件查询
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
|
||||
// 多条件查询(必须使用AND包装)
|
||||
users = db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
#### 复杂条件查询
|
||||
|
||||
**重要说明:多个条件必须使用AND或OR包装,不能直接在根Map中写多个字段条件**
|
||||
|
||||
```go
|
||||
// AND条件(多个条件必须用AND包装)
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
"name[~]": "张",
|
||||
},
|
||||
})
|
||||
|
||||
// OR条件
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"OR": common.Map{
|
||||
"status": 1,
|
||||
"type": 2,
|
||||
},
|
||||
})
|
||||
|
||||
// 混合条件(嵌套AND/OR)
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"OR": common.Map{
|
||||
"age[<]": 30,
|
||||
"level[>]": 5,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 带ORDER BY、LIMIT等特殊条件
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
"ORDER": "id DESC",
|
||||
"LIMIT": 10,
|
||||
})
|
||||
|
||||
// 带多个特殊条件
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"OR": common.Map{
|
||||
"level": "vip",
|
||||
"balance[>]": 1000,
|
||||
},
|
||||
"ORDER": []string{"created_time DESC", "id ASC"},
|
||||
"GROUP": "department",
|
||||
"LIMIT": []int{0, 20}, // offset 0, limit 20
|
||||
})
|
||||
```
|
||||
|
||||
### 获取单条记录(Get)
|
||||
|
||||
```go
|
||||
// 获取单个用户
|
||||
user := db.Get("user", "*", common.Map{
|
||||
"id": 1,
|
||||
})
|
||||
|
||||
// 获取指定字段
|
||||
user := db.Get("user", "id,name,email", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
```
|
||||
|
||||
### 插入(Insert)
|
||||
|
||||
```go
|
||||
// 基本插入
|
||||
id := db.Insert("user", common.Map{
|
||||
"name": "张三",
|
||||
"email": "zhangsan@example.com",
|
||||
"age": 25,
|
||||
"status": 1,
|
||||
"created_time[#]": "NOW()", // [#]表示直接插入SQL函数
|
||||
})
|
||||
|
||||
// 返回插入的ID
|
||||
fmt.Println("插入的用户ID:", id)
|
||||
```
|
||||
|
||||
### 更新(Update)
|
||||
|
||||
```go
|
||||
// 基本更新
|
||||
affected := db.Update("user", common.Map{
|
||||
"name": "李四",
|
||||
"email": "lisi@example.com",
|
||||
"updated_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"id": 1,
|
||||
})
|
||||
|
||||
// 条件更新
|
||||
affected := db.Update("user", common.Map{
|
||||
"status": 0,
|
||||
}, common.Map{
|
||||
"age[<]": 18,
|
||||
"status": 1,
|
||||
})
|
||||
|
||||
fmt.Println("更新的记录数:", affected)
|
||||
```
|
||||
|
||||
### 删除(Delete)
|
||||
|
||||
```go
|
||||
// 根据ID删除
|
||||
affected := db.Delete("user", common.Map{
|
||||
"id": 1,
|
||||
})
|
||||
|
||||
// 条件删除
|
||||
affected := db.Delete("user", common.Map{
|
||||
"status": 0,
|
||||
"created_time[<]": "2023-01-01",
|
||||
})
|
||||
|
||||
fmt.Println("删除的记录数:", affected)
|
||||
```
|
||||
|
||||
## 链式查询构建器
|
||||
|
||||
HoTimeDB提供了链式查询构建器,让查询更加直观:
|
||||
|
||||
```go
|
||||
// 基本链式查询
|
||||
users := db.Table("user").
|
||||
Where("status", 1).
|
||||
And("age[>]", 18).
|
||||
Order("created_time DESC").
|
||||
Limit(10, 20). // offset, limit
|
||||
Select()
|
||||
|
||||
// 链式获取单条记录
|
||||
user := db.Table("user").
|
||||
Where("id", 1).
|
||||
Get()
|
||||
|
||||
// 链式更新
|
||||
affected := db.Table("user").
|
||||
Where("id", 1).
|
||||
Update(common.Map{
|
||||
"name": "新名称",
|
||||
"updated_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
// 链式删除
|
||||
affected := db.Table("user").
|
||||
Where("status", 0).
|
||||
Delete()
|
||||
|
||||
// 链式统计
|
||||
count := db.Table("user").
|
||||
Where("status", 1).
|
||||
Count()
|
||||
```
|
||||
|
||||
### 链式条件组合
|
||||
|
||||
```go
|
||||
// 复杂条件组合
|
||||
users := db.Table("user").
|
||||
Where("status", 1).
|
||||
And("age[>=]", 18).
|
||||
Or(common.Map{
|
||||
"level[>]": 5,
|
||||
"vip": 1,
|
||||
}).
|
||||
Order("created_time DESC", "id ASC").
|
||||
Group("department").
|
||||
Limit(0, 20).
|
||||
Select("id,name,email,age")
|
||||
```
|
||||
|
||||
## 条件查询语法
|
||||
|
||||
HoTimeDB支持丰富的条件查询语法,类似于Medoo:
|
||||
|
||||
### 基本比较
|
||||
|
||||
```go
|
||||
// 等于
|
||||
"id": 1
|
||||
|
||||
// 不等于
|
||||
"id[!]": 1
|
||||
|
||||
// 大于
|
||||
"age[>]": 18
|
||||
|
||||
// 大于等于
|
||||
"age[>=]": 18
|
||||
|
||||
// 小于
|
||||
"age[<]": 60
|
||||
|
||||
// 小于等于
|
||||
"age[<=]": 60
|
||||
```
|
||||
|
||||
### 模糊查询
|
||||
|
||||
```go
|
||||
// LIKE %keyword%
|
||||
"name[~]": "张"
|
||||
|
||||
// LIKE keyword% (右边任意)
|
||||
"name[~!]": "张"
|
||||
|
||||
// LIKE %keyword (左边任意)
|
||||
"name[!~]": "san"
|
||||
|
||||
// 手动LIKE(需要手动添加%)
|
||||
"name[~~]": "%张%"
|
||||
```
|
||||
|
||||
### 区间查询
|
||||
|
||||
```go
|
||||
// BETWEEN
|
||||
"age[<>]": []int{18, 60}
|
||||
|
||||
// NOT BETWEEN
|
||||
"age[><]": []int{18, 25}
|
||||
```
|
||||
|
||||
### IN查询
|
||||
|
||||
```go
|
||||
// IN
|
||||
"id": []int{1, 2, 3, 4, 5}
|
||||
|
||||
// NOT IN
|
||||
"id[!]": []int{1, 2, 3}
|
||||
```
|
||||
|
||||
### NULL查询
|
||||
|
||||
```go
|
||||
// IS NULL
|
||||
"deleted_at": nil
|
||||
|
||||
// IS NOT NULL
|
||||
"deleted_at[!]": nil
|
||||
```
|
||||
|
||||
### 直接SQL
|
||||
|
||||
```go
|
||||
// 直接插入SQL表达式(注意防注入)
|
||||
"created_time[#]": "> DATE_SUB(NOW(), INTERVAL 1 DAY)"
|
||||
|
||||
// 字段直接赋值(不使用参数化查询)
|
||||
"update_time[#]": "NOW()"
|
||||
|
||||
// 直接SQL片段
|
||||
"[##]": "user.status = 1 AND user.level > 0"
|
||||
```
|
||||
|
||||
## JOIN操作
|
||||
|
||||
### 链式JOIN
|
||||
|
||||
```go
|
||||
// LEFT JOIN
|
||||
users := db.Table("user").
|
||||
LeftJoin("profile", "user.id = profile.user_id").
|
||||
LeftJoin("department", "user.dept_id = department.id").
|
||||
Where("user.status", 1).
|
||||
Select("user.*, profile.avatar, department.name AS dept_name")
|
||||
|
||||
// RIGHT JOIN
|
||||
users := db.Table("user").
|
||||
RightJoin("order", "user.id = order.user_id").
|
||||
Select()
|
||||
|
||||
// INNER JOIN
|
||||
users := db.Table("user").
|
||||
InnerJoin("profile", "user.id = profile.user_id").
|
||||
Select()
|
||||
|
||||
// FULL JOIN
|
||||
users := db.Table("user").
|
||||
FullJoin("profile", "user.id = profile.user_id").
|
||||
Select()
|
||||
```
|
||||
|
||||
### 传统JOIN语法
|
||||
|
||||
```go
|
||||
users := db.Select("user",
|
||||
common.Slice{
|
||||
common.Map{"[>]profile": "user.id = profile.user_id"},
|
||||
common.Map{"[>]department": "user.dept_id = department.id"},
|
||||
},
|
||||
"user.*, profile.avatar, department.name AS dept_name",
|
||||
common.Map{
|
||||
"user.status": 1,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### JOIN类型说明
|
||||
|
||||
- `[>]`: LEFT JOIN
|
||||
- `[<]`: RIGHT JOIN
|
||||
- `[><]`: INNER JOIN
|
||||
- `[<>]`: FULL JOIN
|
||||
|
||||
## 分页查询
|
||||
|
||||
### 基本分页
|
||||
|
||||
```go
|
||||
// 设置分页:页码3,每页20条
|
||||
users := db.Page(3, 20).PageSelect("user", "*", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
|
||||
// 链式分页
|
||||
users := db.Table("user").
|
||||
Where("status", 1).
|
||||
Page(2, 15). // 第2页,每页15条
|
||||
Select()
|
||||
```
|
||||
|
||||
### 分页信息获取
|
||||
|
||||
```go
|
||||
// 获取总数
|
||||
total := db.Count("user", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
|
||||
// 计算分页信息
|
||||
page := 2
|
||||
pageSize := 20
|
||||
offset := (page - 1) * pageSize
|
||||
totalPages := (total + pageSize - 1) / pageSize
|
||||
|
||||
fmt.Printf("总记录数: %d, 总页数: %d, 当前页: %d\n", total, totalPages, page)
|
||||
```
|
||||
|
||||
## 聚合函数
|
||||
|
||||
### 计数
|
||||
|
||||
```go
|
||||
// 总数统计
|
||||
total := db.Count("user")
|
||||
|
||||
// 条件统计
|
||||
activeUsers := db.Count("user", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
|
||||
// JOIN统计
|
||||
count := db.Count("user",
|
||||
common.Slice{
|
||||
common.Map{"[>]profile": "user.id = profile.user_id"},
|
||||
},
|
||||
common.Map{
|
||||
"user.status": 1,
|
||||
"profile.verified": 1,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### 求和
|
||||
|
||||
```go
|
||||
// 基本求和
|
||||
totalAmount := db.Sum("order", "amount")
|
||||
|
||||
// 条件求和
|
||||
paidAmount := db.Sum("order", "amount", common.Map{
|
||||
"status": "paid",
|
||||
"created_time[>]": "2023-01-01",
|
||||
})
|
||||
|
||||
// JOIN求和
|
||||
sum := db.Sum("order", "amount",
|
||||
common.Slice{
|
||||
common.Map{"[>]user": "order.user_id = user.id"},
|
||||
},
|
||||
common.Map{
|
||||
"user.level": "vip",
|
||||
"order.status": "paid",
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## 事务处理
|
||||
|
||||
```go
|
||||
// 事务操作
|
||||
success := db.Action(func(tx db.HoTimeDB) bool {
|
||||
// 在事务中执行多个操作
|
||||
|
||||
// 扣减用户余额
|
||||
affected1 := tx.Update("user", common.Map{
|
||||
"balance[#]": "balance - 100",
|
||||
}, common.Map{
|
||||
"id": 1,
|
||||
})
|
||||
|
||||
if affected1 == 0 {
|
||||
return false // 回滚
|
||||
}
|
||||
|
||||
// 创建订单
|
||||
orderId := tx.Insert("order", common.Map{
|
||||
"user_id": 1,
|
||||
"amount": 100,
|
||||
"status": "paid",
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
if orderId == 0 {
|
||||
return false // 回滚
|
||||
}
|
||||
|
||||
// 添加订单详情
|
||||
detailId := tx.Insert("order_detail", common.Map{
|
||||
"order_id": orderId,
|
||||
"product_id": 1001,
|
||||
"quantity": 1,
|
||||
"price": 100,
|
||||
})
|
||||
|
||||
if detailId == 0 {
|
||||
return false // 回滚
|
||||
}
|
||||
|
||||
return true // 提交
|
||||
})
|
||||
|
||||
if success {
|
||||
fmt.Println("事务执行成功")
|
||||
} else {
|
||||
fmt.Println("事务回滚")
|
||||
fmt.Println("错误:", db.LastErr.GetError())
|
||||
}
|
||||
```
|
||||
|
||||
## 缓存机制
|
||||
|
||||
HoTimeDB集成了缓存功能,可以自动缓存查询结果:
|
||||
|
||||
### 缓存配置
|
||||
|
||||
```go
|
||||
import "code.hoteas.com/golang/hotime/cache"
|
||||
|
||||
// 设置缓存
|
||||
db.HoTimeCache = &cache.HoTimeCache{
|
||||
// 缓存配置
|
||||
}
|
||||
```
|
||||
|
||||
### 缓存行为
|
||||
|
||||
- 查询操作会自动检查缓存
|
||||
- 增删改操作会自动清除相关缓存
|
||||
- 缓存键格式:`表名:查询MD5`
|
||||
- `cached`表不会被缓存
|
||||
|
||||
### 缓存清理
|
||||
|
||||
```go
|
||||
// 手动清除表缓存
|
||||
db.HoTimeCache.Db("user*", nil) // 清除user表所有缓存
|
||||
```
|
||||
|
||||
## 高级特性
|
||||
|
||||
### 调试模式
|
||||
|
||||
```go
|
||||
// 设置调试模式
|
||||
db.Mode = 2
|
||||
|
||||
// 查看最后执行的SQL
|
||||
fmt.Println("最后的SQL:", db.LastQuery)
|
||||
fmt.Println("参数:", db.LastData)
|
||||
fmt.Println("错误:", db.LastErr.GetError())
|
||||
```
|
||||
|
||||
### 主从分离
|
||||
|
||||
```go
|
||||
func createConnection() (master, slave *sql.DB) {
|
||||
// 主库连接
|
||||
master, _ = sql.Open("mysql", "user:password@tcp(master:3306)/database")
|
||||
|
||||
// 从库连接
|
||||
slave, _ = sql.Open("mysql", "user:password@tcp(slave:3306)/database")
|
||||
|
||||
return master, slave
|
||||
}
|
||||
|
||||
db.SetConnect(createConnection)
|
||||
// 查询会自动使用从库,增删改使用主库
|
||||
```
|
||||
|
||||
### 原生SQL执行
|
||||
|
||||
```go
|
||||
// 执行查询SQL
|
||||
results := db.Query("SELECT * FROM user WHERE age > ? AND status = ?", 18, 1)
|
||||
|
||||
// 执行更新SQL
|
||||
result, err := db.Exec("UPDATE user SET last_login = NOW() WHERE id = ?", 1)
|
||||
if err.GetError() == nil {
|
||||
affected, _ := result.RowsAffected()
|
||||
fmt.Println("影响行数:", affected)
|
||||
}
|
||||
```
|
||||
|
||||
### 数据类型处理
|
||||
|
||||
```go
|
||||
// 时间戳插入
|
||||
db.Insert("log", common.Map{
|
||||
"user_id": 1,
|
||||
"action": "login",
|
||||
"created_time[#]": "UNIX_TIMESTAMP()",
|
||||
})
|
||||
|
||||
// JSON数据
|
||||
db.Insert("user", common.Map{
|
||||
"name": "张三",
|
||||
"preferences": `{"theme": "dark", "language": "zh-CN"}`,
|
||||
})
|
||||
```
|
||||
|
||||
### 数据行处理
|
||||
|
||||
HoTimeDB内部的`Row`方法会自动处理不同数据类型:
|
||||
|
||||
```go
|
||||
// 自动转换[]uint8为字符串
|
||||
// 保持其他类型的原始值
|
||||
// 处理NULL值
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
```go
|
||||
// 检查错误
|
||||
users := db.Select("user", "*", common.Map{"status": 1})
|
||||
if db.LastErr.GetError() != nil {
|
||||
fmt.Println("查询错误:", db.LastErr.GetError())
|
||||
return
|
||||
}
|
||||
|
||||
// 插入时检查错误
|
||||
id := db.Insert("user", common.Map{
|
||||
"name": "test",
|
||||
"email": "test@example.com",
|
||||
})
|
||||
|
||||
if id == 0 && db.LastErr.GetError() != nil {
|
||||
fmt.Println("插入失败:", db.LastErr.GetError())
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### IN查询优化
|
||||
|
||||
HoTimeDB会自动优化IN查询,将连续的数字转换为BETWEEN查询以提高性能:
|
||||
|
||||
```go
|
||||
// 这个查询会被自动优化(单个IN条件)
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"id": []int{1, 2, 3, 4, 5, 10, 11, 12},
|
||||
// 自动转为 (id BETWEEN 1 AND 5) OR (id BETWEEN 10 AND 12)
|
||||
})
|
||||
```
|
||||
|
||||
### 批量操作
|
||||
|
||||
```go
|
||||
// 使用事务进行批量插入
|
||||
success := db.Action(func(tx db.HoTimeDB) bool {
|
||||
for i := 0; i < 1000; i++ {
|
||||
id := tx.Insert("user", common.Map{
|
||||
"name": fmt.Sprintf("User%d", i),
|
||||
"email": fmt.Sprintf("user%d@example.com", i),
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
if id == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
```
|
||||
|
||||
## 特殊语法详解
|
||||
|
||||
### 条件标记符说明
|
||||
|
||||
| 标记符 | 功能 | 示例 | 生成SQL |
|
||||
|--------|------|------|---------|
|
||||
| `[>]` | 大于 | `"age[>]": 18` | `age > 18` |
|
||||
| `[<]` | 小于 | `"age[<]": 60` | `age < 60` |
|
||||
| `[>=]` | 大于等于 | `"age[>=]": 18` | `age >= 18` |
|
||||
| `[<=]` | 小于等于 | `"age[<=]": 60` | `age <= 60` |
|
||||
| `[!]` | 不等于/NOT IN | `"id[!]": 1` | `id != 1` |
|
||||
| `[~]` | LIKE模糊查询 | `"name[~]": "张"` | `name LIKE '%张%'` |
|
||||
| `[!~]` | 左模糊 | `"name[!~]": "张"` | `name LIKE '%张'` |
|
||||
| `[~!]` | 右模糊 | `"name[~!]": "张"` | `name LIKE '张%'` |
|
||||
| `[~~]` | 手动LIKE | `"name[~~]": "%张%"` | `name LIKE '%张%'` |
|
||||
| `[<>]` | BETWEEN | `"age[<>]": [18,60]` | `age BETWEEN 18 AND 60` |
|
||||
| `[><]` | NOT BETWEEN | `"age[><]": [18,25]` | `age NOT BETWEEN 18 AND 25` |
|
||||
| `[#]` | 直接SQL | `"time[#]": "NOW()"` | `time = NOW()` |
|
||||
| `[##]` | SQL片段 | `"[##]": "a > b"` | `a > b` |
|
||||
| `[#!]` | 不等于直接SQL | `"status[#!]": "1"` | `status != 1` |
|
||||
| `[!#]` | 不等于直接SQL | `"status[!#]": "1"` | `status != 1` |
|
||||
|
||||
## 与PHP Medoo的差异
|
||||
|
||||
1. **类型系统**: Golang的强类型要求使用`common.Map`和`common.Slice`
|
||||
2. **语法差异**: 某些条件语法可能略有不同
|
||||
3. **错误处理**: 使用Golang的错误处理模式
|
||||
4. **并发安全**: 需要注意并发使用时的安全性
|
||||
5. **缓存集成**: 内置了缓存功能
|
||||
6. **链式调用**: 提供了更丰富的链式API
|
||||
|
||||
## 完整示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"database/sql"
|
||||
"code.hoteas.com/golang/hotime/db"
|
||||
"code.hoteas.com/golang/hotime/common"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化数据库
|
||||
database := &db.HoTimeDB{
|
||||
Prefix: "app_",
|
||||
Mode: 2, // 开发模式
|
||||
}
|
||||
|
||||
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
|
||||
master, _ = sql.Open("mysql", "root:password@tcp(localhost:3306)/testdb")
|
||||
return master, master
|
||||
})
|
||||
|
||||
// 查询用户列表
|
||||
users := database.Table("user").
|
||||
Where("status", 1).
|
||||
And("age[>=]", 18).
|
||||
Order("created_time DESC").
|
||||
Limit(0, 10).
|
||||
Select("id,name,email,age")
|
||||
|
||||
for _, user := range users {
|
||||
fmt.Printf("用户: %s, 邮箱: %s, 年龄: %v\n",
|
||||
user.GetString("name"),
|
||||
user.GetString("email"),
|
||||
user.Get("age"))
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
userId := database.Insert("user", common.Map{
|
||||
"name": "新用户",
|
||||
"email": "new@example.com",
|
||||
"age": 25,
|
||||
"status": 1,
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
fmt.Printf("创建用户ID: %d\n", userId)
|
||||
|
||||
// 更新用户
|
||||
affected := database.Table("user").
|
||||
Where("id", userId).
|
||||
Update(common.Map{
|
||||
"last_login[#]": "NOW()",
|
||||
"login_count[#]": "login_count + 1",
|
||||
})
|
||||
|
||||
fmt.Printf("更新记录数: %d\n", affected)
|
||||
|
||||
// 复杂查询示例
|
||||
orders := database.Table("order").
|
||||
LeftJoin("user", "order.user_id = user.id").
|
||||
LeftJoin("product", "order.product_id = product.id").
|
||||
Where("order.status", "paid").
|
||||
And("order.created_time[>]", "2023-01-01").
|
||||
And(common.Map{
|
||||
"OR": common.Map{
|
||||
"user.level": "vip",
|
||||
"order.amount[>]": 1000,
|
||||
},
|
||||
}).
|
||||
Order("order.created_time DESC").
|
||||
Group("user.id").
|
||||
Select("user.name, user.email, SUM(order.amount) as total_amount, COUNT(*) as order_count")
|
||||
|
||||
// 事务示例
|
||||
success := database.Action(func(tx db.HoTimeDB) bool {
|
||||
// 在事务中执行操作
|
||||
orderId := tx.Insert("order", common.Map{
|
||||
"user_id": userId,
|
||||
"total": 100.50,
|
||||
"status": "pending",
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
if orderId == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
detailId := tx.Insert("order_detail", common.Map{
|
||||
"order_id": orderId,
|
||||
"product_id": 1,
|
||||
"quantity": 2,
|
||||
"price": 50.25,
|
||||
})
|
||||
|
||||
return detailId > 0
|
||||
})
|
||||
|
||||
if success {
|
||||
fmt.Println("订单创建成功")
|
||||
} else {
|
||||
fmt.Println("订单创建失败:", database.LastErr.GetError())
|
||||
}
|
||||
|
||||
// 统计示例
|
||||
totalUsers := database.Count("user", common.Map{"status": 1})
|
||||
totalAmount := database.Sum("order", "amount", common.Map{"status": "paid"})
|
||||
|
||||
fmt.Printf("活跃用户数: %d, 总交易额: %.2f\n", totalUsers, totalAmount)
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 如何处理事务中的错误?
|
||||
A1: 在`Action`函数中返回`false`即可触发回滚,所有操作都会被撤销。
|
||||
|
||||
### Q2: 缓存何时会被清除?
|
||||
A2: 执行`Insert`、`Update`、`Delete`操作时会自动清除对应表的缓存。
|
||||
|
||||
### Q3: 如何执行复杂的原生SQL?
|
||||
A3: 使用`Query`方法执行查询,使用`Exec`方法执行更新操作。
|
||||
|
||||
### Q4: 主从分离如何工作?
|
||||
A4: 查询操作自动使用从库(如果配置了),增删改操作使用主库。
|
||||
|
||||
### Q5: 如何处理NULL值?
|
||||
A5: 使用`nil`作为值,查询时使用`"field": nil`表示`IS NULL`。
|
||||
|
||||
---
|
||||
|
||||
*文档版本: 1.0*
|
||||
*最后更新: 2024年*
|
||||
|
||||
> 本文档基于HoTimeDB源码分析生成,如有疑问请参考源码实现。该ORM框架参考了PHP Medoo的设计理念,但根据Golang语言特性进行了适配和优化。
|
252
db/README.md
Normal file
252
db/README.md
Normal file
@ -0,0 +1,252 @@
|
||||
# HoTimeDB ORM 文档集合
|
||||
|
||||
这是HoTimeDB ORM框架的完整文档集合,包含使用说明、API参考、示例代码和测试数据。
|
||||
|
||||
## ⚠️ 重要更新说明
|
||||
|
||||
**语法修正通知**:经过对源码的深入分析,发现HoTimeDB的条件查询语法有特定规则:
|
||||
- ✅ **单个条件**:可以直接写在Map中
|
||||
- ⚠️ **多个条件**:必须使用`AND`或`OR`包装
|
||||
- 📝 所有文档和示例代码已按正确语法更新
|
||||
|
||||
## 📚 文档列表
|
||||
|
||||
### 1. [HoTimeDB_使用说明.md](./HoTimeDB_使用说明.md)
|
||||
**完整使用说明书** - 详细的功能介绍和使用指南
|
||||
- 🚀 快速开始
|
||||
- ⚙️ 数据库配置
|
||||
- 🔧 基本操作 (CRUD)
|
||||
- 🔗 链式查询构建器
|
||||
- 🔍 条件查询语法
|
||||
- 🔄 JOIN操作
|
||||
- 📄 分页查询
|
||||
- 📊 聚合函数
|
||||
- 🔐 事务处理
|
||||
- 💾 缓存机制
|
||||
- ⚡ 高级特性
|
||||
|
||||
### 2. [HoTimeDB_API参考.md](./HoTimeDB_API参考.md)
|
||||
**快速API参考手册** - 开发时的速查手册
|
||||
- 📖 基本方法
|
||||
- 🔧 CRUD操作
|
||||
- 📊 聚合函数
|
||||
- 📄 分页查询
|
||||
- 🔍 条件语法参考
|
||||
- 🔗 JOIN语法
|
||||
- 🔐 事务处理
|
||||
- 🛠️ 工具方法
|
||||
|
||||
### 3. [示例代码文件](../examples/hotimedb_examples.go)
|
||||
**完整示例代码集合** - 可运行的实际应用示例(语法已修正)
|
||||
- 🏗️ 基本初始化和配置
|
||||
- 📝 基本CRUD操作
|
||||
- 🔗 链式查询操作
|
||||
- 🤝 JOIN查询操作
|
||||
- 🔍 条件查询语法
|
||||
- 📄 分页查询
|
||||
- 📊 聚合函数查询
|
||||
- 🔐 事务处理
|
||||
- 💾 缓存机制
|
||||
- 🔧 原生SQL执行
|
||||
- 🚨 错误处理和调试
|
||||
- ⚡ 性能优化技巧
|
||||
- 🎯 完整应用示例
|
||||
|
||||
### 4. [test_tables.sql](./test_tables.sql)
|
||||
**测试数据库结构** - 快速搭建测试环境
|
||||
- 🏗️ 完整的表结构定义
|
||||
- 📊 测试数据插入
|
||||
- 🔍 索引优化
|
||||
- 👁️ 视图示例
|
||||
- 🔧 存储过程示例
|
||||
|
||||
## 🎯 核心特性
|
||||
|
||||
### 🌟 主要优势
|
||||
- **类Medoo语法**: 参考PHP Medoo设计,语法简洁易懂
|
||||
- **链式查询**: 支持流畅的链式查询构建器
|
||||
- **条件丰富**: 支持丰富的条件查询语法
|
||||
- **事务支持**: 完整的事务处理机制
|
||||
- **缓存集成**: 内置查询结果缓存
|
||||
- **读写分离**: 支持主从数据库配置
|
||||
- **类型安全**: 基于Golang的强类型系统
|
||||
|
||||
### 🔧 支持的数据库
|
||||
- ✅ MySQL
|
||||
- ✅ SQLite
|
||||
- ✅ 其他标准SQL数据库
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
```bash
|
||||
go mod init your-project
|
||||
go get github.com/go-sql-driver/mysql
|
||||
go get github.com/sirupsen/logrus
|
||||
```
|
||||
|
||||
### 2. 创建测试数据库
|
||||
```bash
|
||||
mysql -u root -p < test_tables.sql
|
||||
```
|
||||
|
||||
### 3. 基本使用
|
||||
```go
|
||||
import (
|
||||
"code.hoteas.com/golang/hotime/db"
|
||||
"code.hoteas.com/golang/hotime/common"
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// 初始化数据库
|
||||
database := &db.HoTimeDB{
|
||||
Prefix: "app_",
|
||||
Mode: 2, // 开发模式
|
||||
}
|
||||
|
||||
database.SetConnect(func() (master, slave *sql.DB) {
|
||||
master, _ = sql.Open("mysql", "user:pass@tcp(localhost:3306)/dbname")
|
||||
return master, master
|
||||
})
|
||||
|
||||
// 链式查询(链式语法支持单独Where然后用And添加条件)
|
||||
users := database.Table("user").
|
||||
Where("status", 1). // 链式中可以单独Where
|
||||
And("age[>]", 18). // 用And添加更多条件
|
||||
Order("created_time DESC").
|
||||
Limit(0, 10).
|
||||
Select("id,name,email")
|
||||
|
||||
// 或者使用传统语法(多个条件必须用AND包装)
|
||||
users2 := database.Select("user", "id,name,email", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
"ORDER": "created_time DESC",
|
||||
"LIMIT": []int{0, 10},
|
||||
})
|
||||
```
|
||||
|
||||
## ⚠️ 重要语法规则
|
||||
|
||||
**条件查询语法规则:**
|
||||
- ✅ **单个条件**:可以直接写在Map中
|
||||
- ✅ **多个条件**:必须使用`AND`或`OR`包装
|
||||
- ✅ **特殊参数**:`ORDER`、`GROUP`、`LIMIT`与条件同级
|
||||
|
||||
```go
|
||||
// ✅ 正确:单个条件
|
||||
Map{"status": 1}
|
||||
|
||||
// ✅ 正确:多个条件用AND包装
|
||||
Map{
|
||||
"AND": Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
"ORDER": "id DESC",
|
||||
}
|
||||
|
||||
// ❌ 错误:多个条件不用AND包装
|
||||
Map{
|
||||
"status": 1,
|
||||
"age[>]": 18, // 不支持!
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 条件查询语法速查
|
||||
|
||||
| 语法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"field": value` | `field = ?` | 等于 |
|
||||
| `"field[!]": value` | `field != ?` | 不等于 |
|
||||
| `"field[>]": value` | `field > ?` | 大于 |
|
||||
| `"field[>=]": value` | `field >= ?` | 大于等于 |
|
||||
| `"field[<]": value` | `field < ?` | 小于 |
|
||||
| `"field[<=]": value` | `field <= ?` | 小于等于 |
|
||||
| `"field[~]": "keyword"` | `field LIKE '%keyword%'` | 包含 |
|
||||
| `"field[<>]": [min, max]` | `field BETWEEN ? AND ?` | 区间内 |
|
||||
| `"field": [v1, v2, v3]` | `field IN (?, ?, ?)` | 在集合中 |
|
||||
| `"field": nil` | `field IS NULL` | 为空 |
|
||||
| `"field[#]": "NOW()"` | `field = NOW()` | 直接SQL |
|
||||
|
||||
## 🔗 JOIN语法速查
|
||||
|
||||
| 语法 | SQL | 说明 |
|
||||
|------|-----|------|
|
||||
| `"[>]table"` | `LEFT JOIN` | 左连接 |
|
||||
| `"[<]table"` | `RIGHT JOIN` | 右连接 |
|
||||
| `"[><]table"` | `INNER JOIN` | 内连接 |
|
||||
| `"[<>]table"` | `FULL JOIN` | 全连接 |
|
||||
|
||||
## 🛠️ 链式方法速查
|
||||
|
||||
```go
|
||||
db.Table("table") // 指定表名
|
||||
.Where(key, value) // WHERE条件
|
||||
.And(key, value) // AND条件
|
||||
.Or(map) // OR条件
|
||||
.LeftJoin(table, on) // LEFT JOIN
|
||||
.Order(fields...) // ORDER BY
|
||||
.Group(fields...) // GROUP BY
|
||||
.Limit(offset, limit) // LIMIT
|
||||
.Page(page, pageSize) // 分页
|
||||
.Select(fields...) // 查询
|
||||
.Get(fields...) // 获取单条
|
||||
.Count() // 计数
|
||||
.Update(data) // 更新
|
||||
.Delete() // 删除
|
||||
```
|
||||
|
||||
## ⚡ 性能优化建议
|
||||
|
||||
### 🔍 查询优化
|
||||
- 使用合适的索引字段作为查询条件
|
||||
- IN查询会自动优化为BETWEEN(连续数字)
|
||||
- 避免SELECT *,指定需要的字段
|
||||
- 合理使用LIMIT限制结果集大小
|
||||
|
||||
### 💾 缓存使用
|
||||
- 查询结果会自动缓存
|
||||
- 增删改操作会自动清除缓存
|
||||
- `cached`表不参与缓存
|
||||
|
||||
### 🔐 事务处理
|
||||
- 批量操作使用事务提高性能
|
||||
- 事务中避免长时间操作
|
||||
- 合理设置事务隔离级别
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
### 🔒 安全相关
|
||||
- 使用参数化查询防止SQL注入
|
||||
- `[#]`语法需要注意防止注入
|
||||
- 敏感数据加密存储
|
||||
|
||||
### 🎯 最佳实践
|
||||
- 开发时设置`Mode = 2`便于调试
|
||||
- 生产环境设置`Mode = 0`
|
||||
- 合理设置表前缀
|
||||
- 定期检查慢查询日志
|
||||
|
||||
## 🤝 与PHP Medoo的差异
|
||||
|
||||
1. **类型系统**: 使用`common.Map`和`common.Slice`
|
||||
2. **错误处理**: Golang风格的错误处理
|
||||
3. **链式调用**: 提供更丰富的链式API
|
||||
4. **缓存集成**: 内置缓存功能
|
||||
5. **并发安全**: 需要注意并发使用
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
- 📧 查看源码:`hotimedb.go`
|
||||
- 📖 参考文档:本目录下的各个文档文件
|
||||
- 🔧 示例代码:运行`HoTimeDB_示例代码.go`中的示例
|
||||
|
||||
---
|
||||
|
||||
**HoTimeDB ORM框架 - 让数据库操作更简单!** 🎉
|
||||
|
||||
> 本文档基于HoTimeDB源码分析生成,参考了PHP Medoo的设计理念,并根据Golang语言特性进行了优化。
|
332
db/test_tables.sql
Normal file
332
db/test_tables.sql
Normal file
@ -0,0 +1,332 @@
|
||||
-- HoTimeDB 测试表结构
|
||||
-- 用于测试和示例的MySQL表结构定义
|
||||
-- 请根据实际需要修改表结构和字段类型
|
||||
|
||||
-- 创建数据库(可选)
|
||||
CREATE DATABASE IF NOT EXISTS `hotimedb_test` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
USE `hotimedb_test`;
|
||||
|
||||
-- 用户表
|
||||
DROP TABLE IF EXISTS `app_user`;
|
||||
CREATE TABLE `app_user` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '用户姓名',
|
||||
`email` varchar(255) NOT NULL DEFAULT '' COMMENT '邮箱地址',
|
||||
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码hash',
|
||||
`phone` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
|
||||
`gender` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0-未知 1-男 2-女',
|
||||
`avatar` varchar(500) NOT NULL DEFAULT '' COMMENT '头像URL',
|
||||
`level` varchar(20) NOT NULL DEFAULT 'normal' COMMENT '用户等级 normal-普通 vip-会员 svip-超级会员',
|
||||
`balance` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '账户余额',
|
||||
`login_count` int(11) NOT NULL DEFAULT '0' COMMENT '登录次数',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态 1-正常 0-禁用 -1-删除',
|
||||
`last_login` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_email` (`email`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_level` (`level`),
|
||||
KEY `idx_created_time` (`created_time`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
|
||||
|
||||
-- 用户资料表
|
||||
DROP TABLE IF EXISTS `app_profile`;
|
||||
CREATE TABLE `app_profile` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
|
||||
`real_name` varchar(50) NOT NULL DEFAULT '' COMMENT '真实姓名',
|
||||
`id_card` varchar(20) NOT NULL DEFAULT '' COMMENT '身份证号',
|
||||
`address` varchar(500) NOT NULL DEFAULT '' COMMENT '地址',
|
||||
`bio` text COMMENT '个人简介',
|
||||
`verified` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否认证 1-是 0-否',
|
||||
`preferences` json DEFAULT NULL COMMENT '用户偏好设置JSON',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_user_id` (`user_id`),
|
||||
KEY `idx_verified` (`verified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户资料表';
|
||||
|
||||
-- 部门表
|
||||
DROP TABLE IF EXISTS `app_department`;
|
||||
CREATE TABLE `app_department` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '部门ID',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '部门名称',
|
||||
`parent_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '上级部门ID',
|
||||
`manager_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '部门经理ID',
|
||||
`description` text COMMENT '部门描述',
|
||||
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态 1-正常 0-禁用',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_parent_id` (`parent_id`),
|
||||
KEY `idx_manager_id` (`manager_id`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部门表';
|
||||
|
||||
-- 商品表
|
||||
DROP TABLE IF EXISTS `app_product`;
|
||||
CREATE TABLE `app_product` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品ID',
|
||||
`title` varchar(200) NOT NULL DEFAULT '' COMMENT '商品标题',
|
||||
`description` text COMMENT '商品描述',
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品价格',
|
||||
`stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
|
||||
`category_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID',
|
||||
`brand` varchar(100) NOT NULL DEFAULT '' COMMENT '品牌',
|
||||
`tags` varchar(500) NOT NULL DEFAULT '' COMMENT '标签,逗号分隔',
|
||||
`images` json DEFAULT NULL COMMENT '商品图片JSON数组',
|
||||
`attributes` json DEFAULT NULL COMMENT '商品属性JSON',
|
||||
`sales_count` int(11) NOT NULL DEFAULT '0' COMMENT '销售数量',
|
||||
`view_count` int(11) NOT NULL DEFAULT '0' COMMENT '浏览数量',
|
||||
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态 1-上架 0-下架',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_category_id` (`category_id`),
|
||||
KEY `idx_price` (`price`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_created_time` (`created_time`),
|
||||
FULLTEXT KEY `ft_title_description` (`title`,`description`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商品表';
|
||||
|
||||
-- 订单表
|
||||
DROP TABLE IF EXISTS `app_order`;
|
||||
CREATE TABLE `app_order` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单ID',
|
||||
`order_no` varchar(50) NOT NULL DEFAULT '' COMMENT '订单号',
|
||||
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
|
||||
`product_id` bigint(20) unsigned NOT NULL COMMENT '商品ID',
|
||||
`quantity` int(11) NOT NULL DEFAULT '1' COMMENT '数量',
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '单价',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '总金额',
|
||||
`discount_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '优惠金额',
|
||||
`final_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '实付金额',
|
||||
`status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '订单状态 pending-待付款 paid-已付款 shipped-已发货 completed-已完成 cancelled-已取消',
|
||||
`payment_method` varchar(20) NOT NULL DEFAULT '' COMMENT '支付方式',
|
||||
`shipping_address` json DEFAULT NULL COMMENT '收货地址JSON',
|
||||
`remark` text COMMENT '订单备注',
|
||||
`paid_time` datetime DEFAULT NULL COMMENT '支付时间',
|
||||
`shipped_time` datetime DEFAULT NULL COMMENT '发货时间',
|
||||
`completed_time` datetime DEFAULT NULL COMMENT '完成时间',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_order_no` (`order_no`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_created_time` (`created_time`),
|
||||
KEY `idx_paid_time` (`paid_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';
|
||||
|
||||
-- 订单详情表
|
||||
DROP TABLE IF EXISTS `app_order_detail`;
|
||||
CREATE TABLE `app_order_detail` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`order_id` bigint(20) unsigned NOT NULL COMMENT '订单ID',
|
||||
`product_id` bigint(20) unsigned NOT NULL COMMENT '商品ID',
|
||||
`product_title` varchar(200) NOT NULL DEFAULT '' COMMENT '商品标题',
|
||||
`product_image` varchar(500) NOT NULL DEFAULT '' COMMENT '商品图片',
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '单价',
|
||||
`quantity` int(11) NOT NULL DEFAULT '1' COMMENT '数量',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '小计',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_product_id` (`product_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单详情表';
|
||||
|
||||
-- 支付日志表
|
||||
DROP TABLE IF EXISTS `app_payment_log`;
|
||||
CREATE TABLE `app_payment_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
|
||||
`order_id` bigint(20) unsigned DEFAULT NULL COMMENT '订单ID',
|
||||
`transaction_id` varchar(100) NOT NULL DEFAULT '' COMMENT '交易ID',
|
||||
`type` varchar(20) NOT NULL DEFAULT '' COMMENT '类型 order_payment-订单支付 recharge-充值 refund-退款',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '金额',
|
||||
`method` varchar(20) NOT NULL DEFAULT '' COMMENT '支付方式 balance-余额 alipay-支付宝 wechat-微信',
|
||||
`status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '状态 pending-处理中 success-成功 failed-失败',
|
||||
`description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述',
|
||||
`extra_data` json DEFAULT NULL COMMENT '额外数据JSON',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_transaction_id` (`transaction_id`),
|
||||
KEY `idx_type` (`type`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_created_time` (`created_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付日志表';
|
||||
|
||||
-- 转账日志表
|
||||
DROP TABLE IF EXISTS `app_transfer_log`;
|
||||
CREATE TABLE `app_transfer_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`from_user_id` bigint(20) unsigned NOT NULL COMMENT '转出用户ID',
|
||||
`to_user_id` bigint(20) unsigned NOT NULL COMMENT '转入用户ID',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '转账金额',
|
||||
`type` varchar(20) NOT NULL DEFAULT 'transfer' COMMENT '类型',
|
||||
`status` varchar(20) NOT NULL DEFAULT 'success' COMMENT '状态',
|
||||
`description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_from_user_id` (`from_user_id`),
|
||||
KEY `idx_to_user_id` (`to_user_id`),
|
||||
KEY `idx_created_time` (`created_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转账日志表';
|
||||
|
||||
-- 操作日志表
|
||||
DROP TABLE IF EXISTS `app_operation_log`;
|
||||
CREATE TABLE `app_operation_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
|
||||
`module` varchar(50) NOT NULL DEFAULT '' COMMENT '模块',
|
||||
`action` varchar(50) NOT NULL DEFAULT '' COMMENT '操作',
|
||||
`description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述',
|
||||
`ip` varchar(45) NOT NULL DEFAULT '' COMMENT 'IP地址',
|
||||
`user_agent` varchar(500) NOT NULL DEFAULT '' COMMENT '用户代理',
|
||||
`request_data` json DEFAULT NULL COMMENT '请求数据JSON',
|
||||
`response_data` json DEFAULT NULL COMMENT '响应数据JSON',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态 1-成功 0-失败',
|
||||
`execution_time` int(11) NOT NULL DEFAULT '0' COMMENT '执行时间(毫秒)',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_module` (`module`),
|
||||
KEY `idx_action` (`action`),
|
||||
KEY `idx_created_time` (`created_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='操作日志表';
|
||||
|
||||
-- 缓存表(HoTimeDB内置缓存使用)
|
||||
DROP TABLE IF EXISTS `app_cached`;
|
||||
CREATE TABLE `app_cached` (
|
||||
`key` varchar(255) NOT NULL COMMENT '缓存键',
|
||||
`value` longtext COMMENT '缓存值',
|
||||
`expire_time` datetime DEFAULT NULL COMMENT '过期时间',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`key`),
|
||||
KEY `idx_expire_time` (`expire_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='缓存表';
|
||||
|
||||
-- 批量用户表(用于批量操作示例)
|
||||
DROP TABLE IF EXISTS `app_user_batch`;
|
||||
CREATE TABLE `app_user_batch` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '用户姓名',
|
||||
`email` varchar(255) NOT NULL DEFAULT '' COMMENT '邮箱地址',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态',
|
||||
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_created_time` (`created_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='批量用户表';
|
||||
|
||||
-- 插入测试数据
|
||||
INSERT INTO `app_user` (`name`, `email`, `password`, `age`, `level`, `balance`, `status`, `created_time`) VALUES
|
||||
('张三', 'zhangsan@example.com', 'hashed_password_1', 25, 'normal', 1000.00, 1, '2023-01-15 10:30:00'),
|
||||
('李四', 'lisi@example.com', 'hashed_password_2', 30, 'vip', 5000.00, 1, '2023-02-20 14:20:00'),
|
||||
('王五', 'wangwu@example.com', 'hashed_password_3', 28, 'svip', 10000.00, 1, '2023-03-10 16:45:00'),
|
||||
('赵六', 'zhaoliu@example.com', 'hashed_password_4', 35, 'normal', 500.00, 1, '2023-04-05 09:15:00'),
|
||||
('钱七', 'qianqi@example.com', 'hashed_password_5', 22, 'vip', 2500.00, 0, '2023-05-12 11:30:00');
|
||||
|
||||
INSERT INTO `app_profile` (`user_id`, `real_name`, `verified`) VALUES
|
||||
(1, '张三', 1),
|
||||
(2, '李四', 1),
|
||||
(3, '王五', 1),
|
||||
(4, '赵六', 0),
|
||||
(5, '钱七', 0);
|
||||
|
||||
INSERT INTO `app_department` (`name`, `parent_id`, `description`) VALUES
|
||||
('技术部', 0, '负责技术开发和维护'),
|
||||
('产品部', 0, '负责产品设计和规划'),
|
||||
('市场部', 0, '负责市场推广和销售'),
|
||||
('前端组', 1, '负责前端开发'),
|
||||
('后端组', 1, '负责后端开发');
|
||||
|
||||
INSERT INTO `app_product` (`title`, `description`, `price`, `stock`, `category_id`, `brand`, `sales_count`) VALUES
|
||||
('苹果手机', '最新款苹果手机,性能强劲', 6999.00, 100, 1, '苹果', 50),
|
||||
('华为手机', '国产精品手机,拍照出色', 4999.00, 200, 1, '华为', 80),
|
||||
('小米手机', '性价比之王,配置丰富', 2999.00, 300, 1, '小米', 120),
|
||||
('联想笔记本', '商务办公首选,稳定可靠', 5999.00, 50, 2, '联想', 30),
|
||||
('戴尔笔记本', '游戏性能出色,散热良好', 8999.00, 30, 2, '戴尔', 15);
|
||||
|
||||
INSERT INTO `app_order` (`order_no`, `user_id`, `product_id`, `quantity`, `price`, `amount`, `final_amount`, `status`, `created_time`) VALUES
|
||||
('ORD202301150001', 1, 1, 1, 6999.00, 6999.00, 6999.00, 'paid', '2023-01-15 15:30:00'),
|
||||
('ORD202301160001', 2, 2, 2, 4999.00, 9998.00, 9998.00, 'paid', '2023-01-16 10:20:00'),
|
||||
('ORD202301170001', 3, 3, 1, 2999.00, 2999.00, 2999.00, 'completed', '2023-01-17 14:15:00'),
|
||||
('ORD202301180001', 1, 4, 1, 5999.00, 5999.00, 5999.00, 'pending', '2023-01-18 16:45:00'),
|
||||
('ORD202301190001', 4, 5, 1, 8999.00, 8999.00, 8999.00, 'cancelled', '2023-01-19 11:30:00');
|
||||
|
||||
INSERT INTO `app_order_detail` (`order_id`, `product_id`, `product_title`, `price`, `quantity`, `amount`) VALUES
|
||||
(1, 1, '苹果手机', 6999.00, 1, 6999.00),
|
||||
(2, 2, '华为手机', 4999.00, 2, 9998.00),
|
||||
(3, 3, '小米手机', 2999.00, 1, 2999.00),
|
||||
(4, 4, '联想笔记本', 5999.00, 1, 5999.00),
|
||||
(5, 5, '戴尔笔记本', 8999.00, 1, 8999.00);
|
||||
|
||||
INSERT INTO `app_payment_log` (`user_id`, `order_id`, `type`, `amount`, `method`, `status`, `description`) VALUES
|
||||
(1, 1, 'order_payment', 6999.00, 'balance', 'success', '订单支付'),
|
||||
(2, 2, 'order_payment', 9998.00, 'alipay', 'success', '订单支付'),
|
||||
(3, 3, 'order_payment', 2999.00, 'wechat', 'success', '订单支付'),
|
||||
(2, NULL, 'recharge', 10000.00, 'alipay', 'success', '账户充值'),
|
||||
(3, NULL, 'recharge', 5000.00, 'wechat', 'success', '账户充值');
|
||||
|
||||
-- 创建索引优化查询性能
|
||||
CREATE INDEX idx_user_email_status ON app_user(email, status);
|
||||
CREATE INDEX idx_order_user_status ON app_order(user_id, status);
|
||||
CREATE INDEX idx_order_created_status ON app_order(created_time, status);
|
||||
CREATE INDEX idx_product_category_status ON app_product(category_id, status);
|
||||
|
||||
-- 创建视图(可选)
|
||||
CREATE OR REPLACE VIEW v_user_order_stats AS
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
u.name as user_name,
|
||||
u.email,
|
||||
u.level,
|
||||
COUNT(o.id) as order_count,
|
||||
COALESCE(SUM(o.final_amount), 0) as total_amount,
|
||||
COALESCE(AVG(o.final_amount), 0) as avg_amount,
|
||||
MAX(o.created_time) as last_order_time
|
||||
FROM app_user u
|
||||
LEFT JOIN app_order o ON u.id = o.user_id AND o.status IN ('paid', 'completed')
|
||||
WHERE u.status = 1
|
||||
GROUP BY u.id;
|
||||
|
||||
-- 存储过程示例(可选)
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE GetUserOrderSummary(IN p_user_id BIGINT)
|
||||
BEGIN
|
||||
SELECT
|
||||
u.name,
|
||||
u.email,
|
||||
u.level,
|
||||
u.balance,
|
||||
COUNT(o.id) as order_count,
|
||||
COALESCE(SUM(CASE WHEN o.status = 'paid' THEN o.final_amount END), 0) as paid_amount,
|
||||
COALESCE(SUM(CASE WHEN o.status = 'completed' THEN o.final_amount END), 0) as completed_amount
|
||||
FROM app_user u
|
||||
LEFT JOIN app_order o ON u.id = o.user_id
|
||||
WHERE u.id = p_user_id AND u.status = 1
|
||||
GROUP BY u.id;
|
||||
END //
|
||||
DELIMITER ;
|
||||
|
||||
-- 显示表结构信息
|
||||
SELECT
|
||||
TABLE_NAME as '表名',
|
||||
TABLE_COMMENT as '表注释',
|
||||
TABLE_ROWS as '预估行数',
|
||||
ROUND(DATA_LENGTH/1024/1024, 2) as '数据大小(MB)'
|
||||
FROM information_schema.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE 'app_%'
|
||||
ORDER BY TABLE_NAME;
|
1
example/tpt/css/app.abfb5de2.css
Normal file
1
example/tpt/css/app.abfb5de2.css
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/css/chunk-04a3f664.48404c27.css
Normal file
1
example/tpt/css/chunk-04a3f664.48404c27.css
Normal file
@ -0,0 +1 @@
|
||||
.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.el-upload img[data-v-3b5105e6]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-3b5105e6]{font-size:40px;margin:30% 31%;display:block}.custom-tree-node[data-v-3b5105e6]{font-size:14px;padding-right:8px}.info-descriptions[data-v-4d4566fc] .el-descriptions__body .el-descriptions__label{min-width:90px;text-align:right;background:transparent;color:#606266;font-weight:400}.el-descriptions .is-bordered th[data-v-4d4566fc],.info-descriptions[data-v-4d4566fc] .el-descriptions .is-bordered td{border:transparent;max-width:25vw}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}.dialog-box .el-descriptions__header{margin:20px 0 5px}.dialog-box .el-descriptions__body{background:#fff}.textarea-box *{word-break:break-all;white-space:pre-wrap}.textarea-box table{width:100%!important}.textarea-box img{max-width:80%!important}.textarea-box::-webkit-scrollbar-thumb{height:5px;background-color:rgba(0,0,0,.2)!important}
|
1
example/tpt/css/chunk-0de923e9.c4e8272a.css
Normal file
1
example/tpt/css/chunk-0de923e9.c4e8272a.css
Normal file
@ -0,0 +1 @@
|
||||
body[data-v-c08f3364],dd[data-v-c08f3364],dl[data-v-c08f3364],form[data-v-c08f3364],h1[data-v-c08f3364],h2[data-v-c08f3364],h3[data-v-c08f3364],h4[data-v-c08f3364],h5[data-v-c08f3364],h6[data-v-c08f3364],html[data-v-c08f3364],ol[data-v-c08f3364],p[data-v-c08f3364],pre[data-v-c08f3364],tbody[data-v-c08f3364],textarea[data-v-c08f3364],tfoot[data-v-c08f3364],thead[data-v-c08f3364],ul[data-v-c08f3364]{margin:0;font-size:14px;font-family:Microsoft YaHei}dl[data-v-c08f3364],ol[data-v-c08f3364],ul[data-v-c08f3364]{padding:0}li[data-v-c08f3364]{list-style:none}input[data-v-c08f3364]{border:none;outline:none;font-family:Microsoft YaHei;background-color:#fff}a[data-v-c08f3364]{font-family:Microsoft YaHei;text-decoration:none}[data-v-c08f3364]{margin:0;padding:0}.login[data-v-c08f3364]{position:relative;width:100%;height:100%;background-color:#353d56;background-repeat:no-repeat;background-attachment:fixed;background-size:cover}.login-item[data-v-c08f3364]{position:absolute;top:calc(50% - 30vh);left:60%;min-width:388px;width:18vw;max-width:588px;padding:8vh 30px;box-sizing:border-box;background-size:468px 468px;background:hsla(0,0%,100%,.85);border-radius:10px}.login-item .right-content[data-v-c08f3364]{box-sizing:border-box;width:100%}.login-item .right-content .login-title[data-v-c08f3364]{font-size:26px;font-weight:700;color:#4f619b;text-align:center}.errorMsg[data-v-c08f3364]{width:100%;height:34px;line-height:34px;color:red;font-size:14px;overflow:hidden}.login-item .right-content .inputWrap[data-v-c08f3364]{width:90%;height:32px;line-height:32px;color:#646464;font-size:16px;border:1px solid #b4b4b4;margin:0 auto 5%;padding:2% 10px;border-radius:10px}.login-item .right-content .inputWrap.inputFocus[data-v-c08f3364]{border:1px solid #4f619b;box-shadow:0 0 0 3px rgba(91,113,185,.4)}.login-item .right-content .inputWrap input[data-v-c08f3364]{background-color:transparent;color:#646464;display:inline-block;height:100%;width:80%}.login-btn[data-v-c08f3364]{width:97%;height:52px;text-align:center;line-height:52px;font-size:17px;color:#fff;background-color:#4f619b;border-radius:10px;margin:0 auto;margin-top:50px;cursor:pointer;font-weight:800}
|
1
example/tpt/css/chunk-1a458102.466135f0.css
Normal file
1
example/tpt/css/chunk-1a458102.466135f0.css
Normal file
@ -0,0 +1 @@
|
||||
.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.el-upload img[data-v-51dbed3c]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-51dbed3c]{font-size:40px;margin:30% 31%;display:block}.custom-tree-node[data-v-51dbed3c]{font-size:14px;padding-right:8px}.info-descriptions[data-v-4d4566fc] .el-descriptions__body .el-descriptions__label{min-width:90px;text-align:right;background:transparent;color:#606266;font-weight:400}.el-descriptions .is-bordered th[data-v-4d4566fc],.info-descriptions[data-v-4d4566fc] .el-descriptions .is-bordered td{border:transparent;max-width:25vw}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}.dialog-box .el-descriptions__header{margin:20px 0 5px}.dialog-box .el-descriptions__body{background:#fff}.textarea-box *{word-break:break-all;white-space:pre-wrap}.textarea-box table{width:100%!important}.textarea-box img{max-width:80%!important}.textarea-box::-webkit-scrollbar-thumb{height:5px;background-color:rgba(0,0,0,.2)!important}
|
1
example/tpt/css/chunk-1e57bc5e.04e00b98.css
Normal file
1
example/tpt/css/chunk-1e57bc5e.04e00b98.css
Normal file
@ -0,0 +1 @@
|
||||
.full-screen-container[data-v-12e6b782]{z-index:10000}.file-upload .el-upload{background:transparent;width:100%;height:auto;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-upload .el-upload .el-button{margin-right:10px}.el-upload img[data-v-69e31561]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-69e31561]{font-size:40px;margin:30% 31%;display:block}.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}
|
1
example/tpt/css/chunk-291edee4.508cb3c8.css
Normal file
1
example/tpt/css/chunk-291edee4.508cb3c8.css
Normal file
@ -0,0 +1 @@
|
||||
.left-nav-home-bar{background:#2c3759!important;overflow:hidden;text-overflow:ellipsis}.left-nav-home-bar,.left-nav-home-bar i{color:#fff!important}.el-submenu .el-menu-item{height:40px;line-height:40px;width:auto;min-width:60px;padding:0 10px 0 25px!important;text-overflow:ellipsis;overflow:hidden}.el-menu .el-submenu__title{height:46px;line-height:46px;padding-left:10px!important;text-overflow:ellipsis;overflow:hidden}.left-nav-home-bar i{margin-bottom:6px!important}.el-menu-item-group__title{padding:0 0 0 10px}.el-menu--collapse .el-menu-item-group__title,.el-menu--collapse .el-submenu__title{padding-left:20px!important}.el-menu-item i,.el-submenu__title i{margin-top:-4px;vertical-align:middle;margin:-3px 5px 0 0;right:1px}.head-left[data-v-b2941c10],.head-right[data-v-b2941c10]{display:flex;justify-content:center;flex-direction:column}.head-right[data-v-b2941c10]{align-items:flex-end}.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.el-upload img[data-v-51dbed3c]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-51dbed3c]{font-size:40px;margin:30% 31%;display:block}.custom-tree-node[data-v-51dbed3c]{font-size:14px;padding-right:8px}.info-descriptions[data-v-4d4566fc] .el-descriptions__body .el-descriptions__label{min-width:90px;text-align:right;background:transparent;color:#606266;font-weight:400}.el-descriptions .is-bordered th[data-v-4d4566fc],.info-descriptions[data-v-4d4566fc] .el-descriptions .is-bordered td{border:transparent;max-width:25vw}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}.dialog-box .el-descriptions__header{margin:20px 0 5px}.dialog-box .el-descriptions__body{background:#fff}.textarea-box *{word-break:break-all;white-space:pre-wrap}.textarea-box table{width:100%!important}.textarea-box img{max-width:80%!important}.textarea-box::-webkit-scrollbar-thumb{height:5px;background-color:rgba(0,0,0,.2)!important}.el-dialog{margin:auto!important;top:50%;transform:translateY(-50%)}.el-dialog-div{height:75vh;overflow:auto}.el-dialog__body{padding-top:15px;padding-bottom:45px}.el-dialog-div .el-tabs__header{position:absolute;left:1px;top:69px;width:calc(90vw - 42px);margin:0 20px;z-index:1}.el-dialog-div .el-tabs__content{padding-top:50px}.el-dialog-div .el-affix--fixed{bottom:20vh}.el-dialog-div .el-affix--fixed .el-form-item{padding:0!important;background:transparent!important}
|
1
example/tpt/css/chunk-4aefa5ec.fcc75990.css
Normal file
1
example/tpt/css/chunk-4aefa5ec.fcc75990.css
Normal file
@ -0,0 +1 @@
|
||||
.full-screen-container[data-v-12e6b782]{z-index:10000}.file-upload .el-upload{background:transparent;width:100%;height:auto;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-upload .el-upload .el-button{margin-right:10px}.el-upload img[data-v-96cdfb38]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-96cdfb38]{font-size:40px;margin:30% 31%;display:block}.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}
|
1
example/tpt/css/chunk-4f81d902.91f1ef17.css
Normal file
1
example/tpt/css/chunk-4f81d902.91f1ef17.css
Normal file
@ -0,0 +1 @@
|
||||
.not-show-tab-label .el-tabs__header{display:none}.el-descriptions__body{background:#f0f0f0}.not-show-tab-search{display:none}.el-table__body-wrapper{margin-bottom:4px;padding-bottom:2px}.el-table__body-wrapper::-webkit-scrollbar{width:8px;height:8px}.el-table__body-wrapper::-webkit-scrollbar-track{border-radius:10px;-webkit-box-shadow:inset 0 0 6px hsla(0,0%,93.3%,.3);background-color:#eee}.el-table__body-wrapper::-webkit-scrollbar-thumb{border-radius:10px;-webkit-box-shadow:inset 0 0 6px rgba(145,143,143,.3);background-color:#918f8f}.input-with-select .el-input-group__prepend{background-color:#fff}.daterange-box .select .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.daterange-box .daterange.el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0;vertical-align:bottom}[data-v-e9f58da4] .el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#409eff!important;color:#fff}
|
1
example/tpt/css/chunk-6dd73395.9e906181.css
Normal file
1
example/tpt/css/chunk-6dd73395.9e906181.css
Normal file
@ -0,0 +1 @@
|
||||
.left-nav-home-bar{background:#2c3759!important;overflow:hidden;text-overflow:ellipsis}.left-nav-home-bar,.left-nav-home-bar i{color:#fff!important}.el-submenu .el-menu-item{height:40px;line-height:40px;width:auto;min-width:60px;padding:0 10px 0 25px!important;text-overflow:ellipsis;overflow:hidden}.el-menu .el-submenu__title{height:46px;line-height:46px;padding-left:10px!important;text-overflow:ellipsis;overflow:hidden}.left-nav-home-bar i{margin-bottom:6px!important}.el-menu-item-group__title{padding:0 0 0 10px}.el-menu--collapse .el-menu-item-group__title,.el-menu--collapse .el-submenu__title{padding-left:20px!important}.el-menu-item i,.el-submenu__title i{margin-top:-4px;vertical-align:middle;margin:-3px 5px 0 0;right:1px}.head-left[data-v-b2941c10],.head-right[data-v-b2941c10]{display:flex;justify-content:center;flex-direction:column}.head-right[data-v-b2941c10]{align-items:flex-end}.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.el-upload img[data-v-3b5105e6]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-3b5105e6]{font-size:40px;margin:30% 31%;display:block}.custom-tree-node[data-v-3b5105e6]{font-size:14px;padding-right:8px}.info-descriptions[data-v-4d4566fc] .el-descriptions__body .el-descriptions__label{min-width:90px;text-align:right;background:transparent;color:#606266;font-weight:400}.el-descriptions .is-bordered th[data-v-4d4566fc],.info-descriptions[data-v-4d4566fc] .el-descriptions .is-bordered td{border:transparent;max-width:25vw}.tree-line .el-tree-node{position:relative;padding-left:16px}.tree-line .el-tree-node__content{line-height:18px;font-size:14px}.tree-line .el-tree-node__children{padding-left:16px}.tree-line .el-tree-node:before{content:"";height:100%;width:1px;position:absolute;left:-3px;top:-26px;border-width:1px;border-left:1px dashed #52627c}.tree-line .el-tree-node:last-child:before{height:38px}.tree-line .el-tree-node:after{content:"";width:24px;height:20px;position:absolute;left:-3px;top:12px;border-width:1px;border-top:1px dashed #52627c}.tree-line>.el-tree-node:after{border-top:none}.tree-line>.el-tree-node:before{border-left:none}.tree-line .el-tree-node__expand-icon{font-size:18px;color:#000}.tree-line .el-tree-node__expand-icon.is-leaf{color:transparent}.dialog-box .el-descriptions__header{margin:20px 0 5px}.dialog-box .el-descriptions__body{background:#fff}.textarea-box *{word-break:break-all;white-space:pre-wrap}.textarea-box table{width:100%!important}.textarea-box img{max-width:80%!important}.textarea-box::-webkit-scrollbar-thumb{height:5px;background-color:rgba(0,0,0,.2)!important}.el-dialog{margin:auto!important;top:50%;transform:translateY(-50%)}.el-dialog-div{height:75vh;overflow:auto}.el-dialog__body{padding-top:15px;padding-bottom:45px}.el-dialog-div .el-tabs__header{position:absolute;left:1px;top:69px;width:calc(90vw - 42px);margin:0 20px;z-index:1}.el-dialog-div .el-tabs__content{padding-top:50px}.el-dialog-div .el-affix--fixed{bottom:20vh}.el-dialog-div .el-affix--fixed .el-form-item{padding:0!important;background:transparent!important}
|
1
example/tpt/css/chunk-856f3c38.9b9508b8.css
Normal file
1
example/tpt/css/chunk-856f3c38.9b9508b8.css
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/css/chunk-e3f8e5a6.7876554f.css
Normal file
1
example/tpt/css/chunk-e3f8e5a6.7876554f.css
Normal file
@ -0,0 +1 @@
|
||||
.full-screen-container[data-v-12e6b782]{z-index:10000}.not-show-tab-label .el-tabs__header{display:none}.el-descriptions__body{background:#f0f0f0}.not-show-tab-search{display:none}.el-table__body-wrapper{margin-bottom:4px;padding-bottom:2px}.el-table__body-wrapper::-webkit-scrollbar{width:8px;height:8px}.el-table__body-wrapper::-webkit-scrollbar-track{border-radius:10px;-webkit-box-shadow:inset 0 0 6px hsla(0,0%,93.3%,.3);background-color:#eee}.el-table__body-wrapper::-webkit-scrollbar-thumb{border-radius:10px;-webkit-box-shadow:inset 0 0 6px rgba(145,143,143,.3);background-color:#918f8f}
|
BIN
example/tpt/favicon.ico
Normal file
BIN
example/tpt/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
Binary file not shown.
BIN
example/tpt/fonts/element-icons.abe71f7d.ttf
Normal file
BIN
example/tpt/fonts/element-icons.abe71f7d.ttf
Normal file
Binary file not shown.
BIN
example/tpt/fonts/element-icons.d9491be2.woff
Normal file
BIN
example/tpt/fonts/element-icons.d9491be2.woff
Normal file
Binary file not shown.
3
example/tpt/index.html
Normal file
3
example/tpt/index.html
Normal file
@ -0,0 +1,3 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><script src="js/manage.js"></script><script src="https://api.map.baidu.com/api?v=2.0&ak=bF4Y6tQg94hV2vesn2ZIaUIXO4aRxxRk"></script><title></title><style>body{
|
||||
margin: 0px;
|
||||
}</style><link href="css/chunk-0de923e9.c4e8272a.css" rel="prefetch"><link href="css/chunk-1a458102.466135f0.css" rel="prefetch"><link href="css/chunk-291edee4.508cb3c8.css" rel="prefetch"><link href="css/chunk-4aefa5ec.fcc75990.css" rel="prefetch"><link href="css/chunk-4f81d902.91f1ef17.css" rel="prefetch"><link href="css/chunk-856f3c38.9b9508b8.css" rel="prefetch"><link href="css/chunk-e3f8e5a6.7876554f.css" rel="prefetch"><link href="js/chunk-0de923e9.1f0fca7e.js" rel="prefetch"><link href="js/chunk-1a458102.9a54e9ae.js" rel="prefetch"><link href="js/chunk-25ddb50b.55ec750b.js" rel="prefetch"><link href="js/chunk-291edee4.e8dbe8c8.js" rel="prefetch"><link href="js/chunk-4aefa5ec.c237610d.js" rel="prefetch"><link href="js/chunk-4f81d902.c5fdb7dd.js" rel="prefetch"><link href="js/chunk-7ea17297.38d572ef.js" rel="prefetch"><link href="js/chunk-856f3c38.29c2fcc0.js" rel="prefetch"><link href="js/chunk-e3f8e5a6.ff58e6f8.js" rel="prefetch"><link href="js/chunk-e624c1ca.62513cbc.js" rel="prefetch"><link href="css/app.abfb5de2.css" rel="preload" as="style"><link href="js/app.25e49e88.js" rel="preload" as="script"><link href="css/app.abfb5de2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but hotime doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/app.25e49e88.js"></script></body></html>
|
158
example/tpt/js/app.25e49e88.js
Normal file
158
example/tpt/js/app.25e49e88.js
Normal file
File diff suppressed because one or more lines are too long
158
example/tpt/js/app.9c3b64fc.js
Normal file
158
example/tpt/js/app.9c3b64fc.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-04a3f664.756a9a03.js
Normal file
1
example/tpt/js/chunk-04a3f664.756a9a03.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-0de923e9.1f0fca7e.js
Normal file
1
example/tpt/js/chunk-0de923e9.1f0fca7e.js
Normal file
@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-0de923e9"],{"578a":function(o,n,t){"use strict";t.r(n);t("b0c0");var i=t("f2bf"),s={class:"login-item"},r={class:"right-content"},u={class:"login-title"},l={style:{height:"60px"}},d=Object(i.r)(" 账号:"),b=Object(i.r)(" 密码:");var e=t("2934"),c={name:"Login",data:function(){return{showLog:!1,showLogInfo:"",label:"HoTime DashBoard",form:{name:"",password:""},backgroundImage:window.Hotime.data.name+"/hotime/wallpaper?random=1&type=1",focusName:!1,focusPassword:!1}},methods:{login:function(){var n=this;if(""==this.name||""==this.password)return this.showLogInfo="参数不足!",void(this.showLog=!0);Object(e.a)(window.Hotime.data.name+"/hotime/login",n.form).then(function(o){if(0!=o.status)return n.showLogInfo=o.error.msg,void(n.showLog=!0);location.hash="#/",location.reload()})},getBgImg:function(){var n=this;Object(e.e)(window.Hotime.data.name+"/hotime/wallpaper?random=1").then(function(o){0==o.status&&(n.backgroundImage=o.result.url)})},focusPrice:function(o){this["focus"+o]=!0},blurPrice:function(o){this["focus"+o]=!1}},mounted:function(){var n=this;this.label=window.Hotime.data.label,document.onkeydown=function(o){o=window.event||o;13==(o.keyCode||o.which||o.charCode)&&n.login()}}},a=(t("fad9"),t("6b0d")),t=t.n(a);n.default=t()(c,[["render",function(o,n,t,e,c,a){return Object(i.L)(),Object(i.n)("div",{class:"login",style:Object(i.C)({width:"100%",height:"100vh","background-image":"url("+c.backgroundImage+")"})},[Object(i.o)("div",s,[Object(i.o)("div",r,[Object(i.o)("p",u,Object(i.Y)(c.label),1),Object(i.o)("div",l,[Object(i.lb)(Object(i.o)("p",{class:"errorMsg"},Object(i.Y)(c.showLogInfo),513),[[i.hb,c.showLog]])]),Object(i.o)("p",{class:Object(i.B)(["inputWrap",{inputFocus:c.focusName}])},[d,Object(i.lb)(Object(i.o)("input",{type:"text","onUpdate:modelValue":n[0]||(n[0]=function(o){return c.form.name=o}),class:"accountVal",onKeyup:n[1]||(n[1]=Object(i.mb)(function(){return a.login&&a.login.apply(a,arguments)},["enter"])),onFocus:n[2]||(n[2]=function(o){return a.focusPrice("Name")}),onBlur:n[3]||(n[3]=function(o){return a.blurPrice("Name")})},null,544),[[i.gb,c.form.name]])],2),Object(i.o)("p",{class:Object(i.B)(["inputWrap",{inputFocus:c.focusPassword}])},[b,Object(i.lb)(Object(i.o)("input",{type:"password","onUpdate:modelValue":n[4]||(n[4]=function(o){return c.form.password=o}),class:"passwordVal",onKeyup:n[5]||(n[5]=Object(i.mb)(function(){return a.login&&a.login.apply(a,arguments)},["enter"])),onFocus:n[6]||(n[6]=function(o){return a.focusPrice("Password")}),onBlur:n[7]||(n[7]=function(o){return a.blurPrice("Password")})},null,544),[[i.gb,c.form.password]])],2),Object(i.o)("p",{class:"login-btn",onClick:n[8]||(n[8]=function(){return a.login&&a.login.apply(a,arguments)})},"登录")])])],4)}],["__scopeId","data-v-c08f3364"]])},dbeb:function(o,n,t){},fad9:function(o,n,t){"use strict";t("dbeb")}}]);
|
1
example/tpt/js/chunk-1a458102.9a54e9ae.js
Normal file
1
example/tpt/js/chunk-1a458102.9a54e9ae.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-1e57bc5e.986c6470.js
Normal file
1
example/tpt/js/chunk-1e57bc5e.986c6470.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-25ddb50b.55ec750b.js
Normal file
1
example/tpt/js/chunk-25ddb50b.55ec750b.js
Normal file
@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-25ddb50b"],{"107c":function(t,e,n){var r=n("d039"),c=n("da84").RegExp;t.exports=r(function(){var t=c("(?<a>b)","g");return"b"!==t.exec("b").groups.a||"bc"!=="b".replace(t,"$<a>c")})},"129f":function(t,e){t.exports=Object.is||function(t,e){return t===e?0!==t||1/t==1/e:t!=t&&e!=e}},"14c3":function(t,e,n){var r=n("c6b6"),c=n("9263");t.exports=function(t,e){var n=t.exec;if("function"==typeof n){n=n.call(t,e);if("object"!=typeof n)throw TypeError("RegExp exec method returned something other than an Object or null");return n}if("RegExp"!==r(t))throw TypeError("RegExp#exec called on incompatible receiver");return c.call(t,e)}},"159b":function(t,e,n){var r,c=n("da84"),i=n("fdbc"),o=n("17c2"),a=n("9112");for(r in i){var l=c[r],l=l&&l.prototype;if(l&&l.forEach!==o)try{a(l,"forEach",o)}catch(t){l.forEach=o}}},"17c2":function(t,e,n){"use strict";var r=n("b727").forEach,n=n("a640")("forEach");t.exports=n?[].forEach:function(t){return r(this,t,1<arguments.length?arguments[1]:void 0)}},"83c5":function(t,e,n){"use strict";n("159b");e.a={list:{},constructor:function(){this.list={}},$on:function(t,e){this.list[t]=this.list[t]||[],this.list[t].push(e)},$emit:function(t,e){this.list[t]&&this.list[t].forEach(function(t){t(e)})},$off:function(t){this.list[t]&&delete this.list[t]}}},"841c":function(t,e,n){"use strict";var r=n("d784"),o=n("825a"),a=n("1d80"),l=n("129f"),s=n("577e"),u=n("14c3");r("search",function(r,c,i){return[function(t){var e=a(this),n=null==t?void 0:t[r];return void 0!==n?n.call(t,e):new RegExp(t)[r](s(e))},function(t){var e=o(this),t=s(t),n=i(c,e,t);if(n.done)return n.value;n=e.lastIndex,l(n,0)||(e.lastIndex=0),t=u(e,t);return l(e.lastIndex,n)||(e.lastIndex=n),null===t?-1:t.index}]})},9263:function(t,e,n){"use strict";var r,p=n("577e"),h=n("ad6d"),c=n("9f7f"),i=n("5692"),g=n("7c73"),v=n("69f3").get,o=n("fce3"),n=n("107c"),E=RegExp.prototype.exec,b=i("native-string-replace",String.prototype.replace),I=E,R=(i=/a/,r=/b*/g,E.call(i,"a"),E.call(r,"a"),0!==i.lastIndex||0!==r.lastIndex),y=c.UNSUPPORTED_Y||c.BROKEN_CARET,w=void 0!==/()??/.exec("")[1];(R||w||y||o||n)&&(I=function(t){var e,n,r,c,i,o,a=this,l=v(a),t=p(t),s=l.raw;if(s)return s.lastIndex=a.lastIndex,f=I.call(s,t),a.lastIndex=s.lastIndex,f;var u=l.groups,s=y&&a.sticky,f=h.call(a),l=a.source,d=0,x=t;if(s&&(-1===(f=f.replace("y","")).indexOf("g")&&(f+="g"),x=t.slice(a.lastIndex),0<a.lastIndex&&(!a.multiline||a.multiline&&"\n"!==t.charAt(a.lastIndex-1))&&(l="(?: "+l+")",x=" "+x,d++),e=new RegExp("^(?:"+l+")",f)),w&&(e=new RegExp("^"+l+"$(?!\\s)",f)),R&&(n=a.lastIndex),r=E.call(s?e:a,x),s?r?(r.input=r.input.slice(d),r[0]=r[0].slice(d),r.index=a.lastIndex,a.lastIndex+=r[0].length):a.lastIndex=0:R&&r&&(a.lastIndex=a.global?r.index+r[0].length:n),w&&r&&1<r.length&&b.call(r[0],e,function(){for(c=1;c<arguments.length-2;c++)void 0===arguments[c]&&(r[c]=void 0)}),r&&u)for(r.groups=i=g(null),c=0;c<u.length;c++)i[(o=u[c])[0]]=r[o[1]];return r}),t.exports=I},"9f7f":function(t,e,n){var r=n("d039"),c=n("da84").RegExp;e.UNSUPPORTED_Y=r(function(){var t=c("a","y");return t.lastIndex=2,null!=t.exec("abcd")}),e.BROKEN_CARET=r(function(){var t=c("^r","gy");return t.lastIndex=2,null!=t.exec("str")})},ac1f:function(t,e,n){"use strict";var r=n("23e7"),n=n("9263");r({target:"RegExp",proto:!0,forced:/./.exec!==n},{exec:n})},ad6d:function(t,e,n){"use strict";var r=n("825a");t.exports=function(){var t=r(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.dotAll&&(e+="s"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},d784:function(t,e,n){"use strict";n("ac1f");var l=n("6eeb"),s=n("9263"),u=n("d039"),f=n("b622"),d=n("9112"),x=f("species"),p=RegExp.prototype;t.exports=function(n,t,e,r){var o,c=f(n),a=!u(function(){var t={};return t[c]=function(){return 7},7!=""[n](t)}),i=a&&!u(function(){var t=!1,e=/a/;return"split"===n&&((e={constructor:{}}).constructor[x]=function(){return e},e.flags="",e[c]=/./[c]),e.exec=function(){return t=!0,null},e[c](""),!t});a&&i&&!e||(o=/./[c],i=t(c,""[n],function(t,e,n,r,c){var i=e.exec;return i===s||i===p.exec?a&&!c?{done:!0,value:o.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}}),l(String.prototype,n,i[0]),l(p,c,i[1])),r&&d(p[c],"sham",!0)}},fce3:function(t,e,n){var r=n("d039"),c=n("da84").RegExp;t.exports=r(function(){var t=c(".","s");return!(t.dotAll&&t.exec("\n")&&"s"===t.flags)})}}]);
|
1
example/tpt/js/chunk-291edee4.e8dbe8c8.js
Normal file
1
example/tpt/js/chunk-291edee4.e8dbe8c8.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-4aefa5ec.c237610d.js
Normal file
1
example/tpt/js/chunk-4aefa5ec.c237610d.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-4f81d902.c5fdb7dd.js
Normal file
1
example/tpt/js/chunk-4f81d902.c5fdb7dd.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-6dd73395.87dc9c3e.js
Normal file
1
example/tpt/js/chunk-6dd73395.87dc9c3e.js
Normal file
File diff suppressed because one or more lines are too long
278
example/tpt/js/chunk-7ea17297.38d572ef.js
Normal file
278
example/tpt/js/chunk-7ea17297.38d572ef.js
Normal file
File diff suppressed because one or more lines are too long
2
example/tpt/js/chunk-856f3c38.29c2fcc0.js
Normal file
2
example/tpt/js/chunk-856f3c38.29c2fcc0.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-e3f8e5a6.ff58e6f8.js
Normal file
1
example/tpt/js/chunk-e3f8e5a6.ff58e6f8.js
Normal file
File diff suppressed because one or more lines are too long
1
example/tpt/js/chunk-e624c1ca.62513cbc.js
Normal file
1
example/tpt/js/chunk-e624c1ca.62513cbc.js
Normal file
@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-e624c1ca"],{"4de4":function(t,n,r){"use strict";var e=r("23e7"),o=r("b727").filter;e({target:"Array",proto:!0,forced:!r("1dde")("filter")},{filter:function(t){return o(this,t,1<arguments.length?arguments[1]:void 0)}})},a640:function(t,n,r){"use strict";var e=r("d039");t.exports=function(t,n){var r=[][t];return!!r&&e(function(){r.call(null,n||function(){throw 1},1)})}}}]);
|
13
example/tpt/js/manage.js
Normal file
13
example/tpt/js/manage.js
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2022/7/23 下午7:22
|
||||
* Author:HoTeas
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var Hotime = {
|
||||
vueComponent: {},
|
||||
mapData: {},
|
||||
pageRow:20,
|
||||
tableMapData: {},
|
||||
tableName:"admin"
|
||||
}
|
784
examples/hotimedb_examples.go
Normal file
784
examples/hotimedb_examples.go
Normal file
@ -0,0 +1,784 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
// 实际使用时请替换为正确的导入路径
|
||||
// "code.hoteas.com/golang/hotime/cache"
|
||||
// "code.hoteas.com/golang/hotime/common"
|
||||
// "code.hoteas.com/golang/hotime/db"
|
||||
"code.hoteas.com/golang/hotime/cache"
|
||||
"code.hoteas.com/golang/hotime/common"
|
||||
"code.hoteas.com/golang/hotime/db"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// HoTimeDB使用示例代码集合 - 修正版
|
||||
// 本文件包含了各种常见场景的完整示例代码,所有条件查询语法已修正
|
||||
|
||||
// 示例1: 基本初始化和配置
|
||||
func Example1_BasicSetup() {
|
||||
// 创建数据库实例
|
||||
database := &db.HoTimeDB{
|
||||
Prefix: "app_", // 设置表前缀
|
||||
Mode: 2, // 开发模式,输出SQL日志
|
||||
Type: "mysql", // 数据库类型
|
||||
}
|
||||
|
||||
// 设置日志
|
||||
logger := logrus.New()
|
||||
database.Log = logger
|
||||
|
||||
// 设置连接函数
|
||||
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
|
||||
// 主数据库连接
|
||||
master, dbErr := sql.Open("mysql", "root:password@tcp(localhost:3306)/testdb?charset=utf8&parseTime=true")
|
||||
if dbErr != nil {
|
||||
log.Fatal("数据库连接失败:", dbErr)
|
||||
}
|
||||
|
||||
// 从数据库连接(可选,用于读写分离)
|
||||
slave = master // 这里使用同一个连接,实际项目中可以连接到从库
|
||||
|
||||
return master, slave
|
||||
})
|
||||
|
||||
fmt.Println("数据库初始化完成")
|
||||
}
|
||||
|
||||
// 示例2: 基本CRUD操作(修正版)
|
||||
func Example2_BasicCRUD_Fixed(db *db.HoTimeDB) {
|
||||
// 创建用户
|
||||
fmt.Println("=== 创建用户 ===")
|
||||
userId := db.Insert("user", common.Map{
|
||||
"name": "张三",
|
||||
"email": "zhangsan@example.com",
|
||||
"age": 25,
|
||||
"status": 1,
|
||||
"balance": 1000.50,
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
fmt.Printf("新用户ID: %d\n", userId)
|
||||
|
||||
// 查询用户(单条件,可以不用AND)
|
||||
fmt.Println("\n=== 查询用户 ===")
|
||||
user := db.Get("user", "*", common.Map{
|
||||
"id": userId,
|
||||
})
|
||||
if user != nil {
|
||||
fmt.Printf("用户信息: %+v\n", user)
|
||||
}
|
||||
|
||||
// 更新用户(多条件必须用AND包装)
|
||||
fmt.Println("\n=== 更新用户 ===")
|
||||
affected := db.Update("user", common.Map{
|
||||
"name": "李四",
|
||||
"age": 26,
|
||||
"updated_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"AND": common.Map{
|
||||
"id": userId,
|
||||
"status": 1, // 确保只更新正常状态的用户
|
||||
},
|
||||
})
|
||||
fmt.Printf("更新记录数: %d\n", affected)
|
||||
|
||||
// 软删除用户
|
||||
fmt.Println("\n=== 软删除用户 ===")
|
||||
affected = db.Update("user", common.Map{
|
||||
"deleted_at[#]": "NOW()",
|
||||
"status": 0,
|
||||
}, common.Map{
|
||||
"id": userId, // 单条件,不需要AND
|
||||
})
|
||||
fmt.Printf("软删除记录数: %d\n", affected)
|
||||
}
|
||||
|
||||
// 示例3: 条件查询语法(修正版)
|
||||
func Example3_ConditionQuery_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 条件查询语法示例 ===")
|
||||
|
||||
// ✅ 正确:单个条件
|
||||
users1 := db.Select("user", "*", common.Map{
|
||||
"status": 1,
|
||||
})
|
||||
fmt.Printf("活跃用户: %d个\n", len(users1))
|
||||
|
||||
// ✅ 正确:多个条件用AND包装
|
||||
users2 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
"age[<=]": 60,
|
||||
},
|
||||
})
|
||||
fmt.Printf("活跃的成年用户: %d个\n", len(users2))
|
||||
|
||||
// ✅ 正确:OR条件
|
||||
users3 := db.Select("user", "*", common.Map{
|
||||
"OR": common.Map{
|
||||
"level": "vip",
|
||||
"balance[>]": 5000,
|
||||
},
|
||||
})
|
||||
fmt.Printf("VIP或高余额用户: %d个\n", len(users3))
|
||||
|
||||
// ✅ 正确:条件 + 特殊参数
|
||||
users4 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"age[>=]": 18,
|
||||
},
|
||||
"ORDER": "created_time DESC",
|
||||
"LIMIT": 10,
|
||||
})
|
||||
fmt.Printf("最近的活跃成年用户: %d个\n", len(users4))
|
||||
|
||||
// ✅ 正确:复杂嵌套条件
|
||||
users5 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"OR": common.Map{
|
||||
"age[<]": 30,
|
||||
"level": "vip",
|
||||
},
|
||||
},
|
||||
"ORDER": []string{"level DESC", "created_time DESC"},
|
||||
"LIMIT": []int{0, 20},
|
||||
})
|
||||
fmt.Printf("年轻或VIP的活跃用户: %d个\n", len(users5))
|
||||
|
||||
// ✅ 正确:模糊查询
|
||||
users6 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"name[~]": "张", // 姓名包含"张"
|
||||
"email[~!]": "gmail", // 邮箱以gmail开头
|
||||
"status": 1,
|
||||
},
|
||||
})
|
||||
fmt.Printf("姓张的gmail活跃用户: %d个\n", len(users6))
|
||||
|
||||
// ✅ 正确:范围查询
|
||||
users7 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"age[<>]": []int{18, 35}, // 年龄在18-35之间
|
||||
"balance[><]": []float64{0, 100}, // 余额不在0-100之间
|
||||
"status": 1,
|
||||
},
|
||||
})
|
||||
fmt.Printf("18-35岁且余额>100的活跃用户: %d个\n", len(users7))
|
||||
|
||||
// ✅ 正确:IN查询
|
||||
users8 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"id": []int{1, 2, 3, 4, 5}, // ID在指定范围内
|
||||
"status[!]": []int{0, -1}, // 状态不为0或-1
|
||||
},
|
||||
})
|
||||
fmt.Printf("指定ID的活跃用户: %d个\n", len(users8))
|
||||
}
|
||||
|
||||
// 示例4: 链式查询操作(正确版)
|
||||
func Example4_ChainQuery_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 链式查询示例 ===")
|
||||
|
||||
// 链式查询(链式语法允许单独的Where,然后用And添加更多条件)
|
||||
users := db.Table("user").
|
||||
Where("status", 1). // 链式中可以单独Where
|
||||
And("age[>=]", 18). // 然后用And添加条件
|
||||
And("age[<=]", 60). // 再添加条件
|
||||
Or(common.Map{ // 或者用Or添加OR条件组
|
||||
"level": "vip",
|
||||
"balance[>]": 5000,
|
||||
}).
|
||||
Order("created_time DESC", "id ASC"). // 排序
|
||||
Limit(0, 10). // 限制结果
|
||||
Select("id,name,email,age,balance,level")
|
||||
|
||||
fmt.Printf("链式查询到 %d 个用户\n", len(users))
|
||||
for i, user := range users {
|
||||
fmt.Printf("用户%d: %s (年龄:%v, 余额:%v)\n",
|
||||
i+1,
|
||||
user.GetString("name"),
|
||||
user.Get("age"),
|
||||
user.Get("balance"))
|
||||
}
|
||||
|
||||
// 链式统计查询
|
||||
count := db.Table("user").
|
||||
Where("status", 1).
|
||||
And("age[>=]", 18).
|
||||
Count()
|
||||
fmt.Printf("符合条件的用户总数: %d\n", count)
|
||||
}
|
||||
|
||||
// 示例5: JOIN查询操作(修正版)
|
||||
func Example5_JoinQuery_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== JOIN查询示例 ===")
|
||||
|
||||
// 链式JOIN查询
|
||||
orders := db.Table("order").
|
||||
LeftJoin("user", "order.user_id = user.id").
|
||||
LeftJoin("product", "order.product_id = product.id").
|
||||
Where("order.status", "paid"). // 链式中单个条件可以直接Where
|
||||
And("order.created_time[>]", "2023-01-01"). // 用And添加更多条件
|
||||
Order("order.created_time DESC").
|
||||
Select(`
|
||||
order.id as order_id,
|
||||
order.amount,
|
||||
order.status,
|
||||
order.created_time,
|
||||
user.name as user_name,
|
||||
user.email as user_email,
|
||||
product.title as product_title,
|
||||
product.price as product_price
|
||||
`)
|
||||
|
||||
fmt.Printf("链式JOIN查询到 %d 个订单\n", len(orders))
|
||||
for _, order := range orders {
|
||||
fmt.Printf("订单ID:%v, 用户:%s, 商品:%s, 金额:%v\n",
|
||||
order.Get("order_id"),
|
||||
order.GetString("user_name"),
|
||||
order.GetString("product_title"),
|
||||
order.Get("amount"))
|
||||
}
|
||||
|
||||
// 传统JOIN语法(多个条件必须用AND包装)
|
||||
orders2 := db.Select("order",
|
||||
common.Slice{
|
||||
common.Map{"[>]user": "order.user_id = user.id"},
|
||||
common.Map{"[>]product": "order.product_id = product.id"},
|
||||
},
|
||||
"order.*, user.name as user_name, product.title as product_title",
|
||||
common.Map{
|
||||
"AND": common.Map{
|
||||
"order.status": "paid",
|
||||
"order.created_time[>]": "2023-01-01",
|
||||
},
|
||||
})
|
||||
|
||||
fmt.Printf("传统JOIN语法查询到 %d 个订单\n", len(orders2))
|
||||
}
|
||||
|
||||
// 示例6: 分页查询(修正版)
|
||||
func Example6_PaginationQuery_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 分页查询示例 ===")
|
||||
|
||||
page := 2
|
||||
pageSize := 10
|
||||
|
||||
// 获取总数(单条件)
|
||||
total := db.Count("user", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"deleted_at": nil,
|
||||
},
|
||||
})
|
||||
|
||||
// 分页数据(链式方式)
|
||||
users := db.Table("user").
|
||||
Where("status", 1).
|
||||
And("deleted_at", nil).
|
||||
Order("created_time DESC").
|
||||
Page(page, pageSize).
|
||||
Select("id,name,email,created_time")
|
||||
|
||||
// 使用传统方式的分页查询
|
||||
users2 := db.Page(page, pageSize).PageSelect("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"status": 1,
|
||||
"deleted_at": nil,
|
||||
},
|
||||
"ORDER": "created_time DESC",
|
||||
})
|
||||
|
||||
// 计算分页信息
|
||||
totalPages := (total + pageSize - 1) / pageSize
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
fmt.Printf("总记录数: %d\n", total)
|
||||
fmt.Printf("总页数: %d\n", totalPages)
|
||||
fmt.Printf("当前页: %d\n", page)
|
||||
fmt.Printf("每页大小: %d\n", pageSize)
|
||||
fmt.Printf("偏移量: %d\n", offset)
|
||||
fmt.Printf("链式查询当前页记录数: %d\n", len(users))
|
||||
fmt.Printf("传统查询当前页记录数: %d\n", len(users2))
|
||||
|
||||
for i, user := range users {
|
||||
fmt.Printf(" %d. %s (%s) - %v\n",
|
||||
offset+i+1,
|
||||
user.GetString("name"),
|
||||
user.GetString("email"),
|
||||
user.Get("created_time"))
|
||||
}
|
||||
}
|
||||
|
||||
// 示例7: 聚合函数查询(修正版)
|
||||
func Example7_AggregateQuery_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 聚合函数查询示例 ===")
|
||||
|
||||
// 基本统计
|
||||
userCount := db.Count("user")
|
||||
activeUserCount := db.Count("user", common.Map{"status": 1})
|
||||
totalBalance := db.Sum("user", "balance", common.Map{"status": 1})
|
||||
|
||||
fmt.Printf("总用户数: %d\n", userCount)
|
||||
fmt.Printf("活跃用户数: %d\n", activeUserCount)
|
||||
fmt.Printf("活跃用户总余额: %.2f\n", totalBalance)
|
||||
|
||||
// 分组统计(正确语法)
|
||||
stats := db.Select("user",
|
||||
"level, COUNT(*) as user_count, AVG(age) as avg_age, SUM(balance) as total_balance",
|
||||
common.Map{
|
||||
"status": 1, // 单条件,不需要AND
|
||||
"GROUP": "level",
|
||||
"ORDER": "user_count DESC",
|
||||
})
|
||||
|
||||
fmt.Println("\n按等级分组统计:")
|
||||
for _, stat := range stats {
|
||||
fmt.Printf("等级:%v, 用户数:%v, 平均年龄:%v, 总余额:%v\n",
|
||||
stat.Get("level"),
|
||||
stat.Get("user_count"),
|
||||
stat.Get("avg_age"),
|
||||
stat.Get("total_balance"))
|
||||
}
|
||||
|
||||
// 关联统计(修正版)
|
||||
orderStats := db.Select("order",
|
||||
common.Slice{
|
||||
common.Map{"[>]user": "order.user_id = user.id"},
|
||||
},
|
||||
"user.level, COUNT(order.id) as order_count, SUM(order.amount) as total_amount",
|
||||
common.Map{
|
||||
"AND": common.Map{
|
||||
"order.status": "paid",
|
||||
"order.created_time[>]": "2023-01-01",
|
||||
},
|
||||
"GROUP": "user.level",
|
||||
"ORDER": "total_amount DESC",
|
||||
})
|
||||
|
||||
fmt.Println("\n用户等级订单统计:")
|
||||
for _, stat := range orderStats {
|
||||
fmt.Printf("等级:%v, 订单数:%v, 总金额:%v\n",
|
||||
stat.Get("level"),
|
||||
stat.Get("order_count"),
|
||||
stat.Get("total_amount"))
|
||||
}
|
||||
}
|
||||
|
||||
// 示例8: 事务处理(修正版)
|
||||
func Example8_Transaction_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 事务处理示例 ===")
|
||||
|
||||
// 模拟转账操作
|
||||
fromUserId := int64(1)
|
||||
toUserId := int64(2)
|
||||
amount := 100.0
|
||||
|
||||
success := db.Action(func(tx db.HoTimeDB) bool {
|
||||
// 检查转出账户余额(单条件)
|
||||
fromUser := tx.Get("user", "balance", common.Map{"id": fromUserId})
|
||||
if fromUser == nil {
|
||||
fmt.Println("转出用户不存在")
|
||||
return false
|
||||
}
|
||||
|
||||
fromBalance := fromUser.GetFloat64("balance")
|
||||
if fromBalance < amount {
|
||||
fmt.Println("余额不足")
|
||||
return false
|
||||
}
|
||||
|
||||
// 扣减转出账户余额(多条件必须用AND)
|
||||
affected1 := tx.Update("user", common.Map{
|
||||
"balance[#]": fmt.Sprintf("balance - %.2f", amount),
|
||||
"updated_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"AND": common.Map{
|
||||
"id": fromUserId,
|
||||
"balance[>=]": amount, // 再次确保余额足够
|
||||
},
|
||||
})
|
||||
|
||||
if affected1 == 0 {
|
||||
fmt.Println("扣减余额失败")
|
||||
return false
|
||||
}
|
||||
|
||||
// 增加转入账户余额(单条件)
|
||||
affected2 := tx.Update("user", common.Map{
|
||||
"balance[#]": fmt.Sprintf("balance + %.2f", amount),
|
||||
"updated_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"id": toUserId,
|
||||
})
|
||||
|
||||
if affected2 == 0 {
|
||||
fmt.Println("增加余额失败")
|
||||
return false
|
||||
}
|
||||
|
||||
// 记录转账日志
|
||||
logId := tx.Insert("transfer_log", common.Map{
|
||||
"from_user_id": fromUserId,
|
||||
"to_user_id": toUserId,
|
||||
"amount": amount,
|
||||
"status": "success",
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
if logId == 0 {
|
||||
fmt.Println("记录日志失败")
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Printf("转账成功: 用户%d -> 用户%d, 金额:%.2f\n", fromUserId, toUserId, amount)
|
||||
return true
|
||||
})
|
||||
|
||||
if success {
|
||||
fmt.Println("事务执行成功")
|
||||
} else {
|
||||
fmt.Println("事务回滚")
|
||||
if db.LastErr.GetError() != nil {
|
||||
fmt.Println("错误原因:", db.LastErr.GetError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 示例9: 缓存机制(修正版)
|
||||
func Example9_CacheSystem_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 缓存机制示例 ===")
|
||||
|
||||
// 设置缓存(实际项目中需要配置缓存参数)
|
||||
db.HoTimeCache = &cache.HoTimeCache{}
|
||||
|
||||
// 第一次查询(会缓存结果)
|
||||
fmt.Println("第一次查询(会缓存)...")
|
||||
users1 := db.Select("user", "*", common.Map{
|
||||
"status": 1, // 单条件
|
||||
"LIMIT": 10,
|
||||
})
|
||||
fmt.Printf("查询到 %d 个用户\n", len(users1))
|
||||
|
||||
// 第二次相同查询(从缓存获取)
|
||||
fmt.Println("第二次相同查询(从缓存获取)...")
|
||||
users2 := db.Select("user", "*", common.Map{
|
||||
"status": 1, // 单条件
|
||||
"LIMIT": 10,
|
||||
})
|
||||
fmt.Printf("查询到 %d 个用户\n", len(users2))
|
||||
|
||||
// 更新操作会清除缓存
|
||||
fmt.Println("执行更新操作(会清除缓存)...")
|
||||
affected := db.Update("user", common.Map{
|
||||
"updated_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"id": 1, // 单条件
|
||||
})
|
||||
fmt.Printf("更新 %d 条记录\n", affected)
|
||||
|
||||
// 再次查询(重新从数据库获取并缓存)
|
||||
fmt.Println("更新后再次查询(重新缓存)...")
|
||||
users3 := db.Select("user", "*", common.Map{
|
||||
"status": 1, // 单条件
|
||||
"LIMIT": 10,
|
||||
})
|
||||
fmt.Printf("查询到 %d 个用户\n", len(users3))
|
||||
}
|
||||
|
||||
// 示例10: 性能优化技巧(修正版)
|
||||
func Example10_PerformanceOptimization_Fixed(db *db.HoTimeDB) {
|
||||
fmt.Println("=== 性能优化技巧示例 ===")
|
||||
|
||||
// IN查询优化(连续数字自动转为BETWEEN)
|
||||
fmt.Println("IN查询优化示例...")
|
||||
users := db.Select("user", "*", common.Map{
|
||||
"id": []int{1, 2, 3, 4, 5, 10, 11, 12, 13, 20}, // 单个IN条件,会被优化
|
||||
})
|
||||
fmt.Printf("查询到 %d 个用户\n", len(users))
|
||||
fmt.Println("执行的SQL:", db.LastQuery)
|
||||
|
||||
// 批量插入(使用事务)
|
||||
fmt.Println("\n批量插入示例...")
|
||||
success := db.Action(func(tx db.HoTimeDB) bool {
|
||||
for i := 1; i <= 100; i++ {
|
||||
id := tx.Insert("user_batch", common.Map{
|
||||
"name": fmt.Sprintf("批量用户%d", i),
|
||||
"email": fmt.Sprintf("batch%d@example.com", i),
|
||||
"status": 1,
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
if id == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 每10个用户输出一次进度
|
||||
if i%10 == 0 {
|
||||
fmt.Printf("已插入 %d 个用户\n", i)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if success {
|
||||
fmt.Println("批量插入完成")
|
||||
} else {
|
||||
fmt.Println("批量插入失败")
|
||||
}
|
||||
|
||||
// 索引友好的查询(修正版)
|
||||
fmt.Println("\n索引友好的查询...")
|
||||
recentUsers := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{
|
||||
"created_time[>]": "2023-01-01", // 假设created_time有索引
|
||||
"status": 1, // 假设status有索引
|
||||
},
|
||||
"ORDER": "created_time DESC", // 利用索引排序
|
||||
"LIMIT": 20,
|
||||
})
|
||||
fmt.Printf("查询到 %d 个近期用户\n", len(recentUsers))
|
||||
}
|
||||
|
||||
// 完整的应用示例(修正版)
|
||||
func CompleteExample_Fixed() {
|
||||
fmt.Println("=== HoTimeDB完整应用示例(修正版) ===")
|
||||
|
||||
// 初始化数据库
|
||||
database := &db.HoTimeDB{
|
||||
Prefix: "app_",
|
||||
Mode: 1, // 测试模式
|
||||
Type: "mysql",
|
||||
}
|
||||
|
||||
// 设置连接
|
||||
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
|
||||
// 这里使用实际的数据库连接字符串
|
||||
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
master, dbErr := sql.Open("mysql", dsn)
|
||||
if dbErr != nil {
|
||||
log.Fatal("数据库连接失败:", dbErr)
|
||||
}
|
||||
return master, master
|
||||
})
|
||||
|
||||
// 用户管理系统示例
|
||||
fmt.Println("\n=== 用户管理系统 ===")
|
||||
|
||||
// 1. 创建用户
|
||||
userId := database.Insert("user", common.Map{
|
||||
"name": "示例用户",
|
||||
"email": "example@test.com",
|
||||
"password": "hashed_password",
|
||||
"age": 28,
|
||||
"status": 1,
|
||||
"level": "normal",
|
||||
"balance": 500.00,
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
fmt.Printf("创建用户成功,ID: %d\n", userId)
|
||||
|
||||
// 2. 用户登录更新(多条件用AND)
|
||||
database.Update("user", common.Map{
|
||||
"last_login[#]": "NOW()",
|
||||
"login_count[#]": "login_count + 1",
|
||||
}, common.Map{
|
||||
"AND": common.Map{
|
||||
"id": userId,
|
||||
"status": 1,
|
||||
},
|
||||
})
|
||||
|
||||
// 3. 创建订单
|
||||
orderId := database.Insert("order", common.Map{
|
||||
"user_id": userId,
|
||||
"amount": 299.99,
|
||||
"status": "pending",
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
fmt.Printf("创建订单成功,ID: %d\n", orderId)
|
||||
|
||||
// 4. 订单支付(使用事务,修正版)
|
||||
paymentSuccess := database.Action(func(tx db.HoTimeDB) bool {
|
||||
// 更新订单状态(单条件)
|
||||
affected1 := tx.Update("order", common.Map{
|
||||
"status": "paid",
|
||||
"paid_time[#]": "NOW()",
|
||||
}, common.Map{
|
||||
"id": orderId,
|
||||
})
|
||||
|
||||
if affected1 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 扣减用户余额(多条件用AND)
|
||||
affected2 := tx.Update("user", common.Map{
|
||||
"balance[#]": "balance - 299.99",
|
||||
}, common.Map{
|
||||
"AND": common.Map{
|
||||
"id": userId,
|
||||
"balance[>=]": 299.99, // 确保余额足够
|
||||
},
|
||||
})
|
||||
|
||||
if affected2 == 0 {
|
||||
fmt.Println("余额不足或用户不存在")
|
||||
return false
|
||||
}
|
||||
|
||||
// 记录支付日志
|
||||
logId := tx.Insert("payment_log", common.Map{
|
||||
"user_id": userId,
|
||||
"order_id": orderId,
|
||||
"amount": 299.99,
|
||||
"type": "order_payment",
|
||||
"status": "success",
|
||||
"created_time[#]": "NOW()",
|
||||
})
|
||||
|
||||
return logId > 0
|
||||
})
|
||||
|
||||
if paymentSuccess {
|
||||
fmt.Println("订单支付成功")
|
||||
} else {
|
||||
fmt.Println("订单支付失败")
|
||||
}
|
||||
|
||||
// 5. 查询用户订单列表(链式查询)
|
||||
userOrders := database.Table("order").
|
||||
LeftJoin("user", "order.user_id = user.id").
|
||||
Where("order.user_id", userId). // 链式中单个条件可以直接Where
|
||||
Order("order.created_time DESC").
|
||||
Select(`
|
||||
order.id,
|
||||
order.amount,
|
||||
order.status,
|
||||
order.created_time,
|
||||
order.paid_time,
|
||||
user.name as user_name
|
||||
`)
|
||||
|
||||
fmt.Printf("\n用户订单列表 (%d个订单):\n", len(userOrders))
|
||||
for _, order := range userOrders {
|
||||
fmt.Printf(" 订单ID:%v, 金额:%v, 状态:%s, 创建时间:%v\n",
|
||||
order.Get("id"),
|
||||
order.Get("amount"),
|
||||
order.GetString("status"),
|
||||
order.Get("created_time"))
|
||||
}
|
||||
|
||||
// 6. 生成统计报表(修正版)
|
||||
stats := database.Select("order",
|
||||
common.Slice{
|
||||
common.Map{"[>]user": "order.user_id = user.id"},
|
||||
},
|
||||
`
|
||||
DATE(order.created_time) as date,
|
||||
COUNT(order.id) as order_count,
|
||||
SUM(order.amount) as total_amount,
|
||||
AVG(order.amount) as avg_amount
|
||||
`,
|
||||
common.Map{
|
||||
"AND": common.Map{
|
||||
"order.status": "paid",
|
||||
"order.created_time[>]": "2023-01-01",
|
||||
},
|
||||
"GROUP": "DATE(order.created_time)",
|
||||
"ORDER": "date DESC",
|
||||
"LIMIT": 30,
|
||||
})
|
||||
|
||||
fmt.Printf("\n最近30天订单统计:\n")
|
||||
for _, stat := range stats {
|
||||
fmt.Printf("日期:%v, 订单数:%v, 总金额:%v, 平均金额:%v\n",
|
||||
stat.Get("date"),
|
||||
stat.Get("order_count"),
|
||||
stat.Get("total_amount"),
|
||||
stat.Get("avg_amount"))
|
||||
}
|
||||
|
||||
fmt.Println("\n示例执行完成!")
|
||||
}
|
||||
|
||||
// 语法对比示例
|
||||
func SyntaxComparison() {
|
||||
fmt.Println("=== HoTimeDB语法对比 ===")
|
||||
|
||||
// 模拟数据库对象
|
||||
var db *db.HoTimeDB
|
||||
|
||||
fmt.Println("❌ 错误语法示例(不支持):")
|
||||
fmt.Println(`
|
||||
// 这样写是错误的,多个条件不能直接放在根Map中
|
||||
wrongUsers := db.Select("user", "*", common.Map{
|
||||
"status": 1, // ❌ 错误
|
||||
"age[>]": 18, // ❌ 错误
|
||||
"ORDER": "id DESC",
|
||||
})
|
||||
`)
|
||||
|
||||
fmt.Println("✅ 正确语法示例:")
|
||||
fmt.Println(`
|
||||
// 单个条件可以直接写
|
||||
correctUsers1 := db.Select("user", "*", common.Map{
|
||||
"status": 1, // ✅ 正确,单个条件
|
||||
})
|
||||
|
||||
// 多个条件必须用AND包装
|
||||
correctUsers2 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{ // ✅ 正确,多个条件用AND包装
|
||||
"status": 1,
|
||||
"age[>]": 18,
|
||||
},
|
||||
"ORDER": "id DESC", // ✅ 正确,特殊参数与条件同级
|
||||
})
|
||||
|
||||
// OR条件
|
||||
correctUsers3 := db.Select("user", "*", common.Map{
|
||||
"OR": common.Map{ // ✅ 正确,OR条件
|
||||
"level": "vip",
|
||||
"balance[>]": 1000,
|
||||
},
|
||||
})
|
||||
|
||||
// 嵌套条件
|
||||
correctUsers4 := db.Select("user", "*", common.Map{
|
||||
"AND": common.Map{ // ✅ 正确,嵌套条件
|
||||
"status": 1,
|
||||
"OR": common.Map{
|
||||
"age[<]": 30,
|
||||
"level": "vip",
|
||||
},
|
||||
},
|
||||
"ORDER": "created_time DESC",
|
||||
"LIMIT": 20,
|
||||
})
|
||||
`)
|
||||
|
||||
// 实际不执行查询,只是展示语法
|
||||
_ = db
|
||||
}
|
||||
|
||||
// 运行所有修正后的示例
|
||||
func RunAllFixedExamples() {
|
||||
fmt.Println("开始运行HoTimeDB所有修正后的示例...")
|
||||
fmt.Println("注意:实际运行时需要确保数据库连接正确,并且相关表存在")
|
||||
|
||||
// 展示语法对比
|
||||
SyntaxComparison()
|
||||
|
||||
fmt.Println("请根据实际环境配置数据库连接后运行相应示例")
|
||||
fmt.Println("所有示例代码已修正完毕,语法正确!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 运行语法对比示例
|
||||
RunAllFixedExamples()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user