修改包依赖名称

This commit is contained in:
2025-11-30 13:15:13 +08:00
parent ea4e2e305d
commit 719238b1f0
33 changed files with 701 additions and 98 deletions

View File

@@ -30,8 +30,24 @@
## 安装 ## 安装
### 1. 配置私有仓库(重要)
由于本项目使用私有 Git 仓库,需要先配置 `GOPRIVATE` 环境变量:
```bash ```bash
go get github.com/go-common # 使用 go env 命令配置(推荐,永久生效)
go env -w GOPRIVATE=git.toowon.com
# 验证配置
go env GOPRIVATE
```
**详细配置说明请参考 [SETUP.md](./SETUP.md)**
### 2. 安装模块
```bash
go get git.toowon.com/jimmy/go-commom
``` ```
## 使用示例 ## 使用示例
@@ -50,7 +66,7 @@ go get github.com/go-common
#### 数据库迁移 #### 数据库迁移
```go ```go
import "github.com/go-common/migration" import "git.toowon.com/jimmy/go-commom/migration"
migrator := migration.NewMigrator(db) migrator := migration.NewMigrator(db)
migrator.AddMigration(migration.Migration{ migrator.AddMigration(migration.Migration{
@@ -65,7 +81,7 @@ migrator.Up()
#### 日期转换 #### 日期转换
```go ```go
import "github.com/go-common/datetime" import "git.toowon.com/jimmy/go-commom/datetime"
datetime.SetDefaultTimeZone(datetime.AsiaShanghai) datetime.SetDefaultTimeZone(datetime.AsiaShanghai)
now := datetime.Now() now := datetime.Now()
@@ -74,7 +90,7 @@ str := datetime.FormatDateTime(now)
#### HTTP响应 #### HTTP响应
```go ```go
import "github.com/go-common/http" import "git.toowon.com/jimmy/go-commom/http"
http.Success(w, data) http.Success(w, data)
http.SuccessPage(w, list, total, page, pageSize) http.SuccessPage(w, list, total, page, pageSize)
@@ -84,8 +100,8 @@ http.Error(w, 1001, "业务错误")
#### 中间件 #### 中间件
```go ```go
import ( import (
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
// CORS + 时区中间件 // CORS + 时区中间件
@@ -101,7 +117,7 @@ timezone := http.GetTimezone(r)
#### 配置管理 #### 配置管理
```go ```go
import "github.com/go-common/config" import "git.toowon.com/jimmy/go-commom/config"
// 从文件加载配置 // 从文件加载配置
cfg, err := config.LoadFromFile("./config.json") cfg, err := config.LoadFromFile("./config.json")
@@ -114,7 +130,7 @@ corsConfig := cfg.GetCORS()
#### 文件上传和查看 #### 文件上传和查看
```go ```go
import "github.com/go-common/storage" import "git.toowon.com/jimmy/go-commom/storage"
// 创建存储实例 // 创建存储实例
storage, _ := storage.NewStorage(storage.StorageTypeOSS, cfg) storage, _ := storage.NewStorage(storage.StorageTypeOSS, cfg)
@@ -132,7 +148,7 @@ proxyHandler := storage.NewProxyHandler(storage)
#### 邮件发送 #### 邮件发送
```go ```go
import "github.com/go-common/email" import "git.toowon.com/jimmy/go-commom/email"
// 从配置创建邮件发送器 // 从配置创建邮件发送器
mailer, _ := email.NewEmail(cfg.GetEmail()) mailer, _ := email.NewEmail(cfg.GetEmail())
@@ -147,7 +163,7 @@ mailer.SendSimple(
#### 短信发送 #### 短信发送
```go ```go
import "github.com/go-common/sms" import "git.toowon.com/jimmy/go-commom/sms"
// 从配置创建短信发送器 // 从配置创建短信发送器
smsClient, _ := sms.NewSMS(cfg.GetSMS()) smsClient, _ := sms.NewSMS(cfg.GetSMS())

174
SETUP.md Normal file
View File

@@ -0,0 +1,174 @@
# 项目配置说明
## GOPRIVATE 环境变量配置
由于本项目使用私有 Git 仓库 (`git.toowon.com`),需要配置 `GOPRIVATE` 环境变量,让 Go 工具链知道这些模块是私有的,不要通过公共代理下载。
### 配置方法
#### 方法一:临时配置(当前终端会话有效)
```bash
# macOS/Linux
export GOPRIVATE=git.toowon.com
# Windows (PowerShell)
$env:GOPRIVATE="git.toowon.com"
# Windows (CMD)
set GOPRIVATE=git.toowon.com
```
#### 方法二:永久配置(推荐)
**macOS/Linux:**
1. 编辑 shell 配置文件(根据你使用的 shell 选择):
```bash
# 如果是 zshmacOS 默认)
nano ~/.zshrc
# 如果是 bash
nano ~/.bashrc
# 或
nano ~/.bash_profile
```
2. 添加以下内容:
```bash
export GOPRIVATE=git.toowon.com
```
3. 保存文件并重新加载配置:
```bash
# zsh
source ~/.zshrc
# bash
source ~/.bashrc
```
**Windows:**
1. 打开"系统属性" -> "高级" -> "环境变量"
2. 在"用户变量"或"系统变量"中点击"新建"
3. 变量名:`GOPRIVATE`
4. 变量值:`git.toowon.com`
5. 点击"确定"保存
#### 方法三:使用 go env 命令推荐Go 1.13+
```bash
# 设置 GOPRIVATE
go env -w GOPRIVATE=git.toowon.com
# 查看当前配置
go env GOPRIVATE
# 如果需要设置多个私有仓库,用逗号分隔
go env -w GOPRIVATE=git.toowon.com,github.com/your-org
```
### 验证配置
```bash
# 查看 GOPRIVATE 配置
go env GOPRIVATE
# 应该输出: git.toowon.com
```
### 其他相关环境变量(可选)
如果需要更细粒度的控制,还可以配置:
```bash
# GOPRIVATE: 私有模块,不通过代理下载,不校验 checksum
go env -w GOPRIVATE=git.toowon.com
# GONOPROXY: 不通过代理下载的模块(默认与 GOPRIVATE 相同)
go env -w GONOPROXY=git.toowon.com
# GONOSUMDB: 不校验 checksum 的模块(默认与 GOPRIVATE 相同)
go env -w GONOSUMDB=git.toowon.com
```
### Git 认证配置
由于是私有仓库,还需要配置 Git 认证:
#### 方法一:使用 SSH推荐
1. 确保已配置 SSH 密钥
2. 使用 SSH URL
```bash
git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/"
```
#### 方法二:使用 HTTPS + 个人访问令牌
1. 在 Git 服务器上生成个人访问令牌
2. 配置 Git 凭据:
```bash
git config --global credential.helper store
```
3. 首次访问时会提示输入用户名和令牌
#### 方法三:在 URL 中包含凭据(不推荐,安全性较低)
```bash
# 在 go.mod 中使用(不推荐)
# 或者通过 .netrc 文件配置
```
### 常见问题
#### 问题1go get 失败,提示找不到模块
**解决方案:**
1. 确认 GOPRIVATE 已正确配置
2. 确认 Git 认证已配置
3. 尝试手动克隆仓库验证:
```bash
git clone git@git.toowon.com:jimmy/go-commom.git
```
#### 问题2go mod download 失败
**解决方案:**
```bash
# 清除模块缓存
go clean -modcache
# 重新下载
go mod download
```
#### 问题3IDE 无法识别模块
**解决方案:**
1. 重启 IDE
2. 在 IDE 中执行:`go mod tidy`
3. 确认 IDE 的 Go 环境变量配置正确
### 完整配置示例
```bash
# 1. 配置 GOPRIVATE
go env -w GOPRIVATE=git.toowon.com
# 2. 配置 Git SSH如果使用 SSH
git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/"
# 3. 验证配置
go env | grep GOPRIVATE
# 4. 测试模块下载
go get git.toowon.com/jimmy/go-commom@latest
```
### 参考文档
- [Go Modules 官方文档](https://go.dev/ref/mod)
- [Go 私有模块配置](https://go.dev/ref/mod#private-modules)

View File

@@ -6,7 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
// Config 应用配置 // Config 应用配置

View File

@@ -16,7 +16,7 @@
### 安装 ### 安装
```bash ```bash
go get github.com/go-common go get git.toowon.com/jimmy/go-commom
``` ```
### 使用示例 ### 使用示例
@@ -24,7 +24,7 @@ go get github.com/go-common
#### 数据库迁移 #### 数据库迁移
```go ```go
import "github.com/go-common/migration" import "git.toowon.com/jimmy/go-commom/migration"
migrator := migration.NewMigrator(db) migrator := migration.NewMigrator(db)
migrator.AddMigration(migration.Migration{ migrator.AddMigration(migration.Migration{
@@ -40,7 +40,7 @@ migrator.Up()
#### 日期转换 #### 日期转换
```go ```go
import "github.com/go-common/datetime" import "git.toowon.com/jimmy/go-commom/datetime"
datetime.SetDefaultTimeZone(datetime.AsiaShanghai) datetime.SetDefaultTimeZone(datetime.AsiaShanghai)
now := datetime.Now() now := datetime.Now()
@@ -50,7 +50,7 @@ str := datetime.FormatDateTime(now)
#### HTTP响应 #### HTTP响应
```go ```go
import "github.com/go-common/http" import "git.toowon.com/jimmy/go-commom/http"
http.Success(w, data) http.Success(w, data)
http.SuccessPage(w, list, total, page, pageSize) http.SuccessPage(w, list, total, page, pageSize)
@@ -61,8 +61,8 @@ http.Error(w, 1001, "业务错误")
```go ```go
import ( import (
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
// CORS + 时区中间件 // CORS + 时区中间件
@@ -79,7 +79,7 @@ timezone := http.GetTimezone(r)
#### 配置管理 #### 配置管理
```go ```go
import "github.com/go-common/config" import "git.toowon.com/jimmy/go-commom/config"
// 从文件加载配置 // 从文件加载配置
cfg, err := config.LoadFromFile("./config.json") cfg, err := config.LoadFromFile("./config.json")

View File

@@ -103,7 +103,7 @@
### 1. 加载配置文件 ### 1. 加载配置文件
```go ```go
import "github.com/go-common/config" import "git.toowon.com/jimmy/go-commom/config"
// 从文件加载配置(支持绝对路径和相对路径) // 从文件加载配置(支持绝对路径和相对路径)
config, err := config.LoadFromFile("/path/to/config.json") config, err := config.LoadFromFile("/path/to/config.json")
@@ -167,7 +167,7 @@ addr := config.GetRedisAddr()
corsConfig := config.GetCORS() corsConfig := config.GetCORS()
// 使用CORS中间件 // 使用CORS中间件
import "github.com/go-common/middleware" import "git.toowon.com/jimmy/go-commom/middleware"
chain := middleware.NewChain( chain := middleware.NewChain(
middleware.CORS(corsConfig), middleware.CORS(corsConfig),
) )
@@ -289,8 +289,8 @@ package main
import ( import (
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -19,7 +19,7 @@
### 1. 设置默认时区 ### 1. 设置默认时区
```go ```go
import "github.com/go-common/datetime" import "git.toowon.com/jimmy/go-commom/datetime"
// 设置默认时区为上海时区 // 设置默认时区为上海时区
err := datetime.SetDefaultTimeZone(datetime.AsiaShanghai) err := datetime.SetDefaultTimeZone(datetime.AsiaShanghai)
@@ -401,7 +401,7 @@ import (
"log" "log"
"time" "time"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func main() { func main() {
@@ -428,7 +428,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func main() { func main() {

View File

@@ -18,8 +18,8 @@
```go ```go
import ( import (
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/email" "git.toowon.com/jimmy/go-commom/email"
) )
// 从配置加载 // 从配置加载
@@ -107,7 +107,7 @@ if err != nil {
### 5. 使用Message结构发送便捷方法 ### 5. 使用Message结构发送便捷方法
```go ```go
import "github.com/go-common/email" import "git.toowon.com/jimmy/go-commom/email"
msg := &email.Message{ msg := &email.Message{
To: []string{"to@example.com"}, To: []string{"to@example.com"},
@@ -295,8 +295,8 @@ package main
import ( import (
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/email" "git.toowon.com/jimmy/go-commom/email"
) )
func main() { func main() {

View File

@@ -49,7 +49,7 @@ HTTP Restful工具提供了标准化的HTTP请求和响应处理功能包含
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
// 简单成功响应data为nil // 简单成功响应data为nil
@@ -198,7 +198,7 @@ package main
import ( import (
"net/http" "net/http"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
// 用户列表接口 // 用户列表接口

View File

@@ -29,7 +29,7 @@ CORS中间件用于处理跨域资源共享支持
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
func main() { func main() {
@@ -50,7 +50,7 @@ func main() {
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
func main() { func main() {
@@ -131,9 +131,9 @@ corsHandler := middleware.CORS(corsConfig)(handler)
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func handler(w http.ResponseWriter, r *http.Request) { func handler(w http.ResponseWriter, r *http.Request) {
@@ -162,8 +162,8 @@ func main() {
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func handler(w http.ResponseWriter, r *http.Request) { func handler(w http.ResponseWriter, r *http.Request) {
@@ -183,8 +183,8 @@ func main() {
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func GetUserList(w http.ResponseWriter, r *http.Request) { func GetUserList(w http.ResponseWriter, r *http.Request) {
@@ -240,7 +240,7 @@ X-Timezone: Asia/Shanghai
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
func handler(w http.ResponseWriter, r *http.Request) { func handler(w http.ResponseWriter, r *http.Request) {
@@ -282,9 +282,9 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func apiHandler(w http.ResponseWriter, r *http.Request) { func apiHandler(w http.ResponseWriter, r *http.Request) {
@@ -331,8 +331,8 @@ package main
import ( import (
"net/http" "net/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
func main() { func main() {

View File

@@ -21,7 +21,7 @@
import ( import (
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/go-common/migration" "git.toowon.com/jimmy/go-commom/migration"
) )
// 初始化数据库连接 // 初始化数据库连接
@@ -135,7 +135,46 @@ for _, s := range status {
} }
``` ```
### 6. 生成迁移版本号 ### 6. 重置迁移
#### 方式一:仅清空迁移记录(不回滚数据库变更)
```go
// 直接调用(需要传入确认标志)
err := migrator.Reset(true)
if err != nil {
log.Fatal(err)
}
// 交互式确认(推荐,会提示警告信息)
err := migrator.ResetWithConfirm()
if err != nil {
log.Fatal(err)
}
```
#### 方式二:回滚所有迁移并清空记录
```go
// 直接调用(需要传入确认标志)
err := migrator.ResetAll(true)
if err != nil {
log.Fatal(err)
}
// 交互式确认(推荐,会提示警告信息)
err := migrator.ResetAllWithConfirm()
if err != nil {
log.Fatal(err)
}
```
**注意:**
- `Reset()` 仅清空迁移记录表,不会回滚已执行的数据库变更
- `ResetAll()` 会回滚所有已应用的迁移执行Down函数然后清空记录
- 交互式方法会显示详细的警告信息,需要输入确认文本才能执行
### 7. 生成迁移版本号
```go ```go
// 生成基于时间戳的版本号 // 生成基于时间戳的版本号
@@ -194,6 +233,44 @@ type Migration struct {
**返回:** 迁移状态列表和错误信息 **返回:** 迁移状态列表和错误信息
#### Reset(confirm bool) error
重置所有迁移记录(仅清空记录表,不回滚数据库变更)。
**参数:**
- `confirm`: 确认标志必须为true才能执行
**返回:** 错误信息
**注意:** 此操作只清空迁移记录不会回滚已执行的迁移操作。如果需要回滚迁移请先使用Down方法逐个回滚。
#### ResetWithConfirm() error
交互式重置所有迁移记录(带确认提示)。
**返回:** 错误信息
**说明:** 会显示警告信息,需要输入"RESET"(全大写)才能执行。
#### ResetAll(confirm bool) error
重置所有迁移并回滚所有已应用的迁移。
**参数:**
- `confirm`: 确认标志必须为true才能执行
**返回:** 错误信息
**注意:** 此操作会回滚所有已应用的迁移执行Down函数然后清空迁移记录。操作不可逆请谨慎使用。
#### ResetAllWithConfirm() error
交互式重置所有迁移并回滚(带确认提示)。
**返回:** 错误信息
**说明:** 会显示警告信息,需要输入"RESET ALL"(全大写)才能执行。
### MigrationStatus 结构体 ### MigrationStatus 结构体
```go ```go
@@ -230,11 +307,16 @@ type MigrationStatus struct {
## 注意事项 ## 注意事项
1. 迁移版本号必须唯一,建议使用时间戳格式 1. **迁移版本号**必须唯一,建议使用时间戳格式
2. 迁移操作在事务中执行,失败会自动回滚 2. **事务支持**迁移操作在事务中执行,失败会自动回滚
3. 迁移记录表会自动创建,无需手动创建 3. **自动创建表**迁移记录表会自动创建,无需手动创建
4. 如果迁移文件没有对应的down文件回滚操作会失败 4. **回滚文件**如果迁移文件没有对应的down文件回滚操作会失败
5. 迁移按版本号升序执行,确保顺序正确 5. **执行顺序**迁移按版本号升序执行,确保顺序正确
6. **重置操作**
- `Reset()` 只清空迁移记录,不会回滚数据库变更
- `ResetAll()` 会回滚所有迁移,操作不可逆
- 建议使用交互式方法(`ResetWithConfirm``ResetAllWithConfirm`)以确保安全
- 在生产环境使用重置功能前,请确保已备份数据库
## 示例 ## 示例

View File

@@ -19,8 +19,8 @@
```go ```go
import ( import (
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/sms" "git.toowon.com/jimmy/go-commom/sms"
) )
// 从配置加载 // 从配置加载
@@ -102,7 +102,7 @@ if err != nil {
### 5. 使用SendRequest结构发送便捷方法 ### 5. 使用SendRequest结构发送便捷方法
```go ```go
import "github.com/go-common/sms" import "git.toowon.com/jimmy/go-commom/sms"
req := &sms.SendRequest{ req := &sms.SendRequest{
PhoneNumbers: []string{"13800138000", "13900139000"}, PhoneNumbers: []string{"13800138000", "13900139000"},
@@ -329,8 +329,8 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/sms" "git.toowon.com/jimmy/go-commom/sms"
) )
func main() { func main() {

View File

@@ -21,8 +21,8 @@
```go ```go
import ( import (
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
// 加载配置 // 加载配置
@@ -50,7 +50,7 @@ if err != nil {
import ( import (
"context" "context"
"os" "os"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
// 打开文件 // 打开文件
@@ -81,7 +81,7 @@ fmt.Printf("File URL: %s\n", url)
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
// 创建上传处理器 // 创建上传处理器
@@ -125,7 +125,7 @@ curl -X POST http://localhost:8080/upload \
```go ```go
import ( import (
"net/http" "net/http"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
// 创建代理查看处理器 // 创建代理查看处理器
@@ -144,7 +144,7 @@ GET /file?key=images/test.jpg
### 5. 生成对象键 ### 5. 生成对象键
```go ```go
import "github.com/go-common/storage" import "git.toowon.com/jimmy/go-commom/storage"
// 生成简单对象键 // 生成简单对象键
objectKey := storage.GenerateObjectKey("images/", "test.jpg") objectKey := storage.GenerateObjectKey("images/", "test.jpg")
@@ -288,9 +288,9 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
func main() { func main() {
@@ -344,8 +344,8 @@ import (
"log" "log"
"os" "os"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
func main() { func main() {

View File

@@ -8,7 +8,7 @@ import (
"net/smtp" "net/smtp"
"time" "time"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
) )
// Email 邮件发送器 // Email 邮件发送器

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
// "gorm.io/driver/mysql" // "gorm.io/driver/mysql"
// "gorm.io/gorm" // "gorm.io/gorm"
) )

View File

@@ -5,7 +5,7 @@ import (
"log" "log"
"time" "time"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func main() { func main() {

View File

@@ -5,7 +5,7 @@ import (
"log" "log"
"time" "time"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
func main() { func main() {

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/email" "git.toowon.com/jimmy/go-commom/email"
) )
func main() { func main() {

View File

@@ -4,7 +4,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
) )
// 用户结构 // 用户结构

View File

@@ -4,9 +4,9 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
"github.com/go-common/http" "git.toowon.com/jimmy/go-commom/http"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
// 示例使用CORS和时区中间件 // 示例使用CORS和时区中间件

View File

@@ -6,7 +6,7 @@ import (
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/go-common/migration" "git.toowon.com/jimmy/go-commom/migration"
) )
func main() { func main() {

View File

@@ -0,0 +1,105 @@
package main
import (
"fmt"
"log"
"git.toowon.com/jimmy/go-commom/migration"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func main() {
// 初始化数据库连接使用SQLite作为示例
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// 创建迁移器
migrator := migration.NewMigrator(db)
// 添加一些迁移
migrator.AddMigrations(
migration.Migration{
Version: "20240101000001",
Description: "create_users_table",
Up: func(db *gorm.DB) error {
return db.Exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
)
`).Error
},
Down: func(db *gorm.DB) error {
return db.Exec("DROP TABLE IF EXISTS users").Error
},
},
migration.Migration{
Version: "20240101000002",
Description: "add_created_at_to_users",
Up: func(db *gorm.DB) error {
return db.Exec("ALTER TABLE users ADD COLUMN created_at DATETIME").Error
},
Down: func(db *gorm.DB) error {
return db.Exec("ALTER TABLE users DROP COLUMN created_at").Error
},
},
)
// 执行迁移
fmt.Println("=== Executing migrations ===")
err = migrator.Up()
if err != nil {
log.Fatal("Failed to run migrations:", err)
}
// 查看状态
fmt.Println("\n=== Migration status ===")
status, err := migrator.Status()
if err != nil {
log.Fatal("Failed to get status:", err)
}
for _, s := range status {
fmt.Printf("Version: %s, Description: %s, Applied: %v\n",
s.Version, s.Description, s.Applied)
}
// 示例1仅清空迁移记录不回滚数据库变更
fmt.Println("\n=== Example 1: Reset migration records only ===")
fmt.Println("Note: This only clears records, not database changes")
// 直接调用(需要确认标志)
// err = migrator.Reset(true)
// if err != nil {
// log.Fatal("Failed to reset:", err)
// }
// 交互式确认(推荐)
// 取消注释下面的代码来测试交互式重置
// err = migrator.ResetWithConfirm()
// if err != nil {
// log.Fatal("Failed to reset with confirm:", err)
// }
// 示例2回滚所有迁移并清空记录
fmt.Println("\n=== Example 2: Reset all migrations (rollback + clear records) ===")
fmt.Println("Note: This will rollback all migrations and clear records")
// 直接调用(需要确认标志)
// err = migrator.ResetAll(true)
// if err != nil {
// log.Fatal("Failed to reset all:", err)
// }
// 交互式确认(推荐)
// 取消注释下面的代码来测试交互式重置
// err = migrator.ResetAllWithConfirm()
// if err != nil {
// log.Fatal("Failed to reset all with confirm:", err)
// }
fmt.Println("\nNote: Reset functions are commented out for safety.")
fmt.Println("Uncomment the code above to test reset functionality.")
}

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/sms" "git.toowon.com/jimmy/go-commom/sms"
) )
func main() { func main() {

View File

@@ -4,9 +4,9 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
"github.com/go-common/storage" "git.toowon.com/jimmy/go-commom/storage"
) )
func main() { func main() {

7
go.mod
View File

@@ -1,14 +1,17 @@
module github.com/go-common module git.toowon.com/jimmy/go-commom
go 1.21 go 1.21
require ( require (
gorm.io/driver/mysql v1.5.2 gorm.io/driver/mysql v1.5.2
gorm.io/gorm v1.25.5 gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.30.0
) )
require ( require (
github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
golang.org/x/text v0.20.0 // indirect
) )

10
go.sum
View File

@@ -5,8 +5,14 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=

View File

@@ -7,7 +7,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/go-common/middleware" "git.toowon.com/jimmy/go-commom/middleware"
) )
// ParseJSON 解析JSON请求体 // ParseJSON 解析JSON请求体

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"net/http" "net/http"
"github.com/go-common/datetime" "git.toowon.com/jimmy/go-commom/datetime"
) )
// TimezoneKey context中存储时区的key // TimezoneKey context中存储时区的key

View File

@@ -1,6 +1,7 @@
package migration package migration
import ( import (
"bufio"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -371,3 +372,219 @@ func LoadMigrationsFromFiles(dir string, pattern string) ([]Migration, error) {
func GenerateVersion() string { func GenerateVersion() string {
return strconv.FormatInt(time.Now().Unix(), 10) return strconv.FormatInt(time.Now().Unix(), 10)
} }
// Reset 重置所有迁移(清空迁移记录表)
// confirm: 确认标志必须为true才能执行重置
// 注意:此操作会清空所有迁移记录,但不会回滚已执行的迁移操作
// 如果需要回滚迁移请先使用Down方法逐个回滚
func (m *Migrator) Reset(confirm bool) error {
if !confirm {
return fmt.Errorf("reset operation requires explicit confirmation (confirm must be true)")
}
if err := m.initTable(); err != nil {
return err
}
// 获取已应用的迁移数量
applied, err := m.getAppliedMigrations()
if err != nil {
return err
}
count := len(applied)
if count == 0 {
fmt.Println("No migrations to reset")
return nil
}
// 清空迁移记录表
err = m.db.Exec(fmt.Sprintf("DELETE FROM %s", m.tableName)).Error
if err != nil {
return fmt.Errorf("failed to reset migrations: %w", err)
}
fmt.Printf("Reset completed: %d migration record(s) cleared\n", count)
fmt.Println("WARNING: This only clears migration records, not the actual database changes.")
fmt.Println("If you need to rollback database changes, use Down() method before reset.")
return nil
}
// ResetWithConfirm 交互式重置所有迁移(带确认提示)
// 会提示用户数据会被清空,需要输入确认才能执行
func (m *Migrator) ResetWithConfirm() error {
if err := m.initTable(); err != nil {
return err
}
// 获取已应用的迁移数量
applied, err := m.getAppliedMigrations()
if err != nil {
return err
}
count := len(applied)
if count == 0 {
fmt.Println("No migrations to reset")
return nil
}
// 显示警告信息
fmt.Println("=" + strings.Repeat("=", 60) + "=")
fmt.Println("WARNING: This operation will clear all migration records!")
fmt.Println("=" + strings.Repeat("=", 60) + "=")
fmt.Printf("This will clear %d migration record(s) from the database.\n", count)
fmt.Println()
fmt.Println("IMPORTANT NOTES:")
fmt.Println(" 1. This operation only clears migration records, NOT the actual database changes.")
fmt.Println(" 2. If you need to rollback database changes, use Down() method before reset.")
fmt.Println(" 3. After reset, you may need to re-run migrations if the database structure changed.")
fmt.Println()
fmt.Print("Type 'RESET' (all caps) to confirm, or anything else to cancel: ")
// 读取用户输入
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
return fmt.Errorf("failed to read confirmation: %w", err)
}
// 去除换行符和空格
input = strings.TrimSpace(input)
// 检查确认
if input != "RESET" {
fmt.Println("Reset operation cancelled.")
return nil
}
// 执行重置
return m.Reset(true)
}
// ResetAll 重置所有迁移并回滚所有已应用的迁移
// confirm: 确认标志必须为true才能执行
// 注意:此操作会回滚所有已应用的迁移,然后清空迁移记录
func (m *Migrator) ResetAll(confirm bool) error {
if !confirm {
return fmt.Errorf("reset all operation requires explicit confirmation (confirm must be true)")
}
if err := m.initTable(); err != nil {
return err
}
// 获取已应用的迁移
applied, err := m.getAppliedMigrations()
if err != nil {
return err
}
count := len(applied)
if count == 0 {
fmt.Println("No migrations to reset")
return nil
}
// 回滚所有已应用的迁移
fmt.Printf("Rolling back %d migration(s)...\n", count)
// 排序迁移(倒序)
sort.Slice(m.migrations, func(i, j int) bool {
return m.migrations[i].Version > m.migrations[j].Version
})
// 回滚所有已应用的迁移
for _, migration := range m.migrations {
if !applied[migration.Version] {
continue
}
if migration.Down == nil {
fmt.Printf("Warning: Migration %s has no Down function, skipping rollback\n", migration.Version)
continue
}
// 开始事务
tx := m.db.Begin()
if tx.Error != nil {
return fmt.Errorf("failed to begin transaction: %w", tx.Error)
}
// 执行回滚
if err := migration.Down(tx); err != nil {
tx.Rollback()
return fmt.Errorf("failed to rollback migration %s: %w", migration.Version, err)
}
// 删除迁移记录
if err := m.recordMigrationWithDB(tx, migration.Version, migration.Description, false); err != nil {
tx.Rollback()
return err
}
// 提交事务
if err := tx.Commit().Error; err != nil {
return fmt.Errorf("failed to commit rollback %s: %w", migration.Version, err)
}
fmt.Printf("Rolled back migration: %s - %s\n", migration.Version, migration.Description)
}
fmt.Printf("Reset all completed: %d migration(s) rolled back and records cleared\n", count)
return nil
}
// ResetAllWithConfirm 交互式重置所有迁移并回滚(带确认提示)
// 会提示用户数据会被清空,需要输入确认才能执行
func (m *Migrator) ResetAllWithConfirm() error {
if err := m.initTable(); err != nil {
return err
}
// 获取已应用的迁移数量
applied, err := m.getAppliedMigrations()
if err != nil {
return err
}
count := len(applied)
if count == 0 {
fmt.Println("No migrations to reset")
return nil
}
// 显示警告信息
fmt.Println("=" + strings.Repeat("=", 60) + "=")
fmt.Println("WARNING: This operation will rollback ALL migrations and clear records!")
fmt.Println("=" + strings.Repeat("=", 60) + "=")
fmt.Printf("This will rollback %d migration(s) and clear all migration records.\n", count)
fmt.Println()
fmt.Println("IMPORTANT NOTES:")
fmt.Println(" 1. This operation will EXECUTE Down() functions for all applied migrations.")
fmt.Println(" 2. All database changes made by migrations will be REVERTED.")
fmt.Println(" 3. This operation CANNOT be undone!")
fmt.Println(" 4. Make sure you have a database backup before proceeding.")
fmt.Println()
fmt.Print("Type 'RESET ALL' (all caps) to confirm, or anything else to cancel: ")
// 读取用户输入
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
return fmt.Errorf("failed to read confirmation: %w", err)
}
// 去除换行符和空格
input = strings.TrimSpace(input)
// 检查确认
if input != "RESET ALL" {
fmt.Println("Reset all operation cancelled.")
return nil
}
// 执行重置
return m.ResetAll(true)
}

View File

@@ -13,7 +13,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
) )
// SMS 短信发送器 // SMS 短信发送器

View File

@@ -9,7 +9,7 @@ import (
"strings" "strings"
"time" "time"
commonhttp "github.com/go-common/http" commonhttp "git.toowon.com/jimmy/go-commom/http"
) )
// UploadHandler 文件上传处理器 // UploadHandler 文件上传处理器

View File

@@ -6,7 +6,7 @@ import (
"io" "io"
"strings" "strings"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
) )
// MinIOStorage MinIO存储实现 // MinIOStorage MinIO存储实现

View File

@@ -6,7 +6,7 @@ import (
"io" "io"
"strings" "strings"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
) )
// OSSStorage OSS存储实现 // OSSStorage OSS存储实现

View File

@@ -6,7 +6,7 @@ import (
"io" "io"
"time" "time"
"github.com/go-common/config" "git.toowon.com/jimmy/go-commom/config"
) )
// Storage 存储接口 // Storage 存储接口