first commit
This commit is contained in:
100
pkg/api/api.go
Normal file
100
pkg/api/api.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
db, err = sql.Open("postgres", os.Getenv("POSTGRES"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Successfully connected to postgres database")
|
||||
|
||||
// install tables
|
||||
if len(os.Args) >= 2 {
|
||||
if os.Args[1] == "install" {
|
||||
install()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// drop tables
|
||||
if os.Args[1] == "drop" {
|
||||
drop()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// reinstall
|
||||
if os.Args[1] == "reinstall" {
|
||||
drop()
|
||||
install()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// fake data
|
||||
if os.Args[1] == "fake" {
|
||||
fakeData()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// unknown Args
|
||||
log.Println("Unknown args", os.Args)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func NewAPI() *gin.Engine {
|
||||
router := gin.Default()
|
||||
|
||||
// session
|
||||
store := cookie.NewStore([]byte("Miku saves the world!"))
|
||||
router.Use(sessions.Sessions("ais", store))
|
||||
|
||||
// entry point html
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.File("./web/public/index.html")
|
||||
})
|
||||
|
||||
group := router.Group("/api")
|
||||
|
||||
// json error middleware
|
||||
group.Use(func(c *gin.Context) {
|
||||
c.Next()
|
||||
if len(c.Errors) > 0 {
|
||||
c.JSON(-1, gin.H{
|
||||
"errors": c.Errors.Errors(),
|
||||
"note": "gin api error handler",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
group.POST("/login", handelLogin)
|
||||
group.GET("/login", handelGetLoginSession)
|
||||
group.POST("/register", handelRegister)
|
||||
group.POST("/logout", handelLogout)
|
||||
|
||||
customer := group.Group("/customer")
|
||||
customer.GET("/market", handleGetMarket)
|
||||
customer.GET("/market/distance", handleGetMarketByDistance)
|
||||
customer.GET("/market/:marketid", handleGetGoods)
|
||||
customer.POST("/market/:marketid/:goodsid", handleBuy)
|
||||
customer.GET("/purchase", handleGetPurchaseHistory)
|
||||
customer.DELETE("/purchase/:purchaseid", handleDeletePurchaseHistory)
|
||||
customer.GET("/purchase/report", handleCustomerReport)
|
||||
|
||||
supplier := group.Group("/supplier")
|
||||
supplier.GET("/goods", handleGetGodsBySupplier)
|
||||
supplier.POST("/goods", handleCreateGoods)
|
||||
|
||||
return router
|
||||
}
|
||||
38
pkg/api/drop.go
Normal file
38
pkg/api/drop.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var dropSQLString = `
|
||||
drop table purchase;
|
||||
|
||||
drop table tags_on_goods;
|
||||
|
||||
drop table goods;
|
||||
|
||||
drop table tag;
|
||||
|
||||
drop table market;
|
||||
|
||||
drop table users;
|
||||
`
|
||||
|
||||
func drop() {
|
||||
sqls := strings.Split(dropSQLString, "\n\n")
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, sql := range sqls {
|
||||
log.Println("Dropting table with SQL", sql)
|
||||
_, err = tx.Exec(sql)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
log.Println("Successfully drop all tables")
|
||||
}
|
||||
22
pkg/api/encrypt.go
Normal file
22
pkg/api/encrypt.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func EncryptPassword(password string) string {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost)
|
||||
if err != nil {
|
||||
log.Println("Warning: Failed to hash password, fallback to plaintext password")
|
||||
return password
|
||||
}
|
||||
|
||||
return string(hash)
|
||||
}
|
||||
|
||||
func ComparePassword(hashedPassword string, plainTextPassword string) error {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(plainTextPassword))
|
||||
return err
|
||||
}
|
||||
88
pkg/api/fake.go
Normal file
88
pkg/api/fake.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
)
|
||||
|
||||
func fakeData() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fakeUsers(tx)
|
||||
fakeSupplier(tx)
|
||||
fakeMarket(tx)
|
||||
tx.Commit()
|
||||
}
|
||||
|
||||
func fakeUsers(tx *sql.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
username := gofakeit.Username()
|
||||
password := gofakeit.Password(true, true, true, true, true, 1)
|
||||
encryptedPassword := EncryptPassword(password)
|
||||
_, err := tx.Exec(`insert into users (username, password) values ($1, $2)`,
|
||||
username, encryptedPassword)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("fake users", username, password)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeSupplier(tx *sql.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
username := gofakeit.Username()
|
||||
password := EncryptPassword(gofakeit.Password(true, true, true, true, true, 1))
|
||||
_, err := tx.Exec(`insert into users (username, password, role) values ($1, $2, 2)`,
|
||||
username, password)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("fake supplier", username, password)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeMarket(tx *sql.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
addr := gofakeit.Address()
|
||||
name := addr.State
|
||||
description := addr.Address
|
||||
location := fmt.Sprintf("(%f, %f)", addr.Latitude, addr.Longitude)
|
||||
|
||||
row := tx.QueryRow(`insert into market (name, description, location) values ($1, $2, $3) returning id`,
|
||||
name, description, location)
|
||||
|
||||
var mid int64
|
||||
err := row.Scan(&mid)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("fake market", name, description, location)
|
||||
|
||||
fakeProduct(tx, int64(mid))
|
||||
}
|
||||
}
|
||||
|
||||
func fakeProduct(tx *sql.Tx, mid int64) {
|
||||
for i := 0; i < 10; i++ {
|
||||
name := gofakeit.BeerName()
|
||||
description := gofakeit.BeerStyle()
|
||||
quantity := rand.Intn(390)
|
||||
price := gofakeit.Price(39, 390)
|
||||
_, err := tx.Exec(`insert into goods (name, description, supplier_id, market_id, quantity, price) values ($1, $2, $3, $4, $5, $6)`,
|
||||
name, description, 1, mid, quantity, price)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("fake goods", name, description, quantity, price)
|
||||
}
|
||||
}
|
||||
1
pkg/api/handle_buy.go
Normal file
1
pkg/api/handle_buy.go
Normal file
@@ -0,0 +1 @@
|
||||
package api
|
||||
169
pkg/api/handle_goods.go
Normal file
169
pkg/api/handle_goods.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Goods struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
SupplierId int64 `json:"supplier_id"`
|
||||
MarketId int64 `json:"market_id"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
Price string `json:"price"`
|
||||
}
|
||||
|
||||
func handleGetGoods(c *gin.Context) {
|
||||
marketId, err := strconv.ParseInt(c.Param("marketid"), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
log.Println("select", marketId)
|
||||
rows, err := db.Query(`select id, name, description, quantity, price from goods where market_id = $1 order by name, description`, marketId)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
ret := make([]Goods, 0)
|
||||
var g Goods
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&g.Id, &g.Name, &g.Description, &g.Quantity, &g.Price)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
ret = append(ret, g)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"goods": ret,
|
||||
})
|
||||
}
|
||||
|
||||
func handleBuy(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid").(int64)
|
||||
goodsId := c.Param("goodsid")
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
_, err = tx.Exec(`insert into purchase (user_id, goods_id) values ($1, $2)`, userId, goodsId)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
_, err = tx.Exec(`update goods set quantity = quantity - 1 where id = $1`, goodsId)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
|
||||
func handleGetPurchaseHistory(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid").(int64)
|
||||
|
||||
rows, err := db.Query(`
|
||||
select p.id, p.quantity, p.purchased_time, g.name, g.price
|
||||
from purchase p
|
||||
join goods g on p.goods_id = g.id
|
||||
where p.user_id = $1
|
||||
order by p.purchased_time desc
|
||||
`, userId)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
type Ret struct {
|
||||
Id int64 `json:"id"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
PurchasedTime time.Time `json:"purchased_time"`
|
||||
Goods Goods `json:"goods"`
|
||||
}
|
||||
|
||||
ret := make([]Ret, 0)
|
||||
for rows.Next() {
|
||||
var i Ret
|
||||
err = rows.Scan(&i.Id, &i.Quantity, &i.PurchasedTime, &i.Goods.Name, &i.Goods.Price)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
ret = append(ret, i)
|
||||
}
|
||||
|
||||
c.JSON(200, ret)
|
||||
}
|
||||
|
||||
func handleDeletePurchaseHistory(c *gin.Context) {
|
||||
id, err := strconv.ParseInt(c.Param("purchaseid"), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`delete from purchase where id = $1`, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
|
||||
func handleGetGodsBySupplier(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid").(int64)
|
||||
rows, err := db.Query(`select id, name, description, quantity, price from goods where supplier_id = $1 order by name, description`, userId)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
ret := make([]Goods, 0)
|
||||
for rows.Next() {
|
||||
var g Goods
|
||||
err = rows.Scan(&g.Id, &g.Name, &g.Description, &g.Quantity, &g.Price)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
ret = append(ret, g)
|
||||
}
|
||||
|
||||
c.JSON(200, ret)
|
||||
}
|
||||
|
||||
func handleCreateGoods(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid").(int64)
|
||||
g := &Goods{}
|
||||
err := c.BindJSON(g)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
g.SupplierId = userId
|
||||
_, err = db.Exec(`insert into goods (name, description, supplier_id, market_id, quantity, price) values ($1, $2, $3, $4, $5, $6)`,
|
||||
g.Name, g.Description, g.SupplierId, g.MarketId, g.Quantity,g.Price)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
60
pkg/api/handle_market.go
Normal file
60
pkg/api/handle_market.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package api
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Market struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
func handleGetMarket(c *gin.Context) {
|
||||
markets := make([]Market, 0)
|
||||
|
||||
rows, err := db.Query(`select id, name, description, location from market`)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var market Market
|
||||
err = rows.Scan(&market.Id, &market.Name, &market.Description, &market.Location)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
markets = append(markets, market)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"markets": markets,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetMarketByDistance(c *gin.Context) {
|
||||
markets := make([]Market, 0)
|
||||
point := c.Query("point")
|
||||
|
||||
rows, err := db.Query(`select id, name, description, location, location<->$1 as dist from market order by dist`, point)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
var x string
|
||||
for rows.Next() {
|
||||
var market Market
|
||||
err = rows.Scan(&market.Id, &market.Name, &market.Description, &market.Location, &x)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
markets = append(markets, market)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"markets": markets,
|
||||
})
|
||||
}
|
||||
28
pkg/api/handle_report.go
Normal file
28
pkg/api/handle_report.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func handleCustomerReport(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid").(int64)
|
||||
|
||||
row := db.QueryRow(`select sum(g.price)
|
||||
from purchase p
|
||||
join goods g on p.goods_id = g.id
|
||||
where p.user_id = $1`,
|
||||
userId)
|
||||
|
||||
var sumPrice string
|
||||
err := row.Scan(&sumPrice)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"sum": sumPrice,
|
||||
})
|
||||
}
|
||||
100
pkg/api/handle_user.go
Normal file
100
pkg/api/handle_user.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Balance string `json:"balance"`
|
||||
Location string `json:"location"`
|
||||
Role int64 `json:"role"`
|
||||
RegisterTime time.Time `json:"register_time"`
|
||||
}
|
||||
|
||||
var SESSION_NAME = "ais"
|
||||
|
||||
func handelLogout(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Clear()
|
||||
session.Save()
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
|
||||
func handelGetLoginSession(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userId := session.Get("userid")
|
||||
if userId == nil {
|
||||
c.JSON(200, gin.H{})
|
||||
return
|
||||
}
|
||||
user := &User{}
|
||||
row := db.QueryRow(`select id, username, balance, location, role from users where id=$1`, userId)
|
||||
err := row.Scan(&user.Id, &user.Username, &user.Balance, &user.Location, &user.Role)
|
||||
if err != nil {
|
||||
c.AbortWithError(403, err)
|
||||
return
|
||||
}
|
||||
c.JSON(200, user)
|
||||
}
|
||||
|
||||
func handelLogin(c *gin.Context) {
|
||||
user := &User{}
|
||||
err := c.BindJSON(user)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
var encryptedPassowrd string
|
||||
row := db.QueryRow(`select id, username, balance, location, role, password from users where username=$1`,
|
||||
user.Username)
|
||||
err = row.Scan(&user.Id, &user.Username, &user.Balance, &user.Location, &user.Role, &encryptedPassowrd)
|
||||
if err != nil {
|
||||
c.AbortWithError(403, err)
|
||||
return
|
||||
}
|
||||
|
||||
// validate password
|
||||
err = ComparePassword(encryptedPassowrd, user.Password)
|
||||
if err != nil {
|
||||
c.AbortWithError(403, err)
|
||||
return
|
||||
}
|
||||
|
||||
// set session
|
||||
session := sessions.Default(c)
|
||||
session.Set("userid", user.Id)
|
||||
session.Save()
|
||||
|
||||
c.JSON(200, user)
|
||||
|
||||
}
|
||||
|
||||
func handelRegister(c *gin.Context) {
|
||||
user := &User{}
|
||||
err := c.BindJSON(user)
|
||||
if err != nil {
|
||||
c.AbortWithError(401, err)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedPassowrd := EncryptPassword(user.Password)
|
||||
|
||||
ret := db.QueryRow(`insert into users(username, password) values ($1, $2) returning id`,
|
||||
user.Username, encryptedPassowrd)
|
||||
|
||||
err = ret.Scan(&user.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(401, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{})
|
||||
|
||||
}
|
||||
75
pkg/api/install.go
Normal file
75
pkg/api/install.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var initSQLString string = `create table users(
|
||||
id serial primary key,
|
||||
username varchar(30) not null unique,
|
||||
password varchar(64) not null,
|
||||
balance money not null default 0,
|
||||
role integer not null default 1,
|
||||
location point not null default '(0, 0)',
|
||||
registerd_time timestamp not null default now()
|
||||
);
|
||||
|
||||
create table market (
|
||||
id serial primary key,
|
||||
name varchar(100) not null,
|
||||
description text not null,
|
||||
location point not null
|
||||
)
|
||||
|
||||
create table tag (
|
||||
id serial primary key,
|
||||
name varchar(30) not null
|
||||
);
|
||||
|
||||
create table goods (
|
||||
id serial primary key,
|
||||
name varchar(100) not null,
|
||||
description text not null,
|
||||
create_time timestamp not null default now(),
|
||||
supplier_id integer not null references users(id),
|
||||
market_id integer not null references market(id),
|
||||
quantity numeric not null check (quantity >= 0),
|
||||
price money not null,
|
||||
data jsonb
|
||||
);
|
||||
|
||||
create table tags_on_goods(
|
||||
tag_id integer not null references tag(id),
|
||||
goods_id integer not null references goods(id),
|
||||
primary key (tag_id, goods_id)
|
||||
);
|
||||
|
||||
create table purchase (
|
||||
id serial primary key,
|
||||
user_id integer not null references users(id),
|
||||
goods_id integer not null references goods(id),
|
||||
quantity numeric not null default 1,
|
||||
purchased_time timestamp not null default now()
|
||||
);
|
||||
|
||||
insert into users (username, password) values ('a', 'a');
|
||||
`
|
||||
|
||||
func install() {
|
||||
sqls := strings.Split(initSQLString, "\n\n")
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, sql := range sqls {
|
||||
log.Println("Installing table with SQL", sql)
|
||||
_, err = tx.Exec(sql)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
log.Println("Successfully install all tables")
|
||||
}
|
||||
Reference in New Issue
Block a user