golang gorm time json

存储到数据库为时间格式,json序列化与反序列化为时间戳

base.go
import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"github.com/jinzhu/gorm"
	"strconv"
	"time"
)

type GormBase struct {
	CreatedAt JSONTime   `json:"created_at" gorm:"type:timestamp"`
	UpdateAt  JSONTime   `json:"updated_at" gorm:"type:timestamp"`
	DeletedAt *time.Time `json:"-" gorm:"type:timestamp"`
}

type JSONTime struct {
	Time time.Time
}

/*
*@ date 2019-04-19
*@ author majianyu
*@ description only support second
 */
func (this JSONTime) MarshalJSON() ([]byte, error) {
	return json.Marshal(this.Time.Unix())
}

/*
*@ date 2019-04-19
*@ author majianyu
*@ description support second and millisecond parse
 */
func (this *JSONTime) UnmarshalJSON(b []byte) error {
	str := string(b)
	ts, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return err
	}
	if len(str) == 10 {
		// second
		this.Time = time.Unix(ts, 0)
	} else if len(str) == 13 {
		// millisecond
		this.Time = time.Unix(0, ts*1e6)
	} else {
		return fmt.Errorf("can not convert %s to time", str)
	}
	return nil
}
func (this JSONTime) Value() (driver.Value, error) {
	var zeroTime time.Time
	if this.Time.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return this.Time, nil
}
func (this *JSONTime) Scan(v interface{}) error {
	value, ok := v.(time.Time)
	if ok {
		*this = JSONTime{Time: value}
		return nil
	}
	return fmt.Errorf("can not convert %v to timestamp", v)

}

func (g *GormBase) BeforeUpdate(scope *gorm.Scope) error {
	scope.SetColumn("UpdateAt", time.Now())
	return nil
}

func (g *GormBase) BeforeCreate(scope *gorm.Scope) error {
	if g.UpdateAt.Time.Unix() <= 0 {
		scope.SetColumn("UpdateAt", time.Now())
	}

	scope.SetColumn("CreatedAt", time.Now())
	return nil
}
Scanner.go

type Report struct {
	tableName struct{}   `sql:"toptutor.report"`
	CourseID  string     `json:"course_id" gorm:"type:uuid"`
	OrderID   string     `json:"order_id" gorm:"type:uuid"`
	Questions []Question `json:"questions" gorm:"type:jsonb"`
	gormstruct.GormBase
}
type Question struct {
	ID         int    `json:"id"`
	Question   string `json:"question"`
	Answer     string `json:"answer"`
	ZhQuestion string `json:"zh_question"`
	ZhAnswer   string `json:"zh_answer"`
}

func (this *Question) Scan(v interface{}) error {
	value, ok := v.([]byte)
	if ok {
		q := Question{}
		err := json.Unmarshal(value, &q)
		if err != nil {
			return fmt.Errorf("can not convert %v to Question", v)
		}
		*this = q
		return nil
	}
	return fmt.Errorf("can not convert %v to Question", v)
}
func (this Question) Value() (driver.Value, error) {
	bytes, err := json.Marshal(this)
	if err != nil {
		return nil, err
	}
	return bytes, nil
}

golang 获取应用功能

给定公钥的App对象的函数

get.go
package app

import (
	"encoding/hex"
	"fmt"

	"github.com/omnisyle/goliauth"
)

func GetApp(publicKey, dbURL string) *App {
	app := &App{
		PublicKey: publicKey,
		dbURL:     dbURL,
	}

	dbApp := app.Get()

	secretBytes, err := hex.DecodeString(dbApp.EncryptedSecret)

	if err != nil {
		panic(err)
	}

	decryptedKey, err := goliauth.Decrypt(secretBytes)
	if err != nil {
		panic(err)
	}

	dbApp.SecretKey = fmt.Sprintf(
		"%x",
		decryptedKey,
	)

	return dbApp
}

golang App创建服务

用于在数据库中创建App对象的服务

create.go
package app

import (
	"fmt"

	"github.com/omnisyle/goliauth"
)

func CreateApp(name, dbURL string) *App {
	publicKey := goliauth.NewRandomKey()
	secretKey := goliauth.NewRandomKey()
	encryptedSecret, err := goliauth.Encrypt(secretKey)

	if err != nil {
		panic(err)
	}

	app := &App{
		dbURL:           dbURL,
		Name:            name,
		PublicKey:       fmt.Sprintf("%x", publicKey),
		SecretKey:       fmt.Sprintf("%x", secretKey),
		EncryptedSecret: fmt.Sprintf("%x", encryptedSecret),
	}

	return app.Create()
}

golang App命令处理程序

app命令的处理程序,用于创建和获取应用程序

handlers.go
func createApp(cmd *cobra.Command, args []string) {
	appName := args[0]
	keyPair := app.CreateApp(appName, db)
	printResult(keyPair)
}

func getApp(cmd *cobra.Command, args []string) {
	publicKey := args[0]
	keyPair := app.GetApp(publicKey, db)
	printResult(keyPair)
}

func printResult(keyPair *app.App) {
	fmt.Println("Name: ", keyPair.Name)
	fmt.Println("Public Key: ", keyPair.PublicKey)
	fmt.Println("Secret Key: ", keyPair.SecretKey)
}

golang 用于生成凭证密钥对的App命令

用于生成凭证密钥对的App命令,获取给定公钥的凭证并删除凭证

app.go
package cmd

import (
	"fmt"

	"github.com/omnisyle/goliauth/goliauth/internal/app"
	"github.com/spf13/cobra"
)

var db string

func appCmd() *cobra.Command {
	command := &cobra.Command{
		Use:   "app",
		Short: "Create a public/secret key pair as an app with a name",
	}

	createCmd := &cobra.Command{
		Use:   "create [name]",
		Short: "Create an app with a public/secret key pair",
		Args:  cobra.MinimumNArgs(1),
		Run:   createApp,
	}

	getCmd := &cobra.Command{
		Use:   "get [public key]",
		Short: "Get an app using public key",
		Args:  cobra.MinimumNArgs(1),
		Run:   getApp,
	}

	command.PersistentFlags().StringVarP(
		&db,
		"db",
		"d",
		"",
		"Url to connect to the database",
	)

	command.MarkFlagRequired("db")

	command.AddCommand(
		createCmd,
		getCmd,
	)

	return command
}

golang 眼镜蛇的关键命令

用于生成随机32位密钥并打印到屏幕的命令

key.go
package cmd

import (
	"fmt"

	"github.com/omnisyle/goliauth"
	"github.com/spf13/cobra"
)

func keyCmd() *cobra.Command {
	command := &cobra.Command{
		Use:   "key",
		Short: "Generate a 32 bit random key",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("%x\n", goliauth.NewRandomKey())
		},
	}

	return command
}

golang Root命令将是所有命令的基础

root.go
package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "goliauth",
	Short: "A mini CLI to generate random key and public/secret key pairs for API",
}

// Execute chains all the commands together under the root command which is goliauth
// Usage: `goliauth [command]`
func Execute() {
	rootCmd.AddCommand(
		keyCmd(),
		appCmd(),
	)
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

golang 对于在n秒后中断的循环

time_1.go
for start := time.Now(); time.Since(start) < time.Second; {
    foo();
}
time_2.go
loop:
    for timeout := time.After(time.Second); ; {
        select {
        case <-timeout:
            fmt.Println("x")
            break loop
        default:
        }
        foo();
    }
time_3.go
for stay, timeout := true, time.After(time.Second); stay; {
    foo()
    select {
    case <-timeout:
        stay = false
    default:
	}
}

golang 取引所APIを使用せずに公开APIを使用する

qucoin.go
/*
https://developers.quoine.com/#authentication
*/

package mtquoine

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const ()

// Quoine APIクラス相当
type Quoine struct {
	name    string
	baseuri string
	timeout int // 秒単位で指定
}

// NewQuoine インスタンスを生成して返す
func NewQuoine() (q *Quoine) {
	q = new(Quoine)
	q.name = "quoine"
	q.baseuri = "https://api.quoine.com"
	q.timeout = 1000 * 10 // 10秒

	// var splited = strings.Split(currencyPair, "_")
	// q.TargetCurrency = splited[0]
	// q.BaseCurrency = splited[1]
	return
}

// GetExchangeName 取引所名を返す
// main関数のInterfaceで使用される
func (q *Quoine) GetExchangeName() (name string) {
	return q.name
}

//=============================================================================
// Public API
//=============================================================================

// OrderBook 板情報を返す
// GET /products/:id/price_levels
func (q *Quoine) OrderBook(params url.Values) (result *OrderBookResult, err error) {
	// pp.Println("--------", params["pair"][0])
	productID := q.getCurrencyPair(params["pair"][0])
	path := fmt.Sprintf("/products/%d/price_levels", productID)
	// pp.Println("hogehoge", path)
	req, err := q._publicRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{Timeout: time.Duration(q.timeout) * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("板情報取得エラー:", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.Fatal(resp)
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return
	}

	ob := quoineOrderBook{}
	err = json.Unmarshal([]byte(bytes), &ob)
	if err != nil {
		log.Fatalf("unmrshal failed: %v", err)
		panic(err)
	}

	result = new(OrderBookResult)
	result.Exchange = q.name
	result.AskPrice, err = ob.SellPriceLevels[0][0].Float64()
	result.AskSize, err = ob.SellPriceLevels[0][1].Float64()
	result.BidPrice, err = ob.BuyPriceLevels[0][0].Float64()
	result.BidSize, err = ob.BuyPriceLevels[0][1].Float64()
	result.Spread = result.AskPrice - result.BidPrice
	result.MidPrice = (result.AskPrice + result.BidPrice) / 2.0
	return
}

// Public API用のリクエスト
func (q *Quoine) _publicRequest(method string, path string, values url.Values) (*http.Request, error) {
	encodedParams := values.Encode()
	url := q.baseuri + path
	// pp.Println("URL:", url)
	req, err := http.NewRequest("GET", url, strings.NewReader(encodedParams))
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Quoine-API-Version", "2")
	req.Header.Add("Content-Type", "application/json")
	return req, nil
}

// currency_pair_code
// func getProductID(currencyPair string) (id int) {
func (q *Quoine) getCurrencyPair(currencyPair string) (id int) {
	switch {
	case currencyPair == "BTC_JPY":
		id = 5
	case currencyPair == "ETH_JPY":
		id = 29
	case currencyPair == "BCH_JPY":
		id = 41
	}
	return
}

//=============================================================================
// Quoine
//=============================================================================

// QuoineOrderBook 板情報
type quoineOrderBook struct {
	BuyPriceLevels  [][]json.Number `json:"buy_price_levels,[][]string"`
	SellPriceLevels [][]json.Number `json:"sell_price_levels,[][]string"`
	// BuyPriceLevels  [][]float64 `json:"buy_price_levels,[][]string"`
	// SellPriceLevels [][]float64 `json:"sell_price_levels,[][]string"`
}

// QuoineErrorResult Quoineエラー結果
// {"errors":{"quantity":["less_than_order_size"]}}
type QuoineErrorResult struct {
	Errors struct {
		Quantity []string `json:"quantity"`
	} `json:"errors"`
}

golang golang create / get captchas

golang create / get captchas

main.go
package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/dchest/captcha"
	"github.com/go-redis/redis"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

var redisStore *RedisStore
var RedisClient *redis.Client

type RedisStore struct {
	Expire time.Duration
}

func (rs *RedisStore) Set(id string, digits []byte) {
	RedisClient.Set(id, string(digits), rs.Expire)
}

func (rs *RedisStore) Get(id string, clear bool) (digits []byte) {
	bs, err := RedisClient.Get(id).Bytes()
	if err != nil {
		log.Println(err)
	}

	return bs
}

type genCaptchasResp struct {
	URL string `json:"url"`
	ID  string `json:"id"`
}

func main() {
	if err := NewRedisClient(); err != nil {
		log.Println(err)
		return
	}

	redisStore = &RedisStore{Expire: 2 * time.Minute}
	captcha.SetCustomStore(redisStore)

	s := echo.New()
	s.Use(middleware.CORS())

	s.POST("/api/captchas", genCaptchas)
	s.GET("/captchas/:captcha_id", getCaptcha)

	if err := s.Start(":8899"); err != nil {
		log.Println(err)
		return
	}
}

func NewRedisClient() error {
	readTimeout := 2 * time.Second
	writeTimeout := 2 * time.Second

	RedisClient = redis.NewClient(&redis.Options{
		Network:      "tcp",
		Addr:         "localhost:6379",
		DB:           0,
		ReadTimeout:  readTimeout,
		WriteTimeout: writeTimeout,
	})

	_, err := RedisClient.Ping().Result()
	return err
}

func genCaptchas(ctx echo.Context) (err error) {
	id := captcha.NewLen(6)
	return ctx.JSON(http.StatusOK, &genCaptchasResp{
		URL: fmt.Sprintf("/captchas/%s", id),
		ID:  id,
	})
}

func getCaptcha(ctx echo.Context) (err error) {
	id := ctx.Param("captcha_id")
	w := ctx.Response()
	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
	w.Header().Set("Pragma", "no-cache")
	w.Header().Set("Expires", "0")
	w.Header().Set("Content-Type", "image/png")

	var content bytes.Buffer
	if err := captcha.WriteImage(&content, id, captcha.StdWidth, captcha.StdHeight); err != nil {
		return ctx.String(http.StatusBadRequest, err.Error())
	}

	r := ctx.Request()
	http.ServeContent(w, r, id, time.Time{}, bytes.NewReader(content.Bytes()))
	return nil
}