u
This commit is contained in:
1 parent
200f72eb86
commit
91ae4cb474
19 files changed
+650
-148
No files matched your search
Generated
File renamed without changes.
File renamed without changes.
Generated
+1
-1
@@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/demo-project.iml" filepath="$PROJECT_DIR$/.idea/demo-project.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/base-go-v2.iml" filepath="$PROJECT_DIR$/.idea/base-go-v2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
File renamed without changes.
@@ -0,0 +1 @@
|
||||
package app
|
||||
@@ -0,0 +1,17 @@
|
||||
server:
|
||||
port: 8085
|
||||
|
||||
snowflake:
|
||||
datacenterID: 0
|
||||
workerID: 0
|
||||
|
||||
db:
|
||||
host: 117.72.182.135
|
||||
port: 5432
|
||||
user: postgres
|
||||
password: zhang520..
|
||||
dbname: base
|
||||
|
||||
jwt:
|
||||
secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s
|
||||
expiry: 168h
|
||||
@@ -0,0 +1,27 @@
|
||||
module base-go-v2
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -0,0 +1,59 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/types"
|
||||
"github.com/spf13/viper"
|
||||
"time"
|
||||
)
|
||||
|
||||
// App 全局唯一导出配置实例,初始化默认值
|
||||
var App = &types.AppConfig{
|
||||
Server: types.AppConfig{}.Server,
|
||||
JWT: types.AppConfig{}.JWT,
|
||||
Db: types.AppConfig{}.Db,
|
||||
Snowflake: types.AppConfig{}.Snowflake,
|
||||
}
|
||||
|
||||
// 给部分字段设置默认值
|
||||
func init() {
|
||||
App.Server.Port = 8080
|
||||
App.JWT.Secret = "default_secret"
|
||||
App.JWT.Expiry = time.Hour
|
||||
|
||||
// 数据库只给连接池默认值
|
||||
App.Db.MaxOpenConns = 10
|
||||
App.Db.MaxIdleConns = 5
|
||||
App.Db.ConnMaxLifetime = 30 * time.Minute
|
||||
|
||||
App.Snowflake.DatacenterID = 1
|
||||
App.Snowflake.WorkerID = 1
|
||||
}
|
||||
|
||||
// InitApplicationConfig 初始化配置
|
||||
func InitApplicationConfig(configPath string) error {
|
||||
v := viper.New()
|
||||
v.SetConfigFile(configPath)
|
||||
v.SetConfigType("yaml")
|
||||
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.UnmarshalKey("server", &App.Server); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.UnmarshalKey("jwt", &App.JWT); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.UnmarshalKey("snowflake", &App.Snowflake); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.UnmarshalKey("db", &App.Db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/config"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
var DB *sql.DB
|
||||
|
||||
var defaultPoolConfig = struct {
|
||||
MaxOpenConns int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
}{
|
||||
MaxOpenConns: 10,
|
||||
MaxIdleConns: 5,
|
||||
ConnMaxLifetime: 30 * time.Minute,
|
||||
}
|
||||
|
||||
func InitDb() error {
|
||||
d := config.Db
|
||||
|
||||
// 构造 DSN
|
||||
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
|
||||
d.Host, d.Port, d.User, d.Password, d.Dbname)
|
||||
|
||||
db, err := sql.Open("postgres", dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("打开数据库失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置连接池(使用默认值,如果用户没传)
|
||||
maxOpenConns := d.MaxOpenConns
|
||||
if maxOpenConns == 0 {
|
||||
maxOpenConns = defaultPoolConfig.MaxOpenConns
|
||||
}
|
||||
db.SetMaxOpenConns(maxOpenConns)
|
||||
|
||||
maxIdleConns := d.MaxIdleConns
|
||||
if maxIdleConns == 0 {
|
||||
maxIdleConns = defaultPoolConfig.MaxIdleConns
|
||||
}
|
||||
db.SetMaxIdleConns(maxIdleConns)
|
||||
|
||||
connMaxLifetime := d.ConnMaxLifetime
|
||||
if connMaxLifetime == 0 {
|
||||
connMaxLifetime = defaultPoolConfig.ConnMaxLifetime
|
||||
}
|
||||
db.SetConnMaxLifetime(connMaxLifetime)
|
||||
|
||||
// 测试连接
|
||||
if err := db.Ping(); err != nil {
|
||||
return fmt.Errorf("数据库连接测试失败: %w", err)
|
||||
}
|
||||
|
||||
DB = db
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// AppConfig 统一配置结构体
|
||||
type AppConfig struct {
|
||||
Server struct {
|
||||
Port int
|
||||
}
|
||||
JWT struct {
|
||||
Secret string
|
||||
Expiry time.Duration
|
||||
}
|
||||
Db struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
Dbname string
|
||||
MaxOpenConns int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
}
|
||||
Snowflake struct {
|
||||
DatacenterID uint64
|
||||
WorkerID uint64
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/config"
|
||||
"errors"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTokenExpired = errors.New("token expired")
|
||||
ErrTokenInvalid = errors.New("token invalid")
|
||||
)
|
||||
|
||||
// CustomClaims 定义自己的 payload 结构
|
||||
type CustomClaims struct {
|
||||
UserID string `json:"user_id"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// CreateToken 创建一个 JWT token
|
||||
// 支持 config.App.JWT.Expiry == -1 永不过期
|
||||
func CreateToken(orgID, userID string) (string, error) {
|
||||
claims := CustomClaims{
|
||||
UserID: userID,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
expiry := config.App.JWT.Expiry
|
||||
switch {
|
||||
case expiry > 0:
|
||||
expireAt := time.Now().Add(expiry)
|
||||
claims.ExpiresAt = jwt.NewNumericDate(expireAt)
|
||||
case expiry == -1:
|
||||
// 永不过期,ExpiresAt 设置为 nil
|
||||
claims.ExpiresAt = nil
|
||||
default:
|
||||
// 默认过期时间,例如 1 小时
|
||||
expireAt := time.Now().Add(time.Hour)
|
||||
claims.ExpiresAt = jwt.NewNumericDate(expireAt)
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString([]byte(config.App.JWT.Secret))
|
||||
}
|
||||
|
||||
// VerifyToken 验证并解析 JWT token
|
||||
func VerifyToken(tokenString string) (*CustomClaims, error) {
|
||||
signKey := []byte(config.App.JWT.Secret)
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
// 校验签名算法
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, ErrTokenInvalid
|
||||
}
|
||||
return signKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ErrTokenInvalid
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*CustomClaims)
|
||||
if !ok || !token.Valid {
|
||||
return nil, ErrTokenInvalid
|
||||
}
|
||||
|
||||
// 检查过期:ExpiresAt 为 nil 时永不过期
|
||||
if claims.ExpiresAt != nil && time.Now().After(claims.ExpiresAt.Time) {
|
||||
return nil, ErrTokenExpired
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="4636f4cb-d351-4483-ab41-f250e36fd785" name="更改" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/../../base-project/base-vue/test.html" beforeDir="false" afterPath="$PROJECT_DIR$/../../base-project/base-vue/test.html" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Go File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="GOROOT" url="file://$PROJECT_DIR$/../../../../devtool/golang/go1.24.6" />
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../../.." />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 2
|
||||
}</component>
|
||||
<component name="ProjectId" id="310iJEfnQcn2Pqx78c3M6FhCNFP" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"DefaultGoTemplateProperty": "Go File",
|
||||
"Go Build.go build base-framework/cmd/app.executor": "Run",
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.GoLinterPluginOnboarding": "true",
|
||||
"RunOnceActivity.GoLinterPluginStorageMigration": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"go.import.settings.migrated": "true",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"last_opened_file_path": "C:/Users/Administrator",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.goide.configuration.GoLibrariesConfigurableProvider"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="D:\workspace\code\go-project\base-farmework\pkg\configs" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="D:\workspace\code\go-project\base-farmework\pkg" />
|
||||
<recent name="D:\workspace\code\go-project\base-farmework\pkg\config" />
|
||||
<recent name="D:\workspace\code\go-project\base-farmework\configs" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="go build base-framework/cmd/app" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
|
||||
<module name="base-farmework" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<kind value="PACKAGE" />
|
||||
<package value="base-framework/cmd/app" />
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<filePath value="$PROJECT_DIR$/cmd/app/main.go" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Go Build.go build base-framework/cmd/app" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-gosdk-3b128438d3f6-07d2d2d66b1e-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-251.27812.54" />
|
||||
<option value="bundled-js-predefined-d6986cc7102b-09060db00ec0-JavaScript-GO-251.27812.54" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="默认任务">
|
||||
<changelist id="4636f4cb-d351-4483-ab41-f250e36fd785" name="更改" comment="" />
|
||||
<created>1754667317086</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1754667317086</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<environment>
|
||||
<map>
|
||||
<entry key="GOPROXY" value="https://goproxy.cn,direct" />
|
||||
</map>
|
||||
</environment>
|
||||
<settings-migrated>true</settings-migrated>
|
||||
</component>
|
||||
</project>
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1 +0,0 @@
|
||||
module demo-project
|
||||
@@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
|
||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
|
||||
|
||||
func main() {
|
||||
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
|
||||
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
|
||||
s := "gopher"
|
||||
fmt.Printf("Hello and welcome, %s!\n", s)
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
|
||||
fmt.Println("i =", 100/i)
|
||||
}
|
||||
}
|
||||
@@ -105,3 +105,23 @@ go env -w GOPATH="D:\devtool\golang\gopath"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 错误
|
||||
|
||||
## GoLand运行报错:WARNING: undefined behavior - version of Delve is too old for Go version go1.25.0 (maximum supported version 1.24)
|
||||
|
||||
安装:
|
||||
|
||||
```bash
|
||||
go install github.com/go-delve/delve/cmd/dlv@master
|
||||
```
|
||||
|
||||
修改 goland 配置:`Help\Edit Custom Properties`,新增配置:
|
||||
|
||||
```
|
||||
dlv.path=D:\\devtool\\golang\\gopath\\bin\\dlv.exe
|
||||
```
|
||||
|
||||
这个路径是 `gopath` 配置后的路径,默认在c盘 `dlv.path=C:\\Users\\_user_\\go\\bin\\dlv.exe`
|
||||
|
||||
+285
-4
@@ -12,15 +12,296 @@ myapp/
|
||||
│ ├── handler/ # HTTP handler(路由处理函数)
|
||||
│ ├── repository/ # 数据访问层(如数据库、缓存)
|
||||
│ ├── service/ # 业务逻辑层
|
||||
│ └── model/ # 实体结构体、DTO、表结构等
|
||||
│
|
||||
├── pkg/ # 可被外部引入的公共包
|
||||
│ ├── model/ # 实体结构体、DTO、表结构等
|
||||
│ ├── logger/ # 日志封装
|
||||
│ ├── utils/ # 工具函数
|
||||
│ └── middleware/ # HTTP中间件
|
||||
│
|
||||
|
||||
├── go.mod # Go module 定义
|
||||
├── go.sum # Go module 校验文件
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
|
||||
|
||||
假设有 **用户服务(8080)** 和 **订单服务(8081)**:
|
||||
|
||||
```
|
||||
project/
|
||||
├── cmd/
|
||||
│ ├── user-service/ # main.go,启动用户服务,监听 8080
|
||||
│ └── order-service/ # main.go,启动订单服务,监听 8081
|
||||
│
|
||||
├── internal/ # 内部模块,不对外暴露
|
||||
│ ├── user/ # 用户服务模块
|
||||
│ │ ├── handler.go # HTTP 接口
|
||||
│ │ └── service.go # 业务逻辑
|
||||
│ ├── order/ # 订单服务模块
|
||||
│ │ ├── handler.go
|
||||
│ │ └── service.go
|
||||
│ └── database/ # 数据库连接封装
|
||||
│
|
||||
├── pkg/ # 可复用公共库(跨服务)
|
||||
│ ├── logger/ # 日志封装
|
||||
│ ├── utils/ # 工具函数
|
||||
│ └── types/ # 公共类型定义
|
||||
│
|
||||
└── configs/ # 各服务配置
|
||||
├── user.yaml
|
||||
└── order.yaml
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 项目配置
|
||||
|
||||
## 集成 [fasthttp](https://github.com/valyala/fasthttp)
|
||||
|
||||
```bash
|
||||
go get -u github.com/valyala/fasthttp
|
||||
```
|
||||
|
||||
测试
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
requestHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
fmt.Fprintf(ctx, "Hello, fasthttp!")
|
||||
}
|
||||
|
||||
fmt.Println("Server is running on :8099")
|
||||
if err := fasthttp.ListenAndServe(":8099", requestHandler); err != nil {
|
||||
fmt.Printf("Error in ListenAndServe: %s\n", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出以下内容,即代表成功:
|
||||
|
||||
```
|
||||
Server is running on :8099
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 集成 [postgres](https://github.com/lib/pq)
|
||||
|
||||
```bash
|
||||
go get github.com/lib/pq
|
||||
```
|
||||
|
||||
测试
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq" // pq 驱动
|
||||
)
|
||||
|
||||
func main() {
|
||||
// PostgreSQL 连接信息
|
||||
dsn := "host=localhost port=5432 user=postgres password=123456 dbname=testdb sslmode=disable"
|
||||
|
||||
// 连接数据库
|
||||
db, err := sql.Open("postgres", dsn)
|
||||
if err != nil {
|
||||
log.Fatal("连接数据库失败:", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// 测试连接
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
log.Fatal("数据库无法访问:", err)
|
||||
}
|
||||
fmt.Println("成功连接 PostgreSQL!")
|
||||
|
||||
// 测试查询
|
||||
var now string
|
||||
err = db.QueryRow("SELECT NOW()").Scan(&now)
|
||||
if err != nil {
|
||||
log.Fatal("查询失败:", err)
|
||||
}
|
||||
fmt.Println("数据库当前时间:", now)
|
||||
}
|
||||
```
|
||||
|
||||
输出以下内容,即代表成功:
|
||||
|
||||
```
|
||||
成功连接 PostgreSQL!
|
||||
数据库当前时间: 2025-10-31T03:08:00.125282Z
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 集成 [viper](https://github.com/spf13/viper)
|
||||
|
||||
```bash
|
||||
go get github.com/spf13/viper
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
新建 `application.yaml` :
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8082
|
||||
```
|
||||
|
||||
`main.go`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 设置配置文件路径
|
||||
viper.SetConfigFile("application.yaml")
|
||||
|
||||
// 读取配置文件
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
log.Fatalf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
port := viper.GetInt("server.port")
|
||||
fmt.Println("Server port:", port)
|
||||
|
||||
// 热加载示例
|
||||
viper.WatchConfig()
|
||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
fmt.Println("配置文件修改:", e.Name)
|
||||
newPort := viper.GetInt("server.port")
|
||||
fmt.Println("新的 server.port:", newPort)
|
||||
})
|
||||
|
||||
// 阻塞等待,可以模拟服务运行
|
||||
select {}
|
||||
}
|
||||
```
|
||||
|
||||
输出以下内容,即代表成功:
|
||||
|
||||
```
|
||||
Server port:8082
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 集成 [jwt](https://github.com/golang-jwt/jwt)
|
||||
|
||||
```bash
|
||||
go get -u github.com/golang-jwt/jwt/v5
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// 用于签名的密钥(生产环境不要硬编码)
|
||||
var jwtKey = []byte("my_secret_key")
|
||||
|
||||
func main() {
|
||||
// 用户 ID
|
||||
userID := 123
|
||||
|
||||
// 生成 token
|
||||
tokenString, err := GenerateJWT(userID)
|
||||
if err != nil {
|
||||
log.Fatal("生成 JWT 失败:", err)
|
||||
}
|
||||
fmt.Println("生成的 JWT:", tokenString)
|
||||
|
||||
// 验证 token
|
||||
claims, err := ValidateJWT(tokenString)
|
||||
if err != nil {
|
||||
log.Fatal("验证 JWT 失败:", err)
|
||||
}
|
||||
fmt.Printf("解析后的用户 ID: %v, 过期时间: %v\n", claims.UserID, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
// 自定义 claims
|
||||
type MyClaims struct {
|
||||
UserID int `json:"user_id"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// 生成 JWT
|
||||
func GenerateJWT(userID int) (string, error) {
|
||||
claims := MyClaims{
|
||||
UserID: userID,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), // 1 小时过期
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "myapp",
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(jwtKey)
|
||||
}
|
||||
|
||||
// 验证 JWT
|
||||
func ValidateJWT(tokenString string) (*MyClaims, error) {
|
||||
claims := &MyClaims{}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return jwtKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return nil, fmt.Errorf("token 无效")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
```
|
||||
|
||||
输出以下内容,即代表成功:
|
||||
|
||||
```
|
||||
生成的 JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImlzcyI6Im15YXBwIiwiZXhwIjoxNzYxODg1MTQ2LCJpYXQiOjE3NjE4ODE1NDZ9.f7gZg6YmuYRXzaOLhJu4Vg8tXYgueHIixeoh10caSWg
|
||||
解析后的用户 ID: 123, 过期时间: 2025-10-31 12:32:26 +0800 CST
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 集成 [zap](https://pkg.go.dev/go.uber.org/zap)
|
||||
|
||||
```bash
|
||||
go get -u go.uber.org/zap
|
||||
```
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# 安装
|
||||
|
||||
## Docker安装
|
||||
|
||||
### 1. 拉取镜像
|
||||
|
||||
```
|
||||
docker pull postgres
|
||||
```
|
||||
|
||||
### 2. 创建数据卷
|
||||
|
||||
创建一个数据库以持久化PostgreSQL的数据
|
||||
|
||||
```bash
|
||||
docker volume create postgre-data
|
||||
```
|
||||
|
||||
### 3. 创建并运行容器
|
||||
|
||||
```
|
||||
docker run -d \
|
||||
--name postgres \
|
||||
-e POSTGRES_PASSWORD=123456 \
|
||||
-v ./data/postgres:/var/lib/postgresql/data/pgdata \
|
||||
-p 5432:5432 \
|
||||
postgres
|
||||
|
||||
```
|
||||
|
||||
上述设定了数据卷,以及暴露了端口`5432`,这是PostgreSQL的默认端口。
|
||||
|
||||
Reference in new issue
Block a user