分別初始化工程 go mod

如果沒有.go文件,執(zhí)行go mod tidy可能報錯(warning: “all” matched no packages), 可以先不弄,只初始化模塊就行(go mod init 模塊名)

分別初始化工程 go mod

如果沒有.go文件,執(zhí)行go mod tidy可能報錯(warning: “all” matched no packages), 可以先不弄,只初始化模塊就行(go mod init 模塊名)

# 項目根目錄創(chuàng)建模塊
go mod init go_manager
go mod tidy
# 進入db目錄
cd db
# 初始化模塊
go mod init go_manager_db
go mod tidy
# 進入utils目錄
cd ../utils
# 初始化模塊
go mod init go_manager_utils
go mod tidy
# 進入web目錄
cd ../web
# 初始化模塊
go mod init go_manager_web
go mod tidy

go_manager_db模塊編寫

創(chuàng)建數(shù)據(jù)庫連接(sqlite如果沒有庫會自動建)

// db\main.go
package go_manager_db

import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)

// 數(shù)據(jù)庫相關操作
var db *sqlx.DB

// 初始化數(shù)據(jù)庫連接
func InitDB() (err error) {
dsn := "./manager.db"
// 連接
// Open可能僅校驗參數(shù),而沒有與db間創(chuàng)建連接,
// 要確認db是否可用,需要調用Ping。Connect則相當于Open+Ping。
db, err = sqlx.Connect("sqlite3", dsn)
if err != nil {
fmt.Printf("connect DB failed, err:%v\n", err)
return
}
// 最大連接數(shù)
db.SetMaxOpenConns(100)
// 最大空閑連接數(shù)
db.SetMaxIdleConns(16)
// 初始化方法,建表+插入原始數(shù)據(jù)
CreateRoleTable()
CreateUserTable()
return
}

添加建表方法(初始化權限表和用戶表)

// db\main.go
package go_manager_db

import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)

// 數(shù)據(jù)庫相關操作
var db *sqlx.DB

// 初始化數(shù)據(jù)庫連接
func InitDB() (err error) {......}
// 創(chuàng)建用戶表
func CreateUserTable() error {
sqlc := `
CREATE TABLE IF NOT EXISTS "mal_user" (
-- sqlite 不能用 comment 添加注釋
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , -- '主鍵'
"uname" varchar(20) NOT NULL UNIQUE , -- '用戶昵稱'
"upass" varchar(50) NOT NULL, -- '密碼(md5加密)'
"rid" INTEGER NOT NULL UNIQUE DEFAULT 1 -- '角色id'
);
`
_, err := db.Exec(sqlc)
if err != nil {
fmt.Println(err)
return err
}
// 初始化表
//因為有unique約束,所以不會重復添加
// sqlStr := "insert into mal_user(uname,upass,rid) values(?,?,?)"
Insert("mal_user", []string{"uname", "upass", "rid"}, "admin", "e120012d113ff6ea124a2493453c6dd5", 2)
return nil
}

// 創(chuàng)建權限表
func CreateRoleTable() error {
sqlc := `
CREATE TABLE IF NOT EXISTS "mal_role" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- '主鍵'
"role" varchar(20) NOT NULL UNIQUE DEFAULT 'user' -- '角色(權限)'
);
`
_, err := db.Exec(sqlc)
if err != nil {
return err
}
// 初始化表
// 因為有unique約束,所以不會重復添加
// 有四種權限,id(自增)越大代表權限越大,root>super>admin>user
Insert("mal_role", []string{"role"}, "user")
Insert("mal_role", []string{"role"}, "admin")
Insert("mal_role", []string{"role"}, "super")
Insert("mal_role", []string{"role"}, "root")
return nil
}

base.go: 通用插入和刪除方法

// db\base.go
package go_manager_db

import (
"fmt"
_ "github.com/mattn/go-sqlite3"
utils "go_manager_utils"
)

// 插入數(shù)據(jù)
func Insert(tableName string, params []string, datas ...interface{}) (err error) {
// 拼接 表名(參數(shù)1,參數(shù)2,...)
paramStr := utils.ParamsStr(params)
// 拼接 values(?,?,...)
values := utils.ValueStr(len(params))
var sqlStr = "insert into " + tableName + paramStr + " values" + values
fmt.Println(sqlStr)
_, err = db.Exec(sqlStr, datas...) // 要用...展開
if err != nil {
fmt.Println(err)
fmt.Println("插入數(shù)據(jù)失敗")
return
}
return
}

// 刪除數(shù)據(jù)
func Delete(tableName string, id int64) (err error) {
sqlStr := "delete from " + tableName + " where id=?"
fmt.Println(sqlStr)
_, err = db.Exec(sqlStr, id)
if err != nil {
fmt.Println("刪除數(shù)據(jù)失敗")
return
}
return
}

model.go: 定義數(shù)據(jù)表對應的結構體

package go_manager_db

// 專門定義與數(shù)據(jù)庫交互的結構體

// 用戶表
type MalUser struct {
Id int64 db:"id" json:"Rd"
Uname string db:"uname" json:"Uname"
Upass string db:"upass" json:"Upass"
Rid int64 db:"rid" json:"Rid"
}
// 角色表
type MalRole struct {
Id int64 db:"id" json:"Id"
Role string db:"role" json:"Role"
}

mal_user.go和mal_role.go: 定義用戶表和角色表的crud方法

mal_user.go

package go_manager_db

import (
"fmt"
utils "go_manager_utils"

_ "github.com/mattn/go-sqlite3"
)

// 查數(shù)據(jù)
func GetAllUser() (users []*MalUser, err error) {
sqlStr := select * from mal_user // 查詢,記錄到booklist err = db.Select(&users, sqlStr) if err != nil { fmt.Println("查詢信息失敗") fmt.Println(err) return } return } // 根據(jù)id查數(shù)據(jù) func GetUserById(id int64) (user MalUser, err error) { // 如果返回的是指針,需要初始化 //book=&Book{} sqlStr := "select * from mal_user where id=?" err = db.Get(&user, sqlStr, id) if err != nil { fmt.Println("查詢信息失敗") return } return } // 根據(jù)name查數(shù)據(jù) func GetUserByName(uname string, upass string) (user MalUser, err error) { sqlStr := "select * from mal_user where uname=? and upass=?" err = db.Get(&user, sqlStr, uname, upass) if err != nil { fmt.Println("查詢信息失敗") return } return } // 根據(jù)id改 func UptUserById(uid string, params []string, datas ...interface{}) (err error) { // 拼接參數(shù)列表 xxx=?,xxx=? paramsStr := utils.UptParamsStr(params) // uid直接傳字符串拼接 sqlStr := "update mal_role set " + paramsStr + " where id=" + uid _, err = db.Exec(sqlStr, datas...) if err != nil { fmt.Println("修改信息失敗") return } return }

mal_role.go

package go_manager_db

import (
"fmt"
_ "github.com/mattn/go-sqlite3"
)

// 應該id越大,權限越高,比較方便區(qū)分權限
// user < admin < super < root
// 查數(shù)據(jù)
func GetAllRole() (roles []*MalRole, err error) {
sqlStr := select * from mal_role // 查詢,記錄到booklist err = db.Select(&roles, sqlStr) if err != nil { fmt.Println("查詢信息失敗") fmt.Println(err) return } return } // 根據(jù)id查數(shù)據(jù) func GetRoleById(id int64) (role MalRole, err error) { // 如果返回的是指針,需要初始化 //book=&Book{} sqlStr := "select * from mal_role where id=?" err = db.Get(&role, sqlStr, id) if err != nil { fmt.Println("查詢信息失敗") return } return } // 根據(jù)id改數(shù)據(jù) func UptRoleById(id int64, roleName string) (err error) { // 如果返回的是指針,需要初始化 //book=&Book{} sqlStr := "update mal_role set role=? where id=?" _, err = db.Exec(sqlStr, roleName, id) if err != nil { fmt.Println("修改信息失敗") return } return }

引入項目里的其他模塊: utils

在go.mod末尾添加

replace go_manager_utils => ../utils

go_manager_utils模塊編寫

jwt.go: 編寫加密方法,定時銷毀token方法

package go_manager_util

import (
"crypto/md5"
"fmt"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
"time"
)

// sign 簽名
// 傳入密碼,加密
func SignJWT(secret string, uname string, upass string) (jwtStr string) {
key := []byte(secret)
fmt.Println(secret)
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key},
(&jose.SignerOptions{}).WithType("JWT"))
if err != nil {
panic(err)
}

cl := jwt.Claims{
// Registered claims : 這里有一組預定義的聲明,它們不是強制的,但是推薦
// 比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
Issuer: uname,
Subject: upass,
NotBefore: jwt.NewNumericDate(time.Now()),
Audience: jwt.Audience{"name", "admin"},
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
if err != nil {
panic(err)
}

// fmt.Println(raw)
return raw
}

// 解析jwt
// 傳入key(之前加密的密碼),raw(jwt令牌)
func ParseJWT(key string, raw string) {
var sharedKey = []byte(key)
tok, err := jwt.ParseSigned(raw)
if err != nil {
panic(err)
}
out := jwt.Claims{}
// 解析出issuer(uname)和subject(upass),校驗
if err := tok.Claims(sharedKey, &out); err != nil {
panic(err)
}
fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}

// DM5加密
func MD5(str string) string {
data := []byte(str) //切片
has := md5.Sum(data)
md5str := fmt.Sprintf("%x", has) //將[]byte轉成16進制
return md5str
}

// 銷毀TokenMap的方法
// 定時銷毀token(默認2小時)
func DestoryTokenMap(tokenMap map[string]string) {
for k := range tokenMap {
delete(tokenMap, k)
}
}

myTime.go: 定義定時器方法

package go_manager_util

import (
"time"
)

// 定義函數(shù)類型
type Fn func() error

// 定時器中的成員
type MyTicker struct {
MyTick *time.Ticker
Runner Fn
}
type MyTimer struct {
MyTime *time.Timer
Runner Fn
}

func NewMyTick(interval int, f Fn) *MyTicker {
return &MyTicker{
MyTick: time.NewTicker(time.Duration(interval) * time.Second),
Runner: f,
}
}

// 一次性
func NewMyTimer(interval int, f Fn) *MyTimer {
return &MyTimer{
MyTime: time.NewTimer(time.Duration(interval) * time.Second),
Runner: f,
}
}

// 啟動定時器需要執(zhí)行的任務
func (t *MyTicker) Start() {
for {
select {
case <-t.MyTick.C:
t.Runner()
}
}
}

// 啟動定時器需要執(zhí)行的任務
func (t *MyTimer) Start() {
select {
case <-t.MyTime.C:
t.Runner()
}
}

// func over() error {
// fmt.Println("token過期")
// return nil
// }
// 測試
// func main() {
// t := NewMyTimer(2, over)
// t.Start()
// }

res.go: 響應前端請求的方法

package go_manager_util

import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)

/* 通用響應方法 */
func R(c *gin.Context, err error, msg interface{}, data interface{}) {
// 如果有err,就說明是有錯誤,就返回錯誤響應(msg)
if err != nil {
fmt.Println(err)
c.JSON(http.StatusInternalServerError, gin.H{
"status": 500,
"msg": msg,
})
return
}
// 返回正確響應(data)
c.JSON(http.StatusOK, gin.H{
"status": 200,
"msg": data,
})
}

stringUtils.go: 封裝字符串操作方法

package go_manager_utils

// 拼接sql語句的value
// len是語句有幾個參數(shù)
func ValueStr(len int) (values string) {
// 拼接 values(?,?,...)
values = "("
for i := 0; i < len-1; i++ {
values += "?"
values += ","
}
values += "?"
values += ")"
return
}

// 拼接sql語句update的param
// params是參數(shù)名數(shù)組
func UptParamsStr(params []string) (paramStr string) {
// 拼接參數(shù)列表 xxx=?,xxx=?
paramStr = ""
for i := 0; i < len(params)-1; i++ {
paramStr += params[i]
paramStr += "=?,"
}
paramStr += params[len(params)-1]
paramStr += "=?"
return
}

// 拼接sql語句的param
// params是參數(shù)名數(shù)組
func ParamsStr(params []string) (paramStr string) {
// 拼接 表名(參數(shù)1,參數(shù)2,...)
paramStr = "("
for i := 0; i < len(params)-1; i++ {
paramStr += params[i]
paramStr += ","
}
paramStr += params[len(params)-1]
paramStr += ")"
return
}

運行go mod tidy處理go文件里的依賴

go_manager_web模塊編寫

main.go: 主要邏輯,創(chuàng)建web實例,注冊路由…

package go_manager_web

import (
"fmt"
"github.com/gin-gonic/gin"
db "go_manager_db"
utils "go_manager_utils"
"net/http"
)

// 定義路由組
// 組中組(嵌套路由組)
func DefineRouteGroup(fatherGroup *gin.RouterGroup, groupName string, r *gin.Engine) *gin.RouterGroup {
var group *gin.RouterGroup
if fatherGroup != nil {
// v1/groupName
group = fatherGroup.Group(groupName)
} else {
// /groupName
group = r.Group(groupName)
}
// 返回路由組
return group
}

// 存放 token (不同ip不同token)
var TokenMap = make(map[string]string, 10)

// 定時銷毀token
func timeDT() {
// 兩小時后銷毀
t := utils.NewMyTimer(2*60*60, func() error {
utils.DestoryTokenMap(TokenMap)
return nil
})
t.Start()
fmt.Println(TokenMap)
}

// 路由和處理函數(shù)放在不同文件好像會使中間件失效
func Login(c *gin.Context) {
user := db.MalUser{}
// 綁定json和結構體(接收json,數(shù)據(jù)放入結構體)
if err := c.BindJSON(&user); err != nil {
return
}
uname := user.Uname
upass := user.Upass
userModel, err := db.GetUserByName(uname, upass)
if err != nil || &userModel == nil {
fmt.Println(err)
c.JSON(500, gin.H{
"status": 500,
"msg": "登錄失敗",
})
return
}
token := utils.SignJWT("malred", uname, upass)
// 存入map
// fmt.Println(c.ClientIP(),c.RemoteIP())
TokenMap[c.ClientIP()] = token
fmt.Println(TokenMap)
c.JSON(http.StatusOK, gin.H{
"status": 200,
"msg": "登錄成功",
// 返回jwt令牌(密碼因為前端md5加密過,所以直接放入jwt)
"token": token,
})
go timeDT()
}

// 路由器
// 啟動默認的路由
var r = gin.Default()

// user路由組
var v1 *gin.RouterGroup

func Run() {
// 使用中間件
// 日志
r.Use(gin.Logger())
// 錯誤恢復
r.Use(gin.Recovery())
// 跨域
r.Use(Core())
// 阻止緩存響應
r.Use(NoCache())
// 安全設置
r.Use(Secure())
// 創(chuàng)建路由組v1
v1 = DefineRouteGroup(nil, "v1", r)
v1.POST("login", Login)
// 注冊user的路由
registerUser(Token(), Core())
// 注冊role的路由
registerRole(Token(), Core())
// 啟動webserver,監(jiān)聽本地127.0.0.1(默認)端口
r.Run(":10101")
}

moddilewares.go: 中間件

package go_manager_web

import (
utils "go_manager_utils"
"net/http"
"strconv"
"time"

"github.com/gin-gonic/gin"
)

//解決跨域問題
func Core() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Access-Control-Allow-Methods", "*")
c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")
c.Header("Access-Control-Max-Age", "3600")
c.Header("Access-Control-Allow-Credentials", "true")
//放行索引options
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
//處理請求
c.Next()
}
}

// 權限認證(驗證token)
func Token() gin.HandlerFunc {
return func(c *gin.Context) {
// for k, v := range c.Request.Header {
// fmt.Println(k, v)
// }
secret := c.Request.Header["Secret"] // 獲取前端傳來的secret
token := c.Request.Header["Token"]
if len(token) == 0 {
// 驗證不通過,不再調用后續(xù)的函數(shù)處理
c.Abort()
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "訪問未授權",
})
return
}
timeInt64 := strconv.FormatInt(time.Now().UnixNano()/1e6/1000/60, 10)
md5Str := utils.MD5(timeInt64 + TokenMap[c.ClientIP()])
// fmt.Println(TokenMap[c.ClientIP()], timeInt64)
// fmt.Println(timeInt64 + TokenMap[c.ClientIP()])
// fmt.Println(md5Str, secret[0])
if md5Str != secret[0] {
// 驗證不通過,不再調用后續(xù)的函數(shù)處理
c.Abort()
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "訪問未授權",
})
return
}
// 驗證jwt
// utils.ParseJWT(secret[0][8:11]+secret[0][19:22], token[0])
//處理請求
c.Next()
}
}

// 阻止緩存響應
func NoCache() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
ctx.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
ctx.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
ctx.Next()
}
}

// 響應 options 請求, 并退出
// func Options() gin.HandlerFunc {
// return func(ctx *gin.Context) {
// if ctx.Request.Method != "OPTIONS" {
// ctx.Next()
// } else {
// ctx.Header("Access-Control-Allow-Origin", "*")
// ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
// ctx.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
// ctx.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
// ctx.Header("Content-Type", "application/json")
// ctx.AbortWithStatus(200)
// }
// }
// }

// 安全設置
func Secure() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Header("Access-Control-Allow-Origin", "*")
ctx.Header("X-Frame-Options", "DENY")
ctx.Header("X-Content-Type-Options", "nosniff")
ctx.Header("X-XSS-Protection", "1; mode=block")
if ctx.Request.TLS != nil {
ctx.Header("Strict-Transport-Security", "max-age=31536000")
}

// Also consider adding Content-Security-Policy headers
// ctx.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
}

// todo 權限控制(token攜帶當前用戶的權限信息,過濾低于指定權限的請求)

role.go和user.go: 真的role和user表的web操作

role.go

package go_manager_web

import (
"fmt"
db "go_manager_db"
utils "go_manager_utils"
"strconv"
"github.com/gin-gonic/gin"
)

func GetAllRoleHandler(c *gin.Context) {
roles, err := db.GetAllRole()
// 通用響應
utils.R(c, err, "獲取角色列表失敗", roles)
}
func AddRoleHandler(c *gin.Context) {
// Role := c.PostForm("Role")
// fmt.Println(Role)
role := db.MalRole{}
//綁定json和結構體
if err := c.BindJSON(&role); err != nil {
return
}
Role := role.Role
err := db.Insert("mal_role", []string{"role"}, Role)
// 通用響應
utils.R(c, err, "添加角色失敗", "添加角色成功")
}
func DelRoleHandler(c *gin.Context) {
// 從url獲取參數(shù)
idStr := c.Query("rid")
// fmt.Println(idStr)
rid, err := strconv.ParseInt(idStr, 10, 64)
err = db.Delete("mal_role", rid)
// 通用響應
utils.R(c, err, "刪除角色失敗", "刪除角色成功")
}
func GetOneRoleHandler(c *gin.Context) {
// 從url獲取參數(shù)
idStr := c.Query("rid")
fmt.Println(idStr)
rid, _ := strconv.ParseInt(idStr, 10, 64)
one, err2 := db.GetRoleById(rid)
// 通用響應
utils.R(c, err2, "查詢角色失敗", one)
}
func UptRoleHandler(c *gin.Context) {
role := db.MalRole{}
//綁定json和結構體
if err := c.BindJSON(&role); err != nil {
return
}
rid := role.Id
roleName := role.Role
fmt.Println(role)
err := db.UptRoleById(rid, roleName)
// 通用響應
utils.R(c, err, "修改角色失敗", "修改角色成功")
}
func registerRole(middles ...gin.HandlerFunc) {
// 創(chuàng)建路由組v1/user
role := DefineRouteGroup(v1, "role", r)
// 添加中間件
if middles != nil {
role.Use(middles...)
}
// 獲取所有
role.GET("all", GetAllRoleHandler)
// 添加
role.POST("add", AddRoleHandler)
// 刪除
role.DELETE("del", DelRoleHandler)
// 根據(jù)id獲取
role.GET("id", GetOneRoleHandler)
// 根據(jù)id修改
role.PUT("upt", UptRoleHandler)
}

user.go

package go_manager_web

import (
"fmt"
db "go_manager_db"
utils "go_manager_utils"
"strconv"

"github.com/gin-gonic/gin"
)

func GetAllUserHandler(c *gin.Context) {
users, err := db.GetAllUser()
// 通用響應
utils.R(c, err, "查詢角色失敗", users)
}
func AddUserHandler(c *gin.Context) {
// uname := c.PostForm("uname")
// upass := c.PostForm("upass")
// idStr := c.PostForm("rid")
user := db.MalUser{}
//綁定json和結構體
if err := c.BindJSON(&user); err != nil {
return
}
uname := user.Uname
upass := user.Upass
rid := user.Rid
fmt.Println(user)
// rid, err := strconv.ParseInt(idStr, 10, 64)
err := db.Insert("mal_user", []string{"uname", "upass", "rid"}, uname, upass, rid)
// 通用響應
utils.R(c, err, "添加角色失敗", "添加角色成功")
}
func DelUserHandler(c *gin.Context) {
// 從url獲取參數(shù)
idStr := c.Query("uid")
// fmt.Println(idStr)
uid, err := strconv.ParseInt(idStr, 10, 64)
err = db.Delete("mal_user", uid)
// 通用響應
utils.R(c, err, "刪除角色失敗", "刪除角色成功")
}
func GetOneUserHandler(c *gin.Context) {
// 從url獲取參數(shù)
idStr := c.Query("uid")
fmt.Println(idStr)
uid, _ := strconv.ParseInt(idStr, 10, 64)
one, err2 := db.GetUserById(uid)
// 通用響應
utils.R(c, err2, "查詢角色失敗", one)
}
func UptUserHandler(c *gin.Context) {
// 從url獲取參數(shù)
// uid := c.PostForm("uid")
// uname := c.PostForm("uname")
// upass := c.PostForm("upass")
// ridStr := c.PostForm("rid")
user := db.MalUser{}
//綁定json和結構體
if err := c.BindJSON(&user); err != nil {
return
}
uname := user.Uname
upass := user.Upass
rid := user.Rid
uid := user.Id
// fmt.Println(idStr, UserName)
// rid, _ := strconv.ParseInt(ridStr, 10, 64)
err := db.UptUserById(strconv.FormatInt(uid, 10), []string{"uname", "upass", "rid"}, uname, upass, rid)
// 通用響應
utils.R(c, err, "修改角色失敗", "修改角色成功")
}
func registerUser(middles ...gin.HandlerFunc) {
// 創(chuàng)建路由組v1/user
user := DefineRouteGroup(v1, "user", r)
// 添加中間件
if middles != nil {
user.Use(middles...)
}
user.GET("all", GetAllUserHandler)
// 添加
user.POST("add", AddUserHandler)
// 刪除
user.DELETE("del", DelUserHandler)
// 根據(jù)id獲取
user.GET("id", GetOneUserHandler)
// 根據(jù)id修改
user.PUT("upt", UptUserHandler)
}

運行go mod tidy

忘了,要引用項目里的其他包

replace go_manager_utils => ../utils
replace go_manager_db => ../db

go mod tidy

編寫根目錄的go_manager模塊

main.go

package main

import (
db "go_manager_db"
web "go_manager_web"
)

func main() {
// 初始化數(shù)據(jù)庫
db.InitDB()
// 開啟服務
web.Run()
}

go.mod

module go_manager

go 1.18

replace go_manager_web => ./web

replace go_manager_db => ./db

replace go_manager_utils => ./utils

go mod tidy

測試(可以用go build打包)

完整目錄結構

go run main.go

因為后端存的密碼是md5加密過的,所以前端也要傳md5加密的密碼,二者相同才能通過

安全: 我的安全不咋地,加密的方法是前端根據(jù)當前時間戳(轉為分鐘,防止因為前后端延遲而導致時間戳不一致)+登錄后從后端獲取的token來md5,每次請求都會驗證這個md5(后端也加密(時間戳/60+token)然后對比),這個就不測試了

代碼倉庫:

malred/go-managergitee.com/malguy/go-manager

配套前端管理系統(tǒng)(react18):

GitHub – malred/base-manager: react18通用管理系統(tǒng)模板github.com/malred/base-manager

本文章轉載微信公眾號@米玖二次元

上一篇:

使用OkHttp緩存API調用提高Android應用性能

下一篇:

python實戰(zhàn):通過百度地圖API批量獲取經緯度
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數(shù)據(jù)驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費